diff --git a/BUILD.gn b/BUILD.gn
index 7eecaedb..c6b6735 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -675,12 +675,6 @@
       "//third_party/icu/fuzzers",
       "//third_party/qcms:fuzzers",
     ]
-
-    # TODO(miu): Remove this dependency once the build configuration in
-    # chrome/browser/BUILD.gn is migrated to chrome/browser/media/BUILD.gn.
-    # This dependency here only exists to allow GN to discover the
-    # fuzzer_test target there.
-    deps += [ "//chrome/browser/media:cast_remoting_connector_fuzzer" ]
   }
 
   if (enable_nacl) {
diff --git a/DEPS b/DEPS
index 4621d3d..60b1c085 100644
--- a/DEPS
+++ b/DEPS
@@ -40,7 +40,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '6b65b98996ebc0a511aa46042607a291e6836bed',
+  'skia_revision': 'ec045b431211082563a4352ccf2c9d5b3765048e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -64,7 +64,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '2b44441b1b01a809b6f14d44d64db9a52808cba9',
+  'pdfium_revision': 'f6f68c75ce54a5865fb19dcb075e7734f1639663',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling openmax_dl
   # and whatever else without interference from each other.
@@ -96,7 +96,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '3e38417eedda889b4aa978a777b29de401f85b74',
+  'catapult_revision': '1e3b1949cdcde84079ef3f0fa3f31610e2d1687d',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -262,7 +262,7 @@
     Var('chromium_git') + '/external/github.com/google/pywebsocket.git' + '@' + '2d7b73c3acbd0f41dcab487ae5c97c6feae06ce2',
 
   'src/media/cdm/api':
-    Var('chromium_git') + '/chromium/cdm.git' + '@' + '46eebfa522b06c1f0b52b4233caa56793badf112',
+    Var('chromium_git') + '/chromium/cdm.git' + '@' + '6e4c388c0117fe408b66fbede91081fb1018c5fe',
 
   'src/third_party/mesa/src':
     Var('chromium_git') + '/chromium/deps/mesa.git' + '@' + 'ef811c6bd4de74e13e7035ca882cc77f85793fef',
diff --git a/android_webview/browser/hardware_renderer.cc b/android_webview/browser/hardware_renderer.cc
index 4515045f4..4e3c633 100644
--- a/android_webview/browser/hardware_renderer.cc
+++ b/android_webview/browser/hardware_renderer.cc
@@ -16,8 +16,8 @@
 #include "base/trace_event/trace_event.h"
 #include "cc/output/compositor_frame.h"
 #include "cc/surfaces/compositor_frame_sink_support.h"
+#include "cc/surfaces/frame_sink_manager.h"
 #include "cc/surfaces/local_surface_id_allocator.h"
-#include "cc/surfaces/surface_manager.h"
 #include "ui/gfx/transform.h"
 #include "ui/gl/gl_bindings.h"
 
@@ -33,7 +33,7 @@
       last_committed_layer_tree_frame_sink_id_(0u),
       last_submitted_layer_tree_frame_sink_id_(0u) {
   DCHECK(last_egl_context_);
-  surfaces_->GetSurfaceManager()->RegisterFrameSinkId(frame_sink_id_);
+  surfaces_->GetFrameSinkManager()->RegisterFrameSinkId(frame_sink_id_);
   CreateNewCompositorFrameSinkSupport();
 }
 
@@ -43,7 +43,7 @@
   if (child_id_.is_valid())
     DestroySurface();
   support_.reset();
-  surfaces_->GetSurfaceManager()->InvalidateFrameSinkId(frame_sink_id_);
+  surfaces_->GetFrameSinkManager()->InvalidateFrameSinkId(frame_sink_id_);
 
   // Reset draw constraints.
   render_thread_manager_->PostExternalDrawConstraintsToChildCompositorOnRT(
@@ -263,7 +263,7 @@
   constexpr bool needs_sync_points = true;
   support_.reset();
   support_ = cc::CompositorFrameSinkSupport::Create(
-      this, surfaces_->GetSurfaceManager(), frame_sink_id_, is_root,
+      this, surfaces_->GetFrameSinkManager(), frame_sink_id_, is_root,
       handles_frame_sink_id_invalidation, needs_sync_points);
 }
 
diff --git a/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.cc b/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.cc
index 22af933..be4af71 100644
--- a/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.cc
+++ b/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.cc
@@ -17,7 +17,6 @@
 #include "android_webview/browser/net/aw_web_resource_request.h"
 #include "android_webview/browser/renderer_host/auto_login_parser.h"
 #include "android_webview/common/url_constants.h"
-#include "base/memory/scoped_vector.h"
 #include "components/navigation_interception/intercept_navigation_delegate.h"
 #include "components/safe_browsing_db/safe_browsing_api_handler.h"
 #include "components/web_restrictions/browser/web_restrictions_resource_throttle.h"
diff --git a/android_webview/browser/surfaces_instance.cc b/android_webview/browser/surfaces_instance.cc
index 8855884..eddadbe 100644
--- a/android_webview/browser/surfaces_instance.cc
+++ b/android_webview/browser/surfaces_instance.cc
@@ -20,8 +20,8 @@
 #include "cc/surfaces/compositor_frame_sink_support.h"
 #include "cc/surfaces/display.h"
 #include "cc/surfaces/display_scheduler.h"
+#include "cc/surfaces/frame_sink_manager.h"
 #include "cc/surfaces/local_surface_id_allocator.h"
-#include "cc/surfaces/surface_manager.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/transform.h"
@@ -54,14 +54,14 @@
   // Webview does not own the surface so should not clear it.
   settings.should_clear_root_render_pass = false;
 
-  surface_manager_.reset(new cc::SurfaceManager);
+  frame_sink_manager_.reset(new cc::FrameSinkManager);
   local_surface_id_allocator_.reset(new cc::LocalSurfaceIdAllocator());
 
   constexpr bool is_root = true;
   constexpr bool handles_frame_sink_id_invalidation = true;
   constexpr bool needs_sync_points = true;
   support_ = cc::CompositorFrameSinkSupport::Create(
-      this, surface_manager_.get(), frame_sink_id_, is_root,
+      this, frame_sink_manager_.get(), frame_sink_id_, is_root,
       handles_frame_sink_id_invalidation, needs_sync_points);
 
   begin_frame_source_.reset(new cc::StubBeginFrameSource);
@@ -80,9 +80,9 @@
       nullptr /* gpu_memory_buffer_manager */, settings, frame_sink_id_,
       std::move(output_surface_holder), std::move(scheduler),
       std::move(texture_mailbox_deleter)));
-  display_->Initialize(this, surface_manager_.get());
-  surface_manager_->RegisterBeginFrameSource(begin_frame_source_.get(),
-                                             frame_sink_id_);
+  display_->Initialize(this, frame_sink_manager_->surface_manager());
+  frame_sink_manager_->RegisterBeginFrameSource(begin_frame_source_.get(),
+                                                frame_sink_id_);
 
   display_->SetVisible(true);
 
@@ -92,7 +92,7 @@
 
 SurfacesInstance::~SurfacesInstance() {
   DCHECK_EQ(g_surfaces_instance, this);
-  surface_manager_->UnregisterBeginFrameSource(begin_frame_source_.get());
+  frame_sink_manager_->UnregisterBeginFrameSource(begin_frame_source_.get());
   g_surfaces_instance = nullptr;
   DCHECK(child_ids_.empty());
 }
@@ -106,8 +106,8 @@
   return frame_sink_id_allocator_.NextFrameSinkId();
 }
 
-cc::SurfaceManager* SurfacesInstance::GetSurfaceManager() {
-  return surface_manager_.get();
+cc::FrameSinkManager* SurfacesInstance::GetFrameSinkManager() {
+  return frame_sink_manager_.get();
 }
 
 void SurfacesInstance::DrawAndSwap(const gfx::Size& viewport,
diff --git a/android_webview/browser/surfaces_instance.h b/android_webview/browser/surfaces_instance.h
index 1a261e3..cc8c89a 100644
--- a/android_webview/browser/surfaces_instance.h
+++ b/android_webview/browser/surfaces_instance.h
@@ -19,8 +19,8 @@
 class BeginFrameSource;
 class CompositorFrameSinkSupport;
 class Display;
+class FrameSinkManager;
 class LocalSurfaceIdAllocator;
-class SurfaceManager;
 }
 
 namespace gfx {
@@ -40,7 +40,7 @@
   static scoped_refptr<SurfacesInstance> GetOrCreateInstance();
 
   cc::FrameSinkId AllocateFrameSinkId();
-  cc::SurfaceManager* GetSurfaceManager();
+  cc::FrameSinkManager* GetFrameSinkManager();
 
   void DrawAndSwap(const gfx::Size& viewport,
                    const gfx::Rect& clip,
@@ -79,7 +79,7 @@
 
   cc::FrameSinkId frame_sink_id_;
 
-  std::unique_ptr<cc::SurfaceManager> surface_manager_;
+  std::unique_ptr<cc::FrameSinkManager> frame_sink_manager_;
   std::unique_ptr<cc::BeginFrameSource> begin_frame_source_;
   std::unique_ptr<cc::Display> display_;
   std::unique_ptr<cc::LocalSurfaceIdAllocator> local_surface_id_allocator_;
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContents.java b/android_webview/java/src/org/chromium/android_webview/AwContents.java
index 0c52eabd..6d442ddd 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContents.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java
@@ -1629,8 +1629,7 @@
         }
 
         // If we are reloading the same url, then set transition type as reload.
-        if (params.getUrl() != null
-                && params.getUrl().equals(mWebContents.getUrl())
+        if (params.getUrl() != null && params.getUrl().equals(mWebContents.getLastCommittedUrl())
                 && params.getTransitionType() == PageTransition.LINK) {
             params.setTransitionType(PageTransition.RELOAD);
         }
@@ -1681,13 +1680,15 @@
     }
 
     /**
-     * Get the URL of the current page.
+     * Get the URL of the current page. This is the visible URL of the {@link WebContents} which may
+     * be a pending navigation or the last committed URL. For the last committed URL use
+     * #getLastCommittedUrl().
      *
      * @return The URL of the current page or null if it's empty.
      */
     public String getUrl() {
         if (isDestroyedOrNoOperation(WARN)) return null;
-        String url =  mWebContents.getUrl();
+        String url = mWebContents.getVisibleUrl();
         if (url == null || url.trim().isEmpty()) return null;
         return url;
     }
diff --git a/android_webview/renderer/aw_content_renderer_client.cc b/android_webview/renderer/aw_content_renderer_client.cc
index a71b286..236e85f 100644
--- a/android_webview/renderer/aw_content_renderer_client.cc
+++ b/android_webview/renderer/aw_content_renderer_client.cc
@@ -21,6 +21,7 @@
 #include "base/i18n/rtl.h"
 #include "base/memory/ptr_util.h"
 #include "base/message_loop/message_loop.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/autofill/content/renderer/autofill_agent.h"
@@ -320,12 +321,35 @@
   //
   // Format list mirrors:
   // http://developer.android.com/guide/appendix/media-formats.html
+  //
+  // Enum and extension list are parallel arrays and must stay in sync. These
+  // enum values are written to logs. New enum values can be added, but existing
+  // enums must never be renumbered or deleted and reused.
+  enum MediaPlayerContainers {
+    CONTAINER_3GP = 0,
+    CONTAINER_TS = 1,
+    CONTAINER_MID = 2,
+    CONTAINER_XMF = 3,
+    CONTAINER_MXMF = 4,
+    CONTAINER_RTTTL = 5,
+    CONTAINER_RTX = 6,
+    CONTAINER_OTA = 7,
+    CONTAINER_IMY = 8,
+    MEDIA_PLAYER_CONTAINERS_COUNT,
+  };
   static const char* kMediaPlayerExtensions[] = {
-      ".3gp",  ".ts",    ".flac", ".mid", ".xmf",
-      ".mxmf", ".rtttl", ".rtx",  ".ota", ".imy"};
-  for (auto* extension : kMediaPlayerExtensions) {
-    if (base::EndsWith(url.path(), extension,
+      ".3gp", ".ts", ".mid", ".xmf", ".mxmf", ".rtttl", ".rtx", ".ota", ".imy"};
+  static_assert(arraysize(kMediaPlayerExtensions) ==
+                    MediaPlayerContainers::MEDIA_PLAYER_CONTAINERS_COUNT,
+                "Invalid enum or extension change.");
+
+  for (size_t i = 0; i < arraysize(kMediaPlayerExtensions); ++i) {
+    if (base::EndsWith(url.path(), kMediaPlayerExtensions[i],
                        base::CompareCase::INSENSITIVE_ASCII)) {
+      UMA_HISTOGRAM_ENUMERATION(
+          "Media.WebView.UnsupportedContainer",
+          static_cast<MediaPlayerContainers>(i),
+          MediaPlayerContainers::MEDIA_PLAYER_CONTAINERS_COUNT);
       return true;
     }
   }
diff --git a/android_webview/test/shell/src/org/chromium/android_webview/shell/AwShellActivity.java b/android_webview/test/shell/src/org/chromium/android_webview/shell/AwShellActivity.java
index 1363996..b9f781fb 100644
--- a/android_webview/test/shell/src/org/chromium/android_webview/shell/AwShellActivity.java
+++ b/android_webview/test/shell/src/org/chromium/android_webview/shell/AwShellActivity.java
@@ -247,7 +247,7 @@
                 mNextButton.setVisibility(hasFocus ? View.GONE : View.VISIBLE);
                 mPrevButton.setVisibility(hasFocus ? View.GONE : View.VISIBLE);
                 if (!hasFocus) {
-                    mUrlTextView.setText(mWebContents.getUrl());
+                    mUrlTextView.setText(mWebContents.getVisibleUrl());
                 }
             }
         });
diff --git a/ash/display/display_color_manager_chromeos_unittest.cc b/ash/display/display_color_manager_chromeos_unittest.cc
index dc62624..5ae855c5 100644
--- a/ash/display/display_color_manager_chromeos_unittest.cc
+++ b/ash/display/display_color_manager_chromeos_unittest.cc
@@ -6,7 +6,6 @@
 
 #include "base/files/file_util.h"
 #include "base/memory/ptr_util.h"
-#include "base/memory/scoped_vector.h"
 #include "base/path_service.h"
 #include "base/run_loop.h"
 #include "base/strings/pattern.h"
diff --git a/ash/system/night_light/night_light_toggle_button.cc b/ash/system/night_light/night_light_toggle_button.cc
index 231d655..74b0d45 100644
--- a/ash/system/night_light/night_light_toggle_button.cc
+++ b/ash/system/night_light/night_light_toggle_button.cc
@@ -9,6 +9,7 @@
 #include "ash/system/night_light/night_light_controller.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/tray/tray_popup_item_style.h"
+#include "ui/accessibility/ax_enums.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/paint_vector_icon.h"
@@ -54,6 +55,13 @@
   Update();
 }
 
+void NightLightToggleButton::Toggle() {
+  DCHECK(NightLightController::IsFeatureEnabled());
+  Shell::Get()->night_light_controller()->Toggle();
+  Update();
+  NotifyAccessibilityEvent(ui::AX_EVENT_ARIA_ATTRIBUTE_CHANGED, true);
+}
+
 void NightLightToggleButton::Update() {
   const bool night_light_enabled =
       Shell::Get()->night_light_controller()->GetEnabled();
diff --git a/ash/system/night_light/night_light_toggle_button.h b/ash/system/night_light/night_light_toggle_button.h
index 7a93c8c..5f9bdcd 100644
--- a/ash/system/night_light/night_light_toggle_button.h
+++ b/ash/system/night_light/night_light_toggle_button.h
@@ -16,10 +16,13 @@
   explicit NightLightToggleButton(views::ButtonListener* listener);
   ~NightLightToggleButton() override = default;
 
+  // Toggles the status of NightLight.
+  void Toggle();
+
+ private:
   // Updates the icon and its style based on the status of NightLight.
   void Update();
 
- private:
   // views::View:
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
 
diff --git a/ash/system/tiles/tiles_default_view.cc b/ash/system/tiles/tiles_default_view.cc
index 9b5bd50..efa58bd 100644
--- a/ash/system/tiles/tiles_default_view.cc
+++ b/ash/system/tiles/tiles_default_view.cc
@@ -126,8 +126,7 @@
   } else if (NightLightController::IsFeatureEnabled() &&
              sender == night_light_button_) {
     Shell::Get()->metrics()->RecordUserMetricsAction(UMA_TRAY_NIGHT_LIGHT);
-    Shell::Get()->night_light_controller()->Toggle();
-    night_light_button_->Update();
+    night_light_button_->Toggle();
   } else if (sender == lock_button_) {
     Shell::Get()->metrics()->RecordUserMetricsAction(UMA_TRAY_LOCK_SCREEN);
     chromeos::DBusThreadManager::Get()
diff --git a/ash/wm/lock_state_controller_unittest.cc b/ash/wm/lock_state_controller_unittest.cc
index e498c91..2a26022 100644
--- a/ash/wm/lock_state_controller_unittest.cc
+++ b/ash/wm/lock_state_controller_unittest.cc
@@ -22,7 +22,6 @@
 #include "ash/wm/power_button_controller.h"
 #include "ash/wm/session_state_animator.h"
 #include "base/memory/ptr_util.h"
-#include "base/memory/scoped_vector.h"
 #include "base/time/time.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/fake_session_manager_client.h"
diff --git a/base/BUILD.gn b/base/BUILD.gn
index a617f10..66297135 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -1815,6 +1815,7 @@
 
     # "test/run_all_unittests.cc",
     "json/json_perftest.cc",
+    "synchronization/waitable_event_perftest.cc",
     "threading/thread_perftest.cc",
   ]
   deps = [
@@ -2208,6 +2209,7 @@
     "template_util_unittest.cc",
     "test/histogram_tester_unittest.cc",
     "test/mock_callback_unittest.cc",
+    "test/scoped_feature_list_unittest.cc",
     "test/scoped_mock_time_message_loop_task_runner_unittest.cc",
     "test/scoped_task_environment_unittest.cc",
     "test/scoped_task_scheduler_unittest.cc",
diff --git a/base/message_loop/message_pump_perftest.cc b/base/message_loop/message_pump_perftest.cc
index e9629aa..f5bd0dd 100644
--- a/base/message_loop/message_pump_perftest.cc
+++ b/base/message_loop/message_pump_perftest.cc
@@ -8,7 +8,6 @@
 #include "base/bind.h"
 #include "base/format_macros.h"
 #include "base/memory/ptr_util.h"
-#include "base/memory/scoped_vector.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/stringprintf.h"
 #include "base/synchronization/condition_variable.h"
diff --git a/base/synchronization/waitable_event_perftest.cc b/base/synchronization/waitable_event_perftest.cc
new file mode 100644
index 0000000..1888077a
--- /dev/null
+++ b/base/synchronization/waitable_event_perftest.cc
@@ -0,0 +1,178 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/synchronization/waitable_event.h"
+
+#include <numeric>
+
+#include "base/threading/simple_thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/perf/perf_test.h"
+
+namespace base {
+
+namespace {
+
+class TraceWaitableEvent {
+ public:
+  TraceWaitableEvent(size_t samples)
+      : event_(WaitableEvent::ResetPolicy::AUTOMATIC,
+               WaitableEvent::InitialState::NOT_SIGNALED),
+        samples_(samples) {
+    signal_times_.reserve(samples);
+    wait_times_.reserve(samples);
+  }
+
+  ~TraceWaitableEvent() = default;
+
+  void Signal() {
+    TimeTicks start = TimeTicks::Now();
+    event_.Signal();
+    signal_times_.push_back(TimeTicks::Now() - start);
+  }
+
+  void Wait() {
+    TimeTicks start = TimeTicks::Now();
+    event_.Wait();
+    wait_times_.push_back(TimeTicks::Now() - start);
+  }
+
+  bool TimedWaitUntil(const TimeTicks& end_time) {
+    TimeTicks start = TimeTicks::Now();
+    bool signaled = event_.TimedWaitUntil(end_time);
+    wait_times_.push_back(TimeTicks::Now() - start);
+    return signaled;
+  }
+
+  bool IsSignaled() { return event_.IsSignaled(); }
+
+  const std::vector<TimeDelta>& signal_times() const { return signal_times_; }
+  const std::vector<TimeDelta>& wait_times() const { return wait_times_; }
+  size_t samples() const { return samples_; }
+
+ private:
+  WaitableEvent event_;
+
+  std::vector<TimeDelta> signal_times_;
+  std::vector<TimeDelta> wait_times_;
+
+  const size_t samples_;
+
+  DISALLOW_COPY_AND_ASSIGN(TraceWaitableEvent);
+};
+
+class SignalerThread : public SimpleThread {
+ public:
+  SignalerThread(TraceWaitableEvent* waiter, TraceWaitableEvent* signaler)
+      : SimpleThread("WaitableEventPerfTest signaler"),
+        waiter_(waiter),
+        signaler_(signaler) {}
+
+  ~SignalerThread() override = default;
+
+  void Run() override {
+    while (!stop_event_.IsSignaled()) {
+      if (waiter_)
+        waiter_->Wait();
+      if (signaler_)
+        signaler_->Signal();
+    }
+  }
+
+  // Signals the thread to stop on the next iteration of its loop (which
+  // will happen immediately if no |waiter_| is present or is signaled.
+  void RequestStop() { stop_event_.Signal(); }
+
+ private:
+  WaitableEvent stop_event_{WaitableEvent::ResetPolicy::MANUAL,
+                            WaitableEvent::InitialState::NOT_SIGNALED};
+  TraceWaitableEvent* waiter_;
+  TraceWaitableEvent* signaler_;
+  DISALLOW_COPY_AND_ASSIGN(SignalerThread);
+};
+
+void PrintPerfWaitableEvent(const TraceWaitableEvent* event,
+                            const std::string& modifier,
+                            const std::string& trace) {
+  TimeDelta signal_time = std::accumulate(
+      event->signal_times().begin(), event->signal_times().end(), TimeDelta());
+  TimeDelta wait_time = std::accumulate(event->wait_times().begin(),
+                                        event->wait_times().end(), TimeDelta());
+  perf_test::PrintResult(
+      "signal_time", modifier, trace,
+      static_cast<size_t>(signal_time.InNanoseconds()) / event->samples(),
+      "ns/sample", true);
+  perf_test::PrintResult(
+      "wait_time", modifier, trace,
+      static_cast<size_t>(wait_time.InNanoseconds()) / event->samples(),
+      "ns/sample", true);
+}
+
+}  // namespace
+
+TEST(WaitableEventPerfTest, SingleThread) {
+  const size_t kSamples = 1000;
+
+  TraceWaitableEvent event(kSamples);
+
+  for (size_t i = 0; i < kSamples; ++i) {
+    event.Signal();
+    event.Wait();
+  }
+
+  PrintPerfWaitableEvent(&event, "", "singlethread-1000-samples");
+}
+
+TEST(WaitableEventPerfTest, MultipleThreads) {
+  const size_t kSamples = 1000;
+
+  TraceWaitableEvent waiter(kSamples);
+  TraceWaitableEvent signaler(kSamples);
+
+  // The other thread will wait and signal on the respective opposite events.
+  SignalerThread thread(&signaler, &waiter);
+  thread.Start();
+
+  for (size_t i = 0; i < kSamples; ++i) {
+    signaler.Signal();
+    waiter.Wait();
+  }
+
+  // Signal the stop event and then make sure the signaler event it is
+  // waiting on is also signaled.
+  thread.RequestStop();
+  signaler.Signal();
+
+  thread.Join();
+
+  PrintPerfWaitableEvent(&waiter, "_waiter", "multithread-1000-samples");
+  PrintPerfWaitableEvent(&signaler, "_signaler", "multithread-1000-samples");
+}
+
+TEST(WaitableEventPerfTest, Throughput) {
+  // Reserve a lot of sample space.
+  const size_t kCapacity = 500000;
+  TraceWaitableEvent event(kCapacity);
+
+  SignalerThread thread(nullptr, &event);
+  thread.Start();
+
+  TimeTicks end_time = TimeTicks::Now() + TimeDelta::FromSeconds(1);
+  size_t count = 0;
+  while (event.TimedWaitUntil(end_time)) {
+    ++count;
+  }
+
+  thread.RequestStop();
+  thread.Join();
+
+  perf_test::PrintResult("counts", "", "throughput", count, "signals", true);
+  PrintPerfWaitableEvent(&event, "", "throughput");
+
+  // Make sure that allocation didn't happen during the test.
+  EXPECT_LE(event.signal_times().capacity(), kCapacity);
+  EXPECT_LE(event.wait_times().capacity(), kCapacity);
+}
+
+}  // namespace base
diff --git a/base/test/scoped_feature_list.cc b/base/test/scoped_feature_list.cc
index f0f3f4e..22b20b9 100644
--- a/base/test/scoped_feature_list.cc
+++ b/base/test/scoped_feature_list.cc
@@ -4,24 +4,79 @@
 
 #include "base/test/scoped_feature_list.h"
 
+#include <algorithm>
 #include <string>
+#include <vector>
+
+#include "base/stl_util.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
 
 namespace base {
 namespace test {
 
 namespace {
 
-static std::string GetFeatureString(
+std::vector<StringPiece> GetFeatureVector(
     const std::initializer_list<base::Feature>& features) {
-  std::string output;
+  std::vector<StringPiece> output;
   for (const base::Feature& feature : features) {
-    if (!output.empty())
-      output += ",";
-    output += feature.name;
+    output.push_back(feature.name);
   }
+
   return output;
 }
 
+// Extracts a feature name from a feature state string. For example, given
+// the input "*MyLovelyFeature<SomeFieldTrial", returns "MyLovelyFeature".
+StringPiece GetFeatureName(StringPiece feature) {
+  StringPiece feature_name = feature;
+
+  // Remove default info.
+  if (feature_name.starts_with("*"))
+    feature_name = feature_name.substr(1);
+
+  // Remove field_trial info.
+  std::size_t index = feature_name.find("<");
+  if (index != std::string::npos)
+    feature_name = feature_name.substr(0, index);
+
+  return feature_name;
+}
+
+struct Features {
+  std::vector<StringPiece> enabled_feature_list;
+  std::vector<StringPiece> disabled_feature_list;
+};
+
+// Merges previously-specified feature overrides with those passed into one of
+// the Init() methods. |features| should be a list of features previously
+// overridden to be in the |override_state|. |merged_features| should contain
+// the enabled and disabled features passed into the Init() method, plus any
+// overrides merged as a result of previous calls to this function.
+void OverrideFeatures(const std::string& features,
+                      base::FeatureList::OverrideState override_state,
+                      Features* merged_features) {
+  std::vector<StringPiece> features_list =
+      SplitStringPiece(features, ",", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
+
+  for (StringPiece feature : features_list) {
+    StringPiece feature_name = GetFeatureName(feature);
+
+    if (ContainsValue(merged_features->enabled_feature_list, feature_name) ||
+        ContainsValue(merged_features->disabled_feature_list, feature_name))
+      continue;
+
+    if (override_state == FeatureList::OverrideState::OVERRIDE_ENABLE_FEATURE) {
+      merged_features->enabled_feature_list.push_back(feature);
+    } else {
+      DCHECK_EQ(override_state,
+                FeatureList::OverrideState::OVERRIDE_DISABLE_FEATURE);
+      merged_features->disabled_feature_list.push_back(feature);
+    }
+  }
+}
+
 }  // namespace
 
 ScopedFeatureList::ScopedFeatureList() {}
@@ -40,13 +95,6 @@
   InitWithFeatureList(std::move(feature_list));
 }
 
-void ScopedFeatureList::InitWithFeatures(
-    const std::initializer_list<base::Feature>& enabled_features,
-    const std::initializer_list<base::Feature>& disabled_features) {
-  InitFromCommandLine(GetFeatureString(enabled_features),
-                      GetFeatureString(disabled_features));
-}
-
 void ScopedFeatureList::InitWithFeatureList(
     std::unique_ptr<FeatureList> feature_list) {
   DCHECK(!original_feature_list_);
@@ -62,12 +110,43 @@
   InitWithFeatureList(std::move(feature_list));
 }
 
+void ScopedFeatureList::InitWithFeatures(
+    const std::initializer_list<base::Feature>& enabled_features,
+    const std::initializer_list<base::Feature>& disabled_features) {
+  Features merged_features;
+  merged_features.enabled_feature_list = GetFeatureVector(enabled_features);
+  merged_features.disabled_feature_list = GetFeatureVector(disabled_features);
+
+  base::FeatureList* feature_list = base::FeatureList::GetInstance();
+
+  // |current_enabled_features| and |current_disabled_features| must declare out
+  // of if scope to avoid them out of scope before JoinString calls because
+  // |merged_features| may contains StringPiece which holding pointer points to
+  // |current_enabled_features| and |current_disabled_features|.
+  std::string current_enabled_features;
+  std::string current_disabled_features;
+  if (feature_list) {
+    base::FeatureList::GetInstance()->GetFeatureOverrides(
+        &current_enabled_features, &current_disabled_features);
+    OverrideFeatures(current_enabled_features,
+                     FeatureList::OverrideState::OVERRIDE_ENABLE_FEATURE,
+                     &merged_features);
+    OverrideFeatures(current_disabled_features,
+                     FeatureList::OverrideState::OVERRIDE_DISABLE_FEATURE,
+                     &merged_features);
+  }
+
+  std::string enabled = JoinString(merged_features.enabled_feature_list, ",");
+  std::string disabled = JoinString(merged_features.disabled_feature_list, ",");
+  InitFromCommandLine(enabled, disabled);
+}
+
 void ScopedFeatureList::InitAndEnableFeature(const base::Feature& feature) {
-  InitFromCommandLine(feature.name, std::string());
+  InitWithFeatures({feature}, {});
 }
 
 void ScopedFeatureList::InitAndDisableFeature(const base::Feature& feature) {
-  InitFromCommandLine(std::string(), feature.name);
+  InitWithFeatures({}, {feature});
 }
 
 }  // namespace test
diff --git a/base/test/scoped_feature_list.h b/base/test/scoped_feature_list.h
index 572a263..fb0b39e 100644
--- a/base/test/scoped_feature_list.h
+++ b/base/test/scoped_feature_list.h
@@ -32,29 +32,42 @@
   ScopedFeatureList();
   ~ScopedFeatureList();
 
+  // WARNING: This method will reset any globally configured features to their
+  // default values, which can hide feature interaction bugs. Please use
+  // sparingly.  https://crbug.com/713390
   // Initializes and registers a FeatureList instance with no overrides.
   void Init();
 
+  // WARNING: This method will reset any globally configured features to their
+  // default values, which can hide feature interaction bugs. Please use
+  // sparingly.  https://crbug.com/713390
   // Initializes and registers the given FeatureList instance.
   void InitWithFeatureList(std::unique_ptr<FeatureList> feature_list);
 
-  // Initializes and registers a FeatureList instance with the given enabled
-  // and disabled features.
-  void InitWithFeatures(
-      const std::initializer_list<base::Feature>& enabled_features,
-      const std::initializer_list<base::Feature>& disabled_features);
-
-  // Initializes and registers a FeatureList instance with the given
+  // WARNING: This method will reset any globally configured features to their
+  // default values, which can hide feature interaction bugs. Please use
+  // sparingly.  https://crbug.com/713390
+  // Initializes and registers a FeatureList instance with only the given
   // enabled and disabled features (comma-separated names).
   void InitFromCommandLine(const std::string& enable_features,
                            const std::string& disable_features);
 
-  // Initializes and registers a FeatureList instance enabling a single
-  // feature.
+  // Initializes and registers a FeatureList instance based on present
+  // FeatureList and overridden with the given enabled and disabled features.
+  // Any feature overrides already present in the global FeatureList will
+  // continue to apply, unless they conflict with the overrides passed into this
+  // method. This is important for testing potentially unexpected feature
+  // interactions.
+  void InitWithFeatures(
+      const std::initializer_list<base::Feature>& enabled_features,
+      const std::initializer_list<base::Feature>& disabled_features);
+
+  // Initializes and registers a FeatureList instance based on present
+  // FeatureList and overridden with single enabled feature.
   void InitAndEnableFeature(const base::Feature& feature);
 
-  // Initializes and registers a FeatureList instance disabling a single
-  // feature.
+  // Initializes and registers a FeatureList instance based on present
+  // FeatureList and overridden with single disabled feature.
   void InitAndDisableFeature(const base::Feature& feature);
 
  private:
diff --git a/base/test/scoped_feature_list_unittest.cc b/base/test/scoped_feature_list_unittest.cc
new file mode 100644
index 0000000..21d1b4d
--- /dev/null
+++ b/base/test/scoped_feature_list_unittest.cc
@@ -0,0 +1,188 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/scoped_feature_list.h"
+
+#include <string>
+#include "base/metrics/field_trial.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace test {
+
+namespace {
+
+const base::Feature kTestFeature1{"TestFeature1",
+                                  base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kTestFeature2{"TestFeature2",
+                                  base::FEATURE_DISABLED_BY_DEFAULT};
+
+void ExpectFeatures(const std::string& enabled_features,
+                    const std::string& disabled_features) {
+  base::FeatureList* list = base::FeatureList::GetInstance();
+  std::string actual_enabled_features;
+  std::string actual_disabled_features;
+
+  list->GetFeatureOverrides(&actual_enabled_features,
+                            &actual_disabled_features);
+
+  EXPECT_EQ(enabled_features, actual_enabled_features);
+  EXPECT_EQ(disabled_features, actual_disabled_features);
+}
+
+}  // namespace
+
+class ScopedFeatureListTest : public testing::Test {
+ public:
+  ScopedFeatureListTest() {
+    // Clear default feature list.
+    std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
+    feature_list->InitializeFromCommandLine(std::string(), std::string());
+    original_feature_list_ = base::FeatureList::ClearInstanceForTesting();
+    base::FeatureList::SetInstance(std::move(feature_list));
+  }
+
+  ~ScopedFeatureListTest() override {
+    // Restore feature list.
+    if (original_feature_list_) {
+      base::FeatureList::ClearInstanceForTesting();
+      base::FeatureList::RestoreInstanceForTesting(
+          std::move(original_feature_list_));
+    }
+  }
+
+ private:
+  // Save the present FeatureList and restore it after test finish.
+  std::unique_ptr<FeatureList> original_feature_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedFeatureListTest);
+};
+
+TEST_F(ScopedFeatureListTest, BasicScoped) {
+  ExpectFeatures(std::string(), std::string());
+  EXPECT_FALSE(FeatureList::IsEnabled(kTestFeature1));
+  {
+    test::ScopedFeatureList feature_list1;
+    feature_list1.InitFromCommandLine("TestFeature1", std::string());
+    ExpectFeatures("TestFeature1", std::string());
+    EXPECT_TRUE(FeatureList::IsEnabled(kTestFeature1));
+  }
+  ExpectFeatures(std::string(), std::string());
+  EXPECT_FALSE(FeatureList::IsEnabled(kTestFeature1));
+}
+
+TEST_F(ScopedFeatureListTest, EnableFeatureOverrideDisable) {
+  test::ScopedFeatureList feature_list1;
+  feature_list1.InitWithFeatures({}, {kTestFeature1});
+
+  {
+    test::ScopedFeatureList feature_list2;
+    feature_list2.InitWithFeatures({kTestFeature1}, {});
+    ExpectFeatures("TestFeature1", std::string());
+  }
+}
+
+TEST_F(ScopedFeatureListTest, FeatureOverrideNotMakeDuplicate) {
+  test::ScopedFeatureList feature_list1;
+  feature_list1.InitWithFeatures({}, {kTestFeature1});
+
+  {
+    test::ScopedFeatureList feature_list2;
+    feature_list2.InitWithFeatures({}, {kTestFeature1});
+    ExpectFeatures(std::string(), "TestFeature1");
+  }
+}
+
+TEST_F(ScopedFeatureListTest, FeatureOverrideFeatureWithDefault) {
+  test::ScopedFeatureList feature_list1;
+  feature_list1.InitFromCommandLine("*TestFeature1", std::string());
+
+  {
+    test::ScopedFeatureList feature_list2;
+    feature_list2.InitWithFeatures({kTestFeature1}, {});
+    ExpectFeatures("TestFeature1", std::string());
+  }
+}
+
+TEST_F(ScopedFeatureListTest, FeatureOverrideFeatureWithDefault2) {
+  test::ScopedFeatureList feature_list1;
+  feature_list1.InitFromCommandLine("*TestFeature1", std::string());
+
+  {
+    test::ScopedFeatureList feature_list2;
+    feature_list2.InitWithFeatures({}, {kTestFeature1});
+    ExpectFeatures(std::string(), "TestFeature1");
+  }
+}
+
+TEST_F(ScopedFeatureListTest, FeatureOverrideFeatureWithEnabledFieldTried) {
+  test::ScopedFeatureList feature_list1;
+
+  std::unique_ptr<FeatureList> feature_list(new FeatureList);
+  FieldTrialList field_trial_list(nullptr);
+  FieldTrial* trial = FieldTrialList::CreateFieldTrial("TrialExample", "A");
+  feature_list->RegisterFieldTrialOverride(
+      kTestFeature1.name, FeatureList::OVERRIDE_ENABLE_FEATURE, trial);
+  feature_list1.InitWithFeatureList(std::move(feature_list));
+
+  {
+    test::ScopedFeatureList feature_list2;
+    feature_list2.InitWithFeatures({kTestFeature1}, {});
+    ExpectFeatures("TestFeature1", std::string());
+  }
+}
+
+TEST_F(ScopedFeatureListTest, FeatureOverrideFeatureWithDisabledFieldTried) {
+  test::ScopedFeatureList feature_list1;
+
+  std::unique_ptr<FeatureList> feature_list(new FeatureList);
+  FieldTrialList field_trial_list(nullptr);
+  FieldTrial* trial = FieldTrialList::CreateFieldTrial("TrialExample", "A");
+  feature_list->RegisterFieldTrialOverride(
+      kTestFeature1.name, FeatureList::OVERRIDE_DISABLE_FEATURE, trial);
+  feature_list1.InitWithFeatureList(std::move(feature_list));
+
+  {
+    test::ScopedFeatureList feature_list2;
+    feature_list2.InitWithFeatures({kTestFeature1}, {});
+    ExpectFeatures("TestFeature1", std::string());
+  }
+}
+
+TEST_F(ScopedFeatureListTest, FeatureOverrideKeepsOtherExistingFeature) {
+  test::ScopedFeatureList feature_list1;
+  feature_list1.InitWithFeatures({}, {kTestFeature1});
+
+  {
+    test::ScopedFeatureList feature_list2;
+    feature_list2.InitWithFeatures({}, {kTestFeature2});
+    EXPECT_FALSE(FeatureList::IsEnabled(kTestFeature1));
+    EXPECT_FALSE(FeatureList::IsEnabled(kTestFeature2));
+  }
+}
+
+TEST_F(ScopedFeatureListTest, FeatureOverrideKeepsOtherExistingFeature2) {
+  test::ScopedFeatureList feature_list1;
+  feature_list1.InitWithFeatures({}, {kTestFeature1});
+
+  {
+    test::ScopedFeatureList feature_list2;
+    feature_list2.InitWithFeatures({kTestFeature2}, {});
+    ExpectFeatures("TestFeature2", "TestFeature1");
+  }
+}
+
+TEST_F(ScopedFeatureListTest, FeatureOverrideKeepsOtherExistingDefaultFeature) {
+  test::ScopedFeatureList feature_list1;
+  feature_list1.InitFromCommandLine("*TestFeature1", std::string());
+
+  {
+    test::ScopedFeatureList feature_list2;
+    feature_list2.InitWithFeatures({}, {kTestFeature2});
+    ExpectFeatures("*TestFeature1", "TestFeature2");
+  }
+}
+
+}  // namespace test
+}  // namespace base
diff --git a/base/threading/thread_perftest.cc b/base/threading/thread_perftest.cc
index 39f192c..92f24675 100644
--- a/base/threading/thread_perftest.cc
+++ b/base/threading/thread_perftest.cc
@@ -227,8 +227,8 @@
 // using WaitableEvents. We only test four threads (worst-case), but we
 // might want to craft a way to test the best-case (where the thread doesn't
 // end up blocking because the event is already signalled).
-typedef EventPerfTest<base::WaitableEvent> WaitableEventPerfTest;
-TEST_F(WaitableEventPerfTest, EventPingPong) {
+typedef EventPerfTest<base::WaitableEvent> WaitableEventThreadPerfTest;
+TEST_F(WaitableEventThreadPerfTest, EventPingPong) {
   RunPingPongTest("4_WaitableEvent_Threads", 4);
 }
 
diff --git a/base/trace_event/process_memory_dump.h b/base/trace_event/process_memory_dump.h
index c0c2f81..a6b7d6d9 100644
--- a/base/trace_event/process_memory_dump.h
+++ b/base/trace_event/process_memory_dump.h
@@ -15,7 +15,6 @@
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
-#include "base/memory/scoped_vector.h"
 #include "base/trace_event/heap_profiler_serialization_state.h"
 #include "base/trace_event/memory_allocator_dump.h"
 #include "base/trace_event/memory_allocator_dump_guid.h"
diff --git a/base/trace_event/trace_log.h b/base/trace_event/trace_log.h
index b5ebc4f6..9e7db7d 100644
--- a/base/trace_event/trace_log.h
+++ b/base/trace_event/trace_log.h
@@ -16,7 +16,6 @@
 #include "base/atomicops.h"
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
-#include "base/memory/scoped_vector.h"
 #include "base/trace_event/memory_dump_provider.h"
 #include "base/trace_event/trace_config.h"
 #include "base/trace_event/trace_event_impl.h"
diff --git a/build/android/pylib/local/device/local_device_environment.py b/build/android/pylib/local/device/local_device_environment.py
index cc376d18..1e5056b 100644
--- a/build/android/pylib/local/device/local_device_environment.py
+++ b/build/android/pylib/local/device/local_device_environment.py
@@ -138,7 +138,7 @@
 
     @handle_shard_failures_with(on_failure=self.BlacklistDevice)
     def prepare_device(d):
-      d.WaitUntilFullyBooted(timeout=10)
+      d.WaitUntilFullyBooted()
 
       if self._enable_device_cache:
         cache_path = _DeviceCachePath(d)
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index ea261d1e..8b0d79f 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -863,11 +863,11 @@
     "surfaces/direct_layer_tree_frame_sink_unittest.cc",
     "surfaces/display_scheduler_unittest.cc",
     "surfaces/display_unittest.cc",
+    "surfaces/frame_sink_manager_unittest.cc",
     "surfaces/referenced_surface_tracker_unittest.cc",
     "surfaces/surface_aggregator_unittest.cc",
     "surfaces/surface_hittest_unittest.cc",
     "surfaces/surface_manager_ref_unittest.cc",
-    "surfaces/surface_manager_unittest.cc",
     "surfaces/surface_sequence_generator_unittest.cc",
     "surfaces/surface_synchronization_unittest.cc",
     "surfaces/surface_unittest.cc",
diff --git a/cc/layers/heads_up_display_layer_impl.cc b/cc/layers/heads_up_display_layer_impl.cc
index 0050c8e6..f744ccf 100644
--- a/cc/layers/heads_up_display_layer_impl.cc
+++ b/cc/layers/heads_up_display_layer_impl.cc
@@ -223,7 +223,6 @@
     resource_provider->CopyToResource(
         resources_.back()->id(), static_cast<const uint8_t*>(pixmap.addr()),
         internal_content_bounds_);
-    resource_provider->GenerateSyncTokenForResource(resources_.back()->id());
   }
 }
 
diff --git a/cc/layers/texture_layer_impl.cc b/cc/layers/texture_layer_impl.cc
index a81d66ac..4835c97d 100644
--- a/cc/layers/texture_layer_impl.cc
+++ b/cc/layers/texture_layer_impl.cc
@@ -137,8 +137,6 @@
 
       resource_provider->CopyToResource(texture_copy_->id(), pixels,
                                         texture_mailbox_.size_in_pixels());
-      resource_provider->GenerateSyncTokenForResource(texture_copy_->id());
-
       valid_texture_copy_ = true;
     }
   }
diff --git a/cc/layers/video_layer_impl.cc b/cc/layers/video_layer_impl.cc
index 33cb6a3..c1382b3 100644
--- a/cc/layers/video_layer_impl.cc
+++ b/cc/layers/video_layer_impl.cc
@@ -137,7 +137,6 @@
         external_resources.mailboxes[i].is_overlay_candidate()));
     resource_ids.push_back(resource_id);
   }
-  resource_provider->GenerateSyncTokenForResources(resource_ids);
 
   return true;
 }
diff --git a/cc/paint/BUILD.gn b/cc/paint/BUILD.gn
index 57de616..1c53186 100644
--- a/cc/paint/BUILD.gn
+++ b/cc/paint/BUILD.gn
@@ -25,6 +25,10 @@
     "paint_image.h",
     "paint_op_buffer.cc",
     "paint_op_buffer.h",
+    "paint_op_reader.cc",
+    "paint_op_reader.h",
+    "paint_op_writer.cc",
+    "paint_op_writer.h",
     "paint_record.cc",
     "paint_record.h",
     "paint_recorder.cc",
diff --git a/cc/paint/paint_flags.h b/cc/paint/paint_flags.h
index 6954892d..940751c 100644
--- a/cc/paint/paint_flags.h
+++ b/cc/paint/paint_flags.h
@@ -224,6 +224,9 @@
   bool SupportsFoldingAlpha() const;
 
  private:
+  friend class PaintOpReader;
+  friend class PaintOpWriter;
+
   friend const SkPaint& ToSkPaint(const PaintFlags& flags);
   friend const SkPaint* ToSkPaint(const PaintFlags* flags);
 
diff --git a/cc/paint/paint_op_buffer.cc b/cc/paint/paint_op_buffer.cc
index 981622d..907f4e9 100644
--- a/cc/paint/paint_op_buffer.cc
+++ b/cc/paint/paint_op_buffer.cc
@@ -6,6 +6,8 @@
 
 #include "base/containers/stack_container.h"
 #include "cc/paint/display_item_list.h"
+#include "cc/paint/paint_op_reader.h"
+#include "cc/paint/paint_op_writer.h"
 #include "cc/paint/paint_record.h"
 #include "third_party/skia/include/core/SkAnnotation.h"
 #include "third_party/skia/include/core/SkCanvas.h"
@@ -136,6 +138,24 @@
 static_assert(kNumOpTypes == TYPES(M), "Missing op in list");
 #undef M
 
+using SerializeFunction = size_t (*)(const PaintOp* op,
+                                     void* memory,
+                                     size_t size,
+                                     const PaintOp::SerializeOptions& options);
+#define M(T) &T::Serialize,
+static const SerializeFunction g_serialize_functions[kNumOpTypes] = {TYPES(M)};
+#undef M
+
+using DeserializeFunction = PaintOp* (*)(const void* input,
+                                         size_t input_size,
+                                         void* output,
+                                         size_t output_size);
+
+#define M(T) &T::Deserialize,
+static const DeserializeFunction g_deserialize_functions[kNumOpTypes] = {
+    TYPES(M)};
+#undef M
+
 using RasterFunction = void (*)(const PaintOp* op,
                                 SkCanvas* canvas,
                                 const SkMatrix& original_ctm);
@@ -187,6 +207,7 @@
 #undef TYPES
 
 SkRect PaintOp::kUnsetRect = {SK_ScalarInfinity, 0, 0, 0};
+const size_t PaintOp::kMaxSkip;
 
 std::string PaintOpTypeToString(PaintOpType type) {
   switch (type) {
@@ -254,6 +275,785 @@
   return "UNKNOWN";
 }
 
+template <typename T>
+size_t SimpleSerialize(const PaintOp* op, void* memory, size_t size) {
+  if (sizeof(T) > size)
+    return 0;
+  memcpy(memory, op, sizeof(T));
+  return sizeof(T);
+}
+
+size_t AnnotateOp::Serialize(const PaintOp* base_op,
+                             void* memory,
+                             size_t size,
+                             const SerializeOptions& options) {
+  auto* op = static_cast<const AnnotateOp*>(base_op);
+  PaintOpWriter helper(memory, size);
+  helper.Write(op->annotation_type);
+  helper.Write(op->rect);
+  helper.Write(op->data);
+  return helper.size();
+}
+
+size_t ClipPathOp::Serialize(const PaintOp* base_op,
+                             void* memory,
+                             size_t size,
+                             const SerializeOptions& options) {
+  auto* op = static_cast<const ClipPathOp*>(base_op);
+  PaintOpWriter helper(memory, size);
+  helper.Write(op->path);
+  helper.Write(op->op);
+  helper.Write(op->antialias);
+  return helper.size();
+}
+
+size_t ClipRectOp::Serialize(const PaintOp* op,
+                             void* memory,
+                             size_t size,
+                             const SerializeOptions& options) {
+  return SimpleSerialize<ClipRectOp>(op, memory, size);
+}
+
+size_t ClipRRectOp::Serialize(const PaintOp* op,
+                              void* memory,
+                              size_t size,
+                              const SerializeOptions& options) {
+  return SimpleSerialize<ClipRRectOp>(op, memory, size);
+}
+
+size_t ConcatOp::Serialize(const PaintOp* op,
+                           void* memory,
+                           size_t size,
+                           const SerializeOptions& options) {
+  return SimpleSerialize<ConcatOp>(op, memory, size);
+}
+
+size_t DrawArcOp::Serialize(const PaintOp* base_op,
+                            void* memory,
+                            size_t size,
+                            const SerializeOptions& options) {
+  auto* op = static_cast<const DrawArcOp*>(base_op);
+  PaintOpWriter helper(memory, size);
+  helper.Write(op->flags);
+  helper.Write(op->oval);
+  helper.Write(op->start_angle);
+  helper.Write(op->sweep_angle);
+  helper.Write(op->use_center);
+  return helper.size();
+}
+
+size_t DrawCircleOp::Serialize(const PaintOp* base_op,
+                               void* memory,
+                               size_t size,
+                               const SerializeOptions& options) {
+  auto* op = static_cast<const DrawCircleOp*>(base_op);
+  PaintOpWriter helper(memory, size);
+  helper.Write(op->flags);
+  helper.Write(op->cx);
+  helper.Write(op->cy);
+  helper.Write(op->radius);
+  return helper.size();
+}
+
+size_t DrawColorOp::Serialize(const PaintOp* op,
+                              void* memory,
+                              size_t size,
+                              const SerializeOptions& options) {
+  return SimpleSerialize<DrawColorOp>(op, memory, size);
+}
+
+size_t DrawDRRectOp::Serialize(const PaintOp* base_op,
+                               void* memory,
+                               size_t size,
+                               const SerializeOptions& options) {
+  auto* op = static_cast<const DrawDRRectOp*>(base_op);
+  PaintOpWriter helper(memory, size);
+  helper.Write(op->flags);
+  helper.Write(op->outer);
+  helper.Write(op->inner);
+  return helper.size();
+}
+
+size_t DrawImageOp::Serialize(const PaintOp* base_op,
+                              void* memory,
+                              size_t size,
+                              const SerializeOptions& options) {
+  auto* op = static_cast<const DrawImageOp*>(base_op);
+  PaintOpWriter helper(memory, size);
+  helper.Write(op->flags);
+  helper.Write(op->image, options.decode_cache);
+  helper.Write(op->left);
+  helper.Write(op->top);
+  return helper.size();
+}
+
+size_t DrawImageRectOp::Serialize(const PaintOp* base_op,
+                                  void* memory,
+                                  size_t size,
+                                  const SerializeOptions& options) {
+  auto* op = static_cast<const DrawImageRectOp*>(base_op);
+  PaintOpWriter helper(memory, size);
+  helper.Write(op->flags);
+  helper.Write(op->image, options.decode_cache);
+  helper.Write(op->src);
+  helper.Write(op->dst);
+  helper.Write(op->constraint);
+  return helper.size();
+}
+
+size_t DrawIRectOp::Serialize(const PaintOp* base_op,
+                              void* memory,
+                              size_t size,
+                              const SerializeOptions& options) {
+  auto* op = static_cast<const DrawIRectOp*>(base_op);
+  PaintOpWriter helper(memory, size);
+  helper.Write(op->flags);
+  helper.Write(op->rect);
+  return helper.size();
+}
+
+size_t DrawLineOp::Serialize(const PaintOp* base_op,
+                             void* memory,
+                             size_t size,
+                             const SerializeOptions& options) {
+  auto* op = static_cast<const DrawLineOp*>(base_op);
+  PaintOpWriter helper(memory, size);
+  helper.Write(op->flags);
+  helper.Write(op->x0);
+  helper.Write(op->y0);
+  helper.Write(op->x1);
+  helper.Write(op->y1);
+  return helper.size();
+}
+
+size_t DrawOvalOp::Serialize(const PaintOp* base_op,
+                             void* memory,
+                             size_t size,
+                             const SerializeOptions& options) {
+  auto* op = static_cast<const DrawOvalOp*>(base_op);
+  PaintOpWriter helper(memory, size);
+  helper.Write(op->flags);
+  helper.Write(op->oval);
+  return helper.size();
+}
+
+size_t DrawPathOp::Serialize(const PaintOp* base_op,
+                             void* memory,
+                             size_t size,
+                             const SerializeOptions& options) {
+  auto* op = static_cast<const DrawPathOp*>(base_op);
+  PaintOpWriter helper(memory, size);
+  helper.Write(op->flags);
+  helper.Write(op->path);
+  return helper.size();
+}
+
+size_t DrawPosTextOp::Serialize(const PaintOp* base_op,
+                                void* memory,
+                                size_t size,
+                                const SerializeOptions& options) {
+  auto* op = static_cast<const DrawPosTextOp*>(base_op);
+  PaintOpWriter helper(memory, size);
+  helper.Write(op->flags);
+  helper.Write(op->count);
+  helper.Write(op->bytes);
+  helper.WriteArray(op->count, op->GetArray());
+  helper.WriteData(op->bytes, op->GetData());
+  return helper.size();
+}
+
+size_t DrawRecordOp::Serialize(const PaintOp* op,
+                               void* memory,
+                               size_t size,
+                               const SerializeOptions& options) {
+  // TODO(enne): these must be flattened.  Serializing this will not do
+  // anything.
+  NOTREACHED();
+  return 0u;
+}
+
+size_t DrawRectOp::Serialize(const PaintOp* base_op,
+                             void* memory,
+                             size_t size,
+                             const SerializeOptions& options) {
+  auto* op = static_cast<const DrawRectOp*>(base_op);
+  PaintOpWriter helper(memory, size);
+  helper.Write(op->flags);
+  helper.Write(op->rect);
+  return helper.size();
+}
+
+size_t DrawRRectOp::Serialize(const PaintOp* base_op,
+                              void* memory,
+                              size_t size,
+                              const SerializeOptions& options) {
+  auto* op = static_cast<const DrawRRectOp*>(base_op);
+  PaintOpWriter helper(memory, size);
+  helper.Write(op->flags);
+  helper.Write(op->rrect);
+  return helper.size();
+}
+
+size_t DrawTextOp::Serialize(const PaintOp* base_op,
+                             void* memory,
+                             size_t size,
+                             const SerializeOptions& options) {
+  auto* op = static_cast<const DrawTextOp*>(base_op);
+  PaintOpWriter helper(memory, size);
+  helper.Write(op->flags);
+  helper.Write(op->x);
+  helper.Write(op->y);
+  helper.Write(op->bytes);
+  helper.WriteData(op->bytes, op->GetData());
+  return helper.size();
+}
+
+size_t DrawTextBlobOp::Serialize(const PaintOp* base_op,
+                                 void* memory,
+                                 size_t size,
+                                 const SerializeOptions& options) {
+  auto* op = static_cast<const DrawTextBlobOp*>(base_op);
+  PaintOpWriter helper(memory, size);
+  helper.Write(op->flags);
+  helper.Write(op->x);
+  helper.Write(op->y);
+  helper.Write(op->blob);
+  return helper.size();
+}
+
+size_t NoopOp::Serialize(const PaintOp* op,
+                         void* memory,
+                         size_t size,
+                         const SerializeOptions& options) {
+  return SimpleSerialize<NoopOp>(op, memory, size);
+}
+
+size_t RestoreOp::Serialize(const PaintOp* op,
+                            void* memory,
+                            size_t size,
+                            const SerializeOptions& options) {
+  return SimpleSerialize<RestoreOp>(op, memory, size);
+}
+
+size_t RotateOp::Serialize(const PaintOp* op,
+                           void* memory,
+                           size_t size,
+                           const SerializeOptions& options) {
+  return SimpleSerialize<RotateOp>(op, memory, size);
+}
+
+size_t SaveOp::Serialize(const PaintOp* op,
+                         void* memory,
+                         size_t size,
+                         const SerializeOptions& options) {
+  return SimpleSerialize<SaveOp>(op, memory, size);
+}
+
+size_t SaveLayerOp::Serialize(const PaintOp* base_op,
+                              void* memory,
+                              size_t size,
+                              const SerializeOptions& options) {
+  auto* op = static_cast<const SaveLayerOp*>(base_op);
+  PaintOpWriter helper(memory, size);
+  helper.Write(op->flags);
+  helper.Write(op->bounds);
+  return helper.size();
+}
+
+size_t SaveLayerAlphaOp::Serialize(const PaintOp* op,
+                                   void* memory,
+                                   size_t size,
+                                   const SerializeOptions& options) {
+  return SimpleSerialize<SaveLayerAlphaOp>(op, memory, size);
+}
+
+size_t ScaleOp::Serialize(const PaintOp* op,
+                          void* memory,
+                          size_t size,
+                          const SerializeOptions& options) {
+  return SimpleSerialize<ScaleOp>(op, memory, size);
+}
+
+size_t SetMatrixOp::Serialize(const PaintOp* op,
+                              void* memory,
+                              size_t size,
+                              const SerializeOptions& options) {
+  return SimpleSerialize<SetMatrixOp>(op, memory, size);
+}
+
+size_t TranslateOp::Serialize(const PaintOp* op,
+                              void* memory,
+                              size_t size,
+                              const SerializeOptions& options) {
+  return SimpleSerialize<TranslateOp>(op, memory, size);
+}
+
+template <typename T>
+void UpdateTypeAndSkip(T* op) {
+  op->type = static_cast<uint8_t>(T::kType);
+  op->skip = MathUtil::UncheckedRoundUp(sizeof(T), PaintOpBuffer::PaintOpAlign);
+}
+
+template <typename T>
+PaintOp* SimpleDeserialize(const void* input,
+                           size_t input_size,
+                           void* output,
+                           size_t output_size) {
+  if (input_size < sizeof(T))
+    return nullptr;
+  memcpy(output, input, sizeof(T));
+
+  T* op = reinterpret_cast<T*>(output);
+  // Type and skip were already read once, so could have been changed.
+  // Don't trust them and clobber them with something valid.
+  UpdateTypeAndSkip(op);
+  return op;
+}
+
+PaintOp* AnnotateOp::Deserialize(const void* input,
+                                 size_t input_size,
+                                 void* output,
+                                 size_t output_size) {
+  CHECK_GE(output_size, sizeof(AnnotateOp));
+  AnnotateOp* op = new (output) AnnotateOp;
+
+  PaintOpReader helper(input, input_size);
+  helper.Read(&op->annotation_type);
+  helper.Read(&op->rect);
+  helper.Read(&op->data);
+  if (!helper.valid()) {
+    op->~AnnotateOp();
+    return nullptr;
+  }
+
+  UpdateTypeAndSkip(op);
+  return op;
+}
+
+PaintOp* ClipPathOp::Deserialize(const void* input,
+                                 size_t input_size,
+                                 void* output,
+                                 size_t output_size) {
+  CHECK_GE(output_size, sizeof(ClipPathOp));
+  ClipPathOp* op = new (output) ClipPathOp;
+
+  PaintOpReader helper(input, input_size);
+  helper.Read(&op->path);
+  helper.Read(&op->op);
+  helper.Read(&op->antialias);
+  if (!helper.valid()) {
+    op->~ClipPathOp();
+    return nullptr;
+  }
+
+  UpdateTypeAndSkip(op);
+  return op;
+}
+
+PaintOp* ClipRectOp::Deserialize(const void* input,
+                                 size_t input_size,
+                                 void* output,
+                                 size_t output_size) {
+  return SimpleDeserialize<ClipRectOp>(input, input_size, output, output_size);
+}
+
+PaintOp* ClipRRectOp::Deserialize(const void* input,
+                                  size_t input_size,
+                                  void* output,
+                                  size_t output_size) {
+  return SimpleDeserialize<ClipRRectOp>(input, input_size, output, output_size);
+}
+
+PaintOp* ConcatOp::Deserialize(const void* input,
+                               size_t input_size,
+                               void* output,
+                               size_t output_size) {
+  return SimpleDeserialize<ConcatOp>(input, input_size, output, output_size);
+}
+
+PaintOp* DrawArcOp::Deserialize(const void* input,
+                                size_t input_size,
+                                void* output,
+                                size_t output_size) {
+  CHECK_GE(output_size, sizeof(DrawArcOp));
+  DrawArcOp* op = new (output) DrawArcOp;
+
+  PaintOpReader helper(input, input_size);
+  helper.Read(&op->flags);
+  helper.Read(&op->oval);
+  helper.Read(&op->start_angle);
+  helper.Read(&op->sweep_angle);
+  helper.Read(&op->use_center);
+  if (!helper.valid()) {
+    op->~DrawArcOp();
+    return nullptr;
+  }
+  UpdateTypeAndSkip(op);
+  return op;
+}
+
+PaintOp* DrawCircleOp::Deserialize(const void* input,
+                                   size_t input_size,
+                                   void* output,
+                                   size_t output_size) {
+  CHECK_GE(output_size, sizeof(DrawCircleOp));
+  DrawCircleOp* op = new (output) DrawCircleOp;
+
+  PaintOpReader helper(input, input_size);
+  helper.Read(&op->flags);
+  helper.Read(&op->cx);
+  helper.Read(&op->cy);
+  helper.Read(&op->radius);
+  if (!helper.valid()) {
+    op->~DrawCircleOp();
+    return nullptr;
+  }
+  UpdateTypeAndSkip(op);
+  return op;
+}
+
+PaintOp* DrawColorOp::Deserialize(const void* input,
+                                  size_t input_size,
+                                  void* output,
+                                  size_t output_size) {
+  return SimpleDeserialize<DrawColorOp>(input, input_size, output, output_size);
+}
+
+PaintOp* DrawDRRectOp::Deserialize(const void* input,
+                                   size_t input_size,
+                                   void* output,
+                                   size_t output_size) {
+  CHECK_GE(output_size, sizeof(DrawDRRectOp));
+  DrawDRRectOp* op = new (output) DrawDRRectOp;
+
+  PaintOpReader helper(input, input_size);
+  helper.Read(&op->flags);
+  helper.Read(&op->outer);
+  helper.Read(&op->inner);
+  if (!helper.valid()) {
+    op->~DrawDRRectOp();
+    return nullptr;
+  }
+  UpdateTypeAndSkip(op);
+  return op;
+}
+
+PaintOp* DrawImageOp::Deserialize(const void* input,
+                                  size_t input_size,
+                                  void* output,
+                                  size_t output_size) {
+  CHECK_GE(output_size, sizeof(DrawImageOp));
+  DrawImageOp* op = new (output) DrawImageOp;
+
+  PaintOpReader helper(input, input_size);
+  helper.Read(&op->flags);
+  helper.Read(&op->image);
+  helper.Read(&op->left);
+  helper.Read(&op->top);
+  if (!helper.valid()) {
+    op->~DrawImageOp();
+    return nullptr;
+  }
+  UpdateTypeAndSkip(op);
+  return op;
+}
+
+PaintOp* DrawImageRectOp::Deserialize(const void* input,
+                                      size_t input_size,
+                                      void* output,
+                                      size_t output_size) {
+  CHECK_GE(output_size, sizeof(DrawImageRectOp));
+  DrawImageRectOp* op = new (output) DrawImageRectOp;
+
+  PaintOpReader helper(input, input_size);
+  helper.Read(&op->flags);
+  helper.Read(&op->image);
+  helper.Read(&op->src);
+  helper.Read(&op->dst);
+  helper.Read(&op->constraint);
+  if (!helper.valid()) {
+    op->~DrawImageRectOp();
+    return nullptr;
+  }
+  UpdateTypeAndSkip(op);
+  return op;
+}
+
+PaintOp* DrawIRectOp::Deserialize(const void* input,
+                                  size_t input_size,
+                                  void* output,
+                                  size_t output_size) {
+  CHECK_GE(output_size, sizeof(DrawIRectOp));
+  DrawIRectOp* op = new (output) DrawIRectOp;
+
+  PaintOpReader helper(input, input_size);
+  helper.Read(&op->flags);
+  helper.Read(&op->rect);
+  if (!helper.valid()) {
+    op->~DrawIRectOp();
+    return nullptr;
+  }
+  UpdateTypeAndSkip(op);
+  return op;
+}
+
+PaintOp* DrawLineOp::Deserialize(const void* input,
+                                 size_t input_size,
+                                 void* output,
+                                 size_t output_size) {
+  CHECK_GE(output_size, sizeof(DrawLineOp));
+  DrawLineOp* op = new (output) DrawLineOp;
+
+  PaintOpReader helper(input, input_size);
+  helper.Read(&op->flags);
+  helper.Read(&op->x0);
+  helper.Read(&op->y0);
+  helper.Read(&op->x1);
+  helper.Read(&op->y1);
+  if (!helper.valid()) {
+    op->~DrawLineOp();
+    return nullptr;
+  }
+  UpdateTypeAndSkip(op);
+  return op;
+}
+
+PaintOp* DrawOvalOp::Deserialize(const void* input,
+                                 size_t input_size,
+                                 void* output,
+                                 size_t output_size) {
+  CHECK_GE(output_size, sizeof(DrawOvalOp));
+  DrawOvalOp* op = new (output) DrawOvalOp;
+
+  PaintOpReader helper(input, input_size);
+  helper.Read(&op->flags);
+  helper.Read(&op->oval);
+  if (!helper.valid()) {
+    op->~DrawOvalOp();
+    return nullptr;
+  }
+  UpdateTypeAndSkip(op);
+  return op;
+}
+
+PaintOp* DrawPathOp::Deserialize(const void* input,
+                                 size_t input_size,
+                                 void* output,
+                                 size_t output_size) {
+  CHECK_GE(output_size, sizeof(DrawPathOp));
+  DrawPathOp* op = new (output) DrawPathOp;
+
+  PaintOpReader helper(input, input_size);
+  helper.Read(&op->flags);
+  helper.Read(&op->path);
+  if (!helper.valid()) {
+    op->~DrawPathOp();
+    return nullptr;
+  }
+  UpdateTypeAndSkip(op);
+  return op;
+}
+
+PaintOp* DrawPosTextOp::Deserialize(const void* input,
+                                    size_t input_size,
+                                    void* output,
+                                    size_t output_size) {
+  // TODO(enne): This is a bit of a weird condition, but to avoid the code
+  // complexity of every Deserialize function being able to (re)allocate
+  // an aligned buffer of the right size, this function asserts that it
+  // will have enough size for the extra data.  It's guaranteed that any extra
+  // memory is at most |input_size| so that plus the op size is an upper bound.
+  // The caller has to awkwardly do this allocation though, sorry.
+  CHECK_GE(output_size, sizeof(DrawPosTextOp) + input_size);
+  DrawPosTextOp* op = new (output) DrawPosTextOp;
+
+  PaintOpReader helper(input, input_size);
+  helper.Read(&op->flags);
+  helper.Read(&op->count);
+  helper.Read(&op->bytes);
+  if (helper.valid()) {
+    helper.ReadArray(op->count, op->GetArray());
+    helper.ReadData(op->bytes, op->GetData());
+  }
+  if (!helper.valid()) {
+    op->~DrawPosTextOp();
+    return nullptr;
+  }
+
+  op->type = static_cast<uint8_t>(PaintOpType::DrawPosText);
+  op->skip = MathUtil::UncheckedRoundUp(
+      sizeof(DrawPosTextOp) + op->bytes + sizeof(SkPoint) * op->count,
+      PaintOpBuffer::PaintOpAlign);
+
+  return op;
+}
+
+PaintOp* DrawRecordOp::Deserialize(const void* input,
+                                   size_t input_size,
+                                   void* output,
+                                   size_t output_size) {
+  // TODO(enne): these must be flattened and not sent directly.
+  // TODO(enne): could also consider caching these service side.
+  NOTREACHED();
+  return nullptr;
+}
+
+PaintOp* DrawRectOp::Deserialize(const void* input,
+                                 size_t input_size,
+                                 void* output,
+                                 size_t output_size) {
+  CHECK_GE(output_size, sizeof(DrawRectOp));
+  DrawRectOp* op = new (output) DrawRectOp;
+
+  PaintOpReader helper(input, input_size);
+  helper.Read(&op->flags);
+  helper.Read(&op->rect);
+  if (!helper.valid()) {
+    op->~DrawRectOp();
+    return nullptr;
+  }
+  UpdateTypeAndSkip(op);
+  return op;
+}
+
+PaintOp* DrawRRectOp::Deserialize(const void* input,
+                                  size_t input_size,
+                                  void* output,
+                                  size_t output_size) {
+  CHECK_GE(output_size, sizeof(DrawRRectOp));
+  DrawRRectOp* op = new (output) DrawRRectOp;
+
+  PaintOpReader helper(input, input_size);
+  helper.Read(&op->flags);
+  helper.Read(&op->rrect);
+  if (!helper.valid()) {
+    op->~DrawRRectOp();
+    return nullptr;
+  }
+  UpdateTypeAndSkip(op);
+  return op;
+}
+
+PaintOp* DrawTextOp::Deserialize(const void* input,
+                                 size_t input_size,
+                                 void* output,
+                                 size_t output_size) {
+  CHECK_GE(output_size, sizeof(DrawTextOp) + input_size);
+  DrawTextOp* op = new (output) DrawTextOp;
+
+  PaintOpReader helper(input, input_size);
+  helper.Read(&op->flags);
+  helper.Read(&op->x);
+  helper.Read(&op->y);
+  helper.Read(&op->bytes);
+  if (helper.valid())
+    helper.ReadData(op->bytes, op->GetData());
+  if (!helper.valid()) {
+    op->~DrawTextOp();
+    return nullptr;
+  }
+
+  op->type = static_cast<uint8_t>(PaintOpType::DrawText);
+  op->skip = MathUtil::UncheckedRoundUp(sizeof(DrawTextOp) + op->bytes,
+                                        PaintOpBuffer::PaintOpAlign);
+  return op;
+}
+
+PaintOp* DrawTextBlobOp::Deserialize(const void* input,
+                                     size_t input_size,
+                                     void* output,
+                                     size_t output_size) {
+  CHECK_GE(output_size, sizeof(DrawTextBlobOp));
+  DrawTextBlobOp* op = new (output) DrawTextBlobOp;
+
+  PaintOpReader helper(input, input_size);
+  helper.Read(&op->flags);
+  helper.Read(&op->x);
+  helper.Read(&op->y);
+  helper.Read(&op->blob);
+  if (!helper.valid()) {
+    op->~DrawTextBlobOp();
+    return nullptr;
+  }
+  UpdateTypeAndSkip(op);
+  return op;
+}
+
+PaintOp* NoopOp::Deserialize(const void* input,
+                             size_t input_size,
+                             void* output,
+                             size_t output_size) {
+  return SimpleDeserialize<NoopOp>(input, input_size, output, output_size);
+}
+
+PaintOp* RestoreOp::Deserialize(const void* input,
+                                size_t input_size,
+                                void* output,
+                                size_t output_size) {
+  return SimpleDeserialize<RestoreOp>(input, input_size, output, output_size);
+}
+
+PaintOp* RotateOp::Deserialize(const void* input,
+                               size_t input_size,
+                               void* output,
+                               size_t output_size) {
+  return SimpleDeserialize<RotateOp>(input, input_size, output, output_size);
+}
+
+PaintOp* SaveOp::Deserialize(const void* input,
+                             size_t input_size,
+                             void* output,
+                             size_t output_size) {
+  return SimpleDeserialize<SaveOp>(input, input_size, output, output_size);
+}
+
+PaintOp* SaveLayerOp::Deserialize(const void* input,
+                                  size_t input_size,
+                                  void* output,
+                                  size_t output_size) {
+  CHECK_GE(output_size, sizeof(SaveLayerOp));
+  SaveLayerOp* op = new (output) SaveLayerOp;
+
+  PaintOpReader helper(input, input_size);
+  helper.Read(&op->flags);
+  helper.Read(&op->bounds);
+  if (!helper.valid()) {
+    op->~SaveLayerOp();
+    return nullptr;
+  }
+  UpdateTypeAndSkip(op);
+  return op;
+}
+
+PaintOp* SaveLayerAlphaOp::Deserialize(const void* input,
+                                       size_t input_size,
+                                       void* output,
+                                       size_t output_size) {
+  return SimpleDeserialize<SaveLayerAlphaOp>(input, input_size, output,
+                                             output_size);
+}
+
+PaintOp* ScaleOp::Deserialize(const void* input,
+                              size_t input_size,
+                              void* output,
+                              size_t output_size) {
+  return SimpleDeserialize<ScaleOp>(input, input_size, output, output_size);
+}
+
+PaintOp* SetMatrixOp::Deserialize(const void* input,
+                                  size_t input_size,
+                                  void* output,
+                                  size_t output_size) {
+  return SimpleDeserialize<SetMatrixOp>(input, input_size, output, output_size);
+}
+
+PaintOp* TranslateOp::Deserialize(const void* input,
+                                  size_t input_size,
+                                  void* output,
+                                  size_t output_size) {
+  return SimpleDeserialize<TranslateOp>(input, input_size, output, output_size);
+}
+
 void AnnotateOp::Raster(const PaintOp* base_op,
                         SkCanvas* canvas,
                         const SkMatrix& original_ctm) {
@@ -516,6 +1316,59 @@
   g_raster_alpha_functions[type](this, canvas, bounds, alpha);
 }
 
+size_t PaintOp::Serialize(void* memory,
+                          size_t size,
+                          const SerializeOptions& options) const {
+  // Need at least enough room for a skip/type header.
+  if (size < 4)
+    return 0u;
+
+  DCHECK_EQ(0u,
+            reinterpret_cast<uintptr_t>(memory) % PaintOpBuffer::PaintOpAlign);
+
+  size_t written = g_serialize_functions[type](this, memory, size, options);
+  DCHECK_LE(written, size);
+  if (written < 4)
+    return 0u;
+
+  size_t aligned_written =
+      MathUtil::UncheckedRoundUp(written, PaintOpBuffer::PaintOpAlign);
+  if (aligned_written >= kMaxSkip)
+    return 0u;
+  if (aligned_written > size)
+    return 0u;
+
+  // Update skip and type now that the size is known.
+  uint32_t skip = static_cast<uint32_t>(aligned_written);
+  static_cast<uint32_t*>(memory)[0] = type | skip << 8;
+  return skip;
+}
+
+PaintOp* PaintOp::Deserialize(const void* input,
+                              size_t input_size,
+                              void* output,
+                              size_t output_size) {
+  // TODO(enne): assert that output_size is big enough.
+  const PaintOp* serialized = reinterpret_cast<const PaintOp*>(input);
+  uint32_t skip = serialized->skip;
+  if (input_size < skip)
+    return nullptr;
+  if (skip % PaintOpBuffer::PaintOpAlign != 0)
+    return nullptr;
+  uint8_t type = serialized->type;
+  if (type > static_cast<uint8_t>(PaintOpType::LastPaintOpType))
+    return nullptr;
+
+  return g_deserialize_functions[serialized->type](input, skip, output,
+                                                   output_size);
+}
+
+void PaintOp::DestroyThis() {
+  auto func = g_destructor_functions[type];
+  if (func)
+    func(this);
+}
+
 int ClipPathOp::CountSlowPaths() const {
   return antialias && !path.isConvex() ? 1 : 0;
 }
@@ -563,6 +1416,8 @@
   return record->HasNonAAPaint();
 }
 
+AnnotateOp::AnnotateOp() = default;
+
 AnnotateOp::AnnotateOp(PaintCanvas::AnnotationType annotation_type,
                        const SkRect& rect,
                        sk_sp<SkData> data)
@@ -587,6 +1442,8 @@
 
 DrawImageOp::~DrawImageOp() = default;
 
+DrawImageRectOp::DrawImageRectOp() = default;
+
 DrawImageRectOp::DrawImageRectOp(const PaintImage& image,
                                  const SkRect& src,
                                  const SkRect& dst,
@@ -604,6 +1461,8 @@
 
 DrawImageRectOp::~DrawImageRectOp() = default;
 
+DrawPosTextOp::DrawPosTextOp() = default;
+
 DrawPosTextOp::DrawPosTextOp(size_t bytes,
                              size_t count,
                              const PaintFlags& flags)
@@ -611,6 +1470,8 @@
 
 DrawPosTextOp::~DrawPosTextOp() = default;
 
+DrawRecordOp::DrawRecordOp() = default;
+
 DrawRecordOp::DrawRecordOp(sk_sp<const PaintRecord> record)
     : record(std::move(record)) {}
 
@@ -624,6 +1485,8 @@
   return record->HasDiscardableImages();
 }
 
+DrawTextBlobOp::DrawTextBlobOp() = default;
+
 DrawTextBlobOp::DrawTextBlobOp(sk_sp<SkTextBlob> blob,
                                SkScalar x,
                                SkScalar y,
@@ -658,11 +1521,8 @@
 }
 
 void PaintOpBuffer::Reset() {
-  for (auto* op : Iterator(this)) {
-    auto func = g_destructor_functions[op->type];
-    if (func)
-      func(op);
-  }
+  for (auto* op : Iterator(this))
+    op->DestroyThis();
 
   // Leave data_ allocated, reserved_ unchanged.
   used_ = 0;
@@ -793,7 +1653,7 @@
   // Compute a skip such that all ops in the buffer are aligned to the
   // maximum required alignment of all ops.
   size_t skip = MathUtil::UncheckedRoundUp(sizeof_op + bytes, PaintOpAlign);
-  DCHECK_LT(skip, static_cast<size_t>(1) << 24);
+  DCHECK_LT(skip, PaintOp::kMaxSkip);
   if (used_ + skip > reserved_) {
     // Start reserved_ at kInitialBufferSize and then double.
     // ShrinkToFit can make this smaller afterwards.
diff --git a/cc/paint/paint_op_buffer.h b/cc/paint/paint_op_buffer.h
index d3cba68a2..fbbb65d6c 100644
--- a/cc/paint/paint_op_buffer.h
+++ b/cc/paint/paint_op_buffer.h
@@ -27,6 +27,8 @@
 
 namespace cc {
 
+class ImageDecodeCache;
+
 class CC_PAINT_EXPORT ThreadsafeMatrix : public SkMatrix {
  public:
   explicit ThreadsafeMatrix(const SkMatrix& matrix) : SkMatrix(matrix) {
@@ -39,8 +41,18 @@
   explicit ThreadsafePath(const SkPath& path) : SkPath(path) {
     updateBoundsCache();
   }
+  ThreadsafePath() { updateBoundsCache(); }
 };
 
+// See PaintOp::Serialize/Deserialize for comments.  Derived Serialize types
+// don't write the 4 byte type/skip header because they don't know how much
+// data they will need to write.  PaintOp::Serialize itself must update it.
+#define HAS_SERIALIZATION_FUNCTIONS()                                   \
+  static size_t Serialize(const PaintOp* op, void* memory, size_t size, \
+                          const SerializeOptions& options);             \
+  static PaintOp* Deserialize(const void* input, size_t input_size,     \
+                              void* output, size_t output_size);
+
 enum class PaintOpType : uint8_t {
   Annotate,
   ClipPath,
@@ -75,9 +87,10 @@
   LastPaintOpType = Translate,
 };
 
-std::string PaintOpTypeToString(PaintOpType type);
+CC_PAINT_EXPORT std::string PaintOpTypeToString(PaintOpType type);
 
-struct CC_PAINT_EXPORT PaintOp {
+class CC_PAINT_EXPORT PaintOp {
+ public:
   uint32_t type : 8;
   uint32_t skip : 24;
 
@@ -90,6 +103,28 @@
   void Raster(SkCanvas* canvas, const SkMatrix& original_ctm) const;
   bool IsDrawOp() const;
 
+  struct SerializeOptions {
+    ImageDecodeCache* decode_cache = nullptr;
+  };
+
+  // Subclasses should provide a static Serialize() method called from here.
+  // If the op can be serialized to |memory| in no more than |size| bytes,
+  // then return the number of bytes written.  If it won't fit, return 0.
+  size_t Serialize(void* memory,
+                   size_t size,
+                   const SerializeOptions& options) const;
+
+  // Deserializes a PaintOp of this type from a given buffer |input| of
+  // at most |input_size| bytes.  Returns null on any errors.
+  // The PaintOp is deserialized into the |output| buffer and returned
+  // if valid.  nullptr is returned if the deserialization fails.
+  // |output_size| must be at least LargestPaintOp + serialized->skip,
+  // to fit all ops.  The caller is responsible for destroying these ops.
+  static PaintOp* Deserialize(const void* input,
+                              size_t input_size,
+                              void* output,
+                              size_t output_size);
+
   // Only valid for draw ops.
   void RasterWithAlpha(SkCanvas* canvas,
                        const SkRect& bounds,
@@ -107,14 +142,20 @@
   // and display lists.  This doesn't count other objects like paths or blobs.
   size_t AdditionalBytesUsed() const { return 0; }
 
+  // Run the destructor for the derived op type.  Ops are usually contained in
+  // memory buffers and so don't have their destructors run automatically.
+  void DestroyThis();
+
   static constexpr bool kIsDrawOp = false;
   static constexpr bool kHasPaintFlags = false;
+  // Since skip and type fit in a uint32_t, this is the max size of skip.
+  static constexpr size_t kMaxSkip = static_cast<size_t>(1 << 24);
   static SkRect kUnsetRect;
 };
 
-struct CC_PAINT_EXPORT PaintOpWithFlags : PaintOp {
+class CC_PAINT_EXPORT PaintOpWithFlags : public PaintOp {
+ public:
   static constexpr bool kHasPaintFlags = true;
-
   explicit PaintOpWithFlags(const PaintFlags& flags) : flags(flags) {}
 
   int CountSlowPathsFromFlags() const { return flags.getPathEffect() ? 1 : 0; }
@@ -135,9 +176,13 @@
   // a const PaintOpWithFlags* parameter so that it can be used as a function
   // pointer.
   PaintFlags flags;
+
+ protected:
+  PaintOpWithFlags() = default;
 };
 
-struct CC_PAINT_EXPORT PaintOpWithData : PaintOpWithFlags {
+class CC_PAINT_EXPORT PaintOpWithData : public PaintOpWithFlags {
+ public:
   // Having data is just a helper for ops that have a varying amount of data and
   // want a way to store that inline.  This is for ops that pass in a
   // void* and a length.  The void* data is assumed to not have any alignment
@@ -150,6 +195,8 @@
   size_t bytes;
 
  protected:
+  PaintOpWithData() = default;
+
   // For some derived object T, return the internally stored data.
   // This needs the fully derived type to know how much to offset
   // from the start of the top to the data.
@@ -173,13 +220,18 @@
   }
 };
 
-struct CC_PAINT_EXPORT PaintOpWithArrayBase : PaintOpWithFlags {
+class CC_PAINT_EXPORT PaintOpWithArrayBase : public PaintOpWithFlags {
+ public:
   explicit PaintOpWithArrayBase(const PaintFlags& flags)
       : PaintOpWithFlags(flags) {}
+
+ protected:
+  PaintOpWithArrayBase() = default;
 };
 
 template <typename M>
-struct CC_PAINT_EXPORT PaintOpWithArray : PaintOpWithArrayBase {
+class CC_PAINT_EXPORT PaintOpWithArray : public PaintOpWithArrayBase {
+ public:
   // Paint op that has a M[count] and a char[bytes].
   // Array data is stored first so that it can be aligned with T's alignment
   // with the arbitrary unaligned char data after it.
@@ -192,6 +244,8 @@
   size_t count;
 
  protected:
+  PaintOpWithArray() = default;
+
   template <typename T>
   const void* GetDataForThis(const T* op) const {
     static_assert(std::is_convertible<T, PaintOpWithArrayBase>::value,
@@ -233,7 +287,8 @@
   }
 };
 
-struct CC_PAINT_EXPORT AnnotateOp final : PaintOp {
+class CC_PAINT_EXPORT AnnotateOp final : public PaintOp {
+ public:
   enum class AnnotationType {
     URL,
     LinkToDestination,
@@ -248,13 +303,18 @@
   static void Raster(const PaintOp* op,
                      SkCanvas* canvas,
                      const SkMatrix& original_ctm);
+  HAS_SERIALIZATION_FUNCTIONS();
 
   PaintCanvas::AnnotationType annotation_type;
   SkRect rect;
   sk_sp<SkData> data;
+
+ private:
+  AnnotateOp();
 };
 
-struct CC_PAINT_EXPORT ClipPathOp final : PaintOp {
+class CC_PAINT_EXPORT ClipPathOp final : public PaintOp {
+ public:
   static constexpr PaintOpType kType = PaintOpType::ClipPath;
   ClipPathOp(SkPath path, SkClipOp op, bool antialias)
       : path(path), op(op), antialias(antialias) {}
@@ -263,26 +323,36 @@
                      const SkMatrix& original_ctm);
   int CountSlowPaths() const;
   bool HasNonAAPaint() const { return !antialias; }
+  HAS_SERIALIZATION_FUNCTIONS();
 
   ThreadsafePath path;
   SkClipOp op;
   bool antialias;
+
+ private:
+  ClipPathOp() = default;
 };
 
-struct CC_PAINT_EXPORT ClipRectOp final : PaintOp {
+class CC_PAINT_EXPORT ClipRectOp final : public PaintOp {
+ public:
   static constexpr PaintOpType kType = PaintOpType::ClipRect;
   ClipRectOp(const SkRect& rect, SkClipOp op, bool antialias)
       : rect(rect), op(op), antialias(antialias) {}
   static void Raster(const PaintOp* op,
                      SkCanvas* canvas,
                      const SkMatrix& original_ctm);
+  HAS_SERIALIZATION_FUNCTIONS();
 
   SkRect rect;
   SkClipOp op;
   bool antialias;
+
+ private:
+  ClipRectOp() = default;
 };
 
-struct CC_PAINT_EXPORT ClipRRectOp final : PaintOp {
+class CC_PAINT_EXPORT ClipRRectOp final : public PaintOp {
+ public:
   static constexpr PaintOpType kType = PaintOpType::ClipRRect;
   ClipRRectOp(const SkRRect& rrect, SkClipOp op, bool antialias)
       : rrect(rrect), op(op), antialias(antialias) {}
@@ -290,23 +360,33 @@
                      SkCanvas* canvas,
                      const SkMatrix& original_ctm);
   bool HasNonAAPaint() const { return !antialias; }
+  HAS_SERIALIZATION_FUNCTIONS();
 
   SkRRect rrect;
   SkClipOp op;
   bool antialias;
+
+ private:
+  ClipRRectOp() = default;
 };
 
-struct CC_PAINT_EXPORT ConcatOp final : PaintOp {
+class CC_PAINT_EXPORT ConcatOp final : public PaintOp {
+ public:
   static constexpr PaintOpType kType = PaintOpType::Concat;
   explicit ConcatOp(const SkMatrix& matrix) : matrix(matrix) {}
   static void Raster(const PaintOp* op,
                      SkCanvas* canvas,
                      const SkMatrix& original_ctm);
+  HAS_SERIALIZATION_FUNCTIONS();
 
   ThreadsafeMatrix matrix;
+
+ private:
+  ConcatOp() = default;
 };
 
-struct CC_PAINT_EXPORT DrawArcOp final : PaintOpWithFlags {
+class CC_PAINT_EXPORT DrawArcOp final : public PaintOpWithFlags {
+ public:
   static constexpr PaintOpType kType = PaintOpType::DrawArc;
   static constexpr bool kIsDrawOp = true;
   DrawArcOp(const SkRect& oval,
@@ -329,14 +409,19 @@
                               const PaintFlags* flags,
                               SkCanvas* canvas,
                               const SkMatrix& original_ctm);
+  HAS_SERIALIZATION_FUNCTIONS();
 
   SkRect oval;
   SkScalar start_angle;
   SkScalar sweep_angle;
   bool use_center;
+
+ private:
+  DrawArcOp() = default;
 };
 
-struct CC_PAINT_EXPORT DrawCircleOp final : PaintOpWithFlags {
+class CC_PAINT_EXPORT DrawCircleOp final : public PaintOpWithFlags {
+ public:
   static constexpr PaintOpType kType = PaintOpType::DrawCircle;
   static constexpr bool kIsDrawOp = true;
   DrawCircleOp(SkScalar cx,
@@ -354,25 +439,35 @@
                               const PaintFlags* flags,
                               SkCanvas* canvas,
                               const SkMatrix& original_ctm);
+  HAS_SERIALIZATION_FUNCTIONS();
 
   SkScalar cx;
   SkScalar cy;
   SkScalar radius;
+
+ private:
+  DrawCircleOp() = default;
 };
 
-struct CC_PAINT_EXPORT DrawColorOp final : PaintOp {
+class CC_PAINT_EXPORT DrawColorOp final : public PaintOp {
+ public:
   static constexpr PaintOpType kType = PaintOpType::DrawColor;
   static constexpr bool kIsDrawOp = true;
   DrawColorOp(SkColor color, SkBlendMode mode) : color(color), mode(mode) {}
   static void Raster(const PaintOp* op,
                      SkCanvas* canvas,
                      const SkMatrix& original_ctm);
+  HAS_SERIALIZATION_FUNCTIONS();
 
   SkColor color;
   SkBlendMode mode;
+
+ private:
+  DrawColorOp() = default;
 };
 
-struct CC_PAINT_EXPORT DrawDRRectOp final : PaintOpWithFlags {
+class CC_PAINT_EXPORT DrawDRRectOp final : public PaintOpWithFlags {
+ public:
   static constexpr PaintOpType kType = PaintOpType::DrawDRRect;
   static constexpr bool kIsDrawOp = true;
   DrawDRRectOp(const SkRRect& outer,
@@ -389,12 +484,17 @@
                               const PaintFlags* flags,
                               SkCanvas* canvas,
                               const SkMatrix& original_ctm);
+  HAS_SERIALIZATION_FUNCTIONS();
 
   SkRRect outer;
   SkRRect inner;
+
+ private:
+  DrawDRRectOp() = default;
 };
 
-struct CC_PAINT_EXPORT DrawImageOp final : PaintOpWithFlags {
+class CC_PAINT_EXPORT DrawImageOp final : public PaintOpWithFlags {
+ public:
   static constexpr PaintOpType kType = PaintOpType::DrawImage;
   static constexpr bool kIsDrawOp = true;
   DrawImageOp(const PaintImage& image,
@@ -414,13 +514,18 @@
                               const SkMatrix& original_ctm);
   bool HasDiscardableImages() const;
   bool HasNonAAPaint() const { return false; }
+  HAS_SERIALIZATION_FUNCTIONS();
 
   PaintImage image;
   SkScalar left;
   SkScalar top;
+
+ private:
+  DrawImageOp() = default;
 };
 
-struct CC_PAINT_EXPORT DrawImageRectOp final : PaintOpWithFlags {
+class CC_PAINT_EXPORT DrawImageRectOp final : public PaintOpWithFlags {
+ public:
   static constexpr PaintOpType kType = PaintOpType::DrawImageRect;
   static constexpr bool kIsDrawOp = true;
   DrawImageRectOp(const PaintImage& image,
@@ -440,14 +545,19 @@
                               SkCanvas* canvas,
                               const SkMatrix& original_ctm);
   bool HasDiscardableImages() const;
+  HAS_SERIALIZATION_FUNCTIONS();
 
   PaintImage image;
   SkRect src;
   SkRect dst;
   PaintCanvas::SrcRectConstraint constraint;
+
+ private:
+  DrawImageRectOp();
 };
 
-struct CC_PAINT_EXPORT DrawIRectOp final : PaintOpWithFlags {
+class CC_PAINT_EXPORT DrawIRectOp final : public PaintOpWithFlags {
+ public:
   static constexpr PaintOpType kType = PaintOpType::DrawIRect;
   static constexpr bool kIsDrawOp = true;
   DrawIRectOp(const SkIRect& rect, const PaintFlags& flags)
@@ -463,11 +573,16 @@
                               SkCanvas* canvas,
                               const SkMatrix& original_ctm);
   bool HasNonAAPaint() const { return false; }
+  HAS_SERIALIZATION_FUNCTIONS();
 
   SkIRect rect;
+
+ private:
+  DrawIRectOp() = default;
 };
 
-struct CC_PAINT_EXPORT DrawLineOp final : PaintOpWithFlags {
+class CC_PAINT_EXPORT DrawLineOp final : public PaintOpWithFlags {
+ public:
   static constexpr PaintOpType kType = PaintOpType::DrawLine;
   static constexpr bool kIsDrawOp = true;
   DrawLineOp(SkScalar x0,
@@ -486,6 +601,7 @@
                               const PaintFlags* flags,
                               SkCanvas* canvas,
                               const SkMatrix& original_ctm);
+  HAS_SERIALIZATION_FUNCTIONS();
 
   int CountSlowPaths() const;
 
@@ -493,9 +609,13 @@
   SkScalar y0;
   SkScalar x1;
   SkScalar y1;
+
+ private:
+  DrawLineOp() = default;
 };
 
-struct CC_PAINT_EXPORT DrawOvalOp final : PaintOpWithFlags {
+class CC_PAINT_EXPORT DrawOvalOp final : public PaintOpWithFlags {
+ public:
   static constexpr PaintOpType kType = PaintOpType::DrawOval;
   static constexpr bool kIsDrawOp = true;
   DrawOvalOp(const SkRect& oval, const PaintFlags& flags)
@@ -510,11 +630,16 @@
                               const PaintFlags* flags,
                               SkCanvas* canvas,
                               const SkMatrix& original_ctm);
+  HAS_SERIALIZATION_FUNCTIONS();
 
   SkRect oval;
+
+ private:
+  DrawOvalOp() = default;
 };
 
-struct CC_PAINT_EXPORT DrawPathOp final : PaintOpWithFlags {
+class CC_PAINT_EXPORT DrawPathOp final : public PaintOpWithFlags {
+ public:
   static constexpr PaintOpType kType = PaintOpType::DrawPath;
   static constexpr bool kIsDrawOp = true;
   DrawPathOp(const SkPath& path, const PaintFlags& flags)
@@ -530,11 +655,16 @@
                               SkCanvas* canvas,
                               const SkMatrix& original_ctm);
   int CountSlowPaths() const;
+  HAS_SERIALIZATION_FUNCTIONS();
 
   ThreadsafePath path;
+
+ private:
+  DrawPathOp() = default;
 };
 
-struct CC_PAINT_EXPORT DrawPosTextOp final : PaintOpWithArray<SkPoint> {
+class CC_PAINT_EXPORT DrawPosTextOp final : public PaintOpWithArray<SkPoint> {
+ public:
   static constexpr PaintOpType kType = PaintOpType::DrawPosText;
   static constexpr bool kIsDrawOp = true;
   DrawPosTextOp(size_t bytes, size_t count, const PaintFlags& flags);
@@ -549,14 +679,19 @@
                               const PaintFlags* flags,
                               SkCanvas* canvas,
                               const SkMatrix& original_ctm);
+  HAS_SERIALIZATION_FUNCTIONS();
 
   const void* GetData() const { return GetDataForThis(this); }
   void* GetData() { return GetDataForThis(this); }
   const SkPoint* GetArray() const { return GetArrayForThis(this); }
   SkPoint* GetArray() { return GetArrayForThis(this); }
+
+ private:
+  DrawPosTextOp();
 };
 
-struct CC_PAINT_EXPORT DrawRecordOp final : PaintOp {
+class CC_PAINT_EXPORT DrawRecordOp final : public PaintOp {
+ public:
   static constexpr PaintOpType kType = PaintOpType::DrawRecord;
   static constexpr bool kIsDrawOp = true;
   explicit DrawRecordOp(sk_sp<const PaintRecord> record);
@@ -568,11 +703,16 @@
   bool HasDiscardableImages() const;
   int CountSlowPaths() const;
   bool HasNonAAPaint() const;
+  HAS_SERIALIZATION_FUNCTIONS();
 
   sk_sp<const PaintRecord> record;
+
+ private:
+  DrawRecordOp();
 };
 
-struct CC_PAINT_EXPORT DrawRectOp final : PaintOpWithFlags {
+class CC_PAINT_EXPORT DrawRectOp final : public PaintOpWithFlags {
+ public:
   static constexpr PaintOpType kType = PaintOpType::DrawRect;
   static constexpr bool kIsDrawOp = true;
   DrawRectOp(const SkRect& rect, const PaintFlags& flags)
@@ -587,11 +727,16 @@
                               const PaintFlags* flags,
                               SkCanvas* canvas,
                               const SkMatrix& original_ctm);
+  HAS_SERIALIZATION_FUNCTIONS();
 
   SkRect rect;
+
+ private:
+  DrawRectOp() = default;
 };
 
-struct CC_PAINT_EXPORT DrawRRectOp final : PaintOpWithFlags {
+class CC_PAINT_EXPORT DrawRRectOp final : public PaintOpWithFlags {
+ public:
   static constexpr PaintOpType kType = PaintOpType::DrawRRect;
   static constexpr bool kIsDrawOp = true;
   DrawRRectOp(const SkRRect& rrect, const PaintFlags& flags)
@@ -606,11 +751,16 @@
                               const PaintFlags* flags,
                               SkCanvas* canvas,
                               const SkMatrix& original_ctm);
+  HAS_SERIALIZATION_FUNCTIONS();
 
   SkRRect rrect;
+
+ private:
+  DrawRRectOp() = default;
 };
 
-struct CC_PAINT_EXPORT DrawTextOp final : PaintOpWithData {
+class CC_PAINT_EXPORT DrawTextOp final : public PaintOpWithData {
+ public:
   static constexpr PaintOpType kType = PaintOpType::DrawText;
   static constexpr bool kIsDrawOp = true;
   DrawTextOp(size_t bytes, SkScalar x, SkScalar y, const PaintFlags& flags)
@@ -625,15 +775,20 @@
                               const PaintFlags* flags,
                               SkCanvas* canvas,
                               const SkMatrix& original_ctm);
+  HAS_SERIALIZATION_FUNCTIONS();
 
   void* GetData() { return GetDataForThis(this); }
   const void* GetData() const { return GetDataForThis(this); }
 
   SkScalar x;
   SkScalar y;
+
+ private:
+  DrawTextOp() = default;
 };
 
-struct CC_PAINT_EXPORT DrawTextBlobOp final : PaintOpWithFlags {
+class CC_PAINT_EXPORT DrawTextBlobOp final : public PaintOpWithFlags {
+ public:
   static constexpr PaintOpType kType = PaintOpType::DrawTextBlob;
   static constexpr bool kIsDrawOp = true;
   DrawTextBlobOp(sk_sp<SkTextBlob> blob,
@@ -651,44 +806,60 @@
                               const PaintFlags* flags,
                               SkCanvas* canvas,
                               const SkMatrix& original_ctm);
+  HAS_SERIALIZATION_FUNCTIONS();
 
   sk_sp<SkTextBlob> blob;
   SkScalar x;
   SkScalar y;
+
+ private:
+  DrawTextBlobOp();
 };
 
-struct CC_PAINT_EXPORT NoopOp final : PaintOp {
+class CC_PAINT_EXPORT NoopOp final : public PaintOp {
+ public:
   static constexpr PaintOpType kType = PaintOpType::Noop;
   static void Raster(const PaintOp* op,
                      SkCanvas* canvas,
                      const SkMatrix& original_ctm) {}
+  HAS_SERIALIZATION_FUNCTIONS();
 };
 
-struct CC_PAINT_EXPORT RestoreOp final : PaintOp {
+class CC_PAINT_EXPORT RestoreOp final : public PaintOp {
+ public:
   static constexpr PaintOpType kType = PaintOpType::Restore;
   static void Raster(const PaintOp* op,
                      SkCanvas* canvas,
                      const SkMatrix& original_ctm);
+  HAS_SERIALIZATION_FUNCTIONS();
 };
 
-struct CC_PAINT_EXPORT RotateOp final : PaintOp {
+class CC_PAINT_EXPORT RotateOp final : public PaintOp {
+ public:
   static constexpr PaintOpType kType = PaintOpType::Rotate;
   explicit RotateOp(SkScalar degrees) : degrees(degrees) {}
   static void Raster(const PaintOp* op,
                      SkCanvas* canvas,
                      const SkMatrix& original_ctm);
+  HAS_SERIALIZATION_FUNCTIONS();
 
   SkScalar degrees;
+
+ private:
+  RotateOp() = default;
 };
 
-struct CC_PAINT_EXPORT SaveOp final : PaintOp {
+class CC_PAINT_EXPORT SaveOp final : public PaintOp {
+ public:
   static constexpr PaintOpType kType = PaintOpType::Save;
   static void Raster(const PaintOp* op,
                      SkCanvas* canvas,
                      const SkMatrix& original_ctm);
+  HAS_SERIALIZATION_FUNCTIONS();
 };
 
-struct CC_PAINT_EXPORT SaveLayerOp final : PaintOpWithFlags {
+class CC_PAINT_EXPORT SaveLayerOp final : public PaintOpWithFlags {
+ public:
   static constexpr PaintOpType kType = PaintOpType::SaveLayer;
   SaveLayerOp(const SkRect* bounds, const PaintFlags* flags)
       : PaintOpWithFlags(flags ? *flags : PaintFlags()),
@@ -704,11 +875,16 @@
                               SkCanvas* canvas,
                               const SkMatrix& original_ctm);
   bool HasNonAAPaint() const { return false; }
+  HAS_SERIALIZATION_FUNCTIONS();
 
   SkRect bounds;
+
+ private:
+  SaveLayerOp() = default;
 };
 
-struct CC_PAINT_EXPORT SaveLayerAlphaOp final : PaintOp {
+class CC_PAINT_EXPORT SaveLayerAlphaOp final : public PaintOp {
+ public:
   static constexpr PaintOpType kType = PaintOpType::SaveLayerAlpha;
   SaveLayerAlphaOp(const SkRect* bounds,
                    uint8_t alpha,
@@ -719,24 +895,34 @@
   static void Raster(const PaintOp* op,
                      SkCanvas* canvas,
                      const SkMatrix& original_ctm);
+  HAS_SERIALIZATION_FUNCTIONS();
 
   SkRect bounds;
   uint8_t alpha;
   bool preserve_lcd_text_requests;
+
+ private:
+  SaveLayerAlphaOp() = default;
 };
 
-struct CC_PAINT_EXPORT ScaleOp final : PaintOp {
+class CC_PAINT_EXPORT ScaleOp final : public PaintOp {
+ public:
   static constexpr PaintOpType kType = PaintOpType::Scale;
   ScaleOp(SkScalar sx, SkScalar sy) : sx(sx), sy(sy) {}
   static void Raster(const PaintOp* op,
                      SkCanvas* canvas,
                      const SkMatrix& original_ctm);
+  HAS_SERIALIZATION_FUNCTIONS();
 
   SkScalar sx;
   SkScalar sy;
+
+ private:
+  ScaleOp() = default;
 };
 
-struct CC_PAINT_EXPORT SetMatrixOp final : PaintOp {
+class CC_PAINT_EXPORT SetMatrixOp final : public PaintOp {
+ public:
   static constexpr PaintOpType kType = PaintOpType::SetMatrix;
   explicit SetMatrixOp(const SkMatrix& matrix) : matrix(matrix) {}
   // This is the only op that needs the original ctm of the SkCanvas
@@ -748,21 +934,32 @@
   static void Raster(const PaintOp* op,
                      SkCanvas* canvas,
                      const SkMatrix& original_ctm);
+  HAS_SERIALIZATION_FUNCTIONS();
 
   ThreadsafeMatrix matrix;
+
+ private:
+  SetMatrixOp() = default;
 };
 
-struct CC_PAINT_EXPORT TranslateOp final : PaintOp {
+class CC_PAINT_EXPORT TranslateOp final : public PaintOp {
+ public:
   static constexpr PaintOpType kType = PaintOpType::Translate;
   TranslateOp(SkScalar dx, SkScalar dy) : dx(dx), dy(dy) {}
   static void Raster(const PaintOp* op,
                      SkCanvas* canvas,
                      const SkMatrix& original_ctm);
+  HAS_SERIALIZATION_FUNCTIONS();
 
   SkScalar dx;
   SkScalar dy;
+
+ private:
+  TranslateOp() = default;
 };
 
+#undef HAS_SERIALIZATION_FUNCTIONS
+
 using LargestPaintOp = DrawDRRectOp;
 
 class CC_PAINT_EXPORT PaintOpBuffer : public SkRefCnt {
diff --git a/cc/paint/paint_op_buffer_unittest.cc b/cc/paint/paint_op_buffer_unittest.cc
index 913db86..ac1181ca 100644
--- a/cc/paint/paint_op_buffer_unittest.cc
+++ b/cc/paint/paint_op_buffer_unittest.cc
@@ -4,10 +4,12 @@
 
 #include "cc/paint/paint_op_buffer.h"
 #include "base/memory/ptr_util.h"
+#include "base/strings/stringprintf.h"
 #include "cc/paint/display_item_list.h"
 #include "cc/test/skia_common.h"
 #include "cc/test/test_skcanvas.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkWriteBuffer.h"
 #include "third_party/skia/include/effects/SkDashPathEffect.h"
 
 using testing::_;
@@ -916,4 +918,1148 @@
   buffer.Playback(&canvas);
 }
 
+std::vector<float> test_floats = {0.f,
+                                  1.f,
+                                  -1.f,
+                                  2384.981971f,
+                                  0.0001f,
+                                  std::numeric_limits<float>::min(),
+                                  std::numeric_limits<float>::max(),
+                                  std::numeric_limits<float>::infinity()};
+
+std::vector<uint8_t> test_uint8s = {
+    0, 255, 128, 10, 45,
+};
+
+std::vector<SkRect> test_rects = {
+    SkRect::MakeXYWH(1, 2.5, 3, 4), SkRect::MakeXYWH(0, 0, 0, 0),
+    SkRect::MakeLargest(),          SkRect::MakeXYWH(0.5f, 0.5f, 8.2f, 8.2f),
+    SkRect::MakeXYWH(-1, -1, 0, 0), SkRect::MakeXYWH(-100, -101, -102, -103)};
+
+std::vector<SkRRect> test_rrects = {
+    SkRRect::MakeEmpty(), SkRRect::MakeOval(SkRect::MakeXYWH(1, 2, 3, 4)),
+    SkRRect::MakeRect(SkRect::MakeXYWH(-10, 100, 5, 4)),
+    [] {
+      SkRRect rrect = SkRRect::MakeEmpty();
+      rrect.setNinePatch(SkRect::MakeXYWH(10, 20, 30, 40), 1, 2, 3, 4);
+      return rrect;
+    }(),
+};
+
+std::vector<SkIRect> test_irects = {
+    SkIRect::MakeXYWH(1, 2, 3, 4),   SkIRect::MakeXYWH(0, 0, 0, 0),
+    SkIRect::MakeLargest(),          SkIRect::MakeXYWH(0, 0, 10, 10),
+    SkIRect::MakeXYWH(-1, -1, 0, 0), SkIRect::MakeXYWH(-100, -101, -102, -103)};
+
+std::vector<SkMatrix> test_matrices = {
+    SkMatrix(),
+    SkMatrix::MakeScale(3.91f, 4.31f),
+    SkMatrix::MakeTrans(-5.2f, 8.7f),
+    [] {
+      SkMatrix matrix;
+      SkScalar buffer[] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
+      matrix.set9(buffer);
+      return matrix;
+    }(),
+    [] {
+      SkMatrix matrix;
+      SkScalar buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+      matrix.set9(buffer);
+      return matrix;
+    }(),
+};
+
+std::vector<SkPath> test_paths = {
+    [] {
+      SkPath path;
+      path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
+      path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
+      path.lineTo(SkIntToScalar(30), SkIntToScalar(30));
+      path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
+      return path;
+    }(),
+    [] {
+      SkPath path;
+      path.addCircle(2, 2, 5);
+      path.addCircle(3, 4, 2);
+      path.addArc(SkRect::MakeXYWH(1, 2, 3, 4), 5, 6);
+      return path;
+    }(),
+    SkPath(),
+};
+
+// TODO(enne): make this more real.
+std::vector<PaintFlags> test_flags = {
+    PaintFlags(), PaintFlags(), PaintFlags(), PaintFlags(), PaintFlags(),
+};
+
+std::vector<SkColor> test_colors = {
+    SkColorSetARGBInline(0, 0, 0, 0),
+    SkColorSetARGBInline(255, 255, 255, 255),
+    SkColorSetARGBInline(0, 255, 10, 255),
+    SkColorSetARGBInline(255, 0, 20, 255),
+    SkColorSetARGBInline(30, 255, 0, 255),
+    SkColorSetARGBInline(255, 40, 0, 0),
+};
+
+std::vector<std::string> test_strings = {
+    "", "foobar",
+    "blarbideeblarasdfaiousydfp234poiausdofiuapsodfjknla;sdfkjasd;f",
+};
+
+std::vector<std::vector<SkPoint>> test_point_arrays = {
+    std::vector<SkPoint>(),
+    {SkPoint::Make(1, 2)},
+    {SkPoint::Make(1, 2), SkPoint::Make(-5.4f, -3.8f)},
+    {SkPoint::Make(0, 0), SkPoint::Make(5, 6), SkPoint::Make(-1, -1),
+     SkPoint::Make(9, 9), SkPoint::Make(50, 50), SkPoint::Make(100, 100)},
+};
+
+std::vector<sk_sp<SkTextBlob>> test_blobs = {
+    [] {
+      SkPaint font;
+      font.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+
+      SkTextBlobBuilder builder;
+      builder.allocRun(font, 5, 1.2f, 2.3f, &test_rects[0]);
+      return builder.make();
+    }(),
+    [] {
+      SkPaint font;
+      font.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+
+      SkTextBlobBuilder builder;
+      builder.allocRun(font, 5, 1.2f, 2.3f, &test_rects[0]);
+      builder.allocRunPos(font, 16, &test_rects[1]);
+      builder.allocRunPosH(font, 8, 0, &test_rects[2]);
+      return builder.make();
+    }(),
+};
+
+// TODO(enne): In practice, probably all paint images need to be uploaded
+// ahead of time and not be bitmaps. These paint images should be fake
+// gpu resource paint images.
+std::vector<PaintImage> test_images = {
+    PaintImage(PaintImage::GetNextId(),
+               CreateDiscardableImage(gfx::Size(5, 10))),
+    PaintImage(PaintImage::GetNextId(),
+               CreateDiscardableImage(gfx::Size(1, 1))),
+    PaintImage(PaintImage::GetNextId(),
+               CreateDiscardableImage(gfx::Size(50, 50))),
+};
+
+// Writes as many ops in |buffer| as can fit in |output_size| to |output|.
+// Records the numbers of bytes written for each op.
+class SimpleSerializer {
+ public:
+  SimpleSerializer(void* output, size_t output_size)
+      : current_(static_cast<char*>(output)),
+        output_size_(output_size),
+        remaining_(output_size) {}
+
+  void Serialize(const PaintOpBuffer& buffer) {
+    bytes_written_.resize(buffer.size());
+    for (size_t i = 0; i < buffer.size(); ++i)
+      bytes_written_[i] = 0;
+
+    PaintOp::SerializeOptions options;
+
+    size_t op_idx = 0;
+    for (const auto* op : PaintOpBuffer::Iterator(&buffer)) {
+      size_t bytes_written = op->Serialize(current_, remaining_, options);
+      if (!bytes_written)
+        return;
+
+      PaintOp* written = reinterpret_cast<PaintOp*>(current_);
+      EXPECT_EQ(op->GetType(), written->GetType());
+      EXPECT_EQ(bytes_written, written->skip);
+
+      bytes_written_[op_idx] = bytes_written;
+      op_idx++;
+      current_ += bytes_written;
+      remaining_ -= bytes_written;
+
+      // Number of bytes bytes_written must be a multiple of PaintOpAlign
+      // unless the buffer is filled entirely.
+      if (remaining_ != 0u)
+        DCHECK_EQ(0u, bytes_written % PaintOpBuffer::PaintOpAlign);
+    }
+  }
+
+  const std::vector<size_t>& bytes_written() const { return bytes_written_; }
+  size_t TotalBytesWritten() const { return output_size_ - remaining_; }
+
+ private:
+  char* current_ = nullptr;
+  size_t output_size_ = 0u;
+  size_t remaining_ = 0u;
+  std::vector<size_t> bytes_written_;
+};
+
+class DeserializerIterator {
+ public:
+  DeserializerIterator(const void* input, size_t input_size)
+      : DeserializerIterator(input,
+                             static_cast<const char*>(input),
+                             input_size,
+                             input_size) {}
+
+  DeserializerIterator(DeserializerIterator&&) = default;
+  DeserializerIterator& operator=(DeserializerIterator&&) = default;
+
+  ~DeserializerIterator() { DestroyDeserializedOp(); }
+
+  DeserializerIterator begin() {
+    return DeserializerIterator(input_, static_cast<const char*>(input_),
+                                input_size_, input_size_);
+  }
+  DeserializerIterator end() {
+    return DeserializerIterator(
+        input_, static_cast<const char*>(input_) + input_size_, input_size_, 0);
+  }
+  bool operator!=(const DeserializerIterator& other) {
+    return input_ != other.input_ || current_ != other.current_ ||
+           input_size_ != other.input_size_ || remaining_ != other.remaining_;
+  }
+  DeserializerIterator& operator++() {
+    const PaintOp* serialized = reinterpret_cast<const PaintOp*>(current_);
+
+    CHECK_GE(remaining_, serialized->skip);
+    current_ += serialized->skip;
+    remaining_ -= serialized->skip;
+
+    if (remaining_ > 0)
+      CHECK_GE(remaining_, 4u);
+
+    DeserializeCurrentOp();
+
+    return *this;
+  }
+
+  operator bool() const { return remaining_ == 0u; }
+  const PaintOp* operator->() const { return deserialized_op_; }
+  const PaintOp* operator*() const { return deserialized_op_; }
+
+ private:
+  DeserializerIterator(const void* input,
+                       const char* current,
+                       size_t input_size,
+                       size_t remaining)
+      : input_(input),
+        current_(current),
+        input_size_(input_size),
+        remaining_(remaining) {
+    DeserializeCurrentOp();
+  }
+
+  void DestroyDeserializedOp() {
+    if (!deserialized_op_)
+      return;
+    deserialized_op_->DestroyThis();
+    deserialized_op_ = nullptr;
+  }
+
+  void DeserializeCurrentOp() {
+    DestroyDeserializedOp();
+
+    if (!remaining_)
+      return;
+
+    const PaintOp* serialized = reinterpret_cast<const PaintOp*>(current_);
+    size_t required = sizeof(LargestPaintOp) + serialized->skip;
+
+    if (data_size_ < required) {
+      data_.reset(static_cast<char*>(
+          base::AlignedAlloc(required, PaintOpBuffer::PaintOpAlign)));
+      data_size_ = required;
+    }
+    deserialized_op_ =
+        PaintOp::Deserialize(current_, remaining_, data_.get(), data_size_);
+  }
+
+  const void* input_ = nullptr;
+  const char* current_ = nullptr;
+  size_t input_size_ = 0u;
+  size_t remaining_ = 0u;
+  std::unique_ptr<char, base::AlignedFreeDeleter> data_;
+  size_t data_size_ = 0u;
+  PaintOp* deserialized_op_ = nullptr;
+};
+
+void PushAnnotateOps(PaintOpBuffer* buffer) {
+  buffer->push<AnnotateOp>(PaintCanvas::AnnotationType::URL, test_rects[0],
+                           SkData::MakeWithCString("thingerdoowhatchamagig"));
+  // Deliberately test both null and empty SkData.
+  buffer->push<AnnotateOp>(PaintCanvas::AnnotationType::LINK_TO_DESTINATION,
+                           test_rects[1], nullptr);
+  buffer->push<AnnotateOp>(PaintCanvas::AnnotationType::NAMED_DESTINATION,
+                           test_rects[2], SkData::MakeEmpty());
+}
+
+void PushClipPathOps(PaintOpBuffer* buffer) {
+  for (size_t i = 0; i < test_paths.size(); ++i) {
+    SkClipOp op = i % 3 ? SkClipOp::kDifference : SkClipOp::kIntersect;
+    buffer->push<ClipPathOp>(test_paths[i], op, !!(i % 2));
+  }
+}
+
+void PushClipRectOps(PaintOpBuffer* buffer) {
+  for (size_t i = 0; i < test_rects.size(); ++i) {
+    SkClipOp op = i % 2 ? SkClipOp::kIntersect : SkClipOp::kDifference;
+    bool antialias = !!(i % 3);
+    buffer->push<ClipRectOp>(test_rects[i], op, antialias);
+  }
+}
+
+void PushClipRRectOps(PaintOpBuffer* buffer) {
+  for (size_t i = 0; i < test_rrects.size(); ++i) {
+    SkClipOp op = i % 2 ? SkClipOp::kIntersect : SkClipOp::kDifference;
+    bool antialias = !!(i % 3);
+    buffer->push<ClipRRectOp>(test_rrects[i], op, antialias);
+  }
+}
+
+void PushConcatOps(PaintOpBuffer* buffer) {
+  for (size_t i = 0; i < test_matrices.size(); ++i)
+    buffer->push<ConcatOp>(test_matrices[i]);
+}
+
+void PushDrawArcOps(PaintOpBuffer* buffer) {
+  size_t len = std::min(std::min(test_floats.size() - 1, test_flags.size()),
+                        test_rects.size());
+  for (size_t i = 0; i < len; ++i) {
+    bool use_center = !!(i % 2);
+    buffer->push<DrawArcOp>(test_rects[i], test_floats[i], test_floats[i + 1],
+                            use_center, test_flags[i]);
+  }
+}
+
+void PushDrawCircleOps(PaintOpBuffer* buffer) {
+  size_t len = std::min(test_floats.size() - 2, test_flags.size());
+  for (size_t i = 0; i < len; ++i) {
+    buffer->push<DrawCircleOp>(test_floats[i], test_floats[i + 1],
+                               test_floats[i + 2], test_flags[i]);
+  }
+}
+
+void PushDrawColorOps(PaintOpBuffer* buffer) {
+  for (size_t i = 0; i < test_colors.size(); ++i) {
+    buffer->push<DrawColorOp>(test_colors[i], static_cast<SkBlendMode>(i));
+  }
+}
+
+void PushDrawDRRectOps(PaintOpBuffer* buffer) {
+  size_t len = std::min(test_rrects.size() - 1, test_flags.size());
+  for (size_t i = 0; i < len; ++i) {
+    buffer->push<DrawDRRectOp>(test_rrects[i], test_rrects[i + 1],
+                               test_flags[i]);
+  }
+}
+
+void PushDrawImageOps(PaintOpBuffer* buffer) {
+  size_t len = std::min(std::min(test_images.size(), test_flags.size()),
+                        test_floats.size() - 1);
+  for (size_t i = 0; i < len; ++i) {
+    buffer->push<DrawImageOp>(test_images[i], test_floats[i],
+                              test_floats[i + 1], &test_flags[i]);
+  }
+
+  // Test optional flags
+  // TODO(enne): maybe all these optional ops should not be optional.
+  buffer->push<DrawImageOp>(test_images[0], test_floats[0], test_floats[1],
+                            nullptr);
+}
+
+void PushDrawImageRectOps(PaintOpBuffer* buffer) {
+  size_t len = std::min(std::min(test_images.size(), test_flags.size()),
+                        test_rects.size() - 1);
+  for (size_t i = 0; i < len; ++i) {
+    PaintCanvas::SrcRectConstraint constraint =
+        i % 2 ? PaintCanvas::kStrict_SrcRectConstraint
+              : PaintCanvas::kFast_SrcRectConstraint;
+    buffer->push<DrawImageRectOp>(test_images[i], test_rects[i],
+                                  test_rects[i + 1], &test_flags[i],
+                                  constraint);
+  }
+
+  // Test optional flags.
+  buffer->push<DrawImageRectOp>(test_images[0], test_rects[0], test_rects[1],
+                                nullptr,
+                                PaintCanvas::kStrict_SrcRectConstraint);
+}
+
+void PushDrawIRectOps(PaintOpBuffer* buffer) {
+  size_t len = std::min(test_irects.size(), test_flags.size());
+  for (size_t i = 0; i < len; ++i)
+    buffer->push<DrawIRectOp>(test_irects[i], test_flags[i]);
+}
+
+void PushDrawLineOps(PaintOpBuffer* buffer) {
+  size_t len = std::min(test_floats.size() - 3, test_flags.size());
+  for (size_t i = 0; i < len; ++i) {
+    buffer->push<DrawLineOp>(test_floats[i], test_floats[i + 1],
+                             test_floats[i + 2], test_floats[i + 3],
+                             test_flags[i]);
+  }
+}
+
+void PushDrawOvalOps(PaintOpBuffer* buffer) {
+  size_t len = std::min(test_paths.size(), test_flags.size());
+  for (size_t i = 0; i < len; ++i)
+    buffer->push<DrawOvalOp>(test_rects[i], test_flags[i]);
+}
+
+void PushDrawPathOps(PaintOpBuffer* buffer) {
+  size_t len = std::min(test_paths.size(), test_flags.size());
+  for (size_t i = 0; i < len; ++i)
+    buffer->push<DrawPathOp>(test_paths[i], test_flags[i]);
+}
+
+void PushDrawPosTextOps(PaintOpBuffer* buffer) {
+  size_t len = std::min(std::min(test_flags.size(), test_strings.size()),
+                        test_point_arrays.size());
+  for (size_t i = 0; i < len; ++i) {
+    // Make sure empty array works fine.
+    SkPoint* array =
+        test_point_arrays[i].size() > 0 ? &test_point_arrays[i][0] : nullptr;
+    buffer->push_with_array<DrawPosTextOp>(
+        test_strings[i].c_str(), test_strings[i].size() + 1, array,
+        test_point_arrays[i].size(), test_flags[i]);
+  }
+}
+
+void PushDrawRectOps(PaintOpBuffer* buffer) {
+  size_t len = std::min(test_rects.size(), test_flags.size());
+  for (size_t i = 0; i < len; ++i)
+    buffer->push<DrawRectOp>(test_rects[i], test_flags[i]);
+}
+
+void PushDrawRRectOps(PaintOpBuffer* buffer) {
+  size_t len = std::min(test_rrects.size(), test_flags.size());
+  for (size_t i = 0; i < len; ++i)
+    buffer->push<DrawRRectOp>(test_rrects[i], test_flags[i]);
+}
+
+void PushDrawTextOps(PaintOpBuffer* buffer) {
+  size_t len = std::min(std::min(test_strings.size(), test_flags.size()),
+                        test_floats.size() - 1);
+  for (size_t i = 0; i < len; ++i) {
+    buffer->push_with_data<DrawTextOp>(
+        test_strings[i].c_str(), test_strings[i].size() + 1, test_floats[i],
+        test_floats[i + 1], test_flags[i]);
+  }
+}
+
+void PushDrawTextBlobOps(PaintOpBuffer* buffer) {
+  size_t len = std::min(std::min(test_blobs.size(), test_flags.size()),
+                        test_floats.size() - 1);
+  for (size_t i = 0; i < len; ++i) {
+    buffer->push<DrawTextBlobOp>(test_blobs[i], test_floats[i],
+                                 test_floats[i + 1], test_flags[i]);
+  }
+}
+
+void PushNoopOps(PaintOpBuffer* buffer) {
+  buffer->push<NoopOp>();
+  buffer->push<NoopOp>();
+  buffer->push<NoopOp>();
+  buffer->push<NoopOp>();
+}
+
+void PushRestoreOps(PaintOpBuffer* buffer) {
+  buffer->push<RestoreOp>();
+  buffer->push<RestoreOp>();
+  buffer->push<RestoreOp>();
+  buffer->push<RestoreOp>();
+}
+
+void PushRotateOps(PaintOpBuffer* buffer) {
+  for (size_t i = 0; i < test_floats.size(); ++i)
+    buffer->push<RotateOp>(test_floats[i]);
+}
+
+void PushSaveOps(PaintOpBuffer* buffer) {
+  buffer->push<SaveOp>();
+  buffer->push<SaveOp>();
+  buffer->push<SaveOp>();
+  buffer->push<SaveOp>();
+}
+
+void PushSaveLayerOps(PaintOpBuffer* buffer) {
+  size_t len = std::min(test_flags.size(), test_rects.size());
+  for (size_t i = 0; i < len; ++i)
+    buffer->push<SaveLayerOp>(&test_rects[i], &test_flags[i]);
+
+  // Test combinations of optional args.
+  buffer->push<SaveLayerOp>(nullptr, &test_flags[0]);
+  buffer->push<SaveLayerOp>(&test_rects[0], nullptr);
+  buffer->push<SaveLayerOp>(nullptr, nullptr);
+}
+
+void PushSaveLayerAlphaOps(PaintOpBuffer* buffer) {
+  size_t len = std::min(test_uint8s.size(), test_rects.size());
+  for (size_t i = 0; i < len; ++i)
+    buffer->push<SaveLayerAlphaOp>(&test_rects[i], test_uint8s[i], !!(i % 2));
+
+  // Test optional args.
+  buffer->push<SaveLayerAlphaOp>(nullptr, test_uint8s[0], false);
+}
+
+void PushScaleOps(PaintOpBuffer* buffer) {
+  for (size_t i = 0; i < test_floats.size(); i += 2)
+    buffer->push<ScaleOp>(test_floats[i], test_floats[i + 1]);
+}
+
+void PushSetMatrixOps(PaintOpBuffer* buffer) {
+  for (size_t i = 0; i < test_matrices.size(); ++i)
+    buffer->push<SetMatrixOp>(test_matrices[i]);
+}
+
+void PushTranslateOps(PaintOpBuffer* buffer) {
+  for (size_t i = 0; i < test_floats.size(); i += 2)
+    buffer->push<TranslateOp>(test_floats[i], test_floats[i + 1]);
+}
+
+void CompareFlags(const PaintFlags& original, const PaintFlags& written) {}
+
+void CompareImages(const PaintImage& original, const PaintImage& written) {}
+
+void CompareAnnotateOp(const AnnotateOp* original, const AnnotateOp* written) {
+  EXPECT_EQ(original->annotation_type, written->annotation_type);
+  EXPECT_EQ(original->rect, written->rect);
+  EXPECT_EQ(!!original->data, !!written->data);
+  if (original->data) {
+    EXPECT_EQ(original->data->size(), written->data->size());
+    EXPECT_EQ(0, memcmp(original->data->data(), written->data->data(),
+                        written->data->size()));
+  }
+}
+
+void CompareClipPathOp(const ClipPathOp* original, const ClipPathOp* written) {
+  EXPECT_TRUE(original->path == written->path);
+  EXPECT_EQ(original->op, written->op);
+  EXPECT_EQ(original->antialias, written->antialias);
+}
+
+void CompareClipRectOp(const ClipRectOp* original, const ClipRectOp* written) {
+  EXPECT_EQ(original->rect, written->rect);
+  EXPECT_EQ(original->op, written->op);
+  EXPECT_EQ(original->antialias, written->antialias);
+}
+
+void CompareClipRRectOp(const ClipRRectOp* original,
+                        const ClipRRectOp* written) {
+  EXPECT_EQ(original->rrect, written->rrect);
+  EXPECT_EQ(original->op, written->op);
+  EXPECT_EQ(original->antialias, written->antialias);
+}
+
+void CompareConcatOp(const ConcatOp* original, const ConcatOp* written) {
+  EXPECT_EQ(original->matrix, written->matrix);
+  EXPECT_EQ(original->matrix.getType(), written->matrix.getType());
+}
+
+void CompareDrawArcOp(const DrawArcOp* original, const DrawArcOp* written) {
+  CompareFlags(original->flags, written->flags);
+  EXPECT_EQ(original->oval, written->oval);
+  EXPECT_EQ(original->start_angle, written->start_angle);
+  EXPECT_EQ(original->sweep_angle, written->sweep_angle);
+  EXPECT_EQ(original->use_center, written->use_center);
+}
+
+void CompareDrawCircleOp(const DrawCircleOp* original,
+                         const DrawCircleOp* written) {
+  CompareFlags(original->flags, written->flags);
+  EXPECT_EQ(original->cx, written->cx);
+  EXPECT_EQ(original->cy, written->cy);
+  EXPECT_EQ(original->radius, written->radius);
+}
+
+void CompareDrawColorOp(const DrawColorOp* original,
+                        const DrawColorOp* written) {
+  EXPECT_EQ(original->color, written->color);
+}
+
+void CompareDrawDRRectOp(const DrawDRRectOp* original,
+                         const DrawDRRectOp* written) {
+  CompareFlags(original->flags, written->flags);
+  EXPECT_EQ(original->outer, written->outer);
+  EXPECT_EQ(original->inner, written->inner);
+}
+
+void CompareDrawImageOp(const DrawImageOp* original,
+                        const DrawImageOp* written) {
+  CompareFlags(original->flags, written->flags);
+  CompareImages(original->image, written->image);
+  EXPECT_EQ(original->left, written->left);
+  EXPECT_EQ(original->top, written->top);
+}
+
+void CompareDrawImageRectOp(const DrawImageRectOp* original,
+                            const DrawImageRectOp* written) {
+  CompareFlags(original->flags, written->flags);
+  CompareImages(original->image, written->image);
+  EXPECT_EQ(original->src, written->src);
+  EXPECT_EQ(original->dst, written->dst);
+}
+
+void CompareDrawIRectOp(const DrawIRectOp* original,
+                        const DrawIRectOp* written) {
+  CompareFlags(original->flags, written->flags);
+  EXPECT_EQ(original->rect, written->rect);
+}
+
+void CompareDrawLineOp(const DrawLineOp* original, const DrawLineOp* written) {
+  CompareFlags(original->flags, written->flags);
+  EXPECT_EQ(original->x0, written->x0);
+  EXPECT_EQ(original->y0, written->y0);
+  EXPECT_EQ(original->x1, written->x1);
+  EXPECT_EQ(original->y1, written->y1);
+}
+
+void CompareDrawOvalOp(const DrawOvalOp* original, const DrawOvalOp* written) {
+  CompareFlags(original->flags, written->flags);
+  EXPECT_EQ(original->oval, written->oval);
+}
+
+void CompareDrawPathOp(const DrawPathOp* original, const DrawPathOp* written) {
+  CompareFlags(original->flags, written->flags);
+  EXPECT_TRUE(original->path == written->path);
+}
+
+void CompareDrawPosTextOp(const DrawPosTextOp* original,
+                          const DrawPosTextOp* written) {
+  CompareFlags(original->flags, written->flags);
+  ASSERT_EQ(original->bytes, written->bytes);
+  EXPECT_EQ(std::string(static_cast<const char*>(original->GetData())),
+            std::string(static_cast<const char*>(written->GetData())));
+  ASSERT_EQ(original->count, written->count);
+  for (size_t i = 0; i < original->count; ++i)
+    EXPECT_EQ(original->GetArray()[i], written->GetArray()[i]);
+}
+
+void CompareDrawRectOp(const DrawRectOp* original, const DrawRectOp* written) {
+  CompareFlags(original->flags, written->flags);
+  EXPECT_EQ(original->rect, written->rect);
+}
+
+void CompareDrawRRectOp(const DrawRRectOp* original,
+                        const DrawRRectOp* written) {
+  CompareFlags(original->flags, written->flags);
+  EXPECT_EQ(original->rrect, written->rrect);
+}
+
+void CompareDrawTextOp(const DrawTextOp* original, const DrawTextOp* written) {
+  CompareFlags(original->flags, written->flags);
+  EXPECT_EQ(original->x, written->x);
+  EXPECT_EQ(original->y, written->y);
+  ASSERT_EQ(original->bytes, written->bytes);
+  EXPECT_EQ(std::string(static_cast<const char*>(original->GetData())),
+            std::string(static_cast<const char*>(written->GetData())));
+}
+
+void CompareDrawTextBlobOp(const DrawTextBlobOp* original,
+                           const DrawTextBlobOp* written) {
+  CompareFlags(original->flags, written->flags);
+  EXPECT_EQ(original->x, written->x);
+  EXPECT_EQ(original->y, written->y);
+
+  // TODO(enne): implement SkTextBlob serialization: http://crbug.com/737629
+  if (!original->blob || !written->blob)
+    return;
+
+  ASSERT_TRUE(original->blob);
+  ASSERT_TRUE(written->blob);
+
+  // No text blob operator==, so flatten them both and compare.
+  size_t max_size = original->skip;
+
+  std::vector<char> original_mem;
+  original_mem.resize(max_size);
+  SkBinaryWriteBuffer original_flattened(&original_mem[0], max_size);
+  original->blob->flatten(original_flattened);
+  original_mem.resize(original_flattened.bytesWritten());
+
+  std::vector<char> written_mem;
+  written_mem.resize(max_size);
+  SkBinaryWriteBuffer written_flattened(&written_mem[0], max_size);
+  written->blob->flatten(written_flattened);
+  written_mem.resize(written_flattened.bytesWritten());
+
+  ASSERT_EQ(original_mem.size(), written_mem.size());
+  EXPECT_EQ(original_mem, written_mem);
+}
+
+void CompareNoopOp(const NoopOp* original, const NoopOp* written) {
+  // Nothing to compare.
+}
+
+void CompareRestoreOp(const RestoreOp* original, const RestoreOp* written) {
+  // Nothing to compare.
+}
+
+void CompareRotateOp(const RotateOp* original, const RotateOp* written) {
+  EXPECT_EQ(original->degrees, written->degrees);
+}
+
+void CompareSaveOp(const SaveOp* original, const SaveOp* written) {
+  // Nothing to compare.
+}
+
+void CompareSaveLayerOp(const SaveLayerOp* original,
+                        const SaveLayerOp* written) {
+  CompareFlags(original->flags, written->flags);
+  EXPECT_EQ(original->bounds, written->bounds);
+}
+
+void CompareSaveLayerAlphaOp(const SaveLayerAlphaOp* original,
+                             const SaveLayerAlphaOp* written) {
+  EXPECT_EQ(original->bounds, written->bounds);
+  EXPECT_EQ(original->alpha, written->alpha);
+  EXPECT_EQ(original->preserve_lcd_text_requests,
+            written->preserve_lcd_text_requests);
+}
+
+void CompareScaleOp(const ScaleOp* original, const ScaleOp* written) {
+  EXPECT_EQ(original->sx, written->sx);
+  EXPECT_EQ(original->sy, written->sy);
+}
+
+void CompareSetMatrixOp(const SetMatrixOp* original,
+                        const SetMatrixOp* written) {
+  EXPECT_EQ(original->matrix, written->matrix);
+}
+
+void CompareTranslateOp(const TranslateOp* original,
+                        const TranslateOp* written) {
+  EXPECT_EQ(original->dx, written->dx);
+  EXPECT_EQ(original->dy, written->dy);
+}
+
+class PaintOpSerializationTest : public ::testing::TestWithParam<uint8_t> {
+ public:
+  PaintOpType GetParamType() const {
+    return static_cast<PaintOpType>(GetParam());
+  }
+
+  void PushTestOps(PaintOpType type) {
+    switch (type) {
+      case PaintOpType::Annotate:
+        PushAnnotateOps(&buffer_);
+        break;
+      case PaintOpType::ClipPath:
+        PushClipPathOps(&buffer_);
+        break;
+      case PaintOpType::ClipRect:
+        PushClipRectOps(&buffer_);
+        break;
+      case PaintOpType::ClipRRect:
+        PushClipRRectOps(&buffer_);
+        break;
+      case PaintOpType::Concat:
+        PushConcatOps(&buffer_);
+        break;
+      case PaintOpType::DrawArc:
+        PushDrawArcOps(&buffer_);
+        break;
+      case PaintOpType::DrawCircle:
+        PushDrawCircleOps(&buffer_);
+        break;
+      case PaintOpType::DrawColor:
+        PushDrawColorOps(&buffer_);
+        break;
+      case PaintOpType::DrawDRRect:
+        PushDrawDRRectOps(&buffer_);
+        break;
+      case PaintOpType::DrawImage:
+        PushDrawImageOps(&buffer_);
+        break;
+      case PaintOpType::DrawImageRect:
+        PushDrawImageRectOps(&buffer_);
+        break;
+      case PaintOpType::DrawIRect:
+        PushDrawIRectOps(&buffer_);
+        break;
+      case PaintOpType::DrawLine:
+        PushDrawLineOps(&buffer_);
+        break;
+      case PaintOpType::DrawOval:
+        PushDrawOvalOps(&buffer_);
+        break;
+      case PaintOpType::DrawPath:
+        PushDrawPathOps(&buffer_);
+        break;
+      case PaintOpType::DrawPosText:
+        PushDrawPosTextOps(&buffer_);
+        break;
+      case PaintOpType::DrawRecord:
+        // Not supported.
+        break;
+      case PaintOpType::DrawRect:
+        PushDrawRectOps(&buffer_);
+        break;
+      case PaintOpType::DrawRRect:
+        PushDrawRRectOps(&buffer_);
+        break;
+      case PaintOpType::DrawText:
+        PushDrawTextOps(&buffer_);
+        break;
+      case PaintOpType::DrawTextBlob:
+        PushDrawTextBlobOps(&buffer_);
+        break;
+      case PaintOpType::Noop:
+        PushNoopOps(&buffer_);
+        break;
+      case PaintOpType::Restore:
+        PushRestoreOps(&buffer_);
+        break;
+      case PaintOpType::Rotate:
+        PushRotateOps(&buffer_);
+        break;
+      case PaintOpType::Save:
+        PushSaveOps(&buffer_);
+        break;
+      case PaintOpType::SaveLayer:
+        PushSaveLayerOps(&buffer_);
+        break;
+      case PaintOpType::SaveLayerAlpha:
+        PushSaveLayerAlphaOps(&buffer_);
+        break;
+      case PaintOpType::Scale:
+        PushScaleOps(&buffer_);
+        break;
+      case PaintOpType::SetMatrix:
+        PushSetMatrixOps(&buffer_);
+        break;
+      case PaintOpType::Translate:
+        PushTranslateOps(&buffer_);
+        break;
+    }
+  }
+
+  static void ExpectOpsEqual(const PaintOp* original, const PaintOp* written) {
+    ASSERT_TRUE(original);
+    ASSERT_TRUE(written);
+    ASSERT_EQ(original->GetType(), written->GetType());
+    EXPECT_EQ(original->skip, written->skip);
+
+    switch (original->GetType()) {
+      case PaintOpType::Annotate:
+        CompareAnnotateOp(static_cast<const AnnotateOp*>(original),
+                          static_cast<const AnnotateOp*>(written));
+        break;
+      case PaintOpType::ClipPath:
+        CompareClipPathOp(static_cast<const ClipPathOp*>(original),
+                          static_cast<const ClipPathOp*>(written));
+        break;
+      case PaintOpType::ClipRect:
+        CompareClipRectOp(static_cast<const ClipRectOp*>(original),
+                          static_cast<const ClipRectOp*>(written));
+        break;
+      case PaintOpType::ClipRRect:
+        CompareClipRRectOp(static_cast<const ClipRRectOp*>(original),
+                           static_cast<const ClipRRectOp*>(written));
+        break;
+      case PaintOpType::Concat:
+        CompareConcatOp(static_cast<const ConcatOp*>(original),
+                        static_cast<const ConcatOp*>(written));
+        break;
+      case PaintOpType::DrawArc:
+        CompareDrawArcOp(static_cast<const DrawArcOp*>(original),
+                         static_cast<const DrawArcOp*>(written));
+        break;
+      case PaintOpType::DrawCircle:
+        CompareDrawCircleOp(static_cast<const DrawCircleOp*>(original),
+                            static_cast<const DrawCircleOp*>(written));
+        break;
+      case PaintOpType::DrawColor:
+        CompareDrawColorOp(static_cast<const DrawColorOp*>(original),
+                           static_cast<const DrawColorOp*>(written));
+        break;
+      case PaintOpType::DrawDRRect:
+        CompareDrawDRRectOp(static_cast<const DrawDRRectOp*>(original),
+                            static_cast<const DrawDRRectOp*>(written));
+        break;
+      case PaintOpType::DrawImage:
+        CompareDrawImageOp(static_cast<const DrawImageOp*>(original),
+                           static_cast<const DrawImageOp*>(written));
+        break;
+      case PaintOpType::DrawImageRect:
+        CompareDrawImageRectOp(static_cast<const DrawImageRectOp*>(original),
+                               static_cast<const DrawImageRectOp*>(written));
+        break;
+      case PaintOpType::DrawIRect:
+        CompareDrawIRectOp(static_cast<const DrawIRectOp*>(original),
+                           static_cast<const DrawIRectOp*>(written));
+        break;
+      case PaintOpType::DrawLine:
+        CompareDrawLineOp(static_cast<const DrawLineOp*>(original),
+                          static_cast<const DrawLineOp*>(written));
+        break;
+      case PaintOpType::DrawOval:
+        CompareDrawOvalOp(static_cast<const DrawOvalOp*>(original),
+                          static_cast<const DrawOvalOp*>(written));
+        break;
+      case PaintOpType::DrawPath:
+        CompareDrawPathOp(static_cast<const DrawPathOp*>(original),
+                          static_cast<const DrawPathOp*>(written));
+        break;
+      case PaintOpType::DrawPosText:
+        CompareDrawPosTextOp(static_cast<const DrawPosTextOp*>(original),
+                             static_cast<const DrawPosTextOp*>(written));
+        break;
+      case PaintOpType::DrawRecord:
+        // Not supported.
+        break;
+      case PaintOpType::DrawRect:
+        CompareDrawRectOp(static_cast<const DrawRectOp*>(original),
+                          static_cast<const DrawRectOp*>(written));
+        break;
+      case PaintOpType::DrawRRect:
+        CompareDrawRRectOp(static_cast<const DrawRRectOp*>(original),
+                           static_cast<const DrawRRectOp*>(written));
+        break;
+      case PaintOpType::DrawText:
+        CompareDrawTextOp(static_cast<const DrawTextOp*>(original),
+                          static_cast<const DrawTextOp*>(written));
+        break;
+      case PaintOpType::DrawTextBlob:
+        CompareDrawTextBlobOp(static_cast<const DrawTextBlobOp*>(original),
+                              static_cast<const DrawTextBlobOp*>(written));
+        break;
+      case PaintOpType::Noop:
+        CompareNoopOp(static_cast<const NoopOp*>(original),
+                      static_cast<const NoopOp*>(written));
+        break;
+      case PaintOpType::Restore:
+        CompareRestoreOp(static_cast<const RestoreOp*>(original),
+                         static_cast<const RestoreOp*>(written));
+        break;
+      case PaintOpType::Rotate:
+        CompareRotateOp(static_cast<const RotateOp*>(original),
+                        static_cast<const RotateOp*>(written));
+        break;
+      case PaintOpType::Save:
+        CompareSaveOp(static_cast<const SaveOp*>(original),
+                      static_cast<const SaveOp*>(written));
+        break;
+      case PaintOpType::SaveLayer:
+        CompareSaveLayerOp(static_cast<const SaveLayerOp*>(original),
+                           static_cast<const SaveLayerOp*>(written));
+        break;
+      case PaintOpType::SaveLayerAlpha:
+        CompareSaveLayerAlphaOp(static_cast<const SaveLayerAlphaOp*>(original),
+                                static_cast<const SaveLayerAlphaOp*>(written));
+        break;
+      case PaintOpType::Scale:
+        CompareScaleOp(static_cast<const ScaleOp*>(original),
+                       static_cast<const ScaleOp*>(written));
+        break;
+      case PaintOpType::SetMatrix:
+        CompareSetMatrixOp(static_cast<const SetMatrixOp*>(original),
+                           static_cast<const SetMatrixOp*>(written));
+        break;
+      case PaintOpType::Translate:
+        CompareTranslateOp(static_cast<const TranslateOp*>(original),
+                           static_cast<const TranslateOp*>(written));
+        break;
+    }
+  }
+
+  void ResizeOutputBuffer() {
+    // An arbitrary deserialization buffer size that should fit all the ops
+    // in the buffer_.
+    output_size_ = (100 + sizeof(LargestPaintOp)) * buffer_.size();
+    output_.reset(static_cast<char*>(
+        base::AlignedAlloc(output_size_, PaintOpBuffer::PaintOpAlign)));
+  }
+
+  bool IsTypeSupported() {
+    // DrawRecordOps must be flattened and are not currently serialized.
+    // All other types must push non-zero amounts of ops in PushTestOps.
+    return GetParamType() != PaintOpType::DrawRecord;
+  }
+
+ protected:
+  std::unique_ptr<char, base::AlignedFreeDeleter> output_;
+  size_t output_size_ = 0u;
+  PaintOpBuffer buffer_;
+};
+
+INSTANTIATE_TEST_CASE_P(
+    P,
+    PaintOpSerializationTest,
+    ::testing::Range(static_cast<uint8_t>(0),
+                     static_cast<uint8_t>(PaintOpType::LastPaintOpType)));
+
+// Test serializing and then deserializing all test ops.  They should all
+// write successfully and be identical to the original ops in the buffer.
+TEST_P(PaintOpSerializationTest, SmokeTest) {
+  if (!IsTypeSupported())
+    return;
+
+  PushTestOps(GetParamType());
+
+  ResizeOutputBuffer();
+
+  SimpleSerializer serializer(output_.get(), output_size_);
+  serializer.Serialize(buffer_);
+
+  // Expect all ops to write more than 0 bytes.
+  for (size_t i = 0; i < buffer_.size(); ++i) {
+    SCOPED_TRACE(base::StringPrintf(
+        "%s #%zd", PaintOpTypeToString(GetParamType()).c_str(), i));
+    EXPECT_GT(serializer.bytes_written()[i], 0u);
+  }
+
+  PaintOpBuffer::Iterator iter(&buffer_);
+  for (auto* base_written :
+       DeserializerIterator(output_.get(), serializer.TotalBytesWritten())) {
+    SCOPED_TRACE(base::StringPrintf(
+        "%s #%zd", PaintOpTypeToString(GetParamType()).c_str(), iter.op_idx()));
+    ExpectOpsEqual(*iter, base_written);
+    ++iter;
+  }
+
+  EXPECT_EQ(buffer_.size(), iter.op_idx());
+}
+
+// Verify for all test ops that serializing into a smaller size aborts
+// correctly and doesn't write anything.
+TEST_P(PaintOpSerializationTest, SerializationFailures) {
+  if (!IsTypeSupported())
+    return;
+
+  PushTestOps(GetParamType());
+
+  ResizeOutputBuffer();
+
+  SimpleSerializer serializer(output_.get(), output_size_);
+  serializer.Serialize(buffer_);
+  std::vector<size_t> bytes_written = serializer.bytes_written();
+
+  PaintOp::SerializeOptions options;
+
+  for (PaintOpBuffer::Iterator iter(&buffer_); iter; ++iter) {
+    SCOPED_TRACE(base::StringPrintf(
+        "%s #%zd", PaintOpTypeToString(GetParamType()).c_str(), iter.op_idx()));
+    size_t expected_bytes = bytes_written[iter.op_idx()];
+    EXPECT_GT(expected_bytes, 0u);
+
+    // Attempt to write op into a buffer of size |i|, and only expect
+    // it to succeed if the buffer is large enough.
+    for (size_t i = 0; i < bytes_written[iter.op_idx()] + 2; ++i) {
+      size_t written_bytes = iter->Serialize(output_.get(), i, options);
+      if (i >= expected_bytes) {
+        EXPECT_EQ(expected_bytes, written_bytes) << "i: " << i;
+      } else {
+        EXPECT_EQ(0u, written_bytes) << "i: " << i;
+      }
+    }
+  }
+}
+
+// Verify that deserializing test ops from too small buffers aborts
+// correctly, in case the deserialized data is lying about how big it is.
+TEST_P(PaintOpSerializationTest, DeserializationFailures) {
+  if (!IsTypeSupported())
+    return;
+
+  PushTestOps(GetParamType());
+
+  ResizeOutputBuffer();
+
+  SimpleSerializer serializer(output_.get(), output_size_);
+  serializer.Serialize(buffer_);
+
+  char* current = static_cast<char*>(output_.get());
+
+  static constexpr size_t kAlign = PaintOpBuffer::PaintOpAlign;
+  static constexpr size_t kOutputOpSize = sizeof(LargestPaintOp) + 100;
+  std::unique_ptr<char, base::AlignedFreeDeleter> deserialize_buffer_(
+      static_cast<char*>(base::AlignedAlloc(kOutputOpSize, kAlign)));
+
+  for (PaintOpBuffer::Iterator iter(&buffer_); iter; ++iter) {
+    PaintOp* serialized = reinterpret_cast<PaintOp*>(current);
+    uint32_t skip = serialized->skip;
+
+    // Read from buffers of various sizes to make sure that having a serialized
+    // op size that is larger than the input buffer provided causes a
+    // deserialization failure to return nullptr.  Also test a few valid sizes
+    // larger than read size.
+    for (size_t read_size = 0; read_size < skip + kAlign * 2 + 2; ++read_size) {
+      SCOPED_TRACE(
+          base::StringPrintf("%s #%zd, read_size: %zd",
+                             PaintOpTypeToString(GetParamType()).c_str(),
+                             iter.op_idx(), read_size));
+      // Because PaintOp::Deserialize early outs when the input size is < skip
+      // deliberately lie about the skip.  This op tooooootally fits.
+      // This will verify that individual op deserializing code behaves
+      // properly when presented with invalid offsets.
+      serialized->skip = read_size;
+      PaintOp* written = PaintOp::Deserialize(
+          current, read_size, deserialize_buffer_.get(), kOutputOpSize);
+
+      // Skips are only valid if they are aligned.
+      if (read_size >= skip && read_size % kAlign == 0) {
+        ASSERT_NE(nullptr, written);
+        ASSERT_LE(written->skip, kOutputOpSize);
+        EXPECT_EQ(GetParamType(), written->GetType());
+      } else {
+        EXPECT_EQ(nullptr, written);
+      }
+
+      if (written)
+        written->DestroyThis();
+    }
+
+    current += skip;
+  }
+}
+
+// Test generic PaintOp deserializing failure cases.
+TEST(PaintOpBufferTest, PaintOpDeserialize) {
+  static constexpr size_t kSize = sizeof(LargestPaintOp) + 100;
+  static constexpr size_t kAlign = PaintOpBuffer::PaintOpAlign;
+  std::unique_ptr<char, base::AlignedFreeDeleter> input_(
+      static_cast<char*>(base::AlignedAlloc(kSize, kAlign)));
+  std::unique_ptr<char, base::AlignedFreeDeleter> output_(
+      static_cast<char*>(base::AlignedAlloc(kSize, kAlign)));
+
+  PaintOpBuffer buffer;
+  buffer.push<DrawColorOp>(SK_ColorMAGENTA, SkBlendMode::kSrc);
+
+  PaintOpBuffer::Iterator iter(&buffer);
+  PaintOp* op = *iter;
+  ASSERT_TRUE(op);
+
+  PaintOp::SerializeOptions options;
+  size_t bytes_written = op->Serialize(input_.get(), kSize, options);
+  ASSERT_GT(bytes_written, 0u);
+
+  // can deserialize from exactly the right size
+  PaintOp* success =
+      PaintOp::Deserialize(input_.get(), bytes_written, output_.get(), kSize);
+  ASSERT_TRUE(success);
+  success->DestroyThis();
+
+  // fail to deserialize if skip goes past input size
+  // (the DeserializationFailures test above tests if the skip is lying)
+  for (size_t i = 0; i < bytes_written - 1; ++i)
+    EXPECT_FALSE(PaintOp::Deserialize(input_.get(), i, output_.get(), kSize));
+
+  // unaligned skips fail to deserialize
+  PaintOp* serialized = reinterpret_cast<PaintOp*>(input_.get());
+  EXPECT_EQ(0u, serialized->skip % kAlign);
+  serialized->skip -= 1;
+  EXPECT_FALSE(
+      PaintOp::Deserialize(input_.get(), bytes_written, output_.get(), kSize));
+  serialized->skip += 1;
+
+  // bogus types fail to deserialize
+  serialized->type = static_cast<uint8_t>(PaintOpType::LastPaintOpType) + 1;
+  EXPECT_FALSE(
+      PaintOp::Deserialize(input_.get(), bytes_written, output_.get(), kSize));
+}
+
 }  // namespace cc
diff --git a/cc/paint/paint_op_reader.cc b/cc/paint/paint_op_reader.cc
new file mode 100644
index 0000000..318974b
--- /dev/null
+++ b/cc/paint/paint_op_reader.cc
@@ -0,0 +1,130 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/paint/paint_op_reader.h"
+
+#include <stddef.h>
+
+#include "cc/paint/paint_flags.h"
+#include "third_party/skia/include/core/SkPath.h"
+#include "third_party/skia/include/core/SkRRect.h"
+#include "third_party/skia/include/core/SkTextBlob.h"
+
+namespace cc {
+
+template <typename T>
+void PaintOpReader::ReadSimple(T* val) {
+  if (remaining_bytes_ < sizeof(T))
+    valid_ = false;
+  if (!valid_)
+    return;
+
+  *val = reinterpret_cast<const T*>(memory_)[0];
+
+  memory_ += sizeof(T);
+  remaining_bytes_ -= sizeof(T);
+}
+
+void PaintOpReader::ReadData(size_t bytes, void* data) {
+  if (remaining_bytes_ < bytes)
+    valid_ = false;
+  if (!valid_)
+    return;
+  if (bytes == 0)
+    return;
+
+  memcpy(data, memory_, bytes);
+  memory_ += bytes;
+  remaining_bytes_ -= bytes;
+}
+
+void PaintOpReader::ReadArray(size_t count, SkPoint* array) {
+  size_t bytes = count * sizeof(SkPoint);
+  if (remaining_bytes_ < bytes)
+    valid_ = false;
+  // Overflow?
+  if (count > static_cast<size_t>(~0) / sizeof(SkPoint))
+    valid_ = false;
+  if (!valid_)
+    return;
+  if (count == 0)
+    return;
+
+  memcpy(array, memory_, bytes);
+  memory_ += bytes;
+  remaining_bytes_ -= bytes;
+}
+
+void PaintOpReader::Read(SkScalar* data) {
+  ReadSimple(data);
+}
+
+void PaintOpReader::Read(size_t* data) {
+  ReadSimple(data);
+}
+
+void PaintOpReader::Read(uint8_t* data) {
+  ReadSimple(data);
+}
+
+void PaintOpReader::Read(SkRect* rect) {
+  ReadSimple(rect);
+}
+
+void PaintOpReader::Read(SkIRect* rect) {
+  ReadSimple(rect);
+}
+
+void PaintOpReader::Read(SkRRect* rect) {
+  ReadSimple(rect);
+}
+
+void PaintOpReader::Read(SkPath* path) {
+  if (!valid_)
+    return;
+
+  // TODO(enne): Should the writer write how many bytes it expects as well?
+  size_t read_bytes = path->readFromMemory(memory_, remaining_bytes_);
+  if (!read_bytes)
+    valid_ = false;
+
+  memory_ += read_bytes;
+  remaining_bytes_ -= read_bytes;
+}
+
+void PaintOpReader::Read(PaintFlags* flags) {
+  // TODO(enne): implement PaintFlags serialization: http://crbug.com/737629
+}
+
+void PaintOpReader::Read(PaintImage* image) {
+  // TODO(enne): implement PaintImage serialization: http://crbug.com/737629
+}
+
+void PaintOpReader::Read(sk_sp<SkData>* data) {
+  size_t bytes;
+  ReadSimple(&bytes);
+  if (remaining_bytes_ < bytes)
+    valid_ = false;
+  if (!valid_)
+    return;
+
+  // Separate out empty vs not valid cases.
+  if (bytes == 0) {
+    bool has_data = false;
+    Read(&has_data);
+    if (has_data)
+      *data = SkData::MakeEmpty();
+    return;
+  }
+  *data = SkData::MakeWithCopy(memory_, bytes);
+
+  memory_ += bytes;
+  remaining_bytes_ -= bytes;
+}
+
+void PaintOpReader::Read(sk_sp<SkTextBlob>* blob) {
+  // TODO(enne): implement SkTextBlob serialization: http://crbug.com/737629
+}
+
+}  // namespace cc
diff --git a/cc/paint/paint_op_reader.h b/cc/paint/paint_op_reader.h
new file mode 100644
index 0000000..0252178
--- /dev/null
+++ b/cc/paint/paint_op_reader.h
@@ -0,0 +1,76 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_PAINT_PAINT_OP_READER_H_
+#define CC_PAINT_PAINT_OP_READER_H_
+
+#include <vector>
+
+#include "cc/paint/paint_op_writer.h"
+
+namespace cc {
+
+// PaintOpReader takes garbage |memory| and clobbers it with successive
+// read functions.
+class PaintOpReader {
+ public:
+  PaintOpReader(const void* memory, size_t size)
+      : memory_(static_cast<const char*>(memory) +
+                PaintOpWriter::HeaderBytes()),
+        remaining_bytes_(size - PaintOpWriter::HeaderBytes()) {
+    if (size < PaintOpWriter::HeaderBytes())
+      valid_ = false;
+  }
+
+  bool valid() const { return valid_; }
+
+  void ReadData(size_t bytes, void* data);
+  void ReadArray(size_t count, SkPoint* array);
+
+  void Read(SkScalar* data);
+  void Read(size_t* data);
+  void Read(uint8_t* data);
+  void Read(SkRect* rect);
+  void Read(SkIRect* rect);
+  void Read(SkRRect* rect);
+
+  void Read(SkPath* path);
+  void Read(PaintFlags* flags);
+  void Read(PaintImage* image);
+  void Read(sk_sp<SkData>* data);
+  void Read(sk_sp<SkTextBlob>* blob);
+
+  void Read(SkClipOp* op) {
+    uint8_t value = 0u;
+    Read(&value);
+    *op = static_cast<SkClipOp>(value);
+  }
+  void Read(PaintCanvas::AnnotationType* type) {
+    uint8_t value = 0u;
+    Read(&value);
+    *type = static_cast<PaintCanvas::AnnotationType>(value);
+  }
+  void Read(PaintCanvas::SrcRectConstraint* constraint) {
+    uint8_t value = 0u;
+    Read(&value);
+    *constraint = static_cast<PaintCanvas::SrcRectConstraint>(value);
+  }
+  void Read(bool* data) {
+    uint8_t value = 0u;
+    Read(&value);
+    *data = !!value;
+  }
+
+ private:
+  template <typename T>
+  void ReadSimple(T* val);
+
+  const char* memory_ = nullptr;
+  size_t remaining_bytes_ = 0u;
+  bool valid_ = true;
+};
+
+}  // namespace cc
+
+#endif  // CC_PAINT_PAINT_OP_READER_H_
diff --git a/cc/paint/paint_op_writer.cc b/cc/paint/paint_op_writer.cc
new file mode 100644
index 0000000..1248e17
--- /dev/null
+++ b/cc/paint/paint_op_writer.cc
@@ -0,0 +1,104 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/paint/paint_op_writer.h"
+
+#include "cc/paint/paint_flags.h"
+#include "third_party/skia/include/core/SkTextBlob.h"
+
+namespace cc {
+
+template <typename T>
+void PaintOpWriter::WriteSimple(const T& val) {
+  static_assert(base::is_trivially_copyable<T>::value, "");
+  if (sizeof(T) > remaining_bytes_)
+    valid_ = false;
+  if (!valid_)
+    return;
+
+  reinterpret_cast<T*>(memory_)[0] = val;
+
+  memory_ += sizeof(T);
+  remaining_bytes_ -= sizeof(T);
+}
+
+void PaintOpWriter::Write(size_t data) {
+  WriteSimple(data);
+}
+
+void PaintOpWriter::Write(SkScalar data) {
+  WriteSimple(data);
+}
+
+void PaintOpWriter::Write(uint8_t data) {
+  WriteSimple(data);
+}
+
+void PaintOpWriter::Write(const SkRect& rect) {
+  WriteSimple(rect);
+}
+
+void PaintOpWriter::Write(const SkIRect& rect) {
+  WriteSimple(rect);
+}
+
+void PaintOpWriter::Write(const SkRRect& rect) {
+  WriteSimple(rect);
+}
+
+void PaintOpWriter::Write(const SkPath& path) {
+  size_t bytes = path.writeToMemory(nullptr);
+  if (bytes > remaining_bytes_)
+    valid_ = false;
+  if (!valid_)
+    return;
+
+  path.writeToMemory(memory_);
+  memory_ += bytes;
+  remaining_bytes_ -= bytes;
+}
+
+void PaintOpWriter::Write(const PaintFlags& flags) {
+  // TODO(enne): implement PaintFlags serialization: http://crbug.com/737629
+}
+
+void PaintOpWriter::Write(const PaintImage& image, ImageDecodeCache* cache) {
+  // TODO(enne): implement PaintImage serialization: http://crbug.com/737629
+}
+
+void PaintOpWriter::Write(const sk_sp<SkData>& data) {
+  if (data.get() && data->size()) {
+    Write(data->size());
+    WriteData(data->size(), data->data());
+  } else {
+    // Differentiate between nullptr and valid but zero size.  It's not clear
+    // that this happens in practice, but seems better to be consistent.
+    Write(static_cast<size_t>(0));
+    Write(!!data.get());
+  }
+}
+
+void PaintOpWriter::Write(const sk_sp<SkTextBlob>& blob) {
+  // TODO(enne): implement SkTextBlob serialization: http://crbug.com/737629
+}
+
+void PaintOpWriter::WriteData(size_t bytes, const void* input) {
+  if (bytes > remaining_bytes_)
+    valid_ = false;
+  if (!valid_)
+    return;
+  if (bytes == 0)
+    return;
+
+  memcpy(memory_, input, bytes);
+  memory_ += bytes;
+  remaining_bytes_ -= bytes;
+}
+
+void PaintOpWriter::WriteArray(size_t count, const SkPoint* input) {
+  size_t bytes = sizeof(SkPoint) * count;
+  WriteData(bytes, input);
+}
+
+}  // namespace cc
diff --git a/cc/paint/paint_op_writer.h b/cc/paint/paint_op_writer.h
new file mode 100644
index 0000000..0163067f
--- /dev/null
+++ b/cc/paint/paint_op_writer.h
@@ -0,0 +1,71 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_PAINT_PAINT_OP_WRITER_H_
+#define CC_PAINT_PAINT_OP_WRITER_H_
+
+#include "cc/paint/paint_canvas.h"
+
+struct SkRect;
+struct SkIRect;
+class SkRRect;
+
+namespace cc {
+
+class ImageDecodeCache;
+
+class PaintOpWriter {
+ public:
+  PaintOpWriter(void* memory, size_t size)
+      : memory_(static_cast<char*>(memory) + HeaderBytes()),
+        size_(size),
+        remaining_bytes_(size - HeaderBytes()) {
+    // Leave space for header of type/skip.
+    DCHECK_GE(size, HeaderBytes());
+  }
+
+  static size_t constexpr HeaderBytes() { return 4u; }
+
+  // Write a sequence of arbitrary bytes.
+  void WriteData(size_t bytes, const void* input);
+
+  void WriteArray(size_t count, const SkPoint* input);
+
+  size_t size() const { return valid_ ? size_ - remaining_bytes_ : 0u; }
+
+  void Write(SkScalar data);
+  void Write(size_t data);
+  void Write(uint8_t data);
+  void Write(const SkRect& rect);
+  void Write(const SkIRect& rect);
+  void Write(const SkRRect& rect);
+
+  void Write(const SkPath& path);
+  void Write(const PaintFlags& flags);
+  void Write(const PaintImage& image, ImageDecodeCache* cache);
+  void Write(const sk_sp<SkData>& data);
+  void Write(const sk_sp<SkTextBlob>& blob);
+
+  void Write(SkClipOp op) { Write(static_cast<uint8_t>(op)); }
+  void Write(PaintCanvas::AnnotationType type) {
+    Write(static_cast<uint8_t>(type));
+  }
+  void Write(PaintCanvas::SrcRectConstraint constraint) {
+    Write(static_cast<uint8_t>(constraint));
+  }
+  void Write(bool data) { Write(static_cast<uint8_t>(data)); }
+
+ private:
+  template <typename T>
+  void WriteSimple(const T& val);
+
+  char* memory_ = nullptr;
+  size_t size_ = 0u;
+  size_t remaining_bytes_ = 0u;
+  bool valid_ = true;
+};
+
+}  // namespace cc
+
+#endif  // CC_PAINT_PAINT_OP_WRITER_H_
diff --git a/cc/resources/resource_provider.cc b/cc/resources/resource_provider.cc
index ad18719..26975dab 100644
--- a/cc/resources/resource_provider.cc
+++ b/cc/resources/resource_provider.cc
@@ -312,14 +312,11 @@
 
 ResourceProvider::Resource::Resource(Resource&& other) = default;
 
-void ResourceProvider::Resource::set_mailbox(const TextureMailbox& mailbox) {
+void ResourceProvider::Resource::SetMailbox(const TextureMailbox& mailbox) {
   mailbox_ = mailbox;
   if (IsGpuResourceType(type)) {
-    // We assume that the mailbox has a valid sync token or else SetLocallyUsed
-    // must be called after this.
     synchronization_state_ =
-        (mailbox.sync_token().HasData() ? NEEDS_WAIT : SYNCHRONIZED);
-    needs_sync_token_ = false;
+        mailbox.sync_token().HasData() ? NEEDS_WAIT : SYNCHRONIZED;
   } else {
     synchronization_state_ = SYNCHRONIZED;
   }
@@ -328,7 +325,6 @@
 void ResourceProvider::Resource::SetLocallyUsed() {
   synchronization_state_ = LOCALLY_USED;
   mailbox_.set_sync_token(gpu::SyncToken());
-  needs_sync_token_ = IsGpuResourceType(type);
 }
 
 void ResourceProvider::Resource::SetSynchronized() {
@@ -337,13 +333,14 @@
 
 void ResourceProvider::Resource::UpdateSyncToken(
     const gpu::SyncToken& sync_token) {
-  // In the case of context lost, this sync token may be empty since sync tokens
-  // may not be generated unless a successful flush occurred. However, we will
-  // assume the task runner is calling this function properly and update the
-  // state accordingly.
+  DCHECK(IsGpuResourceType(type));
+  // An empty sync token may be used if commands are guaranteed to have run on
+  // the gpu process or in case of context loss.
   mailbox_.set_sync_token(sync_token);
-  synchronization_state_ = NEEDS_WAIT;
-  needs_sync_token_ = false;
+  if (sync_token.HasData())
+    synchronization_state_ = NEEDS_WAIT;
+  else
+    synchronization_state_ = SYNCHRONIZED;
 }
 
 int8_t* ResourceProvider::Resource::GetSyncTokenData() {
@@ -353,10 +350,6 @@
 void ResourceProvider::Resource::WaitSyncToken(gpu::gles2::GLES2Interface* gl) {
   // Make sure we are only called when state actually needs to wait.
   DCHECK_EQ(NEEDS_WAIT, synchronization_state_);
-
-  // Make sure sync token is not stale.
-  DCHECK(!needs_sync_token_);
-
   // In the case of context lost, this sync token may be empty (see comment in
   // the UpdateSyncToken() function). The WaitSyncTokenCHROMIUM() function
   // handles empty sync tokens properly so just wait anyways and update the
@@ -694,7 +687,7 @@
                      Resource::EXTERNAL, GL_LINEAR));
   }
   resource->allocated = true;
-  resource->set_mailbox(mailbox);
+  resource->SetMailbox(mailbox);
   resource->color_space = mailbox.color_space();
   resource->release_callback_impl =
       base::Bind(&SingleReleaseCallbackImpl::Run,
@@ -885,16 +878,14 @@
   Resource* resource = GetResource(id);
   DCHECK(!resource->locked_for_write);
   DCHECK(!resource->lock_for_read_count);
-  DCHECK(resource->origin == Resource::INTERNAL);
+  DCHECK_EQ(resource->origin, Resource::INTERNAL);
+  DCHECK_NE(resource->synchronization_state(), Resource::NEEDS_WAIT);
   DCHECK_EQ(resource->exported_count, 0);
   DCHECK(ReadLockFenceHasPassed(resource));
 
   DCHECK_EQ(image_size.width(), resource->size.width());
   DCHECK_EQ(image_size.height(), resource->size.height());
 
-  if (resource->allocated)
-    WaitSyncTokenIfNeeded(id);
-
   if (resource->type == RESOURCE_TYPE_BITMAP) {
     DCHECK_EQ(RESOURCE_TYPE_BITMAP, resource->type);
     DCHECK(resource->allocated);
@@ -907,6 +898,8 @@
     SkCanvas dest(lock.sk_bitmap());
     dest.writePixels(source_info, image, image_stride, 0, 0);
   } else {
+    // No sync token needed because the lock will set synchronization state to
+    // LOCALLY_USED and a sync token will be generated in PrepareSendToParent.
     ScopedWriteLockGL lock(this, id, false);
     unsigned resource_texture_id = lock.texture_id();
     DCHECK(resource_texture_id);
@@ -924,52 +917,6 @@
                         image_size.height(), GLDataFormat(resource->format),
                         GLDataType(resource->format), image);
     }
-    const uint64_t fence_sync = gl->InsertFenceSyncCHROMIUM();
-    gl->OrderingBarrierCHROMIUM();
-    gpu::SyncToken sync_token;
-    gl->GenUnverifiedSyncTokenCHROMIUM(fence_sync, sync_token.GetData());
-    lock.set_sync_token(sync_token);
-    lock.set_synchronized(true);
-  }
-}
-
-void ResourceProvider::GenerateSyncTokenForResource(ResourceId resource_id) {
-  Resource* resource = GetResource(resource_id);
-  if (!resource->needs_sync_token())
-    return;
-
-  gpu::SyncToken sync_token;
-  GLES2Interface* gl = ContextGL();
-  DCHECK(gl);
-
-  const uint64_t fence_sync = gl->InsertFenceSyncCHROMIUM();
-  gl->OrderingBarrierCHROMIUM();
-  gl->GenUnverifiedSyncTokenCHROMIUM(fence_sync, sync_token.GetData());
-
-  resource->UpdateSyncToken(sync_token);
-  resource->SetSynchronized();
-}
-
-void ResourceProvider::GenerateSyncTokenForResources(
-    const ResourceIdArray& resource_ids) {
-  gpu::SyncToken sync_token;
-  bool created_sync_token = false;
-  for (ResourceId id : resource_ids) {
-    Resource* resource = GetResource(id);
-    if (resource->needs_sync_token()) {
-      if (!created_sync_token) {
-        GLES2Interface* gl = ContextGL();
-        DCHECK(gl);
-
-        const uint64_t fence_sync = gl->InsertFenceSyncCHROMIUM();
-        gl->OrderingBarrierCHROMIUM();
-        gl->GenUnverifiedSyncTokenCHROMIUM(fence_sync, sync_token.GetData());
-        created_sync_token = true;
-      }
-
-      resource->UpdateSyncToken(sync_token);
-      resource->SetSynchronized();
-    }
   }
 }
 
@@ -1191,9 +1138,6 @@
   DCHECK(thread_checker_.CalledOnValidThread());
   Resource* resource = resource_provider_->GetResource(resource_id_);
   DCHECK(resource->locked_for_write);
-  // It's not sufficient to check sync_token_.HasData() here because the sync
-  // might be null because of context loss. Even in that case we want to set the
-  // sync token because it's checked in PrepareSendToParent while drawing.
   if (has_sync_token_)
     resource->UpdateSyncToken(sync_token_);
   if (synchronized_)
@@ -1486,22 +1430,8 @@
   // as pointers so we don't have to look up the resource id multiple times.
   std::vector<Resource*> resources;
   resources.reserve(resource_ids.size());
-  for (const ResourceId id : resource_ids) {
-    Resource* resource = GetResource(id);
-    // Check the synchronization and sync token state when delegated sync points
-    // are required. The only case where we allow a sync token to not be set is
-    // the case where the image is dirty. In that case we will bind the image
-    // lazily and generate a sync token at that point.
-    DCHECK(!settings_.delegated_sync_points_required || resource->dirty_image ||
-           !resource->needs_sync_token());
-
-    // If we are validating the resource to be sent, the resource cannot be
-    // in a LOCALLY_USED state. It must have been properly synchronized.
-    DCHECK(!settings_.delegated_sync_points_required ||
-           Resource::LOCALLY_USED != resource->synchronization_state());
-
-    resources.push_back(resource);
-  }
+  for (const ResourceId id : resource_ids)
+    resources.push_back(GetResource(id));
 
   // Lazily create any mailboxes and verify all unverified sync tokens.
   std::vector<GLbyte*> unverified_sync_tokens;
@@ -1606,9 +1536,9 @@
                              TEXTURE_HINT_IMMUTABLE, RESOURCE_TYPE_GL_TEXTURE,
                              it->format));
       resource->buffer_format = it->buffer_format;
-      resource->set_mailbox(TextureMailbox(it->mailbox_holder.mailbox,
-                                           it->mailbox_holder.sync_token,
-                                           it->mailbox_holder.texture_target));
+      resource->SetMailbox(TextureMailbox(it->mailbox_holder.mailbox,
+                                          it->mailbox_holder.sync_token,
+                                          it->mailbox_holder.texture_target));
       resource->read_lock_fences_enabled = it->read_lock_fences_enabled;
       resource->is_overlay_candidate = it->is_overlay_candidate;
 #if defined(OS_ANDROID)
@@ -1765,7 +1695,7 @@
     gl->ProduceTextureDirectCHROMIUM(resource->gl_id,
                                      mailbox_holder.texture_target,
                                      mailbox_holder.mailbox.name);
-    resource->set_mailbox(TextureMailbox(mailbox_holder));
+    resource->SetMailbox(TextureMailbox(mailbox_holder));
     resource->SetLocallyUsed();
   }
 
diff --git a/cc/resources/resource_provider.h b/cc/resources/resource_provider.h
index af2f322..6f9a5038 100644
--- a/cc/resources/resource_provider.h
+++ b/cc/resources/resource_provider.h
@@ -168,10 +168,6 @@
                       const uint8_t* image,
                       const gfx::Size& image_size);
 
-  // Generates sync tokesn for resources which need a sync token.
-  void GenerateSyncTokenForResource(ResourceId resource_id);
-  void GenerateSyncTokenForResources(const ResourceIdArray& resource_ids);
-
   // Gets the most recent sync token from the indicated resources.
   gpu::SyncToken GetSyncTokenForResources(const ResourceIdArray& resource_ids);
 
@@ -603,14 +599,17 @@
              GLenum filter);
     Resource(Resource&& other);
 
-    bool needs_sync_token() const { return needs_sync_token_; }
+    bool needs_sync_token() const {
+      return type != RESOURCE_TYPE_BITMAP &&
+             synchronization_state_ == LOCALLY_USED;
+    }
 
     SynchronizationState synchronization_state() const {
       return synchronization_state_;
     }
 
     const TextureMailbox& mailbox() const { return mailbox_; }
-    void set_mailbox(const TextureMailbox& mailbox);
+    void SetMailbox(const TextureMailbox& mailbox);
 
     void SetLocallyUsed();
     void SetSynchronized();
@@ -684,7 +683,6 @@
 
    private:
     SynchronizationState synchronization_state_ = SYNCHRONIZED;
-    bool needs_sync_token_ = false;
     TextureMailbox mailbox_;
 
     DISALLOW_COPY_AND_ASSIGN(Resource);
diff --git a/cc/resources/resource_provider_unittest.cc b/cc/resources/resource_provider_unittest.cc
index 29468de9..3ba950a 100644
--- a/cc/resources/resource_provider_unittest.cc
+++ b/cc/resources/resource_provider_unittest.cc
@@ -676,9 +676,6 @@
     resource_ids_to_transfer.push_back(id3);
     resource_ids_to_transfer.push_back(id4);
 
-    child_resource_provider_->GenerateSyncTokenForResources(
-        resource_ids_to_transfer);
-
     std::vector<TransferableResource> list;
     child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
                                                   &list);
@@ -938,9 +935,6 @@
     resource_ids_to_transfer.push_back(id1);
     resource_ids_to_transfer.push_back(id2);
 
-    child_resource_provider_->GenerateSyncTokenForResources(
-        resource_ids_to_transfer);
-
     std::vector<TransferableResource> list;
     child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
                                                   &list);
@@ -1049,8 +1043,6 @@
     resource_ids_to_transfer.push_back(id2);
     resource_ids_to_transfer.push_back(id3);
     std::vector<TransferableResource> list;
-    child_resource_provider_->GenerateSyncTokenForResources(
-        resource_ids_to_transfer);
     child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
                                                   &list);
     ASSERT_EQ(3u, list.size());
@@ -1129,9 +1121,6 @@
     resource_ids_to_transfer.push_back(ids[i]);
   }
 
-  child_resource_provider_->GenerateSyncTokenForResources(
-      resource_ids_to_transfer);
-
   std::vector<TransferableResource> list;
   child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
                                                 &list);
@@ -1188,9 +1177,6 @@
     ResourceProvider::ResourceIdArray resource_ids_to_transfer;
     resource_ids_to_transfer.push_back(id1);
 
-    child_resource_provider_->GenerateSyncTokenForResources(
-        resource_ids_to_transfer);
-
     std::vector<TransferableResource> list;
     child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
                                                   &list);
@@ -1258,9 +1244,6 @@
   ResourceProvider::ResourceIdArray resource_ids_to_transfer;
   resource_ids_to_transfer.push_back(id1);
 
-  child_resource_provider_->GenerateSyncTokenForResources(
-      resource_ids_to_transfer);
-
   std::vector<TransferableResource> list;
   child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
                                                 &list);
@@ -1320,9 +1303,6 @@
   resource_ids_to_transfer.push_back(id1);
   resource_ids_to_transfer.push_back(id2);
 
-  child_resource_provider_->GenerateSyncTokenForResources(
-      resource_ids_to_transfer);
-
   std::vector<TransferableResource> list;
   child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
                                                 &list);
@@ -1388,9 +1368,6 @@
   resource_ids_to_transfer.push_back(id1);
   resource_ids_to_transfer.push_back(id2);
 
-  child_resource_provider_->GenerateSyncTokenForResources(
-      resource_ids_to_transfer);
-
   std::vector<TransferableResource> list;
   child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
                                                 &list);
@@ -1461,9 +1438,6 @@
     resource_ids_to_transfer.push_back(id2);
     resource_ids_to_transfer.push_back(id3);
 
-    child_resource_provider_->GenerateSyncTokenForResources(
-        resource_ids_to_transfer);
-
     std::vector<TransferableResource> list;
     child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
                                                   &list);
@@ -1512,9 +1486,6 @@
     resource_ids_to_transfer.push_back(id1);
     resource_ids_to_transfer.push_back(id2);
 
-    child_resource_provider_->GenerateSyncTokenForResources(
-        resource_ids_to_transfer);
-
     std::vector<TransferableResource> list;
     child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
                                                   &list);
@@ -1582,9 +1553,6 @@
     resource_ids_to_transfer.push_back(id2);
     resource_ids_to_transfer.push_back(id3);
 
-    child_resource_provider_->GenerateSyncTokenForResources(
-        resource_ids_to_transfer);
-
     std::vector<TransferableResource> list;
     child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
                                                   &list);
@@ -1653,7 +1621,6 @@
       gfx::ColorSpace());
   uint8_t data1[4] = { 1, 2, 3, 4 };
   child_resource_provider->CopyToResource(id1, data1, size);
-  child_resource_provider->GenerateSyncTokenForResource(id1);
 
   std::vector<ReturnedResource> returned_to_child;
   int child_id =
@@ -1710,9 +1677,6 @@
     ResourceProvider::ResourceIdArray resource_ids_to_transfer;
     resource_ids_to_transfer.push_back(id1);
 
-    child_resource_provider_->GenerateSyncTokenForResources(
-        resource_ids_to_transfer);
-
     std::vector<TransferableResource> list;
     child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
                                                   &list);
@@ -1770,9 +1734,6 @@
     resource_ids_to_transfer.push_back(id1);
     resource_ids_to_transfer.push_back(id2);
 
-    child_resource_provider_->GenerateSyncTokenForResources(
-        resource_ids_to_transfer);
-
     std::vector<TransferableResource> list;
     child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
                                                   &list);
@@ -1807,9 +1768,6 @@
     resource_ids_to_transfer.push_back(mapped_id1);
     resource_ids_to_transfer.push_back(mapped_id2);
 
-    child_resource_provider_->GenerateSyncTokenForResources(
-        resource_ids_to_transfer);
-
     std::vector<TransferableResource> list;
     resource_provider_->PrepareSendToParent(resource_ids_to_transfer, &list);
 
@@ -1876,9 +1834,6 @@
     resource_ids_to_transfer.push_back(id1);
     resource_ids_to_transfer.push_back(id2);
 
-    child_resource_provider_->GenerateSyncTokenForResources(
-        resource_ids_to_transfer);
-
     std::vector<TransferableResource> list;
     child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
                                                   &list);
@@ -1913,9 +1868,6 @@
     resource_ids_to_transfer.push_back(mapped_id1);
     resource_ids_to_transfer.push_back(mapped_id2);
 
-    child_resource_provider_->GenerateSyncTokenForResources(
-        resource_ids_to_transfer);
-
     std::vector<TransferableResource> list;
     resource_provider_->PrepareSendToParent(resource_ids_to_transfer, &list);
 
@@ -1992,9 +1944,6 @@
     ResourceProvider::ResourceIdArray resource_ids_to_transfer;
     resource_ids_to_transfer.push_back(id);
 
-    child_resource_provider_->GenerateSyncTokenForResources(
-        resource_ids_to_transfer);
-
     std::vector<TransferableResource> list;
     child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
                                                   &list);
@@ -2050,9 +1999,6 @@
     ResourceProvider::ResourceIdArray resource_ids_to_transfer;
     resource_ids_to_transfer.push_back(id);
 
-    child_resource_provider_->GenerateSyncTokenForResources(
-        resource_ids_to_transfer);
-
     std::vector<TransferableResource> list;
     child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
                                                   &list);
@@ -2238,8 +2184,6 @@
       EXPECT_CALL(*child_context,
                   produceTextureDirectCHROMIUM(_, GL_TEXTURE_2D, _));
 
-      child_resource_provider->GenerateSyncTokenForResources(
-          resource_ids_to_transfer);
       child_resource_provider->PrepareSendToParent(resource_ids_to_transfer,
                                                    &list);
       Mock::VerifyAndClearExpectations(child_context);
diff --git a/cc/resources/video_resource_updater.cc b/cc/resources/video_resource_updater.cc
index 2fc54d7..32ac2c6 100644
--- a/cc/resources/video_resource_updater.cc
+++ b/cc/resources/video_resource_updater.cc
@@ -131,6 +131,14 @@
   gpu::SyncToken sync_token_;
 };
 
+gpu::SyncToken GenerateCompositorSyncToken(gpu::gles2::GLES2Interface* gl) {
+  gpu::SyncToken sync_token;
+  const uint64_t fence_sync = gl->InsertFenceSyncCHROMIUM();
+  gl->OrderingBarrierCHROMIUM();
+  gl->GenUnverifiedSyncTokenCHROMIUM(fence_sync, sync_token.GetData());
+  return sync_token;
+}
+
 }  // namespace
 
 VideoResourceUpdater::PlaneResource::PlaneResource(
@@ -426,12 +434,15 @@
                      plane_resource.resource_id());
       external_resources.type = VideoFrameExternalResources::SOFTWARE_RESOURCE;
     } else {
-      // VideoResourceUpdater shares a context with the compositor so
-      // a sync token is not required.
-      TextureMailbox mailbox(plane_resource.mailbox(), gpu::SyncToken(),
+      // Set the sync token otherwise resource is assumed to be synchronized.
+      gpu::SyncToken sync_token =
+          GenerateCompositorSyncToken(context_provider_->ContextGL());
+
+      TextureMailbox mailbox(plane_resource.mailbox(), sync_token,
                              resource_provider_->GetResourceTextureTarget(
                                  plane_resource.resource_id()));
       mailbox.set_color_space(output_color_space);
+
       external_resources.mailboxes.push_back(mailbox);
       external_resources.release_callbacks.push_back(
           base::Bind(&RecycleResource, weak_ptr_factory_.GetWeakPtr(),
@@ -529,10 +540,17 @@
                                          resource_size_pixels);
       plane_resource.SetUniqueId(video_frame->unique_id(), i);
     }
+  }
 
+  // Set the sync token otherwise resource is assumed to be synchronized.
+  gpu::SyncToken sync_token =
+      GenerateCompositorSyncToken(context_provider_->ContextGL());
+
+  for (size_t i = 0; i < plane_resources.size(); ++i) {
+    PlaneResource& plane_resource = *plane_resources[i];
     // VideoResourceUpdater shares a context with the compositor so a
     // sync token is not required.
-    TextureMailbox mailbox(plane_resource.mailbox(), gpu::SyncToken(),
+    TextureMailbox mailbox(plane_resource.mailbox(), sync_token,
                            resource_provider_->GetResourceTextureTarget(
                                plane_resource.resource_id()));
     mailbox.set_color_space(output_color_space);
@@ -605,11 +623,10 @@
   gl->DeleteTextures(1, &src_texture_id);
 
   // Done with the source video frame texture at this point.
-  video_frame->UpdateReleaseSyncToken(&client);
+  gpu::SyncToken sync_token = video_frame->UpdateReleaseSyncToken(&client);
 
-  // VideoResourceUpdater shares a context with the compositor so a
-  // sync token is not required.
-  TextureMailbox mailbox(resource->mailbox(), gpu::SyncToken(), GL_TEXTURE_2D,
+  // Set sync token otherwise resource is assumed to be synchronized.
+  TextureMailbox mailbox(resource->mailbox(), sync_token, GL_TEXTURE_2D,
                          video_frame->coded_size(), false, false);
   mailbox.set_color_space(resource_color_space);
   external_resources->mailboxes.push_back(mailbox);
diff --git a/cc/resources/video_resource_updater.h b/cc/resources/video_resource_updater.h
index c00b9fc..576e300 100644
--- a/cc/resources/video_resource_updater.h
+++ b/cc/resources/video_resource_updater.h
@@ -80,7 +80,6 @@
   VideoFrameExternalResources CreateExternalResourcesFromVideoFrame(
       scoped_refptr<media::VideoFrame> video_frame);
 
-
  private:
   class PlaneResource {
    public:
diff --git a/cc/surfaces/compositor_frame_sink_support.cc b/cc/surfaces/compositor_frame_sink_support.cc
index f27b67f..7d6dfe1 100644
--- a/cc/surfaces/compositor_frame_sink_support.cc
+++ b/cc/surfaces/compositor_frame_sink_support.cc
@@ -11,9 +11,9 @@
 #include "cc/scheduler/begin_frame_source.h"
 #include "cc/surfaces/compositor_frame_sink_support_client.h"
 #include "cc/surfaces/display.h"
+#include "cc/surfaces/frame_sink_manager.h"
 #include "cc/surfaces/surface.h"
 #include "cc/surfaces/surface_info.h"
-#include "cc/surfaces/surface_manager.h"
 #include "cc/surfaces/surface_reference.h"
 
 namespace cc {
@@ -21,7 +21,7 @@
 // static
 std::unique_ptr<CompositorFrameSinkSupport> CompositorFrameSinkSupport::Create(
     CompositorFrameSinkSupportClient* client,
-    SurfaceManager* surface_manager,
+    FrameSinkManager* frame_sink_manager,
     const FrameSinkId& frame_sink_id,
     bool is_root,
     bool handles_frame_sink_id_invalidation,
@@ -30,7 +30,7 @@
       base::WrapUnique(new CompositorFrameSinkSupport(
           client, frame_sink_id, is_root, handles_frame_sink_id_invalidation,
           needs_sync_tokens));
-  support->Init(surface_manager);
+  support->Init(frame_sink_manager);
   return support;
 }
 
@@ -48,9 +48,9 @@
   }
 
   EvictCurrentSurface();
-  surface_manager_->UnregisterFrameSinkManagerClient(frame_sink_id_);
+  frame_sink_manager_->UnregisterFrameSinkManagerClient(frame_sink_id_);
   if (handles_frame_sink_id_invalidation_)
-    surface_manager_->InvalidateFrameSinkId(frame_sink_id_);
+    frame_sink_manager_->InvalidateFrameSinkId(frame_sink_id_);
 }
 
 void CompositorFrameSinkSupport::OnSurfaceActivated(Surface* surface) {
@@ -305,11 +305,12 @@
       handles_frame_sink_id_invalidation_(handles_frame_sink_id_invalidation),
       weak_factory_(this) {}
 
-void CompositorFrameSinkSupport::Init(SurfaceManager* surface_manager) {
-  surface_manager_ = surface_manager;
+void CompositorFrameSinkSupport::Init(FrameSinkManager* frame_sink_manager) {
+  frame_sink_manager_ = frame_sink_manager;
+  surface_manager_ = frame_sink_manager->surface_manager();
   if (handles_frame_sink_id_invalidation_)
-    surface_manager_->RegisterFrameSinkId(frame_sink_id_);
-  surface_manager_->RegisterFrameSinkManagerClient(frame_sink_id_, this);
+    frame_sink_manager_->RegisterFrameSinkId(frame_sink_id_);
+  frame_sink_manager_->RegisterFrameSinkManagerClient(frame_sink_id_, this);
 }
 
 void CompositorFrameSinkSupport::OnBeginFrame(const BeginFrameArgs& args) {
@@ -348,7 +349,7 @@
   seen_first_frame_activation_ = false;
   return surface_manager_->CreateSurface(
       weak_factory_.GetWeakPtr(), surface_info,
-      surface_manager_->GetPrimaryBeginFrameSource(), needs_sync_tokens_);
+      frame_sink_manager_->GetPrimaryBeginFrameSource(), needs_sync_tokens_);
 }
 
 void CompositorFrameSinkSupport::RequestCopyOfSurface(
diff --git a/cc/surfaces/compositor_frame_sink_support.h b/cc/surfaces/compositor_frame_sink_support.h
index 6977020..8dffc5b 100644
--- a/cc/surfaces/compositor_frame_sink_support.h
+++ b/cc/surfaces/compositor_frame_sink_support.h
@@ -24,6 +24,7 @@
 namespace cc {
 
 class CompositorFrameSinkSupportClient;
+class FrameSinkManager;
 class Surface;
 class SurfaceManager;
 
@@ -35,7 +36,7 @@
  public:
   static std::unique_ptr<CompositorFrameSinkSupport> Create(
       CompositorFrameSinkSupportClient* client,
-      SurfaceManager* surface_manager,
+      FrameSinkManager* frame_sink_manager,
       const FrameSinkId& frame_sink_id,
       bool is_root,
       bool handles_frame_sink_id_invalidation,
@@ -45,6 +46,7 @@
 
   const FrameSinkId& frame_sink_id() const { return frame_sink_id_; }
 
+  FrameSinkManager* frame_sink_manager() { return frame_sink_manager_; }
   SurfaceManager* surface_manager() { return surface_manager_; }
 
   // SurfaceClient implementation.
@@ -77,7 +79,7 @@
                              bool handles_frame_sink_id_invalidation,
                              bool needs_sync_tokens);
 
-  void Init(SurfaceManager* surface_manager);
+  void Init(FrameSinkManager* frame_sink_manager);
 
  private:
   // Updates surface references using |active_referenced_surfaces| from the most
@@ -105,6 +107,7 @@
 
   CompositorFrameSinkSupportClient* const client_;
 
+  FrameSinkManager* frame_sink_manager_ = nullptr;
   SurfaceManager* surface_manager_ = nullptr;
 
   const FrameSinkId frame_sink_id_;
diff --git a/cc/surfaces/compositor_frame_sink_support_unittest.cc b/cc/surfaces/compositor_frame_sink_support_unittest.cc
index 7bdf969..38f1681 100644
--- a/cc/surfaces/compositor_frame_sink_support_unittest.cc
+++ b/cc/surfaces/compositor_frame_sink_support_unittest.cc
@@ -11,9 +11,9 @@
 #include "cc/resources/resource_provider.h"
 #include "cc/surfaces/compositor_frame_sink_support_client.h"
 #include "cc/surfaces/frame_sink_id.h"
+#include "cc/surfaces/frame_sink_manager.h"
 #include "cc/surfaces/surface_id.h"
 #include "cc/surfaces/surface_info.h"
-#include "cc/surfaces/surface_manager.h"
 #include "cc/test/begin_frame_args_test.h"
 #include "cc/test/compositor_frame_helpers.h"
 #include "cc/test/fake_external_begin_frame_source.h"
@@ -106,11 +106,11 @@
         local_surface_id_(3, kArbitraryToken),
         frame_sync_token_(GenTestSyncToken(4)),
         consumer_sync_token_(GenTestSyncToken(5)) {
-    manager_.AddObserver(&surface_observer_);
+    manager_.surface_manager()->AddObserver(&surface_observer_);
     support_->SetBeginFrameSource(&begin_frame_source_);
   }
   ~CompositorFrameSinkSupportTest() override {
-    manager_.RemoveObserver(&surface_observer_);
+    manager_.surface_manager()->RemoveObserver(&surface_observer_);
     support_->EvictCurrentSurface();
   }
 
@@ -159,14 +159,18 @@
     fake_support_client_.clear_returned_resources();
   }
 
+  Surface* GetSurfaceForId(const SurfaceId& id) {
+    return manager_.surface_manager()->GetSurfaceForId(id);
+  }
+
   void RefCurrentFrameResources() {
-    Surface* surface = manager_.GetSurfaceForId(
+    Surface* surface = GetSurfaceForId(
         SurfaceId(support_->frame_sink_id(), local_surface_id_));
     support_->RefResources(surface->GetActiveFrame().resource_list);
   }
 
  protected:
-  SurfaceManager manager_;
+  FrameSinkManager manager_;
   FakeCompositorFrameSinkSupportClient fake_support_client_;
   std::unique_ptr<CompositorFrameSinkSupport> support_;
   FakeExternalBeginFrameSource begin_frame_source_;
@@ -522,11 +526,11 @@
 
   std::vector<ReturnedResource> returned_resources = {
       resource.ToReturnedResource()};
-  EXPECT_TRUE(manager_.GetSurfaceForId(id));
+  EXPECT_TRUE(GetSurfaceForId(id));
   EXPECT_CALL(mock_client, DidReceiveCompositorFrameAck(returned_resources))
       .Times(1);
   support->EvictCurrentSurface();
-  EXPECT_FALSE(manager_.GetSurfaceForId(id));
+  EXPECT_FALSE(GetSurfaceForId(id));
 }
 
 // Tests doing an EvictCurrentSurface which has unregistered dependency.
@@ -550,18 +554,18 @@
   local_surface_id_ = LocalSurfaceId();
 
   SurfaceId surface_id(kAnotherArbitraryFrameSinkId, local_surface_id);
-  Surface* surface = manager_.GetSurfaceForId(surface_id);
+  Surface* surface = GetSurfaceForId(surface_id);
   surface->AddDestructionDependency(
       SurfaceSequence(kYetAnotherArbitraryFrameSinkId, 4));
 
   std::vector<ReturnedResource> returned_resource = {
       resource.ToReturnedResource()};
 
-  EXPECT_TRUE(manager_.GetSurfaceForId(surface_id));
+  EXPECT_TRUE(GetSurfaceForId(surface_id));
   EXPECT_CALL(mock_client, DidReceiveCompositorFrameAck(returned_resource))
       .Times(1);
   support->EvictCurrentSurface();
-  EXPECT_FALSE(manager_.GetSurfaceForId(surface_id));
+  EXPECT_FALSE(GetSurfaceForId(surface_id));
 }
 
 // Tests doing an EvictCurrentSurface which has registered dependency.
@@ -588,21 +592,22 @@
   manager_.RegisterFrameSinkId(kYetAnotherArbitraryFrameSinkId);
 
   SurfaceId surface_id(kAnotherArbitraryFrameSinkId, local_surface_id);
-  Surface* surface = manager_.GetSurfaceForId(surface_id);
+  Surface* surface = GetSurfaceForId(surface_id);
   surface->AddDestructionDependency(
       SurfaceSequence(kYetAnotherArbitraryFrameSinkId, 4));
 
   std::vector<ReturnedResource> returned_resources;
-  EXPECT_TRUE(manager_.GetSurfaceForId(surface_id));
+  EXPECT_TRUE(GetSurfaceForId(surface_id));
   support->EvictCurrentSurface();
-  EXPECT_TRUE(manager_.GetSurfaceForId(surface_id));
+  EXPECT_TRUE(GetSurfaceForId(surface_id));
   EXPECT_EQ(0u, execute_count);
 
   returned_resources.push_back(resource.ToReturnedResource());
   EXPECT_CALL(mock_client, DidReceiveCompositorFrameAck(returned_resources))
       .Times(1);
-  manager_.SatisfySequence(SurfaceSequence(kYetAnotherArbitraryFrameSinkId, 4));
-  EXPECT_FALSE(manager_.GetSurfaceForId(surface_id));
+  manager_.surface_manager()->SatisfySequence(
+      SurfaceSequence(kYetAnotherArbitraryFrameSinkId, 4));
+  EXPECT_FALSE(GetSurfaceForId(surface_id));
 }
 
 TEST_F(CompositorFrameSinkSupportTest, DestroySequence) {
@@ -615,22 +620,24 @@
   support2->SubmitCompositorFrame(local_surface_id2, MakeCompositorFrame());
 
   // Check that waiting before the sequence is satisfied works.
-  manager_.GetSurfaceForId(id2)->AddDestructionDependency(
+  GetSurfaceForId(id2)->AddDestructionDependency(
       SurfaceSequence(kYetAnotherArbitraryFrameSinkId, 4));
   support2->EvictCurrentSurface();
 
-  DCHECK(manager_.GetSurfaceForId(id2));
-  manager_.SatisfySequence(SurfaceSequence(kYetAnotherArbitraryFrameSinkId, 4));
-  manager_.SatisfySequence(SurfaceSequence(kYetAnotherArbitraryFrameSinkId, 6));
-  DCHECK(!manager_.GetSurfaceForId(id2));
+  DCHECK(GetSurfaceForId(id2));
+  manager_.surface_manager()->SatisfySequence(
+      SurfaceSequence(kYetAnotherArbitraryFrameSinkId, 4));
+  manager_.surface_manager()->SatisfySequence(
+      SurfaceSequence(kYetAnotherArbitraryFrameSinkId, 6));
+  DCHECK(!GetSurfaceForId(id2));
 
   // Check that waiting after the sequence is satisfied works.
   support2->SubmitCompositorFrame(local_surface_id2, MakeCompositorFrame());
-  DCHECK(manager_.GetSurfaceForId(id2));
-  manager_.GetSurfaceForId(id2)->AddDestructionDependency(
+  DCHECK(GetSurfaceForId(id2));
+  GetSurfaceForId(id2)->AddDestructionDependency(
       SurfaceSequence(kAnotherArbitraryFrameSinkId, 6));
   support2->EvictCurrentSurface();
-  DCHECK(!manager_.GetSurfaceForId(id2));
+  DCHECK(!GetSurfaceForId(id2));
 }
 
 // Tests that Surface ID namespace invalidation correctly allows
@@ -643,19 +650,19 @@
   support_->SubmitCompositorFrame(local_surface_id, MakeCompositorFrame());
 
   manager_.RegisterFrameSinkId(frame_sink_id);
-  manager_.GetSurfaceForId(id)->AddDestructionDependency(
+  GetSurfaceForId(id)->AddDestructionDependency(
       SurfaceSequence(frame_sink_id, 4));
 
   support_->EvictCurrentSurface();
 
   // Verify the dependency has prevented the surface from getting destroyed.
-  EXPECT_TRUE(manager_.GetSurfaceForId(id));
+  EXPECT_TRUE(GetSurfaceForId(id));
 
   manager_.InvalidateFrameSinkId(frame_sink_id);
 
   // Verify that the invalidated namespace caused the unsatisfied sequence
   // to be ignored.
-  EXPECT_FALSE(manager_.GetSurfaceForId(id));
+  EXPECT_FALSE(GetSurfaceForId(id));
 }
 
 TEST_F(CompositorFrameSinkSupportTest, DestroyCycle) {
@@ -681,7 +688,7 @@
     EXPECT_EQ(surface_observer_.last_created_surface_id().local_surface_id(),
               local_surface_id2);
   }
-  manager_.GetSurfaceForId(id2)->AddDestructionDependency(
+  GetSurfaceForId(id2)->AddDestructionDependency(
       SurfaceSequence(kAnotherArbitraryFrameSinkId, 4));
   support2->EvictCurrentSurface();
   // Give local_surface_id_ a frame that references id2.
@@ -691,18 +698,19 @@
     support_->SubmitCompositorFrame(local_surface_id_, std::move(frame));
   }
   support_->EvictCurrentSurface();
-  EXPECT_TRUE(manager_.GetSurfaceForId(id2));
+  EXPECT_TRUE(GetSurfaceForId(id2));
   // local_surface_id_ should be retained by reference from id2.
-  EXPECT_TRUE(manager_.GetSurfaceForId(
-      SurfaceId(support_->frame_sink_id(), local_surface_id_)));
+  EXPECT_TRUE(
+      GetSurfaceForId(SurfaceId(support_->frame_sink_id(), local_surface_id_)));
 
   // Satisfy last destruction dependency for id2.
-  manager_.SatisfySequence(SurfaceSequence(kAnotherArbitraryFrameSinkId, 4));
+  manager_.surface_manager()->SatisfySequence(
+      SurfaceSequence(kAnotherArbitraryFrameSinkId, 4));
 
   // id2 and local_surface_id_ are in a reference cycle that has no surface
   // sequences holding on to it, so they should be destroyed.
-  EXPECT_TRUE(!manager_.GetSurfaceForId(id2));
-  EXPECT_TRUE(!manager_.GetSurfaceForId(
+  EXPECT_TRUE(!GetSurfaceForId(id2));
+  EXPECT_TRUE(!GetSurfaceForId(
       SurfaceId(support_->frame_sink_id(), local_surface_id_)));
 
   local_surface_id_ = LocalSurfaceId();
@@ -791,7 +799,7 @@
   frame.render_pass_list.push_back(RenderPass::Create());
   EXPECT_TRUE(
       support_->SubmitCompositorFrame(local_surface_id_, std::move(frame)));
-  EXPECT_FALSE(manager_.GetSurfaceForId(id));
+  EXPECT_FALSE(GetSurfaceForId(id));
 }
 
 // Check that if a CompositorFrame is received with device scale factor of 0, we
@@ -802,7 +810,7 @@
   frame.metadata.device_scale_factor = 0.f;
   EXPECT_TRUE(
       support_->SubmitCompositorFrame(local_surface_id_, std::move(frame)));
-  EXPECT_FALSE(manager_.GetSurfaceForId(id));
+  EXPECT_FALSE(GetSurfaceForId(id));
 }
 
 // Check that if the size of a CompositorFrame doesn't match the size of the
@@ -817,7 +825,7 @@
   frame.render_pass_list.push_back(std::move(pass));
   EXPECT_TRUE(
       support_->SubmitCompositorFrame(local_surface_id_, std::move(frame)));
-  EXPECT_TRUE(manager_.GetSurfaceForId(id));
+  EXPECT_TRUE(GetSurfaceForId(id));
 
   // Submit a frame with size (5,4). This frame should be rejected and the
   // surface should be destroyed.
@@ -827,7 +835,7 @@
   frame.render_pass_list.push_back(std::move(pass));
   EXPECT_FALSE(
       support_->SubmitCompositorFrame(local_surface_id_, std::move(frame)));
-  EXPECT_FALSE(manager_.GetSurfaceForId(id));
+  EXPECT_FALSE(GetSurfaceForId(id));
 }
 
 // Check that if the device scale factor of a CompositorFrame doesn't match the
@@ -841,7 +849,7 @@
   frame.metadata.device_scale_factor = 0.5f;
   EXPECT_TRUE(
       support_->SubmitCompositorFrame(local_surface_id_, std::move(frame)));
-  EXPECT_TRUE(manager_.GetSurfaceForId(id));
+  EXPECT_TRUE(GetSurfaceForId(id));
 
   // Submit a frame with device scale factor of 0.4. This frame should be
   // rejected and the surface should be destroyed.
@@ -849,7 +857,7 @@
   frame.metadata.device_scale_factor = 0.4f;
   EXPECT_FALSE(
       support_->SubmitCompositorFrame(local_surface_id_, std::move(frame)));
-  EXPECT_FALSE(manager_.GetSurfaceForId(id));
+  EXPECT_FALSE(GetSurfaceForId(id));
 }
 
 TEST_F(CompositorFrameSinkSupportTest, PassesOnBeginFrameAcks) {
diff --git a/cc/surfaces/direct_layer_tree_frame_sink.cc b/cc/surfaces/direct_layer_tree_frame_sink.cc
index 8a3bee4..1b57532 100644
--- a/cc/surfaces/direct_layer_tree_frame_sink.cc
+++ b/cc/surfaces/direct_layer_tree_frame_sink.cc
@@ -9,15 +9,15 @@
 #include "cc/output/layer_tree_frame_sink_client.h"
 #include "cc/surfaces/display.h"
 #include "cc/surfaces/frame_sink_id.h"
+#include "cc/surfaces/frame_sink_manager.h"
 #include "cc/surfaces/local_surface_id_allocator.h"
 #include "cc/surfaces/surface.h"
-#include "cc/surfaces/surface_manager.h"
 
 namespace cc {
 
 DirectLayerTreeFrameSink::DirectLayerTreeFrameSink(
     const FrameSinkId& frame_sink_id,
-    SurfaceManager* surface_manager,
+    FrameSinkManager* frame_sink_manager,
     Display* display,
     scoped_refptr<ContextProvider> context_provider,
     scoped_refptr<ContextProvider> worker_context_provider,
@@ -28,7 +28,7 @@
                          gpu_memory_buffer_manager,
                          shared_bitmap_manager),
       frame_sink_id_(frame_sink_id),
-      surface_manager_(surface_manager),
+      frame_sink_manager_(frame_sink_manager),
       display_(display) {
   DCHECK(thread_checker_.CalledOnValidThread());
   capabilities_.must_always_swap = true;
@@ -39,12 +39,12 @@
 
 DirectLayerTreeFrameSink::DirectLayerTreeFrameSink(
     const FrameSinkId& frame_sink_id,
-    SurfaceManager* surface_manager,
+    FrameSinkManager* frame_sink_manager,
     Display* display,
     scoped_refptr<VulkanContextProvider> vulkan_context_provider)
     : LayerTreeFrameSink(std::move(vulkan_context_provider)),
       frame_sink_id_(frame_sink_id),
-      surface_manager_(surface_manager),
+      frame_sink_manager_(frame_sink_manager),
       display_(display) {
   DCHECK(thread_checker_.CalledOnValidThread());
   capabilities_.must_always_swap = true;
@@ -69,7 +69,7 @@
   constexpr bool is_root = true;
   constexpr bool handles_frame_sink_id_invalidation = false;
   support_ = CompositorFrameSinkSupport::Create(
-      this, surface_manager_, frame_sink_id_, is_root,
+      this, frame_sink_manager_, frame_sink_id_, is_root,
       handles_frame_sink_id_invalidation,
       capabilities_.delegated_sync_points_required);
   begin_frame_source_ = base::MakeUnique<ExternalBeginFrameSource>(this);
@@ -77,7 +77,7 @@
 
   // Avoid initializing GL context here, as this should be sharing the
   // Display's context.
-  display_->Initialize(this, surface_manager_);
+  display_->Initialize(this, frame_sink_manager_->surface_manager());
   return true;
 }
 
diff --git a/cc/surfaces/direct_layer_tree_frame_sink.h b/cc/surfaces/direct_layer_tree_frame_sink.h
index 303357e..b1bcaf5 100644
--- a/cc/surfaces/direct_layer_tree_frame_sink.h
+++ b/cc/surfaces/direct_layer_tree_frame_sink.h
@@ -18,7 +18,7 @@
 namespace cc {
 class Display;
 class LocalSurfaceIdAllocator;
-class SurfaceManager;
+class FrameSinkManager;
 
 // This class submits compositor frames to an in-process Display, with the
 // client's frame being the root surface of the Display.
@@ -28,11 +28,11 @@
       public ExternalBeginFrameSourceClient,
       public NON_EXPORTED_BASE(DisplayClient) {
  public:
-  // The underlying Display, SurfaceManager, and LocalSurfaceIdAllocator must
+  // The underlying Display, FrameSinkManager, and LocalSurfaceIdAllocator must
   // outlive this class.
   DirectLayerTreeFrameSink(
       const FrameSinkId& frame_sink_id,
-      SurfaceManager* surface_manager,
+      FrameSinkManager* frame_sink_manager,
       Display* display,
       scoped_refptr<ContextProvider> context_provider,
       scoped_refptr<ContextProvider> worker_context_provider,
@@ -40,7 +40,7 @@
       SharedBitmapManager* shared_bitmap_manager);
   DirectLayerTreeFrameSink(
       const FrameSinkId& frame_sink_id,
-      SurfaceManager* surface_manager,
+      FrameSinkManager* frame_sink_manager,
       Display* display,
       scoped_refptr<VulkanContextProvider> vulkan_context_provider);
   ~DirectLayerTreeFrameSink() override;
@@ -78,7 +78,7 @@
 
   const FrameSinkId frame_sink_id_;
   LocalSurfaceId local_surface_id_;
-  SurfaceManager* surface_manager_;
+  FrameSinkManager* frame_sink_manager_;
   LocalSurfaceIdAllocator local_surface_id_allocator_;
   Display* display_;
   gfx::Size last_swap_frame_size_;
diff --git a/cc/surfaces/direct_layer_tree_frame_sink_unittest.cc b/cc/surfaces/direct_layer_tree_frame_sink_unittest.cc
index 3558826..d1acb3da 100644
--- a/cc/surfaces/direct_layer_tree_frame_sink_unittest.cc
+++ b/cc/surfaces/direct_layer_tree_frame_sink_unittest.cc
@@ -14,8 +14,8 @@
 #include "cc/surfaces/display.h"
 #include "cc/surfaces/display_scheduler.h"
 #include "cc/surfaces/frame_sink_id.h"
+#include "cc/surfaces/frame_sink_manager.h"
 #include "cc/surfaces/local_surface_id_allocator.h"
-#include "cc/surfaces/surface_manager.h"
 #include "cc/test/begin_frame_args_test.h"
 #include "cc/test/compositor_frame_helpers.h"
 #include "cc/test/fake_layer_tree_frame_sink_client.h"
@@ -46,7 +46,7 @@
         display_size_(1920, 1080),
         display_rect_(display_size_),
         context_provider_(TestContextProvider::Create()) {
-    surface_manager_.RegisterFrameSinkId(kArbitraryFrameSinkId);
+    frame_sink_manager_.RegisterFrameSinkId(kArbitraryFrameSinkId);
 
     std::unique_ptr<FakeOutputSurface> display_output_surface =
         FakeOutputSurface::Create3d();
@@ -65,7 +65,7 @@
         std::move(scheduler),
         base::MakeUnique<TextureMailboxDeleter>(task_runner_.get())));
     layer_tree_frame_sink_.reset(new TestDirectLayerTreeFrameSink(
-        kArbitraryFrameSinkId, &surface_manager_, display_.get(),
+        kArbitraryFrameSinkId, &frame_sink_manager_, display_.get(),
         context_provider_, nullptr, &gpu_memory_buffer_manager_,
         &bitmap_manager_));
 
@@ -107,7 +107,7 @@
 
   const gfx::Size display_size_;
   const gfx::Rect display_rect_;
-  SurfaceManager surface_manager_;
+  FrameSinkManager frame_sink_manager_;
   TestSharedBitmapManager bitmap_manager_;
   TestGpuMemoryBufferManager gpu_memory_buffer_manager_;
 
diff --git a/cc/surfaces/display_unittest.cc b/cc/surfaces/display_unittest.cc
index 5b16888ce..5abeff7 100644
--- a/cc/surfaces/display_unittest.cc
+++ b/cc/surfaces/display_unittest.cc
@@ -18,6 +18,7 @@
 #include "cc/surfaces/display_client.h"
 #include "cc/surfaces/display_scheduler.h"
 #include "cc/surfaces/frame_sink_id.h"
+#include "cc/surfaces/frame_sink_manager.h"
 #include "cc/surfaces/local_surface_id_allocator.h"
 #include "cc/surfaces/surface.h"
 #include "cc/surfaces/surface_manager.h"
@@ -153,7 +154,7 @@
     support_->SubmitCompositorFrame(local_surface_id, std::move(frame));
   }
 
-  SurfaceManager manager_;
+  FrameSinkManager manager_;
   std::unique_ptr<CompositorFrameSinkSupport> support_;
   LocalSurfaceIdAllocator id_allocator_;
   scoped_refptr<base::NullTaskRunner> task_runner_;
@@ -187,7 +188,7 @@
   gfx::ColorSpace color_space_2 = gfx::ColorSpace::CreateSCRGBLinear();
 
   StubDisplayClient client;
-  display_->Initialize(&client, &manager_);
+  display_->Initialize(&client, manager_.surface_manager());
   display_->SetColorSpace(color_space_1, color_space_1);
 
   LocalSurfaceId local_surface_id(id_allocator_.GenerateId());
@@ -445,7 +446,7 @@
   gfx::ColorSpace color_space_2 = gfx::ColorSpace::CreateSCRGBLinear();
 
   StubDisplayClient client;
-  display_->Initialize(&client, &manager_);
+  display_->Initialize(&client, manager_.surface_manager());
   display_->SetColorSpace(color_space_1, color_space_1);
 
   LocalSurfaceId local_surface_id(id_allocator_.GenerateId());
@@ -514,7 +515,7 @@
   SetUpDisplay(settings, std::move(context));
 
   StubDisplayClient client;
-  display_->Initialize(&client, &manager_);
+  display_->Initialize(&client, manager_.surface_manager());
 
   display_->SetLocalSurfaceId(local_surface_id1, 1.f);
 
@@ -584,7 +585,7 @@
   SetUpDisplay(RendererSettings(), TestWebGraphicsContext3D::Create());
 
   CountLossDisplayClient client;
-  display_->Initialize(&client, &manager_);
+  display_->Initialize(&client, manager_.surface_manager());
 
   // Verify DidLoseOutputSurface callback is hooked up correctly.
   EXPECT_EQ(0, client.loss_count());
@@ -605,7 +606,7 @@
   // Set up first display.
   SetUpDisplay(settings, nullptr);
   StubDisplayClient client;
-  display_->Initialize(&client, &manager_);
+  display_->Initialize(&client, manager_.surface_manager());
   display_->SetLocalSurfaceId(local_surface_id, 1.f);
 
   // Set up second frame sink + display.
@@ -624,7 +625,7 @@
   manager_.RegisterBeginFrameSource(begin_frame_source2.get(),
                                     kAnotherFrameSinkId);
   StubDisplayClient client2;
-  display2->Initialize(&client2, &manager_);
+  display2->Initialize(&client2, manager_.surface_manager());
   display2->SetLocalSurfaceId(local_surface_id, 1.f);
 
   display_->Resize(gfx::Size(100, 100));
diff --git a/cc/surfaces/frame_sink_manager.cc b/cc/surfaces/frame_sink_manager.cc
index 2d6078a..5265448 100644
--- a/cc/surfaces/frame_sink_manager.cc
+++ b/cc/surfaces/frame_sink_manager.cc
@@ -8,6 +8,7 @@
 #include <stdint.h>
 
 #include "base/logging.h"
+#include "cc/surfaces/display.h"
 #include "cc/surfaces/frame_sink_manager_client.h"
 #include "cc/surfaces/primary_begin_frame_source.h"
 
@@ -24,7 +25,8 @@
 
 FrameSinkManager::FrameSinkSourceMapping::~FrameSinkSourceMapping() = default;
 
-FrameSinkManager::FrameSinkManager() = default;
+FrameSinkManager::FrameSinkManager(SurfaceManager::LifetimeType lifetime_type)
+    : surface_manager_(lifetime_type) {}
 
 FrameSinkManager::~FrameSinkManager() {
   // All FrameSinks should be unregistered prior to SurfaceManager destruction.
@@ -33,19 +35,18 @@
 }
 
 void FrameSinkManager::RegisterFrameSinkId(const FrameSinkId& frame_sink_id) {
-  bool inserted = valid_frame_sink_ids_.insert(frame_sink_id).second;
-  DCHECK(inserted);
+  surface_manager_.RegisterFrameSinkId(frame_sink_id);
 }
 
 void FrameSinkManager::InvalidateFrameSinkId(const FrameSinkId& frame_sink_id) {
-  valid_frame_sink_ids_.erase(frame_sink_id);
+  surface_manager_.InvalidateFrameSinkId(frame_sink_id);
 }
 
 void FrameSinkManager::RegisterFrameSinkManagerClient(
     const FrameSinkId& frame_sink_id,
     FrameSinkManagerClient* client) {
   DCHECK(client);
-  DCHECK_EQ(valid_frame_sink_ids_.count(frame_sink_id), 1u);
+  DCHECK_EQ(surface_manager_.GetValidFrameSinkIds().count(frame_sink_id), 1u);
 
   clients_[frame_sink_id] = client;
 
@@ -58,7 +59,7 @@
 
 void FrameSinkManager::UnregisterFrameSinkManagerClient(
     const FrameSinkId& frame_sink_id) {
-  DCHECK_EQ(valid_frame_sink_ids_.count(frame_sink_id), 1u);
+  DCHECK_EQ(surface_manager_.GetValidFrameSinkIds().count(frame_sink_id), 1u);
   auto client_iter = clients_.find(frame_sink_id);
   DCHECK(client_iter != clients_.end());
 
@@ -77,7 +78,7 @@
     const FrameSinkId& frame_sink_id) {
   DCHECK(source);
   DCHECK_EQ(registered_sources_.count(source), 0u);
-  DCHECK_EQ(valid_frame_sink_ids_.count(frame_sink_id), 1u);
+  DCHECK_EQ(surface_manager_.GetValidFrameSinkIds().count(frame_sink_id), 1u);
 
   registered_sources_[source] = frame_sink_id;
   RecursivelyAttachBeginFrameSource(frame_sink_id, source);
@@ -120,8 +121,14 @@
     if (client_iter != clients_.end())
       client_iter->second->SetBeginFrameSource(source);
   }
-  for (size_t i = 0; i < mapping.children.size(); ++i)
-    RecursivelyAttachBeginFrameSource(mapping.children[i], source);
+  for (size_t i = 0; i < mapping.children.size(); ++i) {
+    // |frame_sink_source_map_| is a container that can allocate new memory and
+    // move data between buffers. Copy child's FrameSinkId before passing it to
+    // RecursivelyAttachBeginFrameSource so that we don't reference data inside
+    // |frame_sink_source_map_|.
+    FrameSinkId child_copy = mapping.children[i];
+    RecursivelyAttachBeginFrameSource(child_copy, source);
+  }
 }
 
 void FrameSinkManager::RecursivelyDetachBeginFrameSource(
@@ -234,4 +241,8 @@
     RecursivelyAttachBeginFrameSource(source_iter.second, source_iter.first);
 }
 
+void FrameSinkManager::DropTemporaryReference(const SurfaceId& surface_id) {
+  surface_manager_.DropTemporaryReference(surface_id);
+}
+
 }  // namespace cc
diff --git a/cc/surfaces/frame_sink_manager.h b/cc/surfaces/frame_sink_manager.h
index 11c0f97..2833389 100644
--- a/cc/surfaces/frame_sink_manager.h
+++ b/cc/surfaces/frame_sink_manager.h
@@ -8,13 +8,14 @@
 #include <stdint.h>
 
 #include <unordered_map>
-#include <unordered_set>
 #include <vector>
 
+#include "base/containers/flat_map.h"
 #include "base/logging.h"
 #include "base/macros.h"
 #include "cc/surfaces/frame_sink_id.h"
 #include "cc/surfaces/primary_begin_frame_source.h"
+#include "cc/surfaces/surface_manager.h"
 #include "cc/surfaces/surfaces_export.h"
 
 namespace cc {
@@ -22,12 +23,17 @@
 class FrameSinkManagerClient;
 
 namespace test {
+class CompositorFrameSinkSupportTest;
 class SurfaceSynchronizationTest;
 }
 
+// FrameSinkManager manages BeginFrame hierarchy. This is the implementation
+// detail for FrameSinkManagerImpl.
+// TODO(staraz): Merge FrameSinkManager into FrameSinkManagerImpl.
 class CC_SURFACES_EXPORT FrameSinkManager {
  public:
-  FrameSinkManager();
+  explicit FrameSinkManager(SurfaceManager::LifetimeType lifetime_type =
+                                SurfaceManager::LifetimeType::SEQUENCES);
   ~FrameSinkManager();
 
   void RegisterFrameSinkId(const FrameSinkId& frame_sink_id);
@@ -71,13 +77,15 @@
   void UnregisterFrameSinkHierarchy(const FrameSinkId& parent_frame_sink_id,
                                     const FrameSinkId& child_frame_sink_id);
 
-  // Export list of valid frame_sink_ids for SatisfyDestructionDeps in surface
-  // may be removed later when References replace Sequences
-  std::unordered_set<FrameSinkId, FrameSinkIdHash>* GetValidFrameSinkIds() {
-    return &valid_frame_sink_ids_;
-  }
+  // Drops the temporary reference for |surface_id|. If a surface reference has
+  // already been added from the parent to |surface_id| then this will do
+  // nothing.
+  void DropTemporaryReference(const SurfaceId& surface_id);
+
+  SurfaceManager* surface_manager() { return &surface_manager_; }
 
  private:
+  friend class test::CompositorFrameSinkSupportTest;
   friend class test::SurfaceSynchronizationTest;
 
   void RecursivelyAttachBeginFrameSource(const FrameSinkId& frame_sink_id,
@@ -90,11 +98,6 @@
   bool ChildContains(const FrameSinkId& child_frame_sink_id,
                      const FrameSinkId& search_frame_sink_id) const;
 
-  // Set of valid framesink Ids. When a framesink Id  is removed from
-  // this set, any remaining (surface) sequences with that framesink are
-  // considered satisfied.
-  std::unordered_set<FrameSinkId, FrameSinkIdHash> valid_frame_sink_ids_;
-
   // Begin frame source routing. Both BeginFrameSource and
   // CompositorFrameSinkSupport pointers guaranteed alive by callers until
   // unregistered.
@@ -109,9 +112,7 @@
     std::vector<FrameSinkId> children;
   };
 
-  std::unordered_map<FrameSinkId, FrameSinkManagerClient*, FrameSinkIdHash>
-      clients_;
-
+  base::flat_map<FrameSinkId, FrameSinkManagerClient*> clients_;
   std::unordered_map<FrameSinkId, FrameSinkSourceMapping, FrameSinkIdHash>
       frame_sink_source_map_;
 
@@ -122,6 +123,10 @@
 
   PrimaryBeginFrameSource primary_source_;
 
+  // |surface_manager_| should be placed under |primary_source_| so that all
+  // surfaces are destroyed before |primary_source_|.
+  SurfaceManager surface_manager_;
+
   DISALLOW_COPY_AND_ASSIGN(FrameSinkManager);
 };
 
diff --git a/cc/surfaces/surface_manager_unittest.cc b/cc/surfaces/frame_sink_manager_unittest.cc
similarity index 96%
rename from cc/surfaces/surface_manager_unittest.cc
rename to cc/surfaces/frame_sink_manager_unittest.cc
index 4dbce5d..8cd721f4 100644
--- a/cc/surfaces/surface_manager_unittest.cc
+++ b/cc/surfaces/frame_sink_manager_unittest.cc
@@ -5,8 +5,8 @@
 #include <stddef.h>
 
 #include "cc/scheduler/begin_frame_source.h"
+#include "cc/surfaces/frame_sink_manager.h"
 #include "cc/surfaces/frame_sink_manager_client.h"
-#include "cc/surfaces/surface_manager.h"
 #include "cc/test/begin_frame_source_test.h"
 #include "cc/test/fake_external_begin_frame_source.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -19,7 +19,7 @@
       : source_(nullptr), manager_(nullptr), frame_sink_id_(frame_sink_id) {}
 
   FakeFrameSinkManagerClient(const FrameSinkId& frame_sink_id,
-                             SurfaceManager* manager)
+                             FrameSinkManager* manager)
       : source_(nullptr), manager_(nullptr), frame_sink_id_(frame_sink_id) {
     DCHECK(manager);
     Register(manager);
@@ -35,7 +35,7 @@
   BeginFrameSource* source() { return source_; }
   const FrameSinkId& frame_sink_id() { return frame_sink_id_; }
 
-  void Register(SurfaceManager* manager) {
+  void Register(FrameSinkManager* manager) {
     EXPECT_EQ(nullptr, manager_);
     manager_ = manager;
     manager_->RegisterFrameSinkManagerClient(frame_sink_id_, this);
@@ -55,32 +55,32 @@
 
  private:
   BeginFrameSource* source_;
-  SurfaceManager* manager_;
+  FrameSinkManager* manager_;
   FrameSinkId frame_sink_id_;
 };
 
-class SurfaceManagerTest : public testing::Test {
+class FrameSinkManagerTest : public testing::Test {
  public:
   // These tests don't care about namespace registration, so just preregister
   // a set of namespaces that tests can use freely without worrying if they're
   // valid or not.
   enum { MAX_FRAME_SINK = 10 };
 
-  SurfaceManagerTest() {
+  FrameSinkManagerTest() {
     for (size_t i = 0; i < MAX_FRAME_SINK; ++i)
       manager_.RegisterFrameSinkId(FrameSinkId(i, i));
   }
 
-  ~SurfaceManagerTest() override {
+  ~FrameSinkManagerTest() override {
     for (size_t i = 0; i < MAX_FRAME_SINK; ++i)
       manager_.InvalidateFrameSinkId(FrameSinkId(i, i));
   }
 
  protected:
-  SurfaceManager manager_;
+  FrameSinkManager manager_;
 };
 
-TEST_F(SurfaceManagerTest, SingleClients) {
+TEST_F(FrameSinkManagerTest, SingleClients) {
   FakeFrameSinkManagerClient client(FrameSinkId(1, 1));
   FakeFrameSinkManagerClient other_client(FrameSinkId(2, 2));
   StubBeginFrameSource source;
@@ -118,7 +118,7 @@
 // This test verifies that a PrimaryBeginFrameSource will receive BeginFrames
 // from the first BeginFrameSource registered. If that BeginFrameSource goes
 // away then it will receive BeginFrames from the second BeginFrameSource.
-TEST_F(SurfaceManagerTest, PrimaryBeginFrameSource) {
+TEST_F(FrameSinkManagerTest, PrimaryBeginFrameSource) {
   // This PrimaryBeginFrameSource should track the first BeginFrameSource
   // registered with the SurfaceManager.
   testing::NiceMock<MockBeginFrameObserver> obs;
@@ -174,7 +174,7 @@
   begin_frame_source->RemoveObserver(&obs);
 }
 
-TEST_F(SurfaceManagerTest, MultipleDisplays) {
+TEST_F(FrameSinkManagerTest, MultipleDisplays) {
   StubBeginFrameSource root1_source;
   StubBeginFrameSource root2_source;
 
@@ -244,7 +244,7 @@
 // This test verifies that a BeginFrameSource path to the root from a
 // FrameSinkId is preserved even if that FrameSinkId has no children
 // and does not have a corresponding FrameSinkManagerClient.
-TEST_F(SurfaceManagerTest, ParentWithoutClientRetained) {
+TEST_F(FrameSinkManagerTest, ParentWithoutClientRetained) {
   StubBeginFrameSource root_source;
 
   constexpr FrameSinkId kFrameSinkIdRoot(1, 1);
@@ -282,7 +282,7 @@
 // However, this unit test registers the BeginFrameSource AFTER C
 // has been attached to A. This test verifies that the BeginFrameSource
 // propagates all the way to C.
-TEST_F(SurfaceManagerTest,
+TEST_F(FrameSinkManagerTest,
        ParentWithoutClientRetained_LateBeginFrameRegistration) {
   StubBeginFrameSource root_source;
 
@@ -324,7 +324,7 @@
 // are properly set up and cleaned up under the four permutations of orderings
 // of this nesting.
 
-class SurfaceManagerOrderingTest : public SurfaceManagerTest {
+class SurfaceManagerOrderingTest : public FrameSinkManagerTest {
  public:
   SurfaceManagerOrderingTest()
       : client_a_(FrameSinkId(1, 1)),
diff --git a/cc/surfaces/surface.cc b/cc/surfaces/surface.cc
index 99e479d..c578ef2 100644
--- a/cc/surfaces/surface.cc
+++ b/cc/surfaces/surface.cc
@@ -360,8 +360,8 @@
 }
 
 void Surface::SatisfyDestructionDependencies(
-    std::unordered_set<SurfaceSequence, SurfaceSequenceHash>* sequences,
-    std::unordered_set<FrameSinkId, FrameSinkIdHash>* valid_frame_sink_ids) {
+    base::flat_set<SurfaceSequence>* sequences,
+    base::flat_set<FrameSinkId>* valid_frame_sink_ids) {
   base::EraseIf(destruction_dependencies_,
                 [sequences, valid_frame_sink_ids](SurfaceSequence seq) {
                   return (!!sequences->erase(seq) ||
diff --git a/cc/surfaces/surface.h b/cc/surfaces/surface.h
index 8b9be1b9..660a6ecd 100644
--- a/cc/surfaces/surface.h
+++ b/cc/surfaces/surface.h
@@ -124,8 +124,8 @@
   // Satisfy all destruction dependencies that are contained in sequences, and
   // remove them from sequences.
   void SatisfyDestructionDependencies(
-      std::unordered_set<SurfaceSequence, SurfaceSequenceHash>* sequences,
-      std::unordered_set<FrameSinkId, FrameSinkIdHash>* valid_id_namespaces);
+      base::flat_set<SurfaceSequence>* sequences,
+      base::flat_set<FrameSinkId>* valid_id_namespaces);
   size_t GetDestructionDependencyCount() const {
     return destruction_dependencies_.size();
   }
diff --git a/cc/surfaces/surface_aggregator_perftest.cc b/cc/surfaces/surface_aggregator_perftest.cc
index ac7fb4b5..89ef0f9 100644
--- a/cc/surfaces/surface_aggregator_perftest.cc
+++ b/cc/surfaces/surface_aggregator_perftest.cc
@@ -8,6 +8,7 @@
 #include "cc/quads/surface_draw_quad.h"
 #include "cc/quads/texture_draw_quad.h"
 #include "cc/surfaces/compositor_frame_sink_support.h"
+#include "cc/surfaces/frame_sink_manager.h"
 #include "cc/surfaces/surface_aggregator.h"
 #include "cc/surfaces/surface_manager.h"
 #include "cc/test/compositor_frame_helpers.h"
@@ -52,8 +53,8 @@
           nullptr, &manager_, FrameSinkId(1, i + 1), kIsChildRoot,
           kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints);
     }
-    aggregator_.reset(new SurfaceAggregator(&manager_, resource_provider_.get(),
-                                            optimize_damage));
+    aggregator_.reset(new SurfaceAggregator(
+        manager_.surface_manager(), resource_provider_.get(), optimize_damage));
     for (int i = 0; i < num_surfaces; i++) {
       LocalSurfaceId local_surface_id(i + 1, kArbitraryToken);
 
@@ -149,7 +150,7 @@
   }
 
  protected:
-  SurfaceManager manager_;
+  FrameSinkManager manager_;
   scoped_refptr<TestContextProvider> context_provider_;
   std::unique_ptr<SharedBitmapManager> shared_bitmap_manager_;
   std::unique_ptr<ResourceProvider> resource_provider_;
diff --git a/cc/surfaces/surface_aggregator_unittest.cc b/cc/surfaces/surface_aggregator_unittest.cc
index 38af83b..fe1130fd 100644
--- a/cc/surfaces/surface_aggregator_unittest.cc
+++ b/cc/surfaces/surface_aggregator_unittest.cc
@@ -19,6 +19,7 @@
 #include "cc/quads/texture_draw_quad.h"
 #include "cc/resources/shared_bitmap_manager.h"
 #include "cc/surfaces/compositor_frame_sink_support.h"
+#include "cc/surfaces/frame_sink_manager.h"
 #include "cc/surfaces/local_surface_id_allocator.h"
 #include "cc/surfaces/surface.h"
 #include "cc/surfaces/surface_manager.h"
@@ -70,8 +71,8 @@
                                                kRootIsRoot,
                                                kHandlesFrameSinkIdInvalidation,
                                                kNeedsSyncPoints)),
-        aggregator_(&manager_, NULL, use_damage_rect) {
-    manager_.AddObserver(&observer_);
+        aggregator_(manager_.surface_manager(), NULL, use_damage_rect) {
+    manager_.surface_manager()->AddObserver(&observer_);
   }
 
   SurfaceAggregatorTest() : SurfaceAggregatorTest(false) {}
@@ -83,7 +84,7 @@
   }
 
  protected:
-  SurfaceManager manager_;
+  FrameSinkManager manager_;
   FakeSurfaceObserver observer_;
   FakeCompositorFrameSinkSupportClient fake_client_;
   std::unique_ptr<CompositorFrameSinkSupport> support_;
@@ -107,7 +108,7 @@
   void SetUp() override {
     SurfaceAggregatorTest::SetUp();
     root_local_surface_id_ = allocator_.GenerateId();
-    root_surface_ = manager_.GetSurfaceForId(
+    root_surface_ = manager_.surface_manager()->GetSurfaceForId(
         SurfaceId(support_->frame_sink_id(), root_local_surface_id_));
   }
 
@@ -634,8 +635,9 @@
   }
 
   // Ensure copy requests have been removed from root surface.
-  const CompositorFrame& original_frame =
-      manager_.GetSurfaceForId(root_surface_id)->GetActiveFrame();
+  const CompositorFrame& original_frame = manager_.surface_manager()
+                                              ->GetSurfaceForId(root_surface_id)
+                                              ->GetActiveFrame();
   const RenderPassList& original_pass_list = original_frame.render_pass_list;
   ASSERT_EQ(2u, original_pass_list.size());
   DCHECK(original_pass_list[0]->copy_requests.empty());
@@ -1954,13 +1956,13 @@
     resource_provider_ =
         FakeResourceProvider::Create(nullptr, shared_bitmap_manager_.get());
 
-    aggregator_.reset(
-        new SurfaceAggregator(&manager_, resource_provider_.get(), false));
+    aggregator_.reset(new SurfaceAggregator(manager_.surface_manager(),
+                                            resource_provider_.get(), false));
     aggregator_->set_output_is_secure(true);
   }
 
  protected:
-  SurfaceManager manager_;
+  FrameSinkManager manager_;
   std::unique_ptr<SharedBitmapManager> shared_bitmap_manager_;
   std::unique_ptr<ResourceProvider> resource_provider_;
   std::unique_ptr<SurfaceAggregator> aggregator_;
diff --git a/cc/surfaces/surface_hittest_unittest.cc b/cc/surfaces/surface_hittest_unittest.cc
index 9b2b236..386e069 100644
--- a/cc/surfaces/surface_hittest_unittest.cc
+++ b/cc/surfaces/surface_hittest_unittest.cc
@@ -6,6 +6,7 @@
 
 #include "cc/output/compositor_frame.h"
 #include "cc/surfaces/compositor_frame_sink_support.h"
+#include "cc/surfaces/frame_sink_manager.h"
 #include "cc/surfaces/local_surface_id_allocator.h"
 #include "cc/surfaces/surface.h"
 #include "cc/surfaces/surface_hittest.h"
@@ -65,7 +66,7 @@
 // This test verifies that hit testing on a surface that does not exist does
 // not crash.
 TEST(SurfaceHittestTest, Hittest_BadCompositorFrameDoesNotCrash) {
-  SurfaceManager manager;
+  FrameSinkManager manager;
   FrameSinkId root_frame_sink_id(kArbitraryFrameSinkId);
   std::unique_ptr<CompositorFrameSinkSupport> root_support =
       CompositorFrameSinkSupport::Create(
@@ -96,7 +97,7 @@
                                       std::move(root_frame));
 
   {
-    SurfaceHittest hittest(nullptr, &manager);
+    SurfaceHittest hittest(nullptr, manager.surface_manager());
     // It is expected this test will complete without crashes.
     gfx::Transform transform;
     EXPECT_EQ(root_surface_id,
@@ -108,7 +109,7 @@
 }
 
 TEST(SurfaceHittestTest, Hittest_SingleSurface) {
-  SurfaceManager manager;
+  FrameSinkManager manager;
 
   // Set up root FrameSink.
   FrameSinkId root_frame_sink_id(1, 1);
@@ -137,13 +138,13 @@
     },
   };
 
-  RunTests(nullptr, &manager, tests, arraysize(tests));
+  RunTests(nullptr, manager.surface_manager(), tests, arraysize(tests));
 
   root_support->EvictCurrentSurface();
 }
 
 TEST(SurfaceHittestTest, Hittest_ChildSurface) {
-  SurfaceManager manager;
+  FrameSinkManager manager;
 
   // Set up root FrameSink.
   FrameSinkId root_frame_sink_id(1, 1);
@@ -242,7 +243,7 @@
     }
   };
 
-  RunTests(nullptr, &manager, tests, arraysize(tests));
+  RunTests(nullptr, manager.surface_manager(), tests, arraysize(tests));
 
   // Submit another root frame, with a slightly perturbed child Surface.
   root_frame = CreateCompositorFrame(root_rect, &root_pass);
@@ -260,7 +261,7 @@
   // Verify that point (100, 100) no longer falls on the child surface.
   // Verify that the transform to the child surface's space has also shifted.
   {
-    SurfaceHittest hittest(nullptr, &manager);
+    SurfaceHittest hittest(nullptr, manager.surface_manager());
 
     gfx::Point point(100, 100);
     gfx::Transform transform;
@@ -286,7 +287,7 @@
 // This test verifies that hit testing will progress to the next quad if it
 // encounters an invalid RenderPassDrawQuad for whatever reason.
 TEST(SurfaceHittestTest, Hittest_InvalidRenderPassDrawQuad) {
-  SurfaceManager manager;
+  FrameSinkManager manager;
 
   // Set up root FrameSink.
   FrameSinkId root_frame_sink_id(1, 1);
@@ -390,14 +391,14 @@
     }
   };
 
-  RunTests(nullptr, &manager, tests, arraysize(tests));
+  RunTests(nullptr, manager.surface_manager(), tests, arraysize(tests));
 
   root_support->EvictCurrentSurface();
   child_support->EvictCurrentSurface();
 }
 
 TEST(SurfaceHittestTest, Hittest_RenderPassDrawQuad) {
-  SurfaceManager manager;
+  FrameSinkManager manager;
   FrameSinkId root_frame_sink_id(kArbitraryFrameSinkId);
   std::unique_ptr<CompositorFrameSinkSupport> support =
       CompositorFrameSinkSupport::Create(
@@ -494,13 +495,13 @@
     }
   };
 
-  RunTests(nullptr, &manager, tests, arraysize(tests));
+  RunTests(nullptr, manager.surface_manager(), tests, arraysize(tests));
 
   support->EvictCurrentSurface();
 }
 
 TEST(SurfaceHittestTest, Hittest_SingleSurface_WithInsetsDelegate) {
-  SurfaceManager manager;
+  FrameSinkManager manager;
 
   // Set up root FrameSink.
   FrameSinkId root_frame_sink_id(1, 1);
@@ -572,7 +573,8 @@
   };
 
   TestSurfaceHittestDelegate empty_delegate;
-  RunTests(&empty_delegate, &manager, test_expectations_without_insets,
+  RunTests(&empty_delegate, manager.surface_manager(),
+           test_expectations_without_insets,
            arraysize(test_expectations_without_insets));
 
   // Verify that insets have NOT affected hit targeting.
@@ -603,7 +605,8 @@
   TestSurfaceHittestDelegate reject_delegate;
   reject_delegate.AddInsetsForRejectSurface(child_surface_id,
                                             gfx::Insets(10, 10, 10, 10));
-  RunTests(&reject_delegate, &manager, test_expectations_with_reject_insets,
+  RunTests(&reject_delegate, manager.surface_manager(),
+           test_expectations_with_reject_insets,
            arraysize(test_expectations_with_reject_insets));
 
   // Verify that insets have affected hit targeting.
@@ -627,7 +630,8 @@
   TestSurfaceHittestDelegate accept_delegate;
   accept_delegate.AddInsetsForAcceptSurface(child_surface_id,
                                             gfx::Insets(5, 5, 5, 5));
-  RunTests(&accept_delegate, &manager, test_expectations_with_accept_insets,
+  RunTests(&accept_delegate, manager.surface_manager(),
+           test_expectations_with_accept_insets,
            arraysize(test_expectations_with_accept_insets));
 
   // Verify that insets have affected hit targeting.
diff --git a/cc/surfaces/surface_manager.cc b/cc/surfaces/surface_manager.cc
index 3e6815e..341fccd 100644
--- a/cc/surfaces/surface_manager.cc
+++ b/cc/surfaces/surface_manager.cc
@@ -25,9 +25,9 @@
 
 namespace cc {
 
-SurfaceManager::SurfaceReferenceInfo::SurfaceReferenceInfo() {}
+SurfaceManager::SurfaceReferenceInfo::SurfaceReferenceInfo() = default;
 
-SurfaceManager::SurfaceReferenceInfo::~SurfaceReferenceInfo() {}
+SurfaceManager::SurfaceReferenceInfo::~SurfaceReferenceInfo() = default;
 
 SurfaceManager::SurfaceManager(LifetimeType lifetime_type)
     : lifetime_type_(lifetime_type),
@@ -129,11 +129,12 @@
 }
 
 void SurfaceManager::RegisterFrameSinkId(const FrameSinkId& frame_sink_id) {
-  framesink_manager_.RegisterFrameSinkId(frame_sink_id);
+  bool inserted = valid_frame_sink_ids_.insert(frame_sink_id).second;
+  DCHECK(inserted);
 }
 
 void SurfaceManager::InvalidateFrameSinkId(const FrameSinkId& frame_sink_id) {
-  framesink_manager_.InvalidateFrameSinkId(frame_sink_id);
+  valid_frame_sink_ids_.erase(frame_sink_id);
 
   // Remove any temporary references owned by |frame_sink_id|.
   std::vector<SurfaceId> temp_refs_to_clear;
@@ -277,7 +278,7 @@
     const SurfaceId& surface_id = map_entry.first;
     Surface* surface = map_entry.second.get();
     surface->SatisfyDestructionDependencies(&satisfied_sequences_,
-                                  framesink_manager_.GetValidFrameSinkIds());
+                                            &valid_frame_sink_ids_);
 
     if (!IsMarkedForDestruction(surface_id) ||
         surface->GetDestructionDependencyCount() > 0) {
@@ -402,45 +403,6 @@
     temporary_reference_ranges_.erase(frame_sink_id);
 }
 
-void SurfaceManager::RegisterFrameSinkManagerClient(
-    const FrameSinkId& frame_sink_id,
-    FrameSinkManagerClient* client) {
-  framesink_manager_.RegisterFrameSinkManagerClient(frame_sink_id, client);
-}
-
-void SurfaceManager::UnregisterFrameSinkManagerClient(
-    const FrameSinkId& frame_sink_id) {
-  framesink_manager_.UnregisterFrameSinkManagerClient(frame_sink_id);
-}
-
-void SurfaceManager::RegisterBeginFrameSource(
-    BeginFrameSource* source,
-    const FrameSinkId& frame_sink_id) {
-  framesink_manager_.RegisterBeginFrameSource(source, frame_sink_id);
-}
-
-void SurfaceManager::UnregisterBeginFrameSource(BeginFrameSource* source) {
-  framesink_manager_.UnregisterBeginFrameSource(source);
-}
-
-BeginFrameSource* SurfaceManager::GetPrimaryBeginFrameSource() {
-  return framesink_manager_.GetPrimaryBeginFrameSource();
-}
-
-void SurfaceManager::RegisterFrameSinkHierarchy(
-    const FrameSinkId& parent_frame_sink_id,
-    const FrameSinkId& child_frame_sink_id) {
-  framesink_manager_.RegisterFrameSinkHierarchy(parent_frame_sink_id,
-                                                child_frame_sink_id);
-}
-
-void SurfaceManager::UnregisterFrameSinkHierarchy(
-    const FrameSinkId& parent_frame_sink_id,
-    const FrameSinkId& child_frame_sink_id) {
-  framesink_manager_.UnregisterFrameSinkHierarchy(parent_frame_sink_id,
-                                                child_frame_sink_id);
-}
-
 Surface* SurfaceManager::GetSurfaceForId(const SurfaceId& surface_id) {
   DCHECK(thread_checker_.CalledOnValidThread());
   auto it = surface_map_.find(surface_id);
diff --git a/cc/surfaces/surface_manager.h b/cc/surfaces/surface_manager.h
index a3257e2b..b194c0e5 100644
--- a/cc/surfaces/surface_manager.h
+++ b/cc/surfaces/surface_manager.h
@@ -20,7 +20,6 @@
 #include "base/observer_list.h"
 #include "base/threading/thread_checker.h"
 #include "cc/surfaces/frame_sink_id.h"
-#include "cc/surfaces/frame_sink_manager.h"
 #include "cc/surfaces/surface_dependency_tracker.h"
 #include "cc/surfaces/surface_id.h"
 #include "cc/surfaces/surface_observer.h"
@@ -38,9 +37,7 @@
 
 struct BeginFrameAck;
 struct BeginFrameArgs;
-class BeginFrameSource;
 class CompositorFrame;
-class FrameSinkManagerClient;
 class Surface;
 
 namespace test {
@@ -132,35 +129,6 @@
   // possibly because a renderer process has crashed.
   void InvalidateFrameSinkId(const FrameSinkId& frame_sink_id);
 
-  // SurfaceClient, hierarchy, and BeginFrameSource can be registered and
-  // unregistered in any order with respect to each other.
-  //
-  // This happens in practice, e.g. the relationship to between ui::Compositor /
-  // DelegatedFrameHost is known before ui::Compositor has a surface/client).
-  // However, DelegatedFrameHost can register itself as a client before its
-  // relationship with the ui::Compositor is known.
-
-  // Associates a FrameSinkManagerClient with the surface id frame_sink_id it
-  // uses.
-  // FrameSinkManagerClient and surface namespaces/allocators have a 1:1
-  // mapping. Caller guarantees the client is alive between register/unregister.
-  // Reregistering the same namespace when a previous client is active is not
-  // valid.
-  void RegisterFrameSinkManagerClient(const FrameSinkId& frame_sink_id,
-                                      FrameSinkManagerClient* client);
-  void UnregisterFrameSinkManagerClient(const FrameSinkId& frame_sink_id);
-
-  // Associates a |source| with a particular namespace.  That namespace and
-  // any children of that namespace with valid clients can potentially use
-  // that |source|.
-  void RegisterBeginFrameSource(BeginFrameSource* source,
-                                const FrameSinkId& frame_sink_id);
-  void UnregisterBeginFrameSource(BeginFrameSource* source);
-
-  // Returns a stable BeginFrameSource that forwards BeginFrames from the first
-  // available BeginFrameSource.
-  BeginFrameSource* GetPrimaryBeginFrameSource();
-
   // Register a relationship between two namespaces.  This relationship means
   // that surfaces from the child namespace will be displayed in the parent.
   // Children are allowed to use any begin frame source that their parent can
@@ -183,8 +151,8 @@
   // collection to delete unreachable surfaces.
   void RemoveSurfaceReferences(const std::vector<SurfaceReference>& references);
 
-  // Assigns |frame_sink_id| as the owner of the temporary reference to
-  // |surface_id|. If |frame_sink_id| is invalidated the temporary reference
+  // Assigns |owner| as the owner of the temporary reference to
+  // |surface_id|. If |owner| is invalidated the temporary reference
   // will be removed. If a surface reference has already been added from the
   // parent to |surface_id| then this will do nothing.
   void AssignTemporaryReference(const SurfaceId& surface_id,
@@ -205,7 +173,7 @@
   const base::flat_set<SurfaceId>& GetSurfacesThatReferenceChild(
       const SurfaceId& surface_id) const;
 
-  scoped_refptr<SurfaceReferenceFactory> reference_factory() {
+  const scoped_refptr<SurfaceReferenceFactory>& reference_factory() {
     return reference_factory_;
   }
 
@@ -213,6 +181,10 @@
     return lifetime_type_ == LifetimeType::REFERENCES;
   }
 
+  const base::flat_set<FrameSinkId>& GetValidFrameSinkIds() {
+    return valid_frame_sink_ids_;
+  }
+
  private:
   friend class test::SurfaceSynchronizationTest;
   friend class SurfaceManagerRefTest;
@@ -279,8 +251,6 @@
   // Use reference or sequence based lifetime management.
   LifetimeType lifetime_type_;
 
-  FrameSinkManager framesink_manager_;
-
   // SurfaceDependencyTracker needs to be destroyed after Surfaces are destroyed
   // because they will call back into the dependency tracker.
   SurfaceDependencyTracker dependency_tracker_;
@@ -293,7 +263,12 @@
 
   // Set of SurfaceSequences that have been satisfied by a frame but not yet
   // waited on.
-  std::unordered_set<SurfaceSequence, SurfaceSequenceHash> satisfied_sequences_;
+  base::flat_set<SurfaceSequence> satisfied_sequences_;
+
+  // Set of valid FrameSinkIds. When a FrameSinkId is removed from
+  // this set, any remaining (surface) sequences with that FrameSinkId are
+  // considered satisfied.
+  base::flat_set<FrameSinkId> valid_frame_sink_ids_;
 
   // Root SurfaceId that references display root surfaces. There is no Surface
   // with this id, it's for bookkeeping purposes only.
diff --git a/cc/surfaces/surface_manager_ref_unittest.cc b/cc/surfaces/surface_manager_ref_unittest.cc
index c3518e7..9385a95 100644
--- a/cc/surfaces/surface_manager_ref_unittest.cc
+++ b/cc/surfaces/surface_manager_ref_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/containers/flat_set.h"
 #include "base/memory/ptr_util.h"
 #include "cc/surfaces/compositor_frame_sink_support.h"
+#include "cc/surfaces/frame_sink_manager.h"
 #include "cc/surfaces/surface.h"
 #include "cc/surfaces/surface_id.h"
 #include "cc/surfaces/surface_manager.h"
@@ -34,7 +35,7 @@
 // Tests for reference tracking in SurfaceManager.
 class SurfaceManagerRefTest : public testing::Test {
  public:
-  SurfaceManager& manager() { return *manager_; }
+  SurfaceManager& GetSurfaceManager() { return *manager_->surface_manager(); }
 
   // Creates a new Surface with the provided |frame_sink_id| and |local_id|.
   // Will first create a Surfacesupport for |frame_sink_id| if necessary.
@@ -74,31 +75,33 @@
 
   void RemoveSurfaceReference(const SurfaceId& parent_id,
                               const SurfaceId& child_id) {
-    manager_->RemoveSurfaceReferences({SurfaceReference(parent_id, child_id)});
+    manager_->surface_manager()->RemoveSurfaceReferences(
+        {SurfaceReference(parent_id, child_id)});
   }
 
   void AddSurfaceReference(const SurfaceId& parent_id,
                            const SurfaceId& child_id) {
-    manager_->AddSurfaceReferences({SurfaceReference(parent_id, child_id)});
+    manager_->surface_manager()->AddSurfaceReferences(
+        {SurfaceReference(parent_id, child_id)});
   }
 
   // Returns all the references where |surface_id| is the parent.
   const base::flat_set<SurfaceId>& GetReferencesFrom(
       const SurfaceId& surface_id) {
-    return manager().GetSurfacesReferencedByParent(surface_id);
+    return GetSurfaceManager().GetSurfacesReferencedByParent(surface_id);
   }
 
   // Returns all the references where |surface_id| is the child.
   const base::flat_set<SurfaceId>& GetReferencesFor(
       const SurfaceId& surface_id) {
-    return manager().GetSurfacesThatReferenceChild(surface_id);
+    return GetSurfaceManager().GetSurfacesThatReferenceChild(surface_id);
   }
 
   // Temporary references are stored as a map in SurfaceManager. This method
   // converts the map to a vector.
   std::vector<SurfaceId> GetAllTempReferences() {
     std::vector<SurfaceId> temp_references;
-    for (auto& map_entry : manager().temporary_references_)
+    for (auto& map_entry : GetSurfaceManager().temporary_references_)
       temp_references.push_back(map_entry.first);
     return temp_references;
   }
@@ -107,7 +110,7 @@
   // testing::Test:
   void SetUp() override {
     // Start each test with a fresh SurfaceManager instance.
-    manager_ = base::MakeUnique<SurfaceManager>(
+    manager_ = base::MakeUnique<FrameSinkManager>(
         SurfaceManager::LifetimeType::REFERENCES);
   }
   void TearDown() override {
@@ -121,26 +124,26 @@
                      std::unique_ptr<CompositorFrameSinkSupport>,
                      FrameSinkIdHash>
       supports_;
-  std::unique_ptr<SurfaceManager> manager_;
+  std::unique_ptr<FrameSinkManager> manager_;
 };
 
 TEST_F(SurfaceManagerRefTest, AddReference) {
   SurfaceId id1 = CreateSurface(kFrameSink1, 1);
-  AddSurfaceReference(manager().GetRootSurfaceId(), id1);
+  AddSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), id1);
 
   EXPECT_THAT(GetReferencesFor(id1),
-              UnorderedElementsAre(manager().GetRootSurfaceId()));
+              UnorderedElementsAre(GetSurfaceManager().GetRootSurfaceId()));
   EXPECT_THAT(GetReferencesFrom(id1), IsEmpty());
 }
 
 TEST_F(SurfaceManagerRefTest, AddRemoveReference) {
   SurfaceId id1 = CreateSurface(kFrameSink1, 1);
   SurfaceId id2 = CreateSurface(kFrameSink2, 1);
-  AddSurfaceReference(manager().GetRootSurfaceId(), id1);
+  AddSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), id1);
   AddSurfaceReference(id1, id2);
 
   EXPECT_THAT(GetReferencesFor(id1),
-              UnorderedElementsAre(manager().GetRootSurfaceId()));
+              UnorderedElementsAre(GetSurfaceManager().GetRootSurfaceId()));
   EXPECT_THAT(GetReferencesFor(id2), UnorderedElementsAre(id1));
   EXPECT_THAT(GetReferencesFrom(id1), UnorderedElementsAre(id2));
   EXPECT_THAT(GetReferencesFrom(id2), IsEmpty());
@@ -157,15 +160,15 @@
   SurfaceId id2 = CreateSurface(kFrameSink2, 1);
   SurfaceId id3 = CreateSurface(kFrameSink3, 1);
 
-  AddSurfaceReference(manager().GetRootSurfaceId(), id1);
+  AddSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), id1);
   AddSurfaceReference(id1, id2);
   AddSurfaceReference(id2, id3);
 
   // |kFramesink2| received a CompositorFrame with a new size, so it destroys
   // |id2| and creates |id2_next|. No reference have been removed yet.
   SurfaceId id2_next = CreateSurface(kFrameSink2, 2);
-  EXPECT_NE(nullptr, manager().GetSurfaceForId(id2));
-  EXPECT_NE(nullptr, manager().GetSurfaceForId(id2_next));
+  EXPECT_NE(nullptr, GetSurfaceManager().GetSurfaceForId(id2));
+  EXPECT_NE(nullptr, GetSurfaceManager().GetSurfaceForId(id2_next));
 
   // Add references to and from |id2_next|.
   AddSurfaceReference(id1, id2_next);
@@ -180,9 +183,9 @@
   EXPECT_THAT(GetReferencesFor(id3), UnorderedElementsAre(id2_next));
 
   // |id2| should be deleted during GC but other surfaces shouldn't.
-  EXPECT_EQ(nullptr, manager().GetSurfaceForId(id2));
-  EXPECT_NE(nullptr, manager().GetSurfaceForId(id2_next));
-  EXPECT_NE(nullptr, manager().GetSurfaceForId(id3));
+  EXPECT_EQ(nullptr, GetSurfaceManager().GetSurfaceForId(id2));
+  EXPECT_NE(nullptr, GetSurfaceManager().GetSurfaceForId(id2_next));
+  EXPECT_NE(nullptr, GetSurfaceManager().GetSurfaceForId(id3));
 }
 
 TEST_F(SurfaceManagerRefTest, ReferenceCycleGetsDeleted) {
@@ -190,7 +193,7 @@
   SurfaceId id2 = CreateSurface(kFrameSink2, 1);
   SurfaceId id3 = CreateSurface(kFrameSink3, 1);
 
-  AddSurfaceReference(manager().GetRootSurfaceId(), id1);
+  AddSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), id1);
   AddSurfaceReference(id1, id2);
   AddSurfaceReference(id2, id3);
 
@@ -201,39 +204,39 @@
   DestroySurface(id2);
   DestroySurface(id1);
 
-  RemoveSurfaceReference(manager().GetRootSurfaceId(), id1);
+  RemoveSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), id1);
 
   // Removing the reference from the root to id1 should allow all three surfaces
   // to be deleted during GC even with a cycle between 2 and 3.
-  EXPECT_EQ(nullptr, manager().GetSurfaceForId(id1));
-  EXPECT_EQ(nullptr, manager().GetSurfaceForId(id2));
-  EXPECT_EQ(nullptr, manager().GetSurfaceForId(id3));
+  EXPECT_EQ(nullptr, GetSurfaceManager().GetSurfaceForId(id1));
+  EXPECT_EQ(nullptr, GetSurfaceManager().GetSurfaceForId(id2));
+  EXPECT_EQ(nullptr, GetSurfaceManager().GetSurfaceForId(id3));
 }
 
 TEST_F(SurfaceManagerRefTest, SurfacesAreDeletedDuringGarbageCollection) {
   SurfaceId id1 = CreateSurface(kFrameSink1, 1);
   SurfaceId id2 = CreateSurface(kFrameSink2, 1);
 
-  AddSurfaceReference(manager().GetRootSurfaceId(), id1);
+  AddSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), id1);
   AddSurfaceReference(id1, id2);
 
-  EXPECT_NE(nullptr, manager().GetSurfaceForId(id1));
-  EXPECT_NE(nullptr, manager().GetSurfaceForId(id2));
+  EXPECT_NE(nullptr, GetSurfaceManager().GetSurfaceForId(id1));
+  EXPECT_NE(nullptr, GetSurfaceManager().GetSurfaceForId(id2));
 
   // Destroying the surfaces shouldn't delete them yet, since there is still an
   // active reference on all surfaces.
   DestroySurface(id1);
   DestroySurface(id2);
-  EXPECT_NE(nullptr, manager().GetSurfaceForId(id1));
-  EXPECT_NE(nullptr, manager().GetSurfaceForId(id2));
+  EXPECT_NE(nullptr, GetSurfaceManager().GetSurfaceForId(id1));
+  EXPECT_NE(nullptr, GetSurfaceManager().GetSurfaceForId(id2));
 
   // Should delete |id2| when the only reference to it is removed.
   RemoveSurfaceReference(id1, id2);
-  EXPECT_EQ(nullptr, manager().GetSurfaceForId(id2));
+  EXPECT_EQ(nullptr, GetSurfaceManager().GetSurfaceForId(id2));
 
   // Should delete |id1| when the only reference to it is removed.
-  RemoveSurfaceReference(manager().GetRootSurfaceId(), id1);
-  EXPECT_EQ(nullptr, manager().GetSurfaceForId(id1));
+  RemoveSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), id1);
+  EXPECT_EQ(nullptr, GetSurfaceManager().GetSurfaceForId(id1));
 }
 
 TEST_F(SurfaceManagerRefTest, GarbageCollectionWorksRecusively) {
@@ -241,7 +244,7 @@
   SurfaceId id2 = CreateSurface(kFrameSink2, 1);
   SurfaceId id3 = CreateSurface(kFrameSink3, 1);
 
-  AddSurfaceReference(manager().GetRootSurfaceId(), id1);
+  AddSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), id1);
   AddSurfaceReference(id1, id2);
   AddSurfaceReference(id2, id3);
 
@@ -251,24 +254,24 @@
 
   // Destroying the surfaces shouldn't delete them yet, since there is still an
   // active reference on all surfaces.
-  EXPECT_NE(nullptr, manager().GetSurfaceForId(id3));
-  EXPECT_NE(nullptr, manager().GetSurfaceForId(id2));
-  EXPECT_NE(nullptr, manager().GetSurfaceForId(id1));
+  EXPECT_NE(nullptr, GetSurfaceManager().GetSurfaceForId(id3));
+  EXPECT_NE(nullptr, GetSurfaceManager().GetSurfaceForId(id2));
+  EXPECT_NE(nullptr, GetSurfaceManager().GetSurfaceForId(id1));
 
-  RemoveSurfaceReference(manager().GetRootSurfaceId(), id1);
+  RemoveSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), id1);
 
   // Removing the reference from the root to id1 should allow all three surfaces
   // to be deleted during GC.
-  EXPECT_EQ(nullptr, manager().GetSurfaceForId(id1));
-  EXPECT_EQ(nullptr, manager().GetSurfaceForId(id2));
-  EXPECT_EQ(nullptr, manager().GetSurfaceForId(id3));
+  EXPECT_EQ(nullptr, GetSurfaceManager().GetSurfaceForId(id1));
+  EXPECT_EQ(nullptr, GetSurfaceManager().GetSurfaceForId(id2));
+  EXPECT_EQ(nullptr, GetSurfaceManager().GetSurfaceForId(id3));
 }
 
 TEST_F(SurfaceManagerRefTest, TryAddReferenceSameReferenceTwice) {
   SurfaceId id1 = CreateSurface(kFrameSink1, 1);
   SurfaceId id2 = CreateSurface(kFrameSink2, 1);
 
-  AddSurfaceReference(manager().GetRootSurfaceId(), id1);
+  AddSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), id1);
   AddSurfaceReference(id1, id2);
   EXPECT_THAT(GetReferencesFor(id2), SizeIs(1));
   EXPECT_THAT(GetReferencesFrom(id1), SizeIs(1));
@@ -334,12 +337,12 @@
   EXPECT_THAT(GetAllTempReferences(), ElementsAre(surface_id));
 
   // Add a real reference from root to |surface_id|.
-  AddSurfaceReference(manager().GetRootSurfaceId(), surface_id);
+  AddSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), surface_id);
 
   // The temporary reference should be gone and there should now be a surface
   // reference from root to |surface_id|.
   EXPECT_TRUE(GetAllTempReferences().empty());
-  EXPECT_THAT(GetReferencesFrom(manager().GetRootSurfaceId()),
+  EXPECT_THAT(GetReferencesFrom(GetSurfaceManager().GetRootSurfaceId()),
               ElementsAre(surface_id));
 }
 
@@ -415,25 +418,25 @@
 
 TEST_F(SurfaceManagerRefTest, SurfaceWithTemporaryReferenceIsNotDeleted) {
   const SurfaceId id1 = CreateSurface(kFrameSink1, 1);
-  AddSurfaceReference(manager().GetRootSurfaceId(), id1);
+  AddSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), id1);
 
   // We create |id2| and never add a real reference to it. This leaves the
   // temporary reference.
   const SurfaceId id2 = CreateSurface(kFrameSink2, 1);
   ASSERT_THAT(GetAllTempReferences(), UnorderedElementsAre(id2));
-  EXPECT_NE(nullptr, manager().GetSurfaceForId(id2));
+  EXPECT_NE(nullptr, GetSurfaceManager().GetSurfaceForId(id2));
 
   // Destroy both surfaces so they can be garbage collected. We remove the
   // surface reference to |id1| which will run GarbageCollectSurfaces().
   DestroySurface(id1);
   DestroySurface(id2);
-  RemoveSurfaceReference(manager().GetRootSurfaceId(), id1);
+  RemoveSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), id1);
 
   // |id1| is destroyed and has no references, so it's deleted.
-  EXPECT_EQ(nullptr, manager().GetSurfaceForId(id1));
+  EXPECT_EQ(nullptr, GetSurfaceManager().GetSurfaceForId(id1));
 
   // |id2| is destroyed but has a temporary reference, it's not deleted.
-  EXPECT_NE(nullptr, manager().GetSurfaceForId(id2));
+  EXPECT_NE(nullptr, GetSurfaceManager().GetSurfaceForId(id2));
 }
 
 // Checks that when a temporary reference is assigned an owner, if the owner is
@@ -444,11 +447,11 @@
   ASSERT_THAT(GetAllTempReferences(), UnorderedElementsAre(id1));
 
   // |id1| should have a temporary reference after an owner is assigned.
-  manager().AssignTemporaryReference(id1, kFrameSink1);
+  GetSurfaceManager().AssignTemporaryReference(id1, kFrameSink1);
   ASSERT_THAT(GetAllTempReferences(), UnorderedElementsAre(id1));
 
   // When |kFrameSink1| is invalidated the temporary reference will be removed.
-  manager().InvalidateFrameSinkId(kFrameSink1);
+  GetSurfaceManager().InvalidateFrameSinkId(kFrameSink1);
   ASSERT_THAT(GetAllTempReferences(), IsEmpty());
 }
 
@@ -456,10 +459,10 @@
 // ownership. Invalidating the old owner shouldn't do anything.
 TEST_F(SurfaceManagerRefTest, InvalidateHasNoEffectOnSurfaceReferences) {
   const SurfaceId parent_id = CreateSurface(kFrameSink1, 1);
-  AddSurfaceReference(manager().GetRootSurfaceId(), parent_id);
+  AddSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), parent_id);
 
   const SurfaceId id1 = CreateSurface(kFrameSink2, 1);
-  manager().AssignTemporaryReference(id1, kFrameSink1);
+  GetSurfaceManager().AssignTemporaryReference(id1, kFrameSink1);
   ASSERT_THAT(GetAllTempReferences(), UnorderedElementsAre(id1));
 
   // Adding a real surface reference will remove the temporary reference.
@@ -480,7 +483,7 @@
   // An example of why this could happen is the window server doesn't know the
   // owner, maybe it has crashed and been cleanup already, and asks to drop the
   // temporary reference.
-  manager().DropTemporaryReference(id1);
+  GetSurfaceManager().DropTemporaryReference(id1);
   ASSERT_THAT(GetAllTempReferences(), IsEmpty());
 }
 
@@ -489,7 +492,7 @@
 // client crashing, so it's
 TEST_F(SurfaceManagerRefTest, TempReferencesWithClientCrash) {
   const SurfaceId parent_id = CreateSurface(kFrameSink1, 1);
-  AddSurfaceReference(manager().GetRootSurfaceId(), parent_id);
+  AddSurfaceReference(GetSurfaceManager().GetRootSurfaceId(), parent_id);
 
   const SurfaceId id1a = CreateSurface(kFrameSink2, 1);
   const SurfaceId id1b = CreateSurface(kFrameSink2, 2);
@@ -498,7 +501,7 @@
 
   // Assign |id1a| to |kFrameSink1|. This doesn't change the temporary
   // reference, it just assigns as owner to it.
-  manager().AssignTemporaryReference(id1a, kFrameSink1);
+  GetSurfaceManager().AssignTemporaryReference(id1a, kFrameSink1);
   ASSERT_THAT(GetAllTempReferences(), UnorderedElementsAre(id1a, id1b));
 
   // If the parent client crashes then the FrameSink connection will be closed
@@ -510,7 +513,7 @@
   // If the parent has crashed then the window server will have already removed
   // it from the ServerWindow hierarchy and won't have an owner for |id2b|. The
   // window server will ask to drop the reference instead.
-  manager().DropTemporaryReference(id1b);
+  GetSurfaceManager().DropTemporaryReference(id1b);
   ASSERT_THAT(GetAllTempReferences(), IsEmpty());
 }
 
diff --git a/cc/surfaces/surface_synchronization_unittest.cc b/cc/surfaces/surface_synchronization_unittest.cc
index 02c35f3..dd7ee7d 100644
--- a/cc/surfaces/surface_synchronization_unittest.cc
+++ b/cc/surfaces/surface_synchronization_unittest.cc
@@ -4,8 +4,8 @@
 
 #include "base/containers/flat_set.h"
 #include "cc/surfaces/compositor_frame_sink_support.h"
+#include "cc/surfaces/frame_sink_manager.h"
 #include "cc/surfaces/surface_id.h"
-#include "cc/surfaces/surface_manager.h"
 #include "cc/test/begin_frame_args_test.h"
 #include "cc/test/compositor_frame_helpers.h"
 #include "cc/test/fake_external_begin_frame_source.h"
@@ -70,7 +70,7 @@
 class SurfaceSynchronizationTest : public testing::Test {
  public:
   SurfaceSynchronizationTest()
-      : surface_manager_(SurfaceManager::LifetimeType::REFERENCES),
+      : frame_sink_manager_(SurfaceManager::LifetimeType::REFERENCES),
         surface_observer_(false) {}
   ~SurfaceSynchronizationTest() override {}
 
@@ -99,17 +99,20 @@
     return support(index).GetCurrentSurfaceForTesting();
   }
 
-  SurfaceManager& surface_manager() { return surface_manager_; }
+  FrameSinkManager& frame_sink_manager() { return frame_sink_manager_; }
 
   // Returns all the references where |surface_id| is the parent.
   const base::flat_set<SurfaceId>& GetChildReferences(
       const SurfaceId& surface_id) {
-    return surface_manager().GetSurfacesReferencedByParent(surface_id);
+    return frame_sink_manager()
+        .surface_manager()
+        ->GetSurfacesReferencedByParent(surface_id);
   }
 
   // Returns true if there is a temporary reference for |surface_id|.
   bool HasTemporaryReference(const SurfaceId& surface_id) {
-    return surface_manager().HasTemporaryReference(surface_id);
+    return frame_sink_manager().surface_manager()->HasTemporaryReference(
+        surface_id);
   }
 
   FakeExternalBeginFrameSource* begin_frame_source() {
@@ -135,31 +138,31 @@
         base::MakeUnique<FakeExternalBeginFrameSource>(0.f, false);
     begin_frame_source_->SetClient(&begin_frame_source_client_);
     now_src_ = base::MakeUnique<base::SimpleTestTickClock>();
-    surface_manager_.AddObserver(&surface_observer_);
+    frame_sink_manager_.surface_manager()->AddObserver(&surface_observer_);
     supports_.push_back(CompositorFrameSinkSupport::Create(
-        &support_client_, &surface_manager_, kDisplayFrameSink, kIsRoot,
+        &support_client_, &frame_sink_manager_, kDisplayFrameSink, kIsRoot,
         kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints));
     supports_.push_back(CompositorFrameSinkSupport::Create(
-        &support_client_, &surface_manager_, kParentFrameSink, kIsChildRoot,
+        &support_client_, &frame_sink_manager_, kParentFrameSink, kIsChildRoot,
         kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints));
     supports_.push_back(CompositorFrameSinkSupport::Create(
-        &support_client_, &surface_manager_, kChildFrameSink1, kIsChildRoot,
+        &support_client_, &frame_sink_manager_, kChildFrameSink1, kIsChildRoot,
         kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints));
     supports_.push_back(CompositorFrameSinkSupport::Create(
-        &support_client_, &surface_manager_, kChildFrameSink2, kIsChildRoot,
+        &support_client_, &frame_sink_manager_, kChildFrameSink2, kIsChildRoot,
         kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints));
 
     // Normally, the BeginFrameSource would be registered by the Display. We
     // register it here so that BeginFrames are received by the display support,
     // for use in the PassesOnBeginFrameAcks test. Other supports do not receive
     // BeginFrames, since the frame sink hierarchy is not set up in this test.
-    surface_manager_.RegisterBeginFrameSource(begin_frame_source_.get(),
-                                              kDisplayFrameSink);
+    frame_sink_manager_.RegisterBeginFrameSource(begin_frame_source_.get(),
+                                                 kDisplayFrameSink);
   }
 
   void TearDown() override {
-    surface_manager_.RemoveObserver(&surface_observer_);
-    surface_manager_.UnregisterBeginFrameSource(begin_frame_source_.get());
+    frame_sink_manager_.surface_manager()->RemoveObserver(&surface_observer_);
+    frame_sink_manager_.UnregisterBeginFrameSource(begin_frame_source_.get());
 
     begin_frame_source_->SetClient(nullptr);
     begin_frame_source_.reset();
@@ -170,14 +173,19 @@
   }
 
   bool IsMarkedForDestruction(const SurfaceId& surface_id) {
-    return surface_manager_.IsMarkedForDestruction(surface_id);
+    return frame_sink_manager_.surface_manager()->IsMarkedForDestruction(
+        surface_id);
+  }
+
+  Surface* GetSurfaceForId(const SurfaceId& surface_id) {
+    return frame_sink_manager_.surface_manager()->GetSurfaceForId(surface_id);
   }
 
  protected:
   testing::NiceMock<MockCompositorFrameSinkSupportClient> support_client_;
 
  private:
-  SurfaceManager surface_manager_;
+  FrameSinkManager frame_sink_manager_;
   FakeSurfaceObserver surface_observer_;
   FakeExternalBeginFrameSourceClient begin_frame_source_client_;
   std::unique_ptr<FakeExternalBeginFrameSource> begin_frame_source_;
@@ -200,7 +208,8 @@
   // A surface reference from the top-level root is added and there shouldn't be
   // a temporary reference.
   EXPECT_FALSE(HasTemporaryReference(display_id_first));
-  EXPECT_THAT(GetChildReferences(surface_manager().GetRootSurfaceId()),
+  EXPECT_THAT(GetChildReferences(
+                  frame_sink_manager().surface_manager()->GetRootSurfaceId()),
               UnorderedElementsAre(display_id_first));
 
   // Submit a CompositorFrame for the second display root surface.
@@ -210,11 +219,12 @@
   // A surface reference from the top-level root to |display_id_second| should
   // be added and the reference to |display_root_first| removed.
   EXPECT_FALSE(HasTemporaryReference(display_id_second));
-  EXPECT_THAT(GetChildReferences(surface_manager().GetRootSurfaceId()),
+  EXPECT_THAT(GetChildReferences(
+                  frame_sink_manager().surface_manager()->GetRootSurfaceId()),
               UnorderedElementsAre(display_id_second));
 
   // Surface |display_id_first| is unreachable and should get deleted.
-  EXPECT_EQ(nullptr, surface_manager().GetSurfaceForId(display_id_first));
+  EXPECT_EQ(nullptr, GetSurfaceForId(display_id_first));
 }
 
 // The parent Surface is blocked on |child_id1| and |child_id2|.
@@ -756,7 +766,7 @@
                                          std::move(frame));
 
   // Verify that the old surface has an active frame and no pending frame.
-  Surface* old_surface = surface_manager().GetSurfaceForId(parent_id1);
+  Surface* old_surface = GetSurfaceForId(parent_id1);
   ASSERT_NE(nullptr, old_surface);
   EXPECT_TRUE(old_surface->HasActiveFrame());
   EXPECT_FALSE(old_surface->HasPendingFrame());
@@ -773,7 +783,7 @@
                                          std::move(frame2));
 
   // Verify that the new surface has an active frame and no pending frames.
-  Surface* surface = surface_manager().GetSurfaceForId(parent_id2);
+  Surface* surface = GetSurfaceForId(parent_id2);
   ASSERT_NE(nullptr, surface);
   EXPECT_TRUE(surface->HasActiveFrame());
   EXPECT_FALSE(surface->HasPendingFrame());
@@ -839,7 +849,7 @@
                                          std::move(frame2));
 
   // Verify that the old surface has both an active and a pending frame.
-  Surface* old_surface = surface_manager().GetSurfaceForId(parent_id1);
+  Surface* old_surface = GetSurfaceForId(parent_id1);
   ASSERT_NE(nullptr, old_surface);
   EXPECT_TRUE(old_surface->HasActiveFrame());
   EXPECT_TRUE(old_surface->HasPendingFrame());
@@ -849,7 +859,7 @@
                                          MakeCompositorFrame());
 
   // Verify that the new surface has an active frame only.
-  Surface* surface = surface_manager().GetSurfaceForId(parent_id2);
+  Surface* surface = GetSurfaceForId(parent_id2);
   ASSERT_NE(nullptr, surface);
   EXPECT_TRUE(surface->HasActiveFrame());
   EXPECT_FALSE(surface->HasPendingFrame());
@@ -905,7 +915,7 @@
                                          std::move(frame));
 
   // Verify that the old surface has an active frame only.
-  Surface* old_surface = surface_manager().GetSurfaceForId(parent_id1);
+  Surface* old_surface = GetSurfaceForId(parent_id1);
   ASSERT_NE(nullptr, old_surface);
   EXPECT_TRUE(old_surface->HasActiveFrame());
   EXPECT_FALSE(old_surface->HasPendingFrame());
@@ -923,7 +933,7 @@
                                          std::move(frame2));
 
   // Verify that the new surface has a pending frame and no active frame.
-  Surface* surface = surface_manager().GetSurfaceForId(parent_id2);
+  Surface* surface = GetSurfaceForId(parent_id2);
   ASSERT_NE(nullptr, surface);
   EXPECT_TRUE(surface->HasPendingFrame());
   EXPECT_FALSE(surface->HasActiveFrame());
@@ -983,12 +993,12 @@
   const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 3);
 
   // Create the child surface by submitting a frame to it.
-  EXPECT_EQ(nullptr, surface_manager().GetSurfaceForId(child_id));
+  EXPECT_EQ(nullptr, GetSurfaceForId(child_id));
   child_support1().SubmitCompositorFrame(child_id.local_surface_id(),
                                          MakeCompositorFrame());
 
   // Verify that the child surface is created.
-  Surface* surface = surface_manager().GetSurfaceForId(child_id);
+  Surface* surface = GetSurfaceForId(child_id);
   EXPECT_NE(nullptr, surface);
 
   // Add a reference from the parent to the child.
@@ -1000,7 +1010,7 @@
   // Attempt to destroy the child surface. The surface must still exist since
   // the parent needs it but it will be marked as destroyed.
   child_support1().EvictCurrentSurface();
-  surface = surface_manager().GetSurfaceForId(child_id);
+  surface = GetSurfaceForId(child_id);
   EXPECT_NE(nullptr, surface);
   EXPECT_TRUE(IsMarkedForDestruction(child_id));
 
@@ -1011,7 +1021,7 @@
 
   // Verify that the surface that was marked destroyed is recovered and is being
   // used again.
-  Surface* surface2 = surface_manager().GetSurfaceForId(child_id);
+  Surface* surface2 = GetSurfaceForId(child_id);
   EXPECT_EQ(surface, surface2);
   EXPECT_FALSE(IsMarkedForDestruction(child_id));
 }
@@ -1025,7 +1035,7 @@
   // Submit the first frame. Creates the surface.
   child_support1().SubmitCompositorFrame(child_id.local_surface_id(),
                                          MakeCompositorFrame());
-  EXPECT_NE(nullptr, surface_manager().GetSurfaceForId(child_id));
+  EXPECT_NE(nullptr, GetSurfaceForId(child_id));
 
   // Add a reference from parent.
   parent_support().SubmitCompositorFrame(
@@ -1039,13 +1049,13 @@
 
   // Destroy the surface.
   child_support1().EvictCurrentSurface();
-  EXPECT_EQ(nullptr, surface_manager().GetSurfaceForId(child_id));
+  EXPECT_EQ(nullptr, GetSurfaceForId(child_id));
 
   // Submit another frame with the same local surface id. This should work fine
   // and a new surface must be created.
   child_support1().SubmitCompositorFrame(child_id.local_surface_id(),
                                          MakeCompositorFrame());
-  EXPECT_NE(nullptr, surface_manager().GetSurfaceForId(child_id));
+  EXPECT_NE(nullptr, GetSurfaceForId(child_id));
 }
 
 // This test verifies that a crash does not occur if garbage collection is
diff --git a/cc/surfaces/surface_unittest.cc b/cc/surfaces/surface_unittest.cc
index 297a1670..ff6316595 100644
--- a/cc/surfaces/surface_unittest.cc
+++ b/cc/surfaces/surface_unittest.cc
@@ -6,8 +6,9 @@
 #include "base/memory/ptr_util.h"
 #include "cc/output/copy_output_result.h"
 #include "cc/surfaces/compositor_frame_sink_support.h"
+#include "cc/surfaces/frame_sink_manager.h"
 #include "cc/surfaces/local_surface_id_allocator.h"
-#include "cc/surfaces/surface_manager.h"
+#include "cc/surfaces/surface_dependency_tracker.h"
 #include "cc/test/begin_frame_args_test.h"
 #include "cc/test/compositor_frame_helpers.h"
 #include "cc/test/fake_external_begin_frame_source.h"
@@ -24,19 +25,20 @@
 constexpr bool kNeedsSyncPoints = true;
 
 TEST(SurfaceTest, SurfaceLifetime) {
-  SurfaceManager manager;
+  FrameSinkManager frame_sink_manager;
+  SurfaceManager* surface_manager = frame_sink_manager.surface_manager();
   std::unique_ptr<CompositorFrameSinkSupport> support =
       CompositorFrameSinkSupport::Create(
-          nullptr, &manager, kArbitraryFrameSinkId, kIsRoot,
+          nullptr, &frame_sink_manager, kArbitraryFrameSinkId, kIsRoot,
           kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints);
 
   LocalSurfaceId local_surface_id(6, base::UnguessableToken::Create());
   SurfaceId surface_id(kArbitraryFrameSinkId, local_surface_id);
   support->SubmitCompositorFrame(local_surface_id, test::MakeCompositorFrame());
-  EXPECT_TRUE(manager.GetSurfaceForId(surface_id));
+  EXPECT_TRUE(surface_manager->GetSurfaceForId(surface_id));
   support->EvictCurrentSurface();
 
-  EXPECT_EQ(NULL, manager.GetSurfaceForId(surface_id));
+  EXPECT_EQ(NULL, surface_manager->GetSurfaceForId(surface_id));
 }
 
 TEST(SurfaceTest, SurfaceIds) {
@@ -56,23 +58,24 @@
 // Test that CopyOutputRequests can outlive the current frame and be
 // aggregated on the next frame.
 TEST(SurfaceTest, CopyRequestLifetime) {
-  SurfaceManager manager;
+  FrameSinkManager frame_sink_manager;
+  SurfaceManager* surface_manager = frame_sink_manager.surface_manager();
   std::unique_ptr<CompositorFrameSinkSupport> support =
       CompositorFrameSinkSupport::Create(
-          nullptr, &manager, kArbitraryFrameSinkId, kIsRoot,
+          nullptr, &frame_sink_manager, kArbitraryFrameSinkId, kIsRoot,
           kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints);
 
   LocalSurfaceId local_surface_id(6, base::UnguessableToken::Create());
   SurfaceId surface_id(kArbitraryFrameSinkId, local_surface_id);
   CompositorFrame frame = test::MakeCompositorFrame();
   support->SubmitCompositorFrame(local_surface_id, std::move(frame));
-  Surface* surface = manager.GetSurfaceForId(surface_id);
+  Surface* surface = surface_manager->GetSurfaceForId(surface_id);
   ASSERT_TRUE(!!surface);
 
   bool copy_called = false;
   support->RequestCopyOfSurface(CopyOutputRequest::CreateRequest(
       base::Bind(&TestCopyResultCallback, &copy_called)));
-  EXPECT_TRUE(manager.GetSurfaceForId(surface_id));
+  EXPECT_TRUE(surface_manager->GetSurfaceForId(surface_id));
   EXPECT_FALSE(copy_called);
 
   int max_frame = 3, start_id = 200;
diff --git a/cc/surfaces/surfaces_pixeltest.cc b/cc/surfaces/surfaces_pixeltest.cc
index 2917c086..aabb74a 100644
--- a/cc/surfaces/surfaces_pixeltest.cc
+++ b/cc/surfaces/surfaces_pixeltest.cc
@@ -7,6 +7,7 @@
 #include "cc/quads/solid_color_draw_quad.h"
 #include "cc/quads/surface_draw_quad.h"
 #include "cc/surfaces/compositor_frame_sink_support.h"
+#include "cc/surfaces/frame_sink_manager.h"
 #include "cc/surfaces/local_surface_id_allocator.h"
 #include "cc/surfaces/surface.h"
 #include "cc/surfaces/surface_aggregator.h"
@@ -43,7 +44,7 @@
   ~SurfacesPixelTest() override { support_->EvictCurrentSurface(); }
 
  protected:
-  SurfaceManager manager_;
+  FrameSinkManager manager_;
   LocalSurfaceIdAllocator allocator_;
   std::unique_ptr<CompositorFrameSinkSupport> support_;
 };
@@ -90,7 +91,8 @@
   SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id);
   support_->SubmitCompositorFrame(root_local_surface_id, std::move(root_frame));
 
-  SurfaceAggregator aggregator(&manager_, resource_provider_.get(), true);
+  SurfaceAggregator aggregator(manager_.surface_manager(),
+                               resource_provider_.get(), true);
   CompositorFrame aggregated_frame = aggregator.Aggregate(root_surface_id);
 
   bool discard_alpha = false;
@@ -172,7 +174,8 @@
                                          std::move(child_frame));
   }
 
-  SurfaceAggregator aggregator(&manager_, resource_provider_.get(), true);
+  SurfaceAggregator aggregator(manager_.surface_manager(),
+                               resource_provider_.get(), true);
   CompositorFrame aggregated_frame = aggregator.Aggregate(root_surface_id);
 
   bool discard_alpha = false;
@@ -313,7 +316,8 @@
                                          std::move(child_frame));
   }
 
-  SurfaceAggregator aggregator(&manager_, resource_provider_.get(), true);
+  SurfaceAggregator aggregator(manager_.surface_manager(),
+                               resource_provider_.get(), true);
   CompositorFrame aggregated_frame = aggregator.Aggregate(root_surface_id);
 
   bool discard_alpha = false;
diff --git a/cc/test/test_layer_tree_frame_sink.cc b/cc/test/test_layer_tree_frame_sink.cc
index 0ef669a1..9e93c09 100644
--- a/cc/test/test_layer_tree_frame_sink.cc
+++ b/cc/test/test_layer_tree_frame_sink.cc
@@ -41,8 +41,8 @@
       refresh_rate_(refresh_rate),
       task_runner_(std::move(task_runner)),
       frame_sink_id_(kLayerTreeFrameSinkId),
-      surface_manager_(new SurfaceManager),
-      local_surface_id_allocator_(new LocalSurfaceIdAllocator()),
+      frame_sink_manager_(new FrameSinkManager),
+      local_surface_id_allocator_(new LocalSurfaceIdAllocator),
       external_begin_frame_source_(this),
       weak_ptr_factory_(this) {
   // Always use sync tokens so that code paths in resource provider that deal
@@ -99,14 +99,14 @@
   constexpr bool handles_frame_sink_id_invalidation = true;
   constexpr bool needs_sync_points = true;
   support_ = CompositorFrameSinkSupport::Create(
-      this, surface_manager_.get(), frame_sink_id_, is_root,
+      this, frame_sink_manager_.get(), frame_sink_id_, is_root,
       handles_frame_sink_id_invalidation, needs_sync_points);
   client_->SetBeginFrameSource(&external_begin_frame_source_);
   if (begin_frame_source_) {
-    surface_manager_->RegisterBeginFrameSource(begin_frame_source_.get(),
-                                               frame_sink_id_);
+    frame_sink_manager_->RegisterBeginFrameSource(begin_frame_source_.get(),
+                                                  frame_sink_id_);
   }
-  display_->Initialize(this, surface_manager_.get());
+  display_->Initialize(this, frame_sink_manager_->surface_manager());
   display_->renderer_for_testing()->SetEnlargePassTextureAmountForTesting(
       enlarge_pass_texture_amount_);
   display_->SetVisible(true);
@@ -115,13 +115,13 @@
 
 void TestLayerTreeFrameSink::DetachFromClient() {
   if (begin_frame_source_)
-    surface_manager_->UnregisterBeginFrameSource(begin_frame_source_.get());
+    frame_sink_manager_->UnregisterBeginFrameSource(begin_frame_source_.get());
   client_->SetBeginFrameSource(nullptr);
   support_ = nullptr;
   display_ = nullptr;
   begin_frame_source_ = nullptr;
   local_surface_id_allocator_ = nullptr;
-  surface_manager_ = nullptr;
+  frame_sink_manager_ = nullptr;
   test_client_ = nullptr;
   LayerTreeFrameSink::DetachFromClient();
 }
diff --git a/cc/test/test_layer_tree_frame_sink.h b/cc/test/test_layer_tree_frame_sink.h
index ed72e96..8feb90a8 100644
--- a/cc/test/test_layer_tree_frame_sink.h
+++ b/cc/test/test_layer_tree_frame_sink.h
@@ -13,8 +13,8 @@
 #include "cc/surfaces/compositor_frame_sink_support_client.h"
 #include "cc/surfaces/display.h"
 #include "cc/surfaces/display_client.h"
+#include "cc/surfaces/frame_sink_manager.h"
 #include "cc/surfaces/local_surface_id_allocator.h"
-#include "cc/surfaces/surface_manager.h"
 
 namespace base {
 class SingleThreadTaskRunner;
@@ -113,7 +113,7 @@
   FrameSinkId frame_sink_id_;
   // TODO(danakj): These don't need to be stored in unique_ptrs when
   // LayerTreeFrameSink is owned/destroyed on the compositor thread.
-  std::unique_ptr<SurfaceManager> surface_manager_;
+  std::unique_ptr<FrameSinkManager> frame_sink_manager_;
   std::unique_ptr<LocalSurfaceIdAllocator> local_surface_id_allocator_;
   LocalSurfaceId local_surface_id_;
   gfx::Size display_size_;
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 707d7a75..99f22caa 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -4124,7 +4124,6 @@
   data.opaque = bitmap.GetOpaque();
   ui_resource_map_[uid] = data;
 
-  resource_provider_->GenerateSyncTokenForResource(id);
   MarkUIResourceNotEvicted(uid);
 }
 
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn
index 7cd13b9..ccd3744 100644
--- a/chrome/BUILD.gn
+++ b/chrome/BUILD.gn
@@ -1767,6 +1767,7 @@
       deps += [
         "//device/gamepad",
         "//device/sensors",
+        "//ui/events/devices",
       ]
     }
   }
diff --git a/chrome/VERSION b/chrome/VERSION
index f73397dc..c1e4964 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=61
 MINOR=0
-BUILD=3151
+BUILD=3152
 PATCH=0
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml
index bcec228a..618cb09 100644
--- a/chrome/android/java/res/values/dimens.xml
+++ b/chrome/android/java/res/values/dimens.xml
@@ -485,4 +485,8 @@
     <!-- Search widget dimensions -->
     <dimen name="search_activity_location_bar_margin_start">16dp</dimen>
     <dimen name="search_activity_location_bar_margin_end">8dp</dimen>
+
+    <!-- Reader Mode dimensions -->
+    <!-- Padding surrounding the message. -->
+    <dimen name="reader_mode_infobar_text_padding">8dp</dimen>
 </resources>
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 df794bf..c8238aea 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -1732,14 +1732,7 @@
         if (!mUIInitialized) return false;
         final Tab currentTab = getActivityTab();
 
-        // Close the bottom sheet before trying to navigate back.
-        if (getBottomSheet() != null
-                && getBottomSheet().getSheetState() != BottomSheet.SHEET_STATE_PEEK) {
-            getBottomSheet().getBottomSheetMetrics().setSheetCloseReason(
-                    BottomSheetMetrics.CLOSED_BY_BACK_PRESS);
-            getBottomSheet().setSheetState(BottomSheet.SHEET_STATE_PEEK, true);
-            return true;
-        }
+        if (getBottomSheet() != null && getBottomSheet().handleBackPress()) return true;
 
         if (currentTab == null) {
             recordBackPressedUma("currentTab is null", BACK_PRESSED_TAB_IS_NULL);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java
index 939a3d62..cc6da7b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser;
 
+import android.app.Activity;
 import android.app.KeyguardManager;
 import android.app.PendingIntent;
 import android.app.SearchManager;
@@ -183,7 +184,8 @@
     private static final String PACKAGE_MESSENGER = "com.google.android.apps.messaging";
     private static final String PACKAGE_LINE = "jp.naver.line.android";
     private static final String PACKAGE_WHATSAPP = "com.whatsapp";
-    private static final String FACEBOOK_LINK_PREFIX = "http://m.facebook.com/l.php?";
+    private static final String FACEBOOK_REFERRER_URL = "android-app://m.facebook.com";
+    private static final String FACEBOOK_INTERNAL_BROWSER_REFERRER = "http://m.facebook.com";
     private static final String TWITTER_LINK_PREFIX = "http://t.co/";
     private static final String NEWS_LINK_PREFIX = "http://news.google.com/news/url?";
 
@@ -292,12 +294,22 @@
         ExternalAppId externalId = ExternalAppId.OTHER;
         if (appId == null) {
             String url = getUrlFromIntent(intent);
+            String referrer = getReferrerUrl(intent);
             if (url != null && url.startsWith(TWITTER_LINK_PREFIX)) {
                 externalId = ExternalAppId.TWITTER;
-            } else if (url != null && url.startsWith(FACEBOOK_LINK_PREFIX)) {
+            } else if (FACEBOOK_REFERRER_URL.equals(referrer)) {
+                // This happens when "Links Open Externally" is checked in the Facebook app.
                 externalId = ExternalAppId.FACEBOOK;
             } else if (url != null && url.startsWith(NEWS_LINK_PREFIX)) {
                 externalId = ExternalAppId.NEWS;
+            } else {
+                Bundle headers = IntentUtils.safeGetBundleExtra(intent, Browser.EXTRA_HEADERS);
+                if (headers != null
+                        && FACEBOOK_INTERNAL_BROWSER_REFERRER.equals(headers.get("Referer"))) {
+                    // This happens when "Links Open Externally" is unchecked in the Facebook app,
+                    // and we use "Open With..." from the internal browser.
+                    externalId = ExternalAppId.FACEBOOK;
+                }
             }
         } else {
             if (appId.equals(PACKAGE_PLUS)) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java
index b8adf07..0212c7d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java
@@ -200,6 +200,7 @@
                 .setNegativeButton(R.string.cancel, null)
                 .setPositiveButton(confirmButtonLabel, null)
                 .create();
+        mDialog.setCanceledOnTouchOutside(false);
         mDialog.setOnDismissListener(this);
 
         mShouldRequestExpirationDate = shouldRequestExpirationDate;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
index bddb157a..66032ff 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
@@ -505,7 +505,7 @@
         if (baseWebContents == null) return null;
 
         try {
-            return new URL(baseWebContents.getUrl());
+            return new URL(baseWebContents.getLastCommittedUrl());
         } catch (MalformedURLException e) {
             return null;
         }
@@ -1163,7 +1163,7 @@
     }
 
     /**
-     * Gets the current loaded URL in a ContentViewCore.
+     * Gets the currently loading or loaded URL in a ContentViewCore.
      *
      * @param searchContentViewCore The given ContentViewCore.
      * @return The current loaded URL.
@@ -1173,8 +1173,8 @@
         // not yet committed being processed. Otherwise, get the URL from the WebContents.
         NavigationEntry entry =
                 searchContentViewCore.getWebContents().getNavigationController().getPendingEntry();
-        String url =
-                entry != null ? entry.getUrl() : searchContentViewCore.getWebContents().getUrl();
+        String url = entry != null ? entry.getUrl()
+                                   : searchContentViewCore.getWebContents().getLastCommittedUrl();
         return url;
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java b/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java
index 4067eb9..8288d30 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java
@@ -390,7 +390,7 @@
         WebContents baseWebContents = getBasePageWebContents();
         if (baseWebContents == null || mChromeActivity == null || mTabModelSelector == null) return;
 
-        String url = baseWebContents.getUrl();
+        String url = baseWebContents.getLastCommittedUrl();
         if (url == null) return;
 
         ReaderModeTabInfo info = mTabStatusMap.get(mTabModelSelector.getCurrentTabId());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarCompactLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarCompactLayout.java
index 285ef4f..a613b69 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarCompactLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarCompactLayout.java
@@ -57,9 +57,9 @@
         if (weight <= 0.0f) {
             params = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, mCompactInfoBarSize);
         } else {
-            params = new LinearLayout.LayoutParams(0, mCompactInfoBarSize);
-            params.weight = weight;
+            params = new LinearLayout.LayoutParams(0, LayoutParams.WRAP_CONTENT, weight);
         }
+        view.setMinimumHeight(mCompactInfoBarSize);
         params.gravity = Gravity.BOTTOM;
         addView(view, indexOfChild(mCloseButton), params);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/ReaderModeInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/ReaderModeInfoBar.java
index 14948a1..e3a895c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/ReaderModeInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/ReaderModeInfoBar.java
@@ -48,7 +48,6 @@
     protected void createCompactLayoutContent(InfoBarCompactLayout layout) {
         TextView prompt = new TextView(getContext());
         prompt.setText(R.string.reader_view_text);
-        prompt.setSingleLine();
         prompt.setTextSize(TypedValue.COMPLEX_UNIT_PX,
                 getContext().getResources().getDimension(R.dimen.infobar_text_size));
         prompt.setTextColor(
@@ -57,6 +56,9 @@
         prompt.setOnClickListener(mNavigateListener);
 
         layout.findViewById(R.id.infobar_icon).setOnClickListener(mNavigateListener);
+        final int messagePadding = getContext().getResources().getDimensionPixelOffset(
+                R.dimen.reader_mode_infobar_text_padding);
+        prompt.setPadding(0, messagePadding, 0, messagePadding);
         layout.addContent(prompt, 1f);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/Footer.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/Footer.java
index 712e931..dd851e9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/Footer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/Footer.java
@@ -11,8 +11,8 @@
 import android.widget.TextView;
 
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.suggestions.SuggestionsNavigationDelegate;
+import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.ui.text.NoUnderlineClickableSpan;
 import org.chromium.ui.text.SpanApplier;
 
@@ -59,7 +59,7 @@
                     new SpanApplier.SpanInfo("<link>", "</link>", link)));
             textView.setMovementMethod(LinkMovementMethod.getInstance());
 
-            if (ChromeFeatureList.isEnabled(ChromeFeatureList.CHROME_HOME)) {
+            if (FeatureUtilities.isChromeHomeEnabled()) {
                 itemView.setPadding(itemView.getPaddingLeft(),
                         root.getResources().getDimensionPixelSize(
                                 R.dimen.chrome_home_suggestions_footer_padding_top),
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFactory.java
index eb5f582..b88f6a93 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFactory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFactory.java
@@ -27,7 +27,7 @@
     public void create(
             WebContents webContents, Set<String> methods, PaymentAppCreatedCallback callback) {
         AndroidPaymentAppFinder.find(webContents, methods, new PaymentManifestWebDataService(),
-                new PaymentManifestDownloader(webContents), new PaymentManifestParser(),
+                new PaymentManifestDownloader(), new PaymentManifestParser(),
                 new PackageManagerDelegate(), callback);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFinder.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFinder.java
index 9aaa820..8675b11 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFinder.java
@@ -182,6 +182,10 @@
             // manifest file needs to be parsed. The startup can take up to 2 seconds.
             if (!mParser.isUtilityProcessRunning()) mParser.startUtilityProcess();
 
+            // Initialize the native side of the downloader, once we know that a manifest file needs
+            // to be downloaded.
+            if (!mDownloader.isInitialized()) mDownloader.initialize(mWebContents);
+
             mManifestVerifiers.add(
                     new PaymentManifestVerifier(uriMethodName, supportedApps, mWebDataService,
                             mDownloader, mParser, mPackageManagerDelegate, this /* callback */));
@@ -306,6 +310,7 @@
 
         assert mPendingApps.isEmpty();
         mWebDataService.destroy();
+        if (mDownloader.isInitialized()) mDownloader.destroy();
         if (mParser.isUtilityProcessRunning()) mParser.stopUtilityProcess();
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/JourneyLogger.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/JourneyLogger.java
index 57b0ecc..c865c01 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/JourneyLogger.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/JourneyLogger.java
@@ -174,6 +174,13 @@
         }
     }
 
+    /**
+     * Records the fact that the user had an initial form of payment.
+     */
+    public void setUserHadInitialFormOfPayment() {
+        nativeSetUserHadInitialFormOfPayment(mJourneyLoggerAndroid);
+    }
+
     private native long nativeInitJourneyLoggerAndroid(boolean isIncognito, String url);
     private native void nativeDestroy(long nativeJourneyLoggerAndroid);
     private native void nativeSetNumberOfSuggestionsShown(
@@ -194,4 +201,5 @@
     private native void nativeSetCompleted(long nativeJourneyLoggerAndroid);
     private native void nativeSetAborted(long nativeJourneyLoggerAndroid, int reason);
     private native void nativeSetNotShown(long nativeJourneyLoggerAndroid, int reason);
+    private native void nativeSetUserHadInitialFormOfPayment(long nativeJourneyLoggerAndroid);
 }
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
index 96f253fe..007d652 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
@@ -1558,6 +1558,9 @@
         mJourneyLogger.setNumberOfSuggestionsShown(
                 Section.CREDIT_CARDS, numberOfAutofillInstruments);
 
+        // Record whether the user had a form of payment initially.
+        if (!mPendingInstruments.isEmpty()) mJourneyLogger.setUserHadInitialFormOfPayment();
+
         // Possibly pre-select the first instrument on the list.
         int selection = !mPendingInstruments.isEmpty() && mPendingInstruments.get(0).canPreselect()
                 ? 0
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PhotoPickerDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PhotoPickerDialog.java
index db9b9c2..410f8ac 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PhotoPickerDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PhotoPickerDialog.java
@@ -5,10 +5,7 @@
 package org.chromium.chrome.browser.photo_picker;
 
 import android.content.Context;
-import android.os.Bundle;
 import android.support.v7.app.AlertDialog;
-import android.view.ViewGroup.LayoutParams;
-import android.view.WindowManager;
 
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.R;
@@ -43,15 +40,6 @@
     }
 
     @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        getWindow().setLayout(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
-        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
-                WindowManager.LayoutParams.FLAG_FULLSCREEN);
-    }
-
-    @Override
     public void dismiss() {
         super.dismiss();
         mCategoryView.onDialogDismissed();
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 97e8b70..100cb87 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
@@ -7,10 +7,12 @@
 import android.support.annotation.Nullable;
 
 import org.chromium.base.metrics.RecordUserAction;
+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.DownloadUtils;
 import org.chromium.chrome.browser.multiwindow.MultiWindowUtils;
 import org.chromium.chrome.browser.ntp.NewTabPageUma;
@@ -31,6 +33,7 @@
 import org.chromium.content_public.common.Referrer;
 import org.chromium.ui.base.PageTransition;
 import org.chromium.ui.mojom.WindowOpenDisposition;
+import org.chromium.ui.widget.Toast;
 
 /**
  * {@link SuggestionsUiDelegate} implementation.
@@ -184,8 +187,21 @@
     }
 
     private Tab openUrlInNewTab(LoadUrlParams loadUrlParams) {
-        return mTabModelSelector.openNewTab(loadUrlParams, TabLaunchType.FROM_LONGPRESS_BACKGROUND,
-                mHost.getActiveTab(), /* incognito = */ false);
+        Tab tab = mTabModelSelector.openNewTab(loadUrlParams,
+                TabLaunchType.FROM_LONGPRESS_BACKGROUND, mHost.getActiveTab(),
+                /* incognito = */ false);
+
+        // If the bottom sheet NTP UI is showing, a toast is not necessary because the bottom sheet
+        // will be closed when the overview is hidden due to the new tab creation above.
+        // If animations are disabled in the DeviceClassManager, a toast is already displayed for
+        // all tabs opened in the background.
+        // TODO(twellington): Replace this with an animation.
+        if (mActivity.getBottomSheet() != null && !mActivity.getBottomSheet().isShowingNewTab()
+                && DeviceClassManager.enableAnimations()) {
+            Toast.makeText(mActivity, R.string.open_in_new_tab_toast, Toast.LENGTH_SHORT).show();
+        }
+
+        return tab;
     }
 
     private void saveUrlForOffline(String url) {
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 05424a1..44be9d6 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
@@ -1312,7 +1312,7 @@
             });
 
             if (!creatingWebContents && webContents.isLoadingToDifferentDocument()) {
-                didStartPageLoad(webContents.getUrl(), false);
+                didStartPageLoad(webContents.getVisibleUrl(), false);
             }
 
             getAppBannerManager().setIsEnabledForTab(mDelegateFactory.canShowAppBanners(this));
@@ -1890,11 +1890,12 @@
     }
 
     /**
-     * @return The URL associated with the tab.
+     * @return The URL that is currently visible in the location bar. This may not be the same as
+     *         the last committed URL if a new navigation is in progress.
      */
     @CalledByNative
     public String getUrl() {
-        String url = getWebContents() != null ? getWebContents().getUrl() : "";
+        String url = getWebContents() != null ? getWebContents().getVisibleUrl() : "";
 
         // If we have a ContentView, or a NativePage, or the url is not empty, we have a WebContents
         // so cache the WebContent's url. If not use the cached version.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabCreatorManager.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabCreatorManager.java
index cd0a7f2..2db2a9e1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabCreatorManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabCreatorManager.java
@@ -84,8 +84,8 @@
          */
         public final boolean createTabWithWebContents(Tab parent,
                 WebContents webContents, int parentId, TabLaunchType type) {
-            return createTabWithWebContents(parent, webContents, parentId, type,
-                    webContents.getUrl());
+            return createTabWithWebContents(
+                    parent, webContents, parentId, type, webContents.getVisibleUrl());
         }
 
         /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java
index a301c118..5786caf 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java
@@ -70,6 +70,7 @@
 public class VrShellDelegate implements ApplicationStatus.ActivityStateListener,
                                         View.OnSystemUiVisibilityChangeListener {
     private static final String TAG = "VrShellDelegate";
+
     // Pseudo-random number to avoid request id collisions.
     public static final int EXIT_VR_RESULT = 721251;
 
@@ -168,6 +169,8 @@
 
     private View mOverlayView;
 
+    private final VSyncEstimator mVSyncEstimator;
+
     private static final class VrBroadcastReceiver extends BroadcastReceiver {
         private final WeakReference<ChromeActivity> mTargetActivity;
 
@@ -474,6 +477,85 @@
         return ChromeFeatureList.isEnabled(ChromeFeatureList.VR_SHELL);
     }
 
+    private class VSyncEstimator {
+        private static final long NANOS_PER_SECOND = 1000000000;
+
+        private static final long VSYNC_TIMEBASE_UPDATE_DELTA = 1 * NANOS_PER_SECOND;
+        private static final double VSYNC_DRIFT_THRESHOLD = 1.2;
+
+        // Estimates based on too few frames are unstable, probably anything above 2 is reasonable.
+        // Higher numbers will reduce how frequently we update the native vsync base/interval.
+        private static final int MIN_FRAME_COUNT = 5;
+
+        private final long mReportedVSyncNanos;
+        private final long mMaxVSyncIntervalNanos;
+        private final long mMinVSyncIntervalNanos;
+
+        private long mVSyncTimebaseNanos;
+        private long mVSyncIntervalNanos;
+        private long mVSyncIntervalMicros;
+
+        private final FrameCallback mCallback = new FrameCallback() {
+            @Override
+            public void doFrame(long frameTimeNanos) {
+                if (mNativeVrShellDelegate == 0) return;
+                Choreographer.getInstance().postFrameCallback(this);
+                if (mVSyncTimebaseNanos == 0) {
+                    updateVSyncInterval(frameTimeNanos, mVSyncIntervalNanos);
+                    return;
+                }
+                long elapsed = frameTimeNanos - mVSyncTimebaseNanos;
+                // If you're hitting the assert below, you probably added the callback twice.
+                assert elapsed != 0;
+                long count = Math.round(elapsed / (double) mVSyncIntervalNanos);
+                if (count < MIN_FRAME_COUNT) return;
+                long vSyncIntervalNanos = elapsed / count;
+                if (vSyncIntervalNanos > mMaxVSyncIntervalNanos
+                        || vSyncIntervalNanos < mMinVSyncIntervalNanos) {
+                    // This algorithm for computing VSync becomes unstable if it drifts too far from
+                    // the real VSync, which shouldn't happen in practice. If this assert is getting
+                    // hit, something has gone very wrong, but we should probably do something
+                    // reasonable for release builds.
+                    Log.v(TAG, "Error computing VSync interval. Resetting.");
+                    assert false;
+                    vSyncIntervalNanos = mReportedVSyncNanos;
+                }
+                updateVSyncInterval(frameTimeNanos, vSyncIntervalNanos);
+            }
+        };
+
+        public VSyncEstimator() {
+            Display display = ((WindowManager) mActivity.getSystemService(Context.WINDOW_SERVICE))
+                                      .getDefaultDisplay();
+            mReportedVSyncNanos = (long) ((1.0d / display.getRefreshRate()) * NANOS_PER_SECOND);
+            mVSyncIntervalNanos = mReportedVSyncNanos;
+            mMaxVSyncIntervalNanos = (long) (VSYNC_DRIFT_THRESHOLD * mReportedVSyncNanos);
+            mMinVSyncIntervalNanos = (long) (mReportedVSyncNanos / VSYNC_DRIFT_THRESHOLD);
+        }
+
+        void updateVSyncInterval(long frameTimeNanos, long vSyncIntervalNanos) {
+            mVSyncIntervalNanos = vSyncIntervalNanos;
+            long vSyncIntervalMicros = mVSyncIntervalNanos / 1000;
+            if (vSyncIntervalMicros == mVSyncIntervalMicros
+                    && frameTimeNanos - mVSyncTimebaseNanos < VSYNC_TIMEBASE_UPDATE_DELTA) {
+                return;
+            }
+            mVSyncIntervalMicros = vSyncIntervalMicros;
+            mVSyncTimebaseNanos = frameTimeNanos;
+
+            nativeUpdateVSyncInterval(
+                    mNativeVrShellDelegate, mVSyncTimebaseNanos, mVSyncIntervalMicros);
+        }
+
+        public void pause() {
+            Choreographer.getInstance().removeFrameCallback(mCallback);
+        }
+
+        public void resume() {
+            Choreographer.getInstance().postFrameCallback(mCallback);
+        }
+    }
+
     private VrShellDelegate(ChromeActivity activity, VrClassesWrapper wrapper) {
         mActivity = activity;
         mVrClassesWrapper = wrapper;
@@ -485,17 +567,7 @@
         mFeedbackFrequency = VrFeedbackStatus.getFeedbackFrequency();
         mEnterVrHandler = new Handler();
         mExpectPauseOrDonSucceeded = new Handler();
-        Choreographer.getInstance().postFrameCallback(new FrameCallback() {
-            @Override
-            public void doFrame(long frameTimeNanos) {
-                if (mNativeVrShellDelegate == 0) return;
-                Display display =
-                        ((WindowManager) mActivity.getSystemService(Context.WINDOW_SERVICE))
-                                .getDefaultDisplay();
-                nativeUpdateVSyncInterval(
-                        mNativeVrShellDelegate, frameTimeNanos, 1.0d / display.getRefreshRate());
-            }
-        });
+        mVSyncEstimator = new VSyncEstimator();
         ApplicationStatus.registerStateListenerForAllActivities(this);
         if (!mPaused) onResume();
     }
@@ -688,7 +760,10 @@
 
         // onResume needs to be called on GvrLayout after initialization to make sure DON flow works
         // properly.
-        if (!mPaused) mVrShell.resume();
+        if (!mPaused) {
+            mVrShell.resume();
+            mVSyncEstimator.resume();
+        }
 
         maybeSetPresentResult(true, donSuceeded);
         mVrShell.getContainer().setOnSystemUiVisibilityChangeListener(this);
@@ -872,6 +947,7 @@
 
         if (mInVr) {
             mVrShell.resume();
+            mVSyncEstimator.resume();
         }
 
         if (mDonSucceeded) {
@@ -924,7 +1000,10 @@
         cancelPendingVrEntry();
         // We defer pausing of VrShell until the app is stopped to keep head tracking working for
         // as long as possible while going to daydream home.
-        if (mInVr) mVrShell.pause();
+        if (mInVr) {
+            mVrShell.pause();
+            mVSyncEstimator.pause();
+        }
         if (mShowingDaydreamDoff || mProbablyInDon) return;
 
         // TODO(mthiesse): When the user resumes Chrome in a 2D context, we don't want to tear down
@@ -1053,6 +1132,7 @@
 
         restoreWindowMode();
         mVrShell.pause();
+        mVSyncEstimator.pause();
         removeVrViews();
         destroyVrShell();
         if (disableVrMode) mVrClassesWrapper.setVrModeEnabled(mActivity, false);
@@ -1349,8 +1429,8 @@
     private static native void nativeOnLibraryAvailable();
     private native void nativeSetPresentResult(long nativeVrShellDelegate, boolean result);
     private native void nativeDisplayActivate(long nativeVrShellDelegate);
-    private native void nativeUpdateVSyncInterval(long nativeVrShellDelegate, long timebaseNanos,
-            double intervalSeconds);
+    private native void nativeUpdateVSyncInterval(
+            long nativeVrShellDelegate, long timebaseNanos, long intervalMicros);
     private native void nativeOnPause(long nativeVrShellDelegate);
     private native void nativeOnResume(long nativeVrShellDelegate);
     private native void nativeUpdateNonPresentingContext(long nativeVrShellDelegate, long context);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappDirectoryManager.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappDirectoryManager.java
index fd0edbde..a811afa 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappDirectoryManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappDirectoryManager.java
@@ -7,7 +7,6 @@
 import android.annotation.TargetApi;
 import android.app.ActivityManager;
 import android.app.ActivityManager.AppTask;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
@@ -110,21 +109,6 @@
             if (data != null && TextUtils.equals(WebappActivity.WEBAPP_SCHEME, data.getScheme())) {
                 liveWebapps.add(data.getHost());
             }
-
-            // WebappManagedActivities have titles from "WebappActivity0" through "WebappActivity9".
-            ComponentName component = intent.getComponent();
-            if (component != null) {
-                String fullClassName = component.getClassName();
-                int lastPeriodIndex = fullClassName.lastIndexOf(".");
-                if (lastPeriodIndex != -1) {
-                    String className = fullClassName.substring(lastPeriodIndex + 1);
-                    if (className.startsWith(WEBAPP_DIRECTORY_NAME)
-                            && className.length() > WEBAPP_DIRECTORY_NAME.length()) {
-                        String activityIndex = className.substring(WEBAPP_DIRECTORY_NAME.length());
-                        liveWebapps.add(activityIndex);
-                    }
-                }
-            }
         }
 
         if (webappBaseDirectory != null) {
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 3dd7ed9..ee14c15 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
@@ -45,6 +45,7 @@
 import org.chromium.chrome.browser.ntp.NewTabPage;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabModel;
+import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.toolbar.ActionModeController.ActionBarDelegate;
 import org.chromium.chrome.browser.toolbar.BottomToolbarPhone;
@@ -241,6 +242,9 @@
     /** Whether the help bubble has been shown. **/
     private boolean mHasShownTextBubble;
 
+    /** Whether or not the back button was used to enter the tab switcher. */
+    private boolean mBackButtonDismissesChrome;
+
     /**
      * An interface defining content that can be displayed inside of the bottom sheet for Chrome
      * Home.
@@ -484,6 +488,34 @@
     }
 
     /**
+     * Handle a back press event.
+     *     - If the navigation stack is empty, the sheet will be opened to the half state.
+     *         - If the tab switcher is visible, {@link ChromeActivity} will handle the event.
+     *     - If the sheet is open it will be closed unless it was opened by a back press.
+     * @return True if the sheet handled the back press.
+     */
+    public boolean handleBackPress() {
+        Tab tab = getActiveTab();
+        boolean consumeEvent = false;
+
+        if (!isSheetOpen() && tab != null && !tab.canGoBack() && !isInOverviewMode()
+                && tab.getLaunchType() == TabLaunchType.FROM_CHROME_UI) {
+            mBackButtonDismissesChrome = true;
+            setSheetState(SHEET_STATE_HALF, true);
+            return true;
+        } else if (isSheetOpen() && !mBackButtonDismissesChrome) {
+            consumeEvent = true;
+        }
+
+        if (getSheetState() != SHEET_STATE_PEEK) {
+            getBottomSheetMetrics().setSheetCloseReason(BottomSheetMetrics.CLOSED_BY_BACK_PRESS);
+            setSheetState(SHEET_STATE_PEEK, true);
+        }
+
+        return consumeEvent;
+    }
+
+    /**
      * Sets whether the {@link BottomSheet} and its children should react to touch events.
      */
     public void setTouchEnabled(boolean enabled) {
@@ -972,6 +1004,7 @@
     private void onSheetClosed() {
         if (!mIsSheetOpen) return;
 
+        mBackButtonDismissesChrome = false;
         mIsSheetOpen = false;
         for (BottomSheetObserver o : mObservers) o.onSheetClosed();
         announceForAccessibility(getResources().getString(R.string.bottom_sheet_closed));
@@ -1381,6 +1414,13 @@
     }
 
     /**
+     * @return Whether or not the browser is in overview mode.
+     */
+    private boolean isInOverviewMode() {
+        return mActivity != null && mActivity.isInOverviewMode();
+    }
+
+    /**
      * @return Whether the Google 'G' logo should be shown in the location bar.
      */
     public boolean shouldShowGoogleGInLocationBar() {
@@ -1392,16 +1432,12 @@
      * mode, when "find in page" is visible, or when the toolbar is hidden.
      */
     private boolean canMoveSheet() {
-        boolean isInOverviewMode = mTabModelSelector != null
-                && (mTabModelSelector.getCurrentTab() == null
-                           || mTabModelSelector.getCurrentTab().getActivity().isInOverviewMode());
-
         if (mFindInPageView == null) mFindInPageView = findViewById(R.id.find_toolbar);
         boolean isFindInPageVisible =
                 mFindInPageView != null && mFindInPageView.getVisibility() == View.VISIBLE;
 
         return !isToolbarAndroidViewHidden()
-                && (!isInOverviewMode || mNtpController.isShowingNewTabUi())
+                && (!isInOverviewMode() || mNtpController.isShowingNewTabUi())
                 && !isFindInPageVisible;
     }
 
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index 8025440..6d29d30d 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -1525,6 +1525,7 @@
   "javatests/src/org/chromium/chrome/browser/physicalweb/UrlManagerTest.java",
   "javatests/src/org/chromium/chrome/browser/policy/CombinedPolicyProviderTest.java",
   "javatests/src/org/chromium/chrome/browser/payments/CurrencyFormatterTest.java",
+  "javatests/src/org/chromium/chrome/browser/payments/PaymentManifestDownloaderTest.java",
   "javatests/src/org/chromium/chrome/browser/payments/PaymentManifestParserTest.java",
   "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestAbortTest.java",
   "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestBasicCardTest.java",
@@ -1701,6 +1702,7 @@
   "javatests/src/org/chromium/chrome/browser/widget/RadioButtonLayoutTest.java",
   "javatests/src/org/chromium/chrome/browser/widget/RoundedIconGeneratorTest.java",
   "javatests/src/org/chromium/chrome/browser/widget/ToolbarProgressBarTest.java",
+  "javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetBackBehaviorTest.java",
   "javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetContentControllerTest.java",
   "javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetObserverTest.java",
   "javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetNewTabControllerTest.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ModalDialogTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ModalDialogTest.java
index 12bcd77..bc65af1 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ModalDialogTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ModalDialogTest.java
@@ -281,7 +281,7 @@
                 mActivityTestRule.getActivity()
                         .getCurrentContentViewCore()
                         .getWebContents()
-                        .getUrl());
+                        .getLastCommittedUrl());
         executeJavaScriptAndWaitForDialog("history.back();");
 
         jsDialog = getCurrentDialog();
@@ -297,7 +297,7 @@
                 mActivityTestRule.getActivity()
                         .getCurrentContentViewCore()
                         .getWebContents()
-                        .getUrl());
+                        .getLastCommittedUrl());
     }
 
     /**
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/PersonalDataManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/PersonalDataManagerTest.java
index 0f73273..137cc72 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/PersonalDataManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/PersonalDataManagerTest.java
@@ -14,7 +14,6 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.BaseJUnit4ClassRunner;
-import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.RetryOnFailure;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
@@ -121,12 +120,9 @@
     }
 
     @Test
-    /*
     @SmallTest
     @Feature({"Autofill"})
     @RetryOnFailure
-    */
-    @DisabledTest(message = "crbug.com/739575")
     public void testAddAndDeleteProfile()
             throws InterruptedException, ExecutionException, TimeoutException {
         String profileOneGUID = mHelper.setProfile(createTestProfile());
@@ -261,11 +257,8 @@
     }
 
     @Test
-    /*
     @SmallTest
     @Feature({"Autofill"})
-    */
-    @DisabledTest(message = "crbug.com/739575")
     public void testLabels() throws InterruptedException, ExecutionException, TimeoutException {
         AutofillProfile profile1 = new AutofillProfile(
                  "" /* guid */, "https://www.example.com" /* origin */,
@@ -317,11 +310,8 @@
     }
 
     @Test
-    /*
     @SmallTest
     @Feature({"Autofill"})
-    */
-    @DisabledTest(message = "crbug.com/739575")
     public void testProfilesFrecency()
             throws InterruptedException, ExecutionException, TimeoutException {
         // Create 3 profiles.
@@ -447,12 +437,9 @@
     }
 
     @Test
-    /*
     @SmallTest
     @Feature({"Autofill"})
     @RetryOnFailure
-    */
-    @DisabledTest(message = "crbug.com/739575")
     public void testCreditCardUseStatsSettingAndGetting()
             throws InterruptedException, ExecutionException, TimeoutException {
         String guid = mHelper.setCreditCard(
@@ -500,12 +487,9 @@
     }
 
     @Test
-    /*
     @SmallTest
     @Feature({"Autofill"})
     @RetryOnFailure
-    */
-    @DisabledTest(message = "crbug.com/739575")
     public void testRecordAndLogCreditCardUse()
             throws InterruptedException, ExecutionException, TimeoutException {
         String guid = mHelper.setCreditCard(
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentManifestDownloaderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentManifestDownloaderTest.java
new file mode 100644
index 0000000..7b6e847
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentManifestDownloaderTest.java
@@ -0,0 +1,238 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.payments;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+
+import junit.framework.Assert;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.Feature;
+import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.test.ChromeActivityTestRule;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.components.payments.PaymentManifestDownloader;
+import org.chromium.components.payments.PaymentManifestDownloader.ManifestDownloadCallback;
+import org.chromium.content.browser.test.util.Criteria;
+import org.chromium.content.browser.test.util.CriteriaHelper;
+import org.chromium.net.test.EmbeddedTestServer;
+
+import java.net.URI;
+
+/** An integration test for the payment manifest downloader. */
+@RunWith(ChromeJUnit4ClassRunner.class)
+@CommandLineFlags.Add({
+        ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE,
+        ChromeActivityTestRule.DISABLE_NETWORK_PREDICTION_FLAG,
+})
+@MediumTest
+public class PaymentManifestDownloaderTest implements ManifestDownloadCallback {
+    @Rule
+    public ChromeActivityTestRule<ChromeActivity> mRule =
+            new ChromeActivityTestRule<>(ChromeActivity.class);
+
+    private static final String PAYMENT_METHOD_MANIFEST = "{\n"
+            + "  \"default_applications\": [\"app.json\"],\n"
+            + "  \"supported_origins\": [\"https://alicepay.com\"]\n"
+            + "}\n";
+
+    private static final String WEB_APP_MANIFEST = "{\n"
+            + "  \"name\": \"BobPay\",\n"
+            + "  \"related_applications\": [{\n"
+            + "    \"platform\": \"play\",\n"
+            + "    \"id\": \"com.bobpay\",\n"
+            + "    \"min_version\": \"1\",\n"
+            + "    \"fingerprints\": [{\n"
+            + "      \"type\": \"sha256_cert\",\n"
+            + "      \"value\": \"59:5C:88:65:FF:C4:E8:20:CF:F7:3E:C8:64:D0"
+            + ":95:F0:06:19:2E:A6:7B:20:04:D1:03:07:92:E2:A5:31:67:66\"\n"
+            + "    }]\n"
+            + "  }]\n"
+            + "}\n";
+
+    private final PaymentManifestDownloader mDownloader = new PaymentManifestDownloader();
+    private EmbeddedTestServer mServer;
+    private boolean mDownloadComplete;
+    private boolean mDownloadPaymentMethodManifestSuccess;
+    private boolean mDownloadWebAppManifestSuccess;
+    private boolean mDownloadFailure;
+    private String mPaymentMethodManifest;
+    private String mWebAppManifest;
+
+    @Override
+    public void onPaymentMethodManifestDownloadSuccess(String content) {
+        mDownloadComplete = true;
+        mDownloadPaymentMethodManifestSuccess = true;
+        mPaymentMethodManifest = content;
+    }
+
+    @Override
+    public void onWebAppManifestDownloadSuccess(String content) {
+        mDownloadComplete = true;
+        mDownloadWebAppManifestSuccess = true;
+        mWebAppManifest = content;
+    }
+
+    @Override
+    public void onManifestDownloadFailure() {
+        mDownloadComplete = true;
+        mDownloadFailure = true;
+    }
+
+    @Before
+    public void setUp() throws Throwable {
+        mRule.startMainActivityOnBlankPage();
+        mServer = EmbeddedTestServer.createAndStartServer(
+                InstrumentationRegistry.getInstrumentation().getContext());
+        mRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mDownloader.initialize(
+                        mRule.getActivity().getCurrentContentViewCore().getWebContents());
+                mDownloader.allowHttpForTest();
+            }
+        });
+        mDownloadComplete = false;
+        mDownloadPaymentMethodManifestSuccess = false;
+        mDownloadWebAppManifestSuccess = false;
+        mDownloadFailure = false;
+        mPaymentMethodManifest = null;
+        mWebAppManifest = null;
+    }
+
+    @After
+    public void tearDown() throws Throwable {
+        mRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mDownloader.destroy();
+            }
+        });
+        mServer.stopAndDestroyServer();
+    }
+
+    @Test
+    @Feature({"Payments"})
+    public void testDownloadWebAppManifest() throws Throwable {
+        final URI uri = new URI(mServer.getURL("/chrome/test/data/payments/app.json"));
+        mRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mDownloader.downloadWebAppManifest(uri, PaymentManifestDownloaderTest.this);
+            }
+        });
+        CriteriaHelper.pollInstrumentationThread(new Criteria() {
+            @Override
+            public boolean isSatisfied() {
+                return mDownloadComplete;
+            }
+        });
+
+        Assert.assertTrue(
+                "Web app manifest should have been downloaded.", mDownloadWebAppManifestSuccess);
+        Assert.assertEquals(WEB_APP_MANIFEST, mWebAppManifest);
+    }
+
+    @Test
+    @Feature({"Payments"})
+    public void testUnableToDownloadWebAppManifest() throws Throwable {
+        final URI uri = new URI(mServer.getURL("/no-such-app.json"));
+        mRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mDownloader.downloadWebAppManifest(uri, PaymentManifestDownloaderTest.this);
+            }
+        });
+        CriteriaHelper.pollInstrumentationThread(new Criteria() {
+            @Override
+            public boolean isSatisfied() {
+                return mDownloadComplete;
+            }
+        });
+
+        Assert.assertTrue("Web app manifest should not have been downloaded.", mDownloadFailure);
+    }
+
+    @Test
+    @Feature({"Payments"})
+    public void testDownloadPaymentMethodManifest() throws Throwable {
+        final URI uri = new URI(mServer.getURL("/chrome/test/data/payments/webpay"));
+        mRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mDownloader.downloadPaymentMethodManifest(uri, PaymentManifestDownloaderTest.this);
+            }
+        });
+        CriteriaHelper.pollInstrumentationThread(new Criteria() {
+            @Override
+            public boolean isSatisfied() {
+                return mDownloadComplete;
+            }
+        });
+
+        Assert.assertTrue("Payment method manifest should have been downloaded.",
+                mDownloadPaymentMethodManifestSuccess);
+        Assert.assertEquals(PAYMENT_METHOD_MANIFEST, mPaymentMethodManifest);
+    }
+
+    @Test
+    @Feature({"Payments"})
+    public void testUnableToDownloadPaymentMethodManifest() throws Throwable {
+        final URI uri = new URI(mServer.getURL("/no-such-payment-method-name"));
+        mRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mDownloader.downloadPaymentMethodManifest(uri, PaymentManifestDownloaderTest.this);
+            }
+        });
+        CriteriaHelper.pollInstrumentationThread(new Criteria() {
+            @Override
+            public boolean isSatisfied() {
+                return mDownloadComplete;
+            }
+        });
+
+        Assert.assertTrue(
+                "Payment method manifest should have not have been downloaded.", mDownloadFailure);
+    }
+
+    @Test
+    @Feature({"Payments"})
+    public void testSeveralDownloadsAtOnce() throws Throwable {
+        final URI paymentMethodUri1 = new URI(mServer.getURL("/no-such-payment-method-name"));
+        final URI paymentMethodUri2 = new URI(mServer.getURL("/chrome/test/data/payments/webpay"));
+        final URI webAppUri1 = new URI(mServer.getURL("/no-such-app.json"));
+        final URI webAppUri2 = new URI(mServer.getURL("/chrome/test/data/payments/app.json"));
+        mRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mDownloader.downloadPaymentMethodManifest(
+                        paymentMethodUri1, PaymentManifestDownloaderTest.this);
+                mDownloader.downloadPaymentMethodManifest(
+                        paymentMethodUri2, PaymentManifestDownloaderTest.this);
+                mDownloader.downloadWebAppManifest(webAppUri1, PaymentManifestDownloaderTest.this);
+                mDownloader.downloadWebAppManifest(webAppUri2, PaymentManifestDownloaderTest.this);
+            }
+        });
+        CriteriaHelper.pollInstrumentationThread(new Criteria() {
+            @Override
+            public boolean isSatisfied() {
+                return mDownloadWebAppManifestSuccess && mDownloadPaymentMethodManifestSuccess
+                        && mDownloadFailure;
+            }
+        });
+
+        Assert.assertEquals(PAYMENT_METHOD_MANIFEST, mPaymentMethodManifest);
+        Assert.assertEquals(WEB_APP_MANIFEST, mWebAppManifest);
+    }
+}
\ No newline at end of file
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestJourneyLoggerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestJourneyLoggerTest.java
index 475fe80..5302797a 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestJourneyLoggerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestJourneyLoggerTest.java
@@ -5,6 +5,8 @@
 package org.chromium.chrome.browser.payments;
 
 import static org.chromium.chrome.browser.payments.PaymentRequestTestRule.DELAYED_RESPONSE;
+import static org.chromium.chrome.browser.payments.PaymentRequestTestRule.HAVE_INSTRUMENTS;
+import static org.chromium.chrome.browser.payments.PaymentRequestTestRule.IMMEDIATE_RESPONSE;
 import static org.chromium.chrome.browser.payments.PaymentRequestTestRule.NO_INSTRUMENTS;
 
 import android.content.DialogInterface;
@@ -45,7 +47,9 @@
             new PaymentRequestTestRule("payment_request_metrics_test.html", this);
 
     @Override
-    public void onMainActivityStarted()
+    public void onMainActivityStarted() {}
+
+    private void createTestData()
             throws InterruptedException, ExecutionException, TimeoutException {
         AutofillTestHelper mHelper = new AutofillTestHelper();
         // The user has a shipping address and a credit card associated with that address on disk.
@@ -72,6 +76,8 @@
     @Feature({"Payments"})
     public void testNumberOfSuggestionsShown_ShippingAddress_Completed()
             throws InterruptedException, ExecutionException, TimeoutException {
+        createTestData();
+
         // Complete a Payment Request with a credit card.
         mPaymentRequestTestRule.triggerUIAndWait("ccBuy", mPaymentRequestTestRule.getReadyToPay());
         mPaymentRequestTestRule.clickAndWait(
@@ -106,6 +112,8 @@
     @Feature({"Payments"})
     public void testNumberOfSuggestionsShown_ShippingAddress_AbortedByUser()
             throws InterruptedException, ExecutionException, TimeoutException {
+        createTestData();
+
         // Cancel the payment request.
         mPaymentRequestTestRule.triggerUIAndWait("ccBuy", mPaymentRequestTestRule.getReadyToPay());
         mPaymentRequestTestRule.clickAndWait(
@@ -139,6 +147,8 @@
     @Feature({"Payments"})
     public void testNumberOfSelectionEdits_ShippingAddress_Completed()
             throws InterruptedException, ExecutionException, TimeoutException {
+        createTestData();
+
         // Complete a Payment Request with a credit card.
         mPaymentRequestTestRule.triggerUIAndWait("ccBuy", mPaymentRequestTestRule.getReadyToPay());
         mPaymentRequestTestRule.clickInShippingAddressAndWait(
@@ -183,6 +193,8 @@
     @Feature({"Payments"})
     public void testNumberOfSelectionAdds_ShippingAddress_Completed()
             throws InterruptedException, ExecutionException, TimeoutException {
+        createTestData();
+
         // Complete a Payment Request with a credit card.
         mPaymentRequestTestRule.triggerUIAndWait("ccBuy", mPaymentRequestTestRule.getReadyToPay());
         mPaymentRequestTestRule.clickInShippingAddressAndWait(
@@ -230,6 +242,8 @@
     @Feature({"Payments"})
     public void testNumberOfSuggestionsShown_CreditCards_Completed()
             throws InterruptedException, ExecutionException, TimeoutException {
+        createTestData();
+
         // Complete a Payment Request with a credit card.
         mPaymentRequestTestRule.triggerUIAndWait("ccBuy", mPaymentRequestTestRule.getReadyToPay());
         mPaymentRequestTestRule.clickAndWait(
@@ -264,6 +278,8 @@
     @Feature({"Payments"})
     public void testNumberOfSuggestionsShown_CreditCards_AbortedByUser()
             throws InterruptedException, ExecutionException, TimeoutException {
+        createTestData();
+
         // Cancel the payment request.
         mPaymentRequestTestRule.triggerUIAndWait("ccBuy", mPaymentRequestTestRule.getReadyToPay());
         mPaymentRequestTestRule.clickAndWait(
@@ -297,6 +313,8 @@
     @Feature({"Payments"})
     public void testNumberOfSelectionAdds_CreditCards_Completed()
             throws InterruptedException, ExecutionException, TimeoutException {
+        createTestData();
+
         // Complete a Payment Request with a credit card.
         mPaymentRequestTestRule.triggerUIAndWait("ccBuy", mPaymentRequestTestRule.getReadyToPay());
 
@@ -343,6 +361,8 @@
     @Feature({"Payments"})
     public void testNoContactInfoHistogram()
             throws InterruptedException, ExecutionException, TimeoutException {
+        createTestData();
+
         // Complete a Payment Request with a credit card.
         mPaymentRequestTestRule.triggerUIAndWait("ccBuy", mPaymentRequestTestRule.getReadyToPay());
         mPaymentRequestTestRule.clickAndWait(
@@ -375,6 +395,8 @@
     @MediumTest
     @Feature({"Payments"})
     public void testTwoTimes() throws InterruptedException, ExecutionException, TimeoutException {
+        createTestData();
+
         // Complete a Payment Request with a credit card.
         mPaymentRequestTestRule.triggerUIAndWait("ccBuy", mPaymentRequestTestRule.getReadyToPay());
         mPaymentRequestTestRule.clickAndWait(
@@ -453,4 +475,231 @@
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.NumberOfSuggestionsShown.ShippingAddress.Completed", 2));
     }
+
+    /**
+     * Expect that the UserHadInitialFormOfPayment histogram gets logged properly when the user has
+     * at least one credit card on file.
+     */
+    @Test
+    @MediumTest
+    @Feature({"Payments"})
+    public void testUserHadInitialFormOfPayment_AcceptsCardsAndApps_UserHasOnlyCard()
+            throws InterruptedException, ExecutionException, TimeoutException {
+        // Add an address and a credit card on file.
+        AutofillTestHelper mHelper = new AutofillTestHelper();
+        String mBillingAddressId = mHelper.setProfile(new AutofillProfile("", "https://example.com",
+                true, "Jon Doe", "Google", "340 Main St", "CA", "Los Angeles", "", "90291", "",
+                "US", "650-253-0000", "", "en-US"));
+        mHelper.setCreditCard(new CreditCard("", "https://example.com", true, true, "Jon Doe",
+                "4111111111111111", "1111", "12", "2050", "visa", R.drawable.visa_card,
+                CardType.UNKNOWN, mBillingAddressId, "" /* serverId */));
+
+        mPaymentRequestTestRule.triggerUIAndWait(
+                "cardsAndBobPayBuy", mPaymentRequestTestRule.getReadyToPay());
+
+        // The user cancels the Payment Request (trigger the logs).
+        mPaymentRequestTestRule.clickAndWait(
+                R.id.close_button, mPaymentRequestTestRule.getDismissed());
+
+        mPaymentRequestTestRule.expectResultContains(new String[] {"Request cancelled"});
+
+        // Make sure that the fact that the user had a form of payment was recorded.
+        Assert.assertEquals(1,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        "PaymentRequest.UserHadInitialFormOfPayment.EffectOnCompletion",
+                        CompletionStatus.USER_ABORTED));
+
+        // Make sure the opposite metric has no logs.
+        Assert.assertEquals(0,
+                RecordHistogram.getHistogramTotalCountForTesting(
+                        "PaymentRequest.UserDidNotHaveInitialFormOfPayment.EffectOnCompletion"));
+    }
+
+    /**
+     * Expect that the UserHadInitialFormOfPayment histogram gets logged properly when the user has
+     * at least one payment app on file.
+     */
+    @Test
+    @MediumTest
+    @Feature({"Payments"})
+    public void testUserHadInitialFormOfPayment_AcceptsCardsAndApps_UserHasOnlyPaymentApp()
+            throws InterruptedException, ExecutionException, TimeoutException {
+        // Add an address and a payment app on file.
+        AutofillTestHelper mHelper = new AutofillTestHelper();
+        mHelper.setProfile(new AutofillProfile("", "https://example.com", true, "Jon Doe", "Google",
+                "340 Main St", "CA", "Los Angeles", "", "90291", "", "US", "650-253-0000", "",
+                "en-US"));
+        mPaymentRequestTestRule.installPaymentApp(HAVE_INSTRUMENTS, IMMEDIATE_RESPONSE);
+
+        mPaymentRequestTestRule.triggerUIAndWait(
+                "cardsAndBobPayBuy", mPaymentRequestTestRule.getReadyToPay());
+
+        // The user cancels the Payment Request (trigger the logs).
+        mPaymentRequestTestRule.clickAndWait(
+                R.id.close_button, mPaymentRequestTestRule.getDismissed());
+
+        mPaymentRequestTestRule.expectResultContains(new String[] {"Request cancelled"});
+
+        // Make sure that the fact that the user had a form of payment was recorded.
+        Assert.assertEquals(1,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        "PaymentRequest.UserHadInitialFormOfPayment.EffectOnCompletion",
+                        CompletionStatus.USER_ABORTED));
+
+        // Make sure the opposite metric has no logs.
+        Assert.assertEquals(0,
+                RecordHistogram.getHistogramTotalCountForTesting(
+                        "PaymentRequest.UserDidNotHaveInitialFormOfPayment.EffectOnCompletion"));
+    }
+
+    /**
+     * Expect that the UserHadInitialFormOfPayment histogram gets logged properly when the user has
+     * at both a card and a payment app on file.
+     */
+    @Test
+    @MediumTest
+    @Feature({"Payments"})
+    public void testUserHadInitialFormOfPayment_AcceptsCardsAndApps_UserHasCardAndPaymentApp()
+            throws InterruptedException, ExecutionException, TimeoutException {
+        // Add an address, a credit card and a payment app on file.
+        AutofillTestHelper mHelper = new AutofillTestHelper();
+        String mBillingAddressId = mHelper.setProfile(new AutofillProfile("", "https://example.com",
+                true, "Jon Doe", "Google", "340 Main St", "CA", "Los Angeles", "", "90291", "",
+                "US", "650-253-0000", "", "en-US"));
+        mHelper.setCreditCard(new CreditCard("", "https://example.com", true, true, "Jon Doe",
+                "4111111111111111", "1111", "12", "2050", "visa", R.drawable.visa_card,
+                CardType.UNKNOWN, mBillingAddressId, "" /* serverId */));
+        mPaymentRequestTestRule.installPaymentApp(HAVE_INSTRUMENTS, IMMEDIATE_RESPONSE);
+
+        mPaymentRequestTestRule.triggerUIAndWait(
+                "cardsAndBobPayBuy", mPaymentRequestTestRule.getReadyToPay());
+
+        // The user cancels the Payment Request (trigger the logs).
+        mPaymentRequestTestRule.clickAndWait(
+                R.id.close_button, mPaymentRequestTestRule.getDismissed());
+
+        mPaymentRequestTestRule.expectResultContains(new String[] {"Request cancelled"});
+
+        // Make sure that the fact that the user had a form of payment was recorded.
+        Assert.assertEquals(1,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        "PaymentRequest.UserHadInitialFormOfPayment.EffectOnCompletion",
+                        CompletionStatus.USER_ABORTED));
+
+        // Make sure the opposite metric has no logs.
+        Assert.assertEquals(0,
+                RecordHistogram.getHistogramTotalCountForTesting(
+                        "PaymentRequest.UserDidNotHaveInitialFormOfPayment.EffectOnCompletion"));
+    }
+
+    /**
+     * Expect that the UserDidNotHaveInitialFormOfPayment histogram gets logged properly when the
+     * user has no form of payment on file.
+     */
+    @Test
+    @MediumTest
+    @Feature({"Payments"})
+    public void testUserHadInitialFormOfPayment_AcceptsCardsAndApps_UserHasNoCardOrPaymentApp()
+            throws InterruptedException, ExecutionException, TimeoutException {
+        // Add an address on file.
+        new AutofillTestHelper().setProfile(new AutofillProfile("", "https://example.com", true,
+                "Jon Doe", "Google", "340 Main St", "CA", "Los Angeles", "", "90291", "", "US",
+                "650-253-0000", "", "en-US"));
+
+        mPaymentRequestTestRule.triggerUIAndWait(
+                "cardsAndBobPayBuy", mPaymentRequestTestRule.getReadyForInput());
+
+        // The user cancels the Payment Request (trigger the logs).
+        mPaymentRequestTestRule.clickAndWait(
+                R.id.close_button, mPaymentRequestTestRule.getDismissed());
+
+        mPaymentRequestTestRule.expectResultContains(new String[] {"Request cancelled"});
+
+        // Make sure that the fact that the user had no form of payment was recorded.
+        Assert.assertEquals(1,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        "PaymentRequest.UserDidNotHaveInitialFormOfPayment.EffectOnCompletion",
+                        CompletionStatus.USER_ABORTED));
+
+        // Make sure the opposite metric has no logs.
+        Assert.assertEquals(0,
+                RecordHistogram.getHistogramTotalCountForTesting(
+                        "PaymentRequest.UserHadInitialFormOfPayment.EffectOnCompletion"));
+    }
+
+    /**
+     * Expect that the UserDidNotHaveInitialFormOfPayment histogram gets logged properly when the
+     * user has a payment app but the merchant only accepts credit cards.
+     */
+    @Test
+    @MediumTest
+    @Feature({"Payments"})
+    public void testUserHadInitialFormOfPayment_AcceptsCards_UserHasOnlyApp()
+            throws InterruptedException, ExecutionException, TimeoutException {
+        // Add an address and a payment app on file.
+        AutofillTestHelper mHelper = new AutofillTestHelper();
+        mHelper.setProfile(new AutofillProfile("", "https://example.com", true, "Jon Doe", "Google",
+                "340 Main St", "CA", "Los Angeles", "", "90291", "", "US", "650-253-0000", "",
+                "en-US"));
+        mPaymentRequestTestRule.installPaymentApp(HAVE_INSTRUMENTS, IMMEDIATE_RESPONSE);
+
+        mPaymentRequestTestRule.triggerUIAndWait(
+                "ccBuy", mPaymentRequestTestRule.getReadyForInput());
+
+        // The user cancels the Payment Request (trigger the logs).
+        mPaymentRequestTestRule.clickAndWait(
+                R.id.close_button, mPaymentRequestTestRule.getDismissed());
+
+        mPaymentRequestTestRule.expectResultContains(new String[] {"Request cancelled"});
+
+        // Make sure that the fact that the user had no form of payment was recorded.
+        Assert.assertEquals(1,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        "PaymentRequest.UserDidNotHaveInitialFormOfPayment.EffectOnCompletion",
+                        CompletionStatus.USER_ABORTED));
+
+        // Make sure the opposite metric has no logs.
+        Assert.assertEquals(0,
+                RecordHistogram.getHistogramTotalCountForTesting(
+                        "PaymentRequest.UserHadInitialFormOfPayment.EffectOnCompletion"));
+    }
+
+    /**
+     * Expect that the UserDidNotHaveInitialFormOfPayment histogram gets logged properly when the
+     * user has a payment app but the merchant only accepts credit cards.
+     */
+    @Test
+    @MediumTest
+    @Feature({"Payments"})
+    public void testUserHadInitialFormOfPayment_AcceptsCards_UserHasOnlyUnsupportedNetwork()
+            throws InterruptedException, ExecutionException, TimeoutException {
+        // Add an address and a card from an unsupported network on file.
+        AutofillTestHelper mHelper = new AutofillTestHelper();
+        String mBillingAddressId = mHelper.setProfile(new AutofillProfile("", "https://example.com",
+                true, "Jon Doe", "Google", "340 Main St", "CA", "Los Angeles", "", "90291", "",
+                "US", "650-253-0000", "", "en-US"));
+        mHelper.setCreditCard(new CreditCard("", "https://example.com", true, true, "Jon Doe",
+                "378282246310005", "1111", "12", "2050", "amex", R.drawable.visa_card,
+                CardType.UNKNOWN, mBillingAddressId, "" /* serverId */));
+
+        mPaymentRequestTestRule.triggerUIAndWait(
+                "ccBuy", mPaymentRequestTestRule.getReadyForInput());
+
+        // The user cancels the Payment Request (trigger the logs).
+        mPaymentRequestTestRule.clickAndWait(
+                R.id.close_button, mPaymentRequestTestRule.getDismissed());
+
+        mPaymentRequestTestRule.expectResultContains(new String[] {"Request cancelled"});
+
+        // Make sure that the fact that the user had no form of payment was recorded.
+        Assert.assertEquals(1,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        "PaymentRequest.UserDidNotHaveInitialFormOfPayment.EffectOnCompletion",
+                        CompletionStatus.USER_ABORTED));
+
+        // Make sure the opposite metric has no logs.
+        Assert.assertEquals(0,
+                RecordHistogram.getHistogramTotalCountForTesting(
+                        "PaymentRequest.UserHadInitialFormOfPayment.EffectOnCompletion"));
+    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/test/ClearAppDataTestRule.java b/chrome/android/javatests/src/org/chromium/chrome/browser/test/ClearAppDataTestRule.java
index 76e2e90..e8860f8 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/test/ClearAppDataTestRule.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/test/ClearAppDataTestRule.java
@@ -23,7 +23,9 @@
         return new Statement() {
             @Override
             public void evaluate() throws Throwable {
-                ApplicationData.clearAppData(InstrumentationRegistry.getContext());
+                ApplicationData.clearAppData(
+                        InstrumentationRegistry.getInstrumentation().getTargetContext());
+                base.evaluate();
             }
         };
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetBackBehaviorTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetBackBehaviorTest.java
new file mode 100644
index 0000000..7320f19
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetBackBehaviorTest.java
@@ -0,0 +1,282 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.widget.bottomsheet;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import static org.chromium.chrome.test.util.ChromeRestriction.RESTRICTION_TYPE_PHONE;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.provider.Browser;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.Restriction;
+import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.browser.ChromeTabbedActivity;
+import org.chromium.chrome.browser.compositor.layouts.LayoutManagerChrome;
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType;
+import org.chromium.chrome.test.BottomSheetTestRule;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.chrome.test.util.ChromeTabUtils;
+import org.chromium.content.browser.test.util.Criteria;
+import org.chromium.content.browser.test.util.CriteriaHelper;
+import org.chromium.net.test.EmbeddedTestServer;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Tests the behavior of the bottom sheet when used with the back button.
+ */
+@RunWith(ChromeJUnit4ClassRunner.class)
+@CommandLineFlags.Add({BottomSheetTestRule.ENABLE_CHROME_HOME,
+        ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE,
+        BottomSheetTestRule.DISABLE_NETWORK_PREDICTION_FLAG})
+@Restriction(RESTRICTION_TYPE_PHONE) // ChromeHome is only enabled on phones
+public class BottomSheetBackBehaviorTest {
+    private static final String TEST_PAGE = "/chrome/test/data/android/simple.html";
+
+    private BottomSheet mBottomSheet;
+    private ChromeTabbedActivity mActivity;
+    private LayoutManagerChrome mLayoutManager;
+
+    @Rule
+    public BottomSheetTestRule mBottomSheetTestRule = new BottomSheetTestRule();
+
+    @Before
+    public void setUp() throws Exception {
+        mBottomSheetTestRule.startMainActivityOnBlankPage();
+        mBottomSheet = mBottomSheetTestRule.getBottomSheet();
+        mActivity = mBottomSheetTestRule.getActivity();
+        mBottomSheetTestRule.setSheetState(BottomSheet.SHEET_STATE_PEEK, false);
+        mLayoutManager = mActivity.getLayoutManager();
+    }
+
+    @Test
+    @SmallTest
+    public void testBackButton_sheetOpen() {
+        mBottomSheetTestRule.setSheetState(BottomSheet.SHEET_STATE_HALF, false);
+
+        pressBackButton();
+        endBottomSheetAnimations();
+
+        assertFalse("Overview mode should not be showing.", mLayoutManager.overviewVisible());
+        assertEquals("The bottom sheet should be peeking.", BottomSheet.SHEET_STATE_PEEK,
+                mBottomSheet.getSheetState());
+    }
+
+    @Test
+    @SmallTest
+    public void testBackButton_tabSwitcher() throws InterruptedException, TimeoutException {
+        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+            @Override
+            public void run() {
+                mLayoutManager.showOverview(false);
+            }
+        });
+
+        assertTrue("Overview mode should be showing.", mLayoutManager.overviewVisible());
+
+        pressBackButton();
+        endBottomSheetAnimations();
+        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+            @Override
+            public void run() {
+                mLayoutManager.getActiveLayout().finishAnimationsForTests();
+            }
+        });
+
+        assertFalse("Overview mode should not be showing.", mLayoutManager.overviewVisible());
+        assertEquals("The bottom sheet should be peeking.", BottomSheet.SHEET_STATE_PEEK,
+                mBottomSheet.getSheetState());
+    }
+
+    @Test
+    @SmallTest
+    public void testBackButton_backFromInternalNewTab()
+            throws ExecutionException, InterruptedException, TimeoutException {
+        Tab tab = launchNewTabFromChrome("about:blank");
+
+        assertEquals("Tab should be on about:blank.", "about:blank", tab.getUrl());
+
+        // Back button press should open the bottom sheet since backward navigation is not
+        // possible.
+        pressBackButton();
+        endBottomSheetAnimations();
+
+        assertEquals("The bottom sheet should be at half height.", BottomSheet.SHEET_STATE_HALF,
+                mBottomSheet.getSheetState());
+        assertFalse("Overview mode should not be showing.", mLayoutManager.overviewVisible());
+
+        // Final back press should close the sheet and chrome.
+        pressBackButton();
+        endBottomSheetAnimations();
+
+        assertEquals("The bottom sheet should be peeking.", BottomSheet.SHEET_STATE_PEEK,
+                mBottomSheet.getSheetState());
+        assertFalse("Chrome should no longer have focus.", mActivity.hasWindowFocus());
+    }
+
+    @Test
+    @SmallTest
+    public void testBackButton_backFromInternalNewTab_sheetOpen()
+            throws ExecutionException, InterruptedException, TimeoutException {
+        Tab tab = launchNewTabFromChrome("about:blank");
+
+        assertEquals("Tab should be on about:blank.", "about:blank", tab.getUrl());
+
+        mBottomSheetTestRule.setSheetState(BottomSheet.SHEET_STATE_HALF, false);
+
+        // Back button should close the sheet but not send Chrome to the background.
+        pressBackButton();
+        endBottomSheetAnimations();
+
+        assertEquals("The bottom sheet should be peeking.", BottomSheet.SHEET_STATE_PEEK,
+                mBottomSheet.getSheetState());
+        assertTrue("Chrome should have focus.", mActivity.hasWindowFocus());
+
+        // Back button press should open the bottom sheet since backward navigation is not
+        // possible.
+        pressBackButton();
+        endBottomSheetAnimations();
+
+        assertEquals("The bottom sheet should be at half height.", BottomSheet.SHEET_STATE_HALF,
+                mBottomSheet.getSheetState());
+        assertFalse("Overview mode should not be showing.", mLayoutManager.overviewVisible());
+
+        // Final back press should close the sheet and chrome.
+        pressBackButton();
+        endBottomSheetAnimations();
+
+        assertEquals("The bottom sheet should be peeking.", BottomSheet.SHEET_STATE_PEEK,
+                mBottomSheet.getSheetState());
+        assertFalse("Chrome should no longer have focus.", mActivity.hasWindowFocus());
+    }
+
+    @Test
+    @SmallTest
+    public void testBackButton_backWithNavigation()
+            throws ExecutionException, InterruptedException, TimeoutException {
+        final Tab tab = mBottomSheet.getActiveTab();
+
+        EmbeddedTestServer testServer = EmbeddedTestServer.createAndStartServer(
+                InstrumentationRegistry.getInstrumentation().getContext());
+
+        String testUrl = testServer.getURL(TEST_PAGE);
+        ChromeTabUtils.loadUrlOnUiThread(tab, testUrl);
+        ChromeTabUtils.waitForTabPageLoaded(tab, testUrl);
+
+        mBottomSheetTestRule.setSheetState(BottomSheet.SHEET_STATE_HALF, false);
+
+        // Back button should close the bottom sheet.
+        pressBackButton();
+        endBottomSheetAnimations();
+
+        assertEquals("Tab should be on the test page.", testUrl, tab.getUrl());
+        assertEquals("The bottom sheet should be peeking.", BottomSheet.SHEET_STATE_PEEK,
+                mBottomSheet.getSheetState());
+        assertFalse("Overview mode should not be showing.", mLayoutManager.overviewVisible());
+
+        // Next back button press should navigate back.
+        pressBackButton();
+        endBottomSheetAnimations();
+        ChromeTabUtils.waitForTabPageLoaded(tab, "about:blank");
+
+        assertEquals("Tab should be on about:blank.", "about:blank", tab.getUrl());
+        assertEquals("The bottom sheet should be peeking.", BottomSheet.SHEET_STATE_PEEK,
+                mBottomSheet.getSheetState());
+        assertFalse("Overview mode should not be showing.", mLayoutManager.overviewVisible());
+    }
+
+    @Test
+    @SmallTest
+    public void testBackButton_backFromExternalNewTab()
+            throws InterruptedException, TimeoutException {
+        EmbeddedTestServer testServer = EmbeddedTestServer.createAndStartServer(
+                InstrumentationRegistry.getInstrumentation().getContext());
+        launchNewTabFromExternalApp(testServer.getURL(TEST_PAGE));
+
+        // Back button should send Chrome to the background.
+        pressBackButton();
+        endBottomSheetAnimations();
+
+        assertEquals("The bottom sheet should be peeking.", BottomSheet.SHEET_STATE_PEEK,
+                mBottomSheet.getSheetState());
+        assertFalse("Chrome should no longer have focus.", mActivity.hasWindowFocus());
+    }
+
+    /**
+     * Launch a new tab from an "external" app.
+     * @param url The URL to launch in the tab.
+     */
+    private void launchNewTabFromExternalApp(String url) throws InterruptedException {
+        final Intent intent = new Intent(Intent.ACTION_VIEW);
+        intent.putExtra(Browser.EXTRA_APPLICATION_ID, "externalApp");
+        intent.putExtra(Browser.EXTRA_CREATE_NEW_TAB, true);
+        intent.setData(Uri.parse(url));
+
+        final Tab originalTab = mActivity.getActivityTab();
+        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+            @Override
+            public void run() {
+                mActivity.onNewIntent(intent);
+            }
+        });
+        CriteriaHelper.pollUiThread(new Criteria("Failed to select different tab") {
+            @Override
+            public boolean isSatisfied() {
+                return mActivity.getActivityTab() != originalTab;
+            }
+        });
+        ChromeTabUtils.waitForTabPageLoaded(mActivity.getActivityTab(), url);
+    }
+
+    /**
+     * Launch a new tab from Chrome internally.
+     * @param url The URL to launch in the tab.
+     */
+    private Tab launchNewTabFromChrome(final String url) throws ExecutionException {
+        return ThreadUtils.runOnUiThreadBlocking(new Callable<Tab>() {
+            @Override
+            public Tab call() {
+                return mActivity.getTabCreator(false).launchUrl(
+                        url, TabLaunchType.FROM_CHROME_UI);
+            }
+        });
+    }
+
+    /** Notify the activity that the hardware back button was pressed. */
+    private void pressBackButton() {
+        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+            @Override
+            public void run() {
+                mActivity.onBackPressed();
+            }
+        });
+    }
+
+    /** End bottom sheet animations. */
+    private void endBottomSheetAnimations() {
+        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+            @Override
+            public void run() {
+                mBottomSheet.endAnimationsForTests();
+            }
+        });
+    }
+}
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFinderTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFinderTest.java
index 2f55f59a0d..1bb462ca 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFinderTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFinderTest.java
@@ -289,7 +289,10 @@
         Mockito.when(packageManagerDelegate.getPackageInfoWithSignatures("com.bobpay.app"))
                 .thenReturn(bobPayPackageInfo);
 
-        PaymentManifestDownloader downloader = new PaymentManifestDownloader(null) {
+        PaymentManifestDownloader downloader = new PaymentManifestDownloader() {
+            @Override
+            public void initialize(WebContents webContents) {}
+
             @Override
             public void downloadPaymentMethodManifest(URI uri, ManifestDownloadCallback callback) {
                 callback.onPaymentMethodManifestDownloadSuccess("some content here");
@@ -299,6 +302,9 @@
             public void downloadWebAppManifest(URI uri, ManifestDownloadCallback callback) {
                 callback.onWebAppManifestDownloadSuccess("some content here");
             }
+
+            @Override
+            public void destroy() {}
         };
 
         PaymentManifestParser parser = new PaymentManifestParser() {
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/payments/PaymentManifestVerifierTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/payments/PaymentManifestVerifierTest.java
index dbe7fe3..806887bf 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/payments/PaymentManifestVerifierTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/payments/PaymentManifestVerifierTest.java
@@ -19,6 +19,7 @@
 import org.chromium.chrome.browser.payments.PaymentManifestVerifier.ManifestVerifyCallback;
 import org.chromium.components.payments.PaymentManifestDownloader;
 import org.chromium.components.payments.PaymentManifestParser;
+import org.chromium.content_public.browser.WebContents;
 import org.chromium.payments.mojom.WebAppManifestSection;
 
 import java.net.URI;
@@ -55,7 +56,10 @@
         mMatchingApps.add(mAlicePay);
         mMatchingApps.add(mBobPay);
 
-        mDownloader = new PaymentManifestDownloader(null) {
+        mDownloader = new PaymentManifestDownloader() {
+            @Override
+            public void initialize(WebContents webContents) {}
+
             @Override
             public void downloadPaymentMethodManifest(URI uri, ManifestDownloadCallback callback) {
                 callback.onPaymentMethodManifestDownloadSuccess("some content here");
@@ -65,6 +69,9 @@
             public void downloadWebAppManifest(URI uri, ManifestDownloadCallback callback) {
                 callback.onWebAppManifestDownloadSuccess("some content here");
             }
+
+            @Override
+            public void destroy() {}
         };
 
         mWebDataService = Mockito.mock(PaymentManifestWebDataService.class);
@@ -122,12 +129,18 @@
     @Test
     public void testUnableToDownloadPaymentMethodManifest() {
         PaymentManifestVerifier verifier = new PaymentManifestVerifier(
-                mMethodName, mMatchingApps, mWebDataService, new PaymentManifestDownloader(null) {
+                mMethodName, mMatchingApps, mWebDataService, new PaymentManifestDownloader() {
+                    @Override
+                    public void initialize(WebContents webContents) {}
+
                     @Override
                     public void downloadPaymentMethodManifest(
                             URI uri, ManifestDownloadCallback callback) {
                         callback.onManifestDownloadFailure();
                     }
+
+                    @Override
+                    public void destroy() {}
                 }, mParser, mPackageManagerDelegate, mCallback);
 
         verifier.verify();
@@ -138,7 +151,10 @@
     @Test
     public void testUnableToDownloadWebAppManifest() {
         PaymentManifestVerifier verifier = new PaymentManifestVerifier(
-                mMethodName, mMatchingApps, mWebDataService, new PaymentManifestDownloader(null) {
+                mMethodName, mMatchingApps, mWebDataService, new PaymentManifestDownloader() {
+                    @Override
+                    public void initialize(WebContents webContents) {}
+
                     @Override
                     public void downloadPaymentMethodManifest(
                             URI uri, ManifestDownloadCallback callback) {
@@ -149,6 +165,9 @@
                     public void downloadWebAppManifest(URI uri, ManifestDownloadCallback callback) {
                         callback.onManifestDownloadFailure();
                     }
+
+                    @Override
+                    public void destroy() {}
                 }, mParser, mPackageManagerDelegate, mCallback);
 
         verifier.verify();
@@ -220,9 +239,6 @@
 
     private class CountingDownloader extends PaymentManifestDownloader {
         public int mDownloadWebAppManifestCounter;
-        public CountingDownloader() {
-            super(null);
-        }
     }
 
     /** If a single web app manifest fails to download, all downloads should be aborted. */
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index ef2019ff..88db55f1 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -568,8 +568,6 @@
     "mac/security_wrappers.h",
     "media/cast_remoting_connector.cc",
     "media/cast_remoting_connector.h",
-    "media/cast_remoting_connector_messaging.cc",
-    "media/cast_remoting_connector_messaging.h",
     "media/cast_remoting_sender.cc",
     "media/cast_remoting_sender.h",
     "media/media_access_handler.cc",
@@ -847,6 +845,8 @@
     "page_load_metrics/observers/https_engagement_metrics/https_engagement_service_factory.h",
     "page_load_metrics/observers/loading_predictor_page_load_metrics_observer.cc",
     "page_load_metrics/observers/loading_predictor_page_load_metrics_observer.h",
+    "page_load_metrics/observers/local_network_requests_page_load_metrics_observer.cc",
+    "page_load_metrics/observers/local_network_requests_page_load_metrics_observer.h",
     "page_load_metrics/observers/lofi_page_load_metrics_observer.cc",
     "page_load_metrics/observers/lofi_page_load_metrics_observer.h",
     "page_load_metrics/observers/media_page_load_metrics_observer.cc",
@@ -1635,6 +1635,7 @@
     "//media/cast:net",
     "//media/midi",
     "//media/mojo:features",
+    "//media/mojo/interfaces:mirror_service_remoting",
     "//media/mojo/interfaces:remoting",
     "//mojo/common",
     "//mojo/edk/system",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index d0e8a37..0e45042 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -339,6 +339,9 @@
      switches::reader_mode_heuristics::kAlwaysTrue},
     {flag_descriptions::kReaderModeHeuristicsAlwaysOff,
      switches::kReaderModeHeuristics, switches::reader_mode_heuristics::kNone},
+    {flag_descriptions::kReaderModeHeuristicsAllArticles,
+     switches::kReaderModeHeuristics,
+     switches::reader_mode_heuristics::kAllArticles},
 };
 
 const FeatureEntry::Choice kChromeHomeSwipeLogicChoices[] = {
@@ -2413,11 +2416,6 @@
          ntp_snippets::kArticleSuggestionsFeature,
          kRemoteSuggestionsFeatureVariations,
          ntp_snippets::kArticleSuggestionsFeature.name)},
-    {"enable-ntp-recent-offline-tab-suggestions",
-     flag_descriptions::kEnableNtpRecentOfflineTabSuggestionsName,
-     flag_descriptions::kEnableNtpRecentOfflineTabSuggestionsDescription,
-     kOsAndroid,
-     FEATURE_VALUE_TYPE(ntp_snippets::kRecentOfflineTabSuggestionsFeature)},
     {"enable-ntp-asset-download-suggestions",
      flag_descriptions::kEnableNtpAssetDownloadSuggestionsName,
      flag_descriptions::kEnableNtpAssetDownloadSuggestionsDescription,
@@ -2432,11 +2430,6 @@
      flag_descriptions::kEnableNtpBookmarkSuggestionsName,
      flag_descriptions::kEnableNtpBookmarkSuggestionsDescription, kOsAndroid,
      FEATURE_VALUE_TYPE(ntp_snippets::kBookmarkSuggestionsFeature)},
-    {"enable-ntp-physical-web-page-suggestions",
-     flag_descriptions::kEnableNtpPhysicalWebPageSuggestionsName,
-     flag_descriptions::kEnableNtpPhysicalWebPageSuggestionsDescription,
-     kOsAndroid,
-     FEATURE_VALUE_TYPE(ntp_snippets::kPhysicalWebPageSuggestionsFeature)},
     {"enable-ntp-foreign-sessions-suggestions",
      flag_descriptions::kEnableNtpForeignSessionsSuggestionsName,
      flag_descriptions::kEnableNtpForeignSessionsSuggestionsDescription,
@@ -2893,6 +2886,12 @@
      flag_descriptions::kOffMainThreadFetchDescription, kOsAll,
      FEATURE_VALUE_TYPE(features::kOffMainThreadFetch)},
 
+    {"enable-speculative-service-worker-start-on-query-input",
+     flag_descriptions::kSpeculativeServiceWorkerStartOnQueryInputName,
+     flag_descriptions::kSpeculativeServiceWorkerStartOnQueryInputDescription,
+     kOsAll,
+     FEATURE_VALUE_TYPE(omnibox::kSpeculativeServiceWorkerStartOnQueryInput)},
+
 #if defined(OS_MACOSX)
     {"tab-strip-keyboard-focus", flag_descriptions::kTabStripKeyboardFocusName,
      flag_descriptions::kTabStripKeyboardFocusDescription, kOsMac,
diff --git a/chrome/browser/android/logo_service.cc b/chrome/browser/android/logo_service.cc
index 1e007e36..c2fd971e 100644
--- a/chrome/browser/android/logo_service.cc
+++ b/chrome/browser/android/logo_service.cc
@@ -7,8 +7,6 @@
 #include "base/command_line.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "base/threading/sequenced_worker_pool.h"
-#include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/android/chrome_feature_list.h"
 #include "chrome/browser/image_decoder.h"
 #include "chrome/browser/profiles/profile.h"
@@ -52,9 +50,10 @@
     // If the ImageDecoder crashes or otherwise never completes, call
     // OnImageDecodeTimedOut() eventually to ensure that image_decoded_callback_
     // is run.
-    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-        FROM_HERE, base::Bind(&LogoDecoderDelegate::OnDecodeImageFailed,
-                              weak_ptr_factory_.GetWeakPtr()),
+    task_runner()->PostDelayedTask(
+        FROM_HERE,
+        base::Bind(&LogoDecoderDelegate::OnDecodeImageFailed,
+                   weak_ptr_factory_.GetWeakPtr()),
         base::TimeDelta::FromSeconds(kDecodeLogoTimeoutSeconds));
   }
 
@@ -87,9 +86,10 @@
   void DecodeUntrustedImage(
       const scoped_refptr<base::RefCountedString>& encoded_image,
       base::Callback<void(const SkBitmap&)> image_decoded_callback) override {
-    LogoDecoderDelegate* delegate =
-        new LogoDecoderDelegate(image_decoded_callback);
-    ImageDecoder::Start(delegate, encoded_image->data());
+    // TODO(bauerb): Switch to the components/image_fetcher implementation.
+    auto delegate =
+        base::MakeUnique<LogoDecoderDelegate>(image_decoded_callback);
+    ImageDecoder::Start(delegate.release(), encoded_image->data());
   }
 
  private:
@@ -136,9 +136,7 @@
   if (!logo_tracker_) {
     logo_tracker_ = base::MakeUnique<LogoTracker>(
         profile_->GetPath().Append(kCachedLogoDirectory),
-        BrowserThread::GetTaskRunnerForThread(BrowserThread::FILE),
-        BrowserThread::GetBlockingPool(), profile_->GetRequestContext(),
-        base::MakeUnique<ChromeLogoDelegate>());
+        profile_->GetRequestContext(), base::MakeUnique<ChromeLogoDelegate>());
   }
 
   GURL url = use_fixed_logo ? logo_url : GetGoogleDoodleURL(profile_);
diff --git a/chrome/browser/android/vr_shell/ui_elements/textured_element.cc b/chrome/browser/android/vr_shell/ui_elements/textured_element.cc
index e0a7a16..afe2ae8 100644
--- a/chrome/browser/android/vr_shell/ui_elements/textured_element.cc
+++ b/chrome/browser/android/vr_shell/ui_elements/textured_element.cc
@@ -29,7 +29,7 @@
 }
 
 void TexturedElement::UpdateTexture() {
-  if (!initialized_ || !GetTexture()->dirty())
+  if (!initialized_ || !GetTexture()->dirty() || !IsVisible())
     return;
   sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(
       texture_size_.width(), texture_size_.height());
@@ -50,6 +50,7 @@
                              gfx::Transform view_proj_matrix) const {
   if (!initialized_)
     return;
+  DCHECK(!GetTexture()->dirty());
   gfx::SizeF drawn_size = GetTexture()->GetDrawnSize();
   gfx::RectF copy_rect(0, 0, drawn_size.width() / texture_size_.width(),
                        drawn_size.height() / texture_size_.height());
@@ -77,4 +78,8 @@
   UpdateTexture();
 }
 
+void TexturedElement::OnBeginFrame(const base::TimeTicks& begin_frame_time) {
+  UpdateTexture();
+}
+
 }  // namespace vr_shell
diff --git a/chrome/browser/android/vr_shell/ui_elements/textured_element.h b/chrome/browser/android/vr_shell/ui_elements/textured_element.h
index f3e1411..3c009a0 100644
--- a/chrome/browser/android/vr_shell/ui_elements/textured_element.h
+++ b/chrome/browser/android/vr_shell/ui_elements/textured_element.h
@@ -30,6 +30,8 @@
   void Render(UiElementRenderer* renderer,
               gfx::Transform view_proj_matrix) const final;
 
+  void OnBeginFrame(const base::TimeTicks& begin_frame_time) override;
+
  protected:
   virtual UiTexture* GetTexture() const = 0;
   virtual void UpdateTexture();
diff --git a/chrome/browser/android/vr_shell/vr_shell.cc b/chrome/browser/android/vr_shell/vr_shell.cc
index b659625..5da786a 100644
--- a/chrome/browser/android/vr_shell/vr_shell.cc
+++ b/chrome/browser/android/vr_shell/vr_shell.cc
@@ -595,13 +595,13 @@
                             UiUnsupportedMode::kCount);
 }
 
-void VrShell::UpdateVSyncInterval(int64_t timebase_nanos,
-                                  double interval_seconds) {
+void VrShell::UpdateVSyncInterval(base::TimeTicks vsync_timebase,
+                                  base::TimeDelta vsync_interval) {
   PollMediaAccessFlag();
   WaitForGlThread();
   PostToGlThread(FROM_HERE, base::Bind(&VrShellGl::UpdateVSyncInterval,
                                        gl_thread_->GetVrShellGl(),
-                                       timebase_nanos, interval_seconds));
+                                       vsync_timebase, vsync_interval));
 }
 
 void VrShell::PollMediaAccessFlag() {
diff --git a/chrome/browser/android/vr_shell/vr_shell.h b/chrome/browser/android/vr_shell/vr_shell.h
index df45efc..fc72930 100644
--- a/chrome/browser/android/vr_shell/vr_shell.h
+++ b/chrome/browser/android/vr_shell/vr_shell.h
@@ -190,8 +190,8 @@
 
   // device::GvrDelegate implementation.
   void SetWebVRSecureOrigin(bool secure_origin) override;
-  void UpdateVSyncInterval(int64_t timebase_nanos,
-                           double interval_seconds) override;
+  void UpdateVSyncInterval(base::TimeTicks vsync_timebase,
+                           base::TimeDelta vsync_interval) override;
   void CreateVRDisplayInfo(
       const base::Callback<void(device::mojom::VRDisplayInfoPtr)>& callback,
       uint32_t device_id) override;
diff --git a/chrome/browser/android/vr_shell/vr_shell_delegate.cc b/chrome/browser/android/vr_shell/vr_shell_delegate.cc
index 17acec9..1453d8e7 100644
--- a/chrome/browser/android/vr_shell/vr_shell_delegate.cc
+++ b/chrome/browser/android/vr_shell/vr_shell_delegate.cc
@@ -57,8 +57,9 @@
   if (device_provider_) {
     device_provider_->Device()->OnDelegateChanged();
   }
-
-  gvr_delegate_->UpdateVSyncInterval(timebase_nanos_, interval_seconds_);
+  if (vsync_timebase_ != base::TimeTicks()) {
+    gvr_delegate_->UpdateVSyncInterval(vsync_timebase_, vsync_interval_);
+  }
 
   if (pending_successful_present_request_) {
     gvr_delegate_->ConnectPresentingService(
@@ -114,11 +115,12 @@
 void VrShellDelegate::UpdateVSyncInterval(JNIEnv* env,
                                           const JavaParamRef<jobject>& obj,
                                           jlong timebase_nanos,
-                                          jdouble interval_seconds) {
-  timebase_nanos_ = timebase_nanos;
-  interval_seconds_ = interval_seconds;
+                                          jlong interval_micros) {
+  vsync_timebase_ = base::TimeTicks() +
+                    base::TimeDelta::FromMilliseconds(timebase_nanos / 1000);
+  vsync_interval_ = base::TimeDelta::FromMicroseconds(interval_micros);
   if (gvr_delegate_) {
-    gvr_delegate_->UpdateVSyncInterval(timebase_nanos_, interval_seconds_);
+    gvr_delegate_->UpdateVSyncInterval(vsync_timebase_, vsync_interval_);
   }
 }
 
diff --git a/chrome/browser/android/vr_shell/vr_shell_delegate.h b/chrome/browser/android/vr_shell/vr_shell_delegate.h
index 013e8e4..e5ca2c4c 100644
--- a/chrome/browser/android/vr_shell/vr_shell_delegate.h
+++ b/chrome/browser/android/vr_shell/vr_shell_delegate.h
@@ -46,7 +46,7 @@
   void UpdateVSyncInterval(JNIEnv* env,
                            const base::android::JavaParamRef<jobject>& obj,
                            jlong timebase_nanos,
-                           jdouble interval_seconds);
+                           jlong interval_micros);
   void OnPause(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj);
   void OnResume(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj);
   void UpdateNonPresentingContext(
@@ -84,8 +84,8 @@
   device::GvrDeviceProvider* device_provider_ = nullptr;
   device::GvrDelegate* gvr_delegate_ = nullptr;
   base::Callback<void(bool)> present_callback_;
-  int64_t timebase_nanos_ = 0;
-  double interval_seconds_ = 0;
+  base::TimeTicks vsync_timebase_;
+  base::TimeDelta vsync_interval_;
   device::mojom::VRSubmitFrameClientPtr submit_client_;
   device::mojom::VRPresentationProviderRequest presentation_provider_request_;
   bool pending_successful_present_request_ = false;
diff --git a/chrome/browser/android/vr_shell/vr_shell_gl.cc b/chrome/browser/android/vr_shell/vr_shell_gl.cc
index 63222e6..bc91a39 100644
--- a/chrome/browser/android/vr_shell/vr_shell_gl.cc
+++ b/chrome/browser/android/vr_shell/vr_shell_gl.cc
@@ -392,7 +392,6 @@
 
 void VrShellGl::OnContentFrameAvailable() {
   content_surface_texture_->UpdateTexImage();
-  received_frame_ = true;
 }
 
 void VrShellGl::OnWebVRFrameAvailable() {
@@ -1287,25 +1286,23 @@
     OnWebVRFrameAvailable();
   }
 
-  base::TimeTicks now = base::TimeTicks::Now();
-  base::TimeTicks target;
-
   // Don't send VSyncs until we have a timebase/interval.
   if (vsync_interval_.is_zero())
     return;
 
+  base::TimeTicks now = base::TimeTicks::Now();
+  base::TimeTicks target;
   target = now + vsync_interval_;
   int64_t intervals = (target - vsync_timebase_) / vsync_interval_;
   target = vsync_timebase_ + intervals * vsync_interval_;
   task_runner_->PostDelayedTask(FROM_HERE, vsync_task_.callback(),
                                 target - now);
-
-  base::TimeDelta time = intervals * vsync_interval_;
+  base::TimeDelta current = target - vsync_interval_ - base::TimeTicks();
   if (!callback_.is_null()) {
-    SendVSync(time, base::ResetAndReturn(&callback_));
+    SendVSync(current, base::ResetAndReturn(&callback_));
   } else {
     pending_vsync_ = true;
-    pending_time_ = time;
+    pending_time_ = current;
   }
   if (!ShouldDrawWebVr()) {
     DrawFrame(-1);
@@ -1330,13 +1327,15 @@
   SendVSync(pending_time_, std::move(callback));
 }
 
-void VrShellGl::UpdateVSyncInterval(int64_t timebase_nanos,
-                                    double interval_seconds) {
-  vsync_timebase_ = base::TimeTicks();
-  vsync_timebase_ += base::TimeDelta::FromMicroseconds(timebase_nanos / 1000);
-  vsync_interval_ = base::TimeDelta::FromSecondsD(interval_seconds);
-  vsync_task_.Reset(base::Bind(&VrShellGl::OnVSync, base::Unretained(this)));
-  OnVSync();
+void VrShellGl::UpdateVSyncInterval(base::TimeTicks vsync_timebase,
+                                    base::TimeDelta vsync_interval) {
+  bool needs_init = vsync_timebase_ == base::TimeTicks();
+  vsync_timebase_ = vsync_timebase;
+  vsync_interval_ = vsync_interval;
+  if (needs_init) {
+    vsync_task_.Reset(base::Bind(&VrShellGl::OnVSync, base::Unretained(this)));
+    OnVSync();
+  }
 }
 
 void VrShellGl::ForceExitVr() {
diff --git a/chrome/browser/android/vr_shell/vr_shell_gl.h b/chrome/browser/android/vr_shell/vr_shell_gl.h
index 802d24b..8977e81 100644
--- a/chrome/browser/android/vr_shell/vr_shell_gl.h
+++ b/chrome/browser/android/vr_shell/vr_shell_gl.h
@@ -100,7 +100,8 @@
 
   void SetControllerModel(std::unique_ptr<VrControllerModel> model);
 
-  void UpdateVSyncInterval(int64_t timebase_nanos, double interval_seconds);
+  void UpdateVSyncInterval(base::TimeTicks vsync_timebase,
+                           base::TimeDelta vsync_interval);
 
   void CreateVRDisplayInfo(
       const base::Callback<void(device::mojom::VRDisplayInfoPtr)>& callback,
@@ -259,7 +260,6 @@
   base::TimeDelta pending_time_;
   bool pending_vsync_ = false;
   GetVSyncCallback callback_;
-  bool received_frame_ = false;
   mojo::Binding<device::mojom::VRPresentationProvider> binding_;
   device::mojom::VRSubmitFrameClientPtr submit_client_;
 
diff --git a/chrome/browser/autocomplete/autocomplete_browsertest.cc b/chrome/browser/autocomplete/autocomplete_browsertest.cc
index 795186d..deff8885 100644
--- a/chrome/browser/autocomplete/autocomplete_browsertest.cc
+++ b/chrome/browser/autocomplete/autocomplete_browsertest.cc
@@ -111,14 +111,14 @@
 
   EXPECT_FALSE(location_bar->GetDestinationURL().is_valid());
   EXPECT_EQ(base::UTF8ToUTF16(url::kAboutBlankURL), omnibox_view->GetText());
-  EXPECT_TRUE(omnibox_view->IsSelectAll());
+  EXPECT_FALSE(omnibox_view->IsSelectAll());
 
   omnibox_view->SetUserText(base::ASCIIToUTF16("chrome"));
   location_bar->Revert();
 
   EXPECT_FALSE(location_bar->GetDestinationURL().is_valid());
   EXPECT_EQ(base::UTF8ToUTF16(url::kAboutBlankURL), omnibox_view->GetText());
-  EXPECT_TRUE(omnibox_view->IsSelectAll());
+  EXPECT_FALSE(omnibox_view->IsSelectAll());
 }
 
 // Autocomplete test is flaky on ChromeOS.
@@ -163,7 +163,7 @@
     location_bar->Revert();
     EXPECT_FALSE(location_bar->GetDestinationURL().is_valid());
     EXPECT_EQ(base::UTF8ToUTF16(url::kAboutBlankURL), omnibox_view->GetText());
-    EXPECT_TRUE(omnibox_view->IsSelectAll());
+    EXPECT_FALSE(omnibox_view->IsSelectAll());
     const AutocompleteResult& result = autocomplete_controller->result();
     EXPECT_TRUE(result.empty()) << AutocompleteResultAsString(result);
   }
diff --git a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
index f5f29cc..8b95017a 100644
--- a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
+++ b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
@@ -6,6 +6,7 @@
 
 #include <stddef.h>
 
+#include "base/bind.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/utf_string_conversions.h"
@@ -31,6 +32,8 @@
 #include "components/prefs/pref_service.h"
 #include "components/sync/driver/sync_service_utils.h"
 #include "content/public/browser/notification_service.h"
+#include "content/public/browser/service_worker_context.h"
+#include "content/public/browser/storage_partition.h"
 #include "extensions/features/features.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 
@@ -38,9 +41,9 @@
 #include "chrome/browser/autocomplete/keyword_extensions_delegate_impl.h"
 #endif
 
-#if !defined(OS_ANDROID)
 namespace {
 
+#if !defined(OS_ANDROID)
 // This list should be kept in sync with chrome/common/url_constants.h.
 // Only include useful sub-pages, confirmation alerts are not useful.
 const char* const kChromeSettingsSubPages[] = {
@@ -58,9 +61,13 @@
     chrome::kManageProfileSubPage,
 #endif
 };
+#endif  // !defined(OS_ANDROID)
+
+// A callback that does nothing, called after the search service worker is
+// started.
+void NoopCallback(content::StartServiceWorkerForNavigationHintResult) {}
 
 }  // namespace
-#endif  // !defined(OS_ANDROID)
 
 ChromeAutocompleteProviderClient::ChromeAutocompleteProviderClient(
     Profile* profile)
@@ -284,6 +291,21 @@
   image_service->Prefetch(url, traffic_annotation);
 }
 
+void ChromeAutocompleteProviderClient::StartServiceWorker(
+    const GURL& destination_url) {
+  content::StoragePartition* partition =
+      content::BrowserContext::GetDefaultStoragePartition(profile_);
+  if (!partition)
+    return;
+
+  content::ServiceWorkerContext* context = partition->GetServiceWorkerContext();
+  if (!context)
+    return;
+
+  context->StartServiceWorkerForNavigationHint(destination_url,
+                                               base::Bind(&NoopCallback));
+}
+
 void ChromeAutocompleteProviderClient::OnAutocompleteControllerResultReady(
     AutocompleteController* controller) {
   content::NotificationService::current()->Notify(
diff --git a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.h b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.h
index c5b284e0..6ac6792 100644
--- a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.h
+++ b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.h
@@ -53,6 +53,7 @@
       history::KeywordID keyword_id,
       const base::string16& term) override;
   void PrefetchImage(const GURL& url) override;
+  void StartServiceWorker(const GURL& destination_url) override;
   void OnAutocompleteControllerResultReady(
       AutocompleteController* controller) override;
 
diff --git a/chrome/browser/browsing_data/browsing_data_cookie_helper.cc b/chrome/browser/browsing_data/browsing_data_cookie_helper.cc
index 40d9dc85..eb09819 100644
--- a/chrome/browser/browsing_data/browsing_data_cookie_helper.cc
+++ b/chrome/browser/browsing_data/browsing_data_cookie_helper.cc
@@ -68,8 +68,9 @@
     const FetchCallback& callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(!callback.is_null());
-  request_context_getter_->GetURLRequestContext()->cookie_store()->
-      GetAllCookiesAsync(base::Bind(&OnFetchComplete, callback));
+  request_context_getter_->GetURLRequestContext()
+      ->cookie_store()
+      ->GetAllCookiesAsync(base::BindOnce(&OnFetchComplete, callback));
 }
 
 void BrowsingDataCookieHelper::DeleteCookieOnIOThread(
diff --git a/chrome/browser/browsing_data/browsing_data_quota_helper_impl.cc b/chrome/browser/browsing_data/browsing_data_quota_helper_impl.cc
index dceb140..7230d80b 100644
--- a/chrome/browser/browsing_data/browsing_data_quota_helper_impl.cc
+++ b/chrome/browser/browsing_data/browsing_data_quota_helper_impl.cc
@@ -71,9 +71,9 @@
   PendingHosts* pending_hosts = new PendingHosts();
   base::Closure completion = base::BarrierClosure(
       arraysize(types),
-      base::Bind(&BrowsingDataQuotaHelperImpl::OnGetOriginsComplete,
-                 weak_factory_.GetWeakPtr(), callback,
-                 base::Owned(pending_hosts)));
+      base::BindOnce(&BrowsingDataQuotaHelperImpl::OnGetOriginsComplete,
+                     weak_factory_.GetWeakPtr(), callback,
+                     base::Owned(pending_hosts)));
 
   for (const storage::StorageType& type : types) {
     quota_manager_->GetOriginsModifiedSince(
@@ -105,9 +105,9 @@
   QuotaInfoMap* quota_info = new QuotaInfoMap();
   base::Closure completion = base::BarrierClosure(
       pending_hosts->size(),
-      base::Bind(&BrowsingDataQuotaHelperImpl::OnGetHostsUsageComplete,
-                 weak_factory_.GetWeakPtr(), callback,
-                 base::Owned(quota_info)));
+      base::BindOnce(&BrowsingDataQuotaHelperImpl::OnGetHostsUsageComplete,
+                     weak_factory_.GetWeakPtr(), callback,
+                     base::Owned(quota_info)));
 
   for (const auto& itr : *pending_hosts) {
     const std::string& host = itr.first;
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
index 6dc8dde..c1ffa1c3b 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
@@ -960,9 +960,10 @@
       base::WaitableEvent* event =
           plugin_data_remover_->StartRemoving(delete_begin_);
 
-      base::WaitableEventWatcher::EventCallback watcher_callback = base::Bind(
-          &ChromeBrowsingDataRemoverDelegate::OnWaitableEventSignaled,
-          weak_ptr_factory_.GetWeakPtr());
+      base::WaitableEventWatcher::EventCallback watcher_callback =
+          base::BindOnce(
+              &ChromeBrowsingDataRemoverDelegate::OnWaitableEventSignaled,
+              weak_ptr_factory_.GetWeakPtr());
       watcher_.StartWatching(event, std::move(watcher_callback));
     } else {
       // TODO(msramek): Store filters from the currently executed task on the
@@ -1059,9 +1060,9 @@
     clear_reporting_cache_.Start();
     BrowserThread::PostTaskAndReply(
         BrowserThread::IO, FROM_HERE,
-        base::Bind(&ClearReportingCacheOnIOThread,
-                   base::RetainedRef(std::move(context)), data_type_mask,
-                   filter),
+        base::BindOnce(&ClearReportingCacheOnIOThread,
+                       base::RetainedRef(std::move(context)), data_type_mask,
+                       filter),
         UIThreadTrampoline(clear_reporting_cache_.GetCompletionCallback()));
   }
 
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
index f19bb5e..05c1b04e 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
@@ -169,7 +169,7 @@
 void FakeDBusCall(const chromeos::BoolDBusMethodCallback& callback) {
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE,
-      base::Bind(callback, chromeos::DBUS_METHOD_CALL_SUCCESS, true));
+      base::BindOnce(callback, chromeos::DBUS_METHOD_CALL_SUCCESS, true));
 }
 #endif
 
@@ -185,8 +185,8 @@
     get_cookie_success_ = false;
     cookie_store_->GetCookiesWithOptionsAsync(
         kOrigin1, net::CookieOptions(),
-        base::Bind(&RemoveCookieTester::GetCookieCallback,
-                   base::Unretained(this)));
+        base::BindOnce(&RemoveCookieTester::GetCookieCallback,
+                       base::Unretained(this)));
     message_loop_runner->Run();
     return get_cookie_success_;
   }
@@ -197,8 +197,8 @@
     quit_closure_ = message_loop_runner->QuitClosure();
     cookie_store_->SetCookieWithOptionsAsync(
         kOrigin1, "A=1", net::CookieOptions(),
-        base::Bind(&RemoveCookieTester::SetCookieCallback,
-                   base::Unretained(this)));
+        base::BindOnce(&RemoveCookieTester::SetCookieCallback,
+                       base::Unretained(this)));
     message_loop_runner->Run();
   }
 
@@ -253,7 +253,7 @@
     net::URLRequestContext* request_context =
         sb_service->url_request_context()->GetURLRequestContext();
     request_context->cookie_store()->DeleteAllAsync(
-        base::Bind(&RunClosureAfterCookiesCleared, run_loop.QuitClosure()));
+        base::BindOnce(&RunClosureAfterCookiesCleared, run_loop.QuitClosure()));
     run_loop.Run();
 
     SetCookieStore(request_context->cookie_store());
diff --git a/chrome/browser/browsing_data/site_data_counting_helper.cc b/chrome/browser/browsing_data/site_data_counting_helper.cc
index 9002e69..9cb47f8 100644
--- a/chrome/browser/browsing_data/site_data_counting_helper.cc
+++ b/chrome/browser/browsing_data/site_data_counting_helper.cc
@@ -137,7 +137,7 @@
       rq_context->GetURLRequestContext()->cookie_store();
 
   if (cookie_store) {
-    cookie_store->GetAllCookiesAsync(base::Bind(
+    cookie_store->GetAllCookiesAsync(base::BindOnce(
         &SiteDataCountingHelper::GetCookiesCallback, base::Unretained(this)));
   } else {
     BrowserThread::PostTask(
diff --git a/chrome/browser/browsing_data/site_data_counting_helper_unittest.cc b/chrome/browser/browsing_data/site_data_counting_helper_unittest.cc
index 6938e5f..2c322369 100644
--- a/chrome/browser/browsing_data/site_data_counting_helper_unittest.cc
+++ b/chrome/browser/browsing_data/site_data_counting_helper_unittest.cc
@@ -122,8 +122,8 @@
           url, "name", "A=1", url.host(), url.path(), time, base::Time(), time,
           true, false, net::CookieSameSite::DEFAULT_MODE,
           net::COOKIE_PRIORITY_DEFAULT,
-          base::Bind(&SiteDataCountingHelperTest::DoneOnIOThread,
-                     base::Unretained(this)));
+          base::BindOnce(&SiteDataCountingHelperTest::DoneOnIOThread,
+                         base::Unretained(this)));
     }
   }
 
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 537f13d..528d989 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -842,6 +842,13 @@
   tab_helper->CreateChooserService(render_frame_host, std::move(request));
 }
 
+void CreateBudgetService(const service_manager::BindSourceInfo& source_info,
+                         blink::mojom::BudgetServiceRequest request,
+                         content::RenderFrameHost* render_frame_host) {
+  BudgetServiceImpl::Create(render_frame_host->GetProcess()->GetID(),
+                            source_info, std::move(request));
+}
+
 bool GetDataSaverEnabledPref(const PrefService* prefs) {
   // Enable data saver only when data saver pref is enabled and not part of
   // "Disabled" group of "SaveDataHeader" experiment.
@@ -3373,6 +3380,9 @@
 #elif defined(OS_LINUX) || defined(OS_WIN)
   frame_interfaces_->AddInterface(base::Bind(&ShareServiceImpl::Create));
 #endif
+
+  frame_interfaces_parameterized_->AddInterface(
+      base::Bind(&CreateBudgetService));
 }
 
 #if BUILDFLAG(ENABLE_WEBRTC)
diff --git a/chrome/browser/chrome_content_browser_manifest_overlay.json b/chrome/browser/chrome_content_browser_manifest_overlay.json
index aeb4716..df2c387 100644
--- a/chrome/browser/chrome_content_browser_manifest_overlay.json
+++ b/chrome/browser/chrome_content_browser_manifest_overlay.json
@@ -7,6 +7,7 @@
         "renderer": [
           "autofill::mojom::AutofillDriver",
           "autofill::mojom::PasswordManagerDriver",
+          "blink::mojom::BudgetService",
           "chrome::mojom::CacheStatsRecorder",
           "chrome::mojom::NetBenchmarking",
           "extensions::StashService",
@@ -51,6 +52,7 @@
         "renderer": [
           "autofill::mojom::AutofillDriver",
           "autofill::mojom::PasswordManagerDriver",
+          "blink::mojom::BudgetService",
           "blink::mojom::InstalledAppProvider",
           "blink::mojom::ShareService",
           "bluetooth::mojom::AdapterFactory",
diff --git a/chrome/browser/dom_distiller/distillable_page_utils_browsertest.cc b/chrome/browser/dom_distiller/distillable_page_utils_browsertest.cc
index 1234a84..3d5b2f0d 100644
--- a/chrome/browser/dom_distiller/distillable_page_utils_browsertest.cc
+++ b/chrome/browser/dom_distiller/distillable_page_utils_browsertest.cc
@@ -202,4 +202,26 @@
   }
 }
 
+using DistillablePageUtilsBrowserTestAllArticles =
+    DistillablePageUtilsBrowserTestOption<kAllArticles>;
+
+IN_PROC_BROWSER_TEST_F(DistillablePageUtilsBrowserTestAllArticles,
+                       TestDelegate) {
+  const char* paths[] = {kSimpleArticlePath, kSimpleArticleIFramePath};
+  for (unsigned i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i) {
+    testing::InSequence dummy;
+    EXPECT_CALL(holder_, OnResult(true, false)).Times(1);
+    EXPECT_CALL(holder_, OnResult(true, true))
+        .WillOnce(testing::InvokeWithoutArgs(QuitSoon));
+    NavigateAndWait(paths[i], 0);
+  }
+  {
+    testing::InSequence dummy;
+    EXPECT_CALL(holder_, OnResult(false, false)).Times(1);
+    EXPECT_CALL(holder_, OnResult(false, true))
+        .WillOnce(testing::InvokeWithoutArgs(QuitSoon));
+    NavigateAndWait(kNonArticlePath, 0);
+  }
+}
+
 }  // namespace dom_distiller
diff --git a/chrome/browser/feedback/system_logs/system_logs_fetcher.cc b/chrome/browser/feedback/system_logs/system_logs_fetcher.cc
index fe9e2ba..2705636 100644
--- a/chrome/browser/feedback/system_logs/system_logs_fetcher.cc
+++ b/chrome/browser/feedback/system_logs/system_logs_fetcher.cc
@@ -38,11 +38,6 @@
 
 }  // namespace
 
-SystemLogsSource::SystemLogsSource(const std::string& source_name)
-    : source_name_(source_name) {}
-
-SystemLogsSource::~SystemLogsSource() {}
-
 SystemLogsFetcher::SystemLogsFetcher(bool scrub_data)
     : response_(base::MakeUnique<SystemLogsResponse>()),
       num_pending_requests_(0),
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 0fb9012c..912f646ce 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -92,6 +92,14 @@
     R"*("Learning" means that only the database construction is enabled, )*"
     R"*("Prefetching" that learning and prefetching are enabled.)*";
 
+const char kSpeculativeServiceWorkerStartOnQueryInputName[] =
+    "Enable speculative start of a service worker when a search is predicted.";
+
+const char kSpeculativeServiceWorkerStartOnQueryInputDescription[] =
+    "If enabled, when the user enters text in the omnibox that looks like a "
+    "a query, any service worker associated with the search engine the query "
+    "will be sent to is started early.";
+
 const char kOffMainThreadFetchName[] = "Off-main-thread fetch for Web Workers";
 
 const char kOffMainThreadFetchDescription[] =
@@ -1939,11 +1947,13 @@
 const char kReaderModeHeuristicsName[] = "Reader Mode triggering";
 
 const char kReaderModeHeuristicsDescription[] =
-    "Determines what pages the Reader Mode button is shown on.";
+    "Determines what pages the Reader Mode infobar is shown on.";
 
 const char kReaderModeHeuristicsMarkup[] = "With article structured markup";
 
-const char kReaderModeHeuristicsAdaboost[] = "Appears to be an article";
+const char kReaderModeHeuristicsAdaboost[] = "Non-mobile-friendly articles";
+
+const char kReaderModeHeuristicsAllArticles[] = "All articles";
 
 const char kReaderModeHeuristicsAlwaysOff[] = "Never";
 
@@ -2397,14 +2407,6 @@
     "allows to override the source used to retrieve these server-side "
     "suggestions.";
 
-const char kEnableNtpRecentOfflineTabSuggestionsName[] =
-    "Show recent offline tabs on the New Tab page";
-
-const char kEnableNtpRecentOfflineTabSuggestionsDescription[] =
-    "If enabled, the list of content suggestions on the New Tab page will "
-    "contain pages that were captured offline during browsing (see "
-    "#offlining-recent-pages)";
-
 const char kEnableNtpAssetDownloadSuggestionsName[] =
     "Show asset downloads on the New Tab page";
 
@@ -2427,14 +2429,6 @@
     "If enabled, the list of content suggestions on the New Tab page will "
     "contain recently visited bookmarks.";
 
-const char kEnableNtpPhysicalWebPageSuggestionsName[] =
-    "Show Physical Web pages on the New Tab page";
-
-const char kEnableNtpPhysicalWebPageSuggestionsDescription[] =
-    "If enabled, the list of content suggestions on the New Tab page will "
-    "contain pages that are available through Physical Web (see "
-    "#enable-physical-web)";
-
 const char kEnableNtpForeignSessionsSuggestionsName[] =
     "Show recent foreign tabs on the New Tab page";
 
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 916041f..b449e74 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -687,6 +687,9 @@
 extern const char kSpeculativePrefetchName[];
 extern const char kSpeculativePrefetchDescription[];
 
+extern const char kSpeculativeServiceWorkerStartOnQueryInputName[];
+extern const char kSpeculativeServiceWorkerStartOnQueryInputDescription[];
+
 extern const char kSpellingFeedbackFieldTrialName[];
 extern const char kSpellingFeedbackFieldTrialDescription[];
 
@@ -1107,6 +1110,7 @@
 extern const char kReaderModeHeuristicsDescription[];
 extern const char kReaderModeHeuristicsMarkup[];
 extern const char kReaderModeHeuristicsAdaboost[];
+extern const char kReaderModeHeuristicsAllArticles[];
 extern const char kReaderModeHeuristicsAlwaysOff[];
 extern const char kReaderModeHeuristicsAlwaysOn[];
 
diff --git a/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc b/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc
index e13fda3..23e8ba8 100644
--- a/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc
+++ b/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc
@@ -397,6 +397,8 @@
     const GURL& url,
     const net::HostPortPair& host_port_pair,
     const content::GlobalRequestID& request_id,
+    int render_process_id,
+    int render_frame_id,
     ResourceType resource_type,
     bool is_download,
     bool was_cached,
@@ -441,11 +443,15 @@
         page_load_metrics::MetricsWebContentsObserver::FromWebContents(
             web_contents);
     if (metrics_observer) {
+      // Will be null for main or sub frame resources, when browser-side
+      // navigation is enabled.
+      content::RenderFrameHost* render_frame_host_or_null =
+          content::RenderFrameHost::FromID(render_process_id, render_frame_id);
       metrics_observer->OnRequestComplete(
           url, host_port_pair, frame_tree_node_id_getter.Run(), request_id,
-          resource_type, was_cached, std::move(data_reduction_proxy_data),
-          raw_body_bytes, original_content_length, request_creation_time,
-          net_error);
+          render_frame_host_or_null, resource_type, was_cached,
+          std::move(data_reduction_proxy_data), raw_body_bytes,
+          original_content_length, request_creation_time, net_error);
     }
   }
 }
@@ -925,22 +931,23 @@
 
   BrowserThread::PostTask(
       BrowserThread::UI, FROM_HERE,
-      base::BindOnce(&NotifyUIThreadOfRequestComplete,
-                     info->GetWebContentsGetterForRequest(),
-                     info->GetFrameTreeNodeIdGetterForRequest(),
-                     url_request->url(), request_host_port,
-                     info->GetGlobalRequestID(), info->GetResourceType(),
-                     info->IsDownload(), url_request->was_cached(),
-                     base::Passed(&data_reduction_proxy_data), net_error,
-                     url_request->GetTotalReceivedBytes(),
-                     url_request->GetRawBodyBytes(), original_content_length,
-                     url_request->creation_time(),
-                     base::TimeTicks::Now() - url_request->creation_time()));
+      base::BindOnce(
+          &NotifyUIThreadOfRequestComplete,
+          info->GetWebContentsGetterForRequest(),
+          info->GetFrameTreeNodeIdGetterForRequest(), url_request->url(),
+          request_host_port, info->GetGlobalRequestID(), info->GetChildID(),
+          info->GetRenderFrameID(), info->GetResourceType(), info->IsDownload(),
+          url_request->was_cached(), base::Passed(&data_reduction_proxy_data),
+          net_error, url_request->GetTotalReceivedBytes(),
+          url_request->GetRawBodyBytes(), original_content_length,
+          url_request->creation_time(),
+          base::TimeTicks::Now() - url_request->creation_time()));
 }
 
 content::PreviewsState ChromeResourceDispatcherHostDelegate::GetPreviewsState(
     const net::URLRequest& url_request,
-    content::ResourceContext* resource_context) {
+    content::ResourceContext* resource_context,
+    content::PreviewsState previews_to_allow) {
   ProfileIOData* io_data = ProfileIOData::FromResourceContext(resource_context);
   data_reduction_proxy::DataReductionProxyIOData* data_reduction_proxy_io_data =
       io_data->data_reduction_proxy_io_data();
@@ -973,6 +980,15 @@
 
   if (previews_state == content::PREVIEWS_UNSPECIFIED)
     return content::PREVIEWS_OFF;
+
+  // If the allowed previews are limited, ensure we honor those limits.
+  if (previews_state != content::PREVIEWS_OFF &&
+      previews_state != content::PREVIEWS_NO_TRANSFORM) {
+    previews_state = previews_state & previews_to_allow;
+    // If no valid previews are left, set the state explictly to PREVIEWS_OFF.
+    if (previews_state == 0)
+      previews_state = content::PREVIEWS_OFF;
+  }
   return previews_state;
 }
 
diff --git a/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.h b/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.h
index 9511eee4..dfe215f 100644
--- a/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.h
+++ b/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.h
@@ -87,7 +87,8 @@
   // Returns a bitmask of potentially several Previews optimizations.
   content::PreviewsState GetPreviewsState(
       const net::URLRequest& url_request,
-      content::ResourceContext* resource_context) override;
+      content::ResourceContext* resource_context,
+      content::PreviewsState previews_to_allow) override;
   content::NavigationData* GetNavigationData(
       net::URLRequest* request) const override;
   std::unique_ptr<net::ClientCertStore> CreateClientCertStore(
diff --git a/chrome/browser/media/BUILD.gn b/chrome/browser/media/BUILD.gn
deleted file mode 100644
index 6f1a75b..0000000
--- a/chrome/browser/media/BUILD.gn
+++ /dev/null
@@ -1,23 +0,0 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-# TODO(miu): Decentralize build configuration: Move source files and deps
-# specific to chrome/browser/media sources from chrome/browser/BULID.gn and
-# chrome/test/BUILD.gn into this BUILD.gn file. Also, remove the root BUILD.gn
-# dependency on the cast_remoting_connector_fuzzer target after this is done.
-
-import("//testing/libfuzzer/fuzzer_test.gni")
-
-fuzzer_test("cast_remoting_connector_fuzzer") {
-  sources = [
-    "cast_remoting_connector_fuzzertest.cc",
-    "cast_remoting_connector_messaging.cc",
-    "cast_remoting_connector_messaging.h",
-  ]
-  deps = [
-    "//base",
-  ]
-  libfuzzer_options = [ "max_len = 128" ]
-  seed_corpus = "../../test/data/cast/cast_remoting_connector_fuzz_corpus"
-}
diff --git a/chrome/browser/media/cast_remoting_connector.cc b/chrome/browser/media/cast_remoting_connector.cc
index 16b3d5d..9d2388c 100644
--- a/chrome/browser/media/cast_remoting_connector.cc
+++ b/chrome/browser/media/cast_remoting_connector.cc
@@ -10,14 +10,11 @@
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/stringprintf.h"
-#include "chrome/browser/media/cast_remoting_connector_messaging.h"
 #include "chrome/browser/media/cast_remoting_sender.h"
 #include "chrome/browser/media/router/media_router.h"
 #include "chrome/browser/media/router/media_router_factory.h"
-#include "chrome/browser/media/router/route_message_observer.h"
 #include "chrome/browser/sessions/session_tab_helper.h"
 #include "chrome/common/chrome_features.h"
-#include "chrome/common/media_router/media_source_helper.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
@@ -29,8 +26,6 @@
 using media::mojom::RemotingStartFailReason;
 using media::mojom::RemotingStopReason;
 
-using Messaging = CastRemotingConnectorMessaging;
-
 class CastRemotingConnector::RemotingBridge : public media::mojom::Remoter {
  public:
   // Constructs a "bridge" to delegate calls between the given |source| and
@@ -111,25 +106,6 @@
   DISALLOW_COPY_AND_ASSIGN(RemotingBridge);
 };
 
-class CastRemotingConnector::MessageObserver
-    : public media_router::RouteMessageObserver {
- public:
-  MessageObserver(media_router::MediaRouter* router,
-                  const media_router::MediaRoute::Id& route_id,
-                  CastRemotingConnector* connector)
-      : RouteMessageObserver(router, route_id), connector_(connector) {}
-  ~MessageObserver() final {}
-
- private:
-  void OnMessagesReceived(
-      const std::vector<content::PresentationConnectionMessage>& messages)
-      final {
-    connector_->ProcessMessagesFromRoute(messages);
-  }
-
-  CastRemotingConnector* const connector_;
-};
-
 // static
 const void* const CastRemotingConnector::kUserDataKey = &kUserDataKey;
 
@@ -140,12 +116,15 @@
   CastRemotingConnector* connector =
       static_cast<CastRemotingConnector*>(contents->GetUserData(kUserDataKey));
   if (!connector) {
+    // TODO(xjz): Use TabAndroid::GetAndroidId() to get the tab ID when support
+    // remoting on Android.
+    const SessionID::id_type tab_id = SessionTabHelper::IdForTab(contents);
+    if (tab_id == -1)
+      return nullptr;
     connector = new CastRemotingConnector(
         media_router::MediaRouterFactory::GetApiForBrowserContext(
             contents->GetBrowserContext()),
-        media_router::MediaSourceForTabContentRemoting(
-            SessionTabHelper::IdForTab(contents))
-            .id());
+        tab_id);
     contents->SetUserData(kUserDataKey, base::WrapUnique(connector));
   }
   return connector;
@@ -160,8 +139,10 @@
   auto* const contents = content::WebContents::FromRenderFrameHost(host);
   if (!contents)
     return;
-  CastRemotingConnector::Get(contents)->CreateBridge(std::move(source),
-                                                     std::move(request));
+  CastRemotingConnector* const connector = CastRemotingConnector::Get(contents);
+  if (!connector)
+    return;
+  connector->CreateBridge(std::move(source), std::move(request));
 }
 
 namespace {
@@ -175,15 +156,17 @@
 }
 }  // namespace
 
-CastRemotingConnector::CastRemotingConnector(
-    media_router::MediaRouter* router,
-    const media_router::MediaSource::Id& media_source_id)
-    : media_router::MediaRoutesObserver(router),
-      media_source_id_(media_source_id),
+CastRemotingConnector::CastRemotingConnector(media_router::MediaRouter* router,
+                                             int32_t tab_id)
+    : media_router_(router),
+      tab_id_(tab_id),
       enabled_features_(GetFeatureEnabledCapabilities()),
-      session_counter_(0),
       active_bridge_(nullptr),
-      weak_factory_(this) {}
+      binding_(this),
+      weak_factory_(this) {
+  VLOG(2) << "Register CastRemotingConnector for tab_id = " << tab_id_;
+  media_router_->RegisterRemotingSource(tab_id_, this);
+}
 
 CastRemotingConnector::~CastRemotingConnector() {
   // Assume nothing about destruction/shutdown sequence of a tab. For example,
@@ -195,6 +178,38 @@
     notifyee->OnSinkGone();
     notifyee->OnCastRemotingConnectorDestroyed();
   }
+  media_router_->UnregisterRemotingSource(tab_id_);
+}
+
+void CastRemotingConnector::ConnectToService(
+    media::mojom::MirrorServiceRemotingSourceRequest source_request,
+    media::mojom::MirrorServiceRemoterPtr remoter) {
+  DCHECK(!binding_.is_bound());
+  DCHECK(!remoter_);
+  DCHECK(remoter);
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  VLOG(2) << __func__;
+
+  binding_.Bind(std::move(source_request));
+  binding_.set_connection_error_handler(base::Bind(
+      &CastRemotingConnector::OnMirrorServiceStopped, base::Unretained(this)));
+  remoter_ = std::move(remoter);
+  remoter_.set_connection_error_handler(base::Bind(
+      &CastRemotingConnector::OnMirrorServiceStopped, base::Unretained(this)));
+}
+
+void CastRemotingConnector::OnMirrorServiceStopped() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  VLOG(2) << __func__;
+
+  if (binding_.is_bound())
+    binding_.Close();
+  remoter_.reset();
+
+  if (active_bridge_)
+    StopRemoting(active_bridge_, RemotingStopReason::SERVICE_GONE);
+  for (RemotingBridge* notifyee : bridges_)
+    notifyee->OnSinkGone();
 }
 
 void CastRemotingConnector::CreateBridge(media::mojom::RemotingSourcePtr source,
@@ -209,7 +224,8 @@
   DCHECK(bridges_.find(bridge) == bridges_.end());
 
   bridges_.insert(bridge);
-  if (message_observer_ && !active_bridge_)
+  // TODO(xjz): Pass the receiver's capabilities to the source.
+  if (remoter_ && !active_bridge_)
     bridge->OnSinkAvailable(enabled_features_);
 }
 
@@ -226,14 +242,17 @@
 void CastRemotingConnector::StartRemoting(RemotingBridge* bridge) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(bridges_.find(bridge) != bridges_.end());
+  VLOG(2) << __func__;
 
   // Refuse to start if there is no remoting route available, or if remoting is
   // already active.
-  if (!message_observer_) {
-    bridge->OnStartFailed(RemotingStartFailReason::ROUTE_TERMINATED);
+  if (!remoter_) {
+    VLOG(2) << "Remoting start failed: No mirror service connected.";
+    bridge->OnStartFailed(RemotingStartFailReason::SERVICE_NOT_CONNECTED);
     return;
   }
   if (active_bridge_) {
+    VLOG(2) << "Remoting start failed: Cannot start multiple.";
     bridge->OnStartFailed(RemotingStartFailReason::CANNOT_START_MULTIPLE);
     return;
   }
@@ -249,12 +268,10 @@
   }
 
   active_bridge_ = bridge;
+  remoter_->Start();
 
-  // Send a start message to the Cast Provider.
-  ++session_counter_;  // New remoting session ID.
-  SendMessageToProvider(base::StringPrintf(
-      Messaging::kStartRemotingMessageFormat, session_counter_));
-
+  // Assume the remoting session is always started successfully. If any failure
+  // occurs, OnError() will be called.
   bridge->OnStarted();
 }
 
@@ -265,10 +282,11 @@
     media::mojom::RemotingDataStreamSenderRequest audio_sender_request,
     media::mojom::RemotingDataStreamSenderRequest video_sender_request) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  VLOG(2) << __func__;
 
   // Refuse to start if there is no remoting route available, or if remoting is
   // not active for this |bridge|.
-  if (!message_observer_ || active_bridge_ != bridge)
+  if (!remoter_ || active_bridge_ != bridge)
     return;
   // Also, if neither audio nor video pipe was provided, or if a request for a
   // RemotingDataStreamSender was not provided for a data pipe, error-out early.
@@ -279,39 +297,57 @@
     return;
   }
 
-  // Hold on to the data pipe handles and interface requests until one/both
-  // CastRemotingSenders are created and ready for use.
-  pending_audio_pipe_ = std::move(audio_pipe);
-  pending_video_pipe_ = std::move(video_pipe);
-  pending_audio_sender_request_ = std::move(audio_sender_request);
-  pending_video_sender_request_ = std::move(video_sender_request);
+  const bool want_audio = audio_sender_request.is_pending();
+  const bool want_video = video_sender_request.is_pending();
+  remoter_->StartDataStreams(
+      want_audio, want_video,
+      base::BindOnce(&CastRemotingConnector::OnDataStreamsStarted,
+                     weak_factory_.GetWeakPtr(), std::move(audio_pipe),
+                     std::move(video_pipe), std::move(audio_sender_request),
+                     std::move(video_sender_request)));
+}
 
-  // Send a "start streams" message to the Cast Provider. The provider is
-  // responsible for creating and setting up a remoting Cast Streaming session
-  // that will result in new CastRemotingSender instances being created here in
-  // the browser process.
-  SendMessageToProvider(base::StringPrintf(
-      Messaging::kStartStreamsMessageFormat, session_counter_,
-      pending_audio_sender_request_.is_pending() ? 'Y' : 'N',
-      pending_video_sender_request_.is_pending() ? 'Y' : 'N'));
+void CastRemotingConnector::OnDataStreamsStarted(
+    mojo::ScopedDataPipeConsumerHandle audio_pipe,
+    mojo::ScopedDataPipeConsumerHandle video_pipe,
+    media::mojom::RemotingDataStreamSenderRequest audio_sender_request,
+    media::mojom::RemotingDataStreamSenderRequest video_sender_request,
+    int32_t audio_stream_id,
+    int32_t video_stream_id) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK(remoter_);
+  VLOG(2) << __func__ << ": audio_stream_id = " << audio_stream_id
+          << " video_stream_id = " << video_stream_id;
+
+  if (!active_bridge_) {
+    remoter_->Stop(media::mojom::RemotingStopReason::SOURCE_GONE);
+    return;
+  }
+
+  if (audio_sender_request.is_pending() && audio_stream_id > -1) {
+    cast::CastRemotingSender::FindAndBind(
+        audio_stream_id, std::move(audio_pipe), std::move(audio_sender_request),
+        base::Bind(&CastRemotingConnector::OnDataSendFailed,
+                   weak_factory_.GetWeakPtr()));
+  }
+  if (video_sender_request.is_pending() && video_stream_id > -1) {
+    cast::CastRemotingSender::FindAndBind(
+        video_stream_id, std::move(video_pipe), std::move(video_sender_request),
+        base::Bind(&CastRemotingConnector::OnDataSendFailed,
+                   weak_factory_.GetWeakPtr()));
+  }
 }
 
 void CastRemotingConnector::StopRemoting(RemotingBridge* bridge,
                                          RemotingStopReason reason) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  VLOG(2) << __func__ << ": reason = " << reason;
 
   if (active_bridge_ != bridge)
     return;
 
   active_bridge_ = nullptr;
 
-  // Explicitly close the data pipes (and related requests) just in case the
-  // "start streams" operation was interrupted.
-  pending_audio_pipe_.reset();
-  pending_video_pipe_.reset();
-  pending_audio_sender_request_ = nullptr;
-  pending_video_sender_request_ = nullptr;
-
   // Cancel all outstanding callbacks related to the remoting session.
   weak_factory_.InvalidateWeakPtrs();
 
@@ -320,181 +356,75 @@
   bridge->OnSinkGone();
   // Note: At this point, all sources should think the sink is gone.
 
-  SendMessageToProvider(base::StringPrintf(
-      Messaging::kStopRemotingMessageFormat, session_counter_));
-  // Note: Once the Cast Provider sends back an acknowledgement message, all
-  // sources will be notified that the remoting sink is available again.
+  if (remoter_)
+    remoter_->Stop(reason);
 
   bridge->OnStopped(reason);
 }
 
+void CastRemotingConnector::OnStopped(RemotingStopReason reason) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  VLOG(2) << __func__ << ": reason = " << reason;
+
+  if (!active_bridge_)
+    return;
+  StopRemoting(active_bridge_, reason);
+}
+
 void CastRemotingConnector::SendMessageToSink(
     RemotingBridge* bridge, const std::vector<uint8_t>& message) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   // During an active remoting session, simply pass all binary messages through
   // to the sink.
-  if (!message_observer_ || active_bridge_ != bridge)
+  if (!remoter_ || active_bridge_ != bridge)
     return;
-  media_router::MediaRoutesObserver::router()->SendRouteBinaryMessage(
-      message_observer_->route_id(),
-      base::MakeUnique<std::vector<uint8_t>>(message),
-      base::Bind(&CastRemotingConnector::HandleSendMessageResult,
-                 weak_factory_.GetWeakPtr()));
+  remoter_->SendMessageToSink(message);
 }
 
-void CastRemotingConnector::SendMessageToProvider(const std::string& message) {
+void CastRemotingConnector::OnMessageFromSink(
+    const std::vector<uint8_t>& message) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-  if (!message_observer_)
+  // During an active remoting session, simply pass all binary messages through
+  // to the source.
+  if (!active_bridge_)
     return;
-
-  if (active_bridge_) {
-    media_router::MediaRoutesObserver::router()->SendRouteMessage(
-        message_observer_->route_id(), message,
-        base::Bind(&CastRemotingConnector::HandleSendMessageResult,
-                   weak_factory_.GetWeakPtr()));
-  } else {
-    struct Helper {
-      static void IgnoreSendMessageResult(bool ignored) {}
-    };
-    media_router::MediaRoutesObserver::router()->SendRouteMessage(
-        message_observer_->route_id(), message,
-        base::Bind(&Helper::IgnoreSendMessageResult));
-  }
+  active_bridge_->OnMessageFromSink(message);
 }
 
-void CastRemotingConnector::ProcessMessagesFromRoute(
-    const std::vector<content::PresentationConnectionMessage>& messages) {
+void CastRemotingConnector::OnSinkAvailable(
+    media::mojom::SinkCapabilitiesPtr capabilities) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  VLOG(2) << __func__;
 
-  // Note: If any calls to message parsing functions are added/changed here,
-  // please update cast_remoting_connector_fuzzertest.cc as well!
-
-  for (const auto& message : messages) {
-    if (message.is_binary()) {
-      // All binary messages are passed through to the source during an active
-      // remoting session.
-      if (active_bridge_)
-        active_bridge_->OnMessageFromSink(*message.data);
-
-      continue;
-    }
-
-    // This is a notification message from the Cast Provider, about the
-    // execution state of the media remoting session between Chrome and the
-    // remote device.
-
-    // If this is a "start streams" acknowledgement message, the
-    // CastRemotingSenders should now be available to begin consuming from
-    // the data pipes.
-    if (active_bridge_ &&
-        Messaging::IsMessageForSession(
-            *message.message, Messaging::kStartedStreamsMessageFormatPartial,
-            session_counter_)) {
-      if (pending_audio_sender_request_.is_pending()) {
-        cast::CastRemotingSender::FindAndBind(
-            Messaging::GetStreamIdFromStartedMessage(
-                *message.message,
-                Messaging::kStartedStreamsMessageAudioIdSpecifier),
-            std::move(pending_audio_pipe_),
-            std::move(pending_audio_sender_request_),
-            base::Bind(&CastRemotingConnector::OnDataSendFailed,
-                       weak_factory_.GetWeakPtr()));
-      }
-      if (pending_video_sender_request_.is_pending()) {
-        cast::CastRemotingSender::FindAndBind(
-            Messaging::GetStreamIdFromStartedMessage(
-                *message.message,
-                Messaging::kStartedStreamsMessageVideoIdSpecifier),
-            std::move(pending_video_pipe_),
-            std::move(pending_video_sender_request_),
-            base::Bind(&CastRemotingConnector::OnDataSendFailed,
-                       weak_factory_.GetWeakPtr()));
-      }
-    } else if (active_bridge_ &&
-               Messaging::IsMessageForSession(*message.message,
-                                              Messaging::kFailedMessageFormat,
-                                              session_counter_)) {
-      // If this is a failure message, call StopRemoting().
-      StopRemoting(active_bridge_, RemotingStopReason::UNEXPECTED_FAILURE);
-    } else if (Messaging::IsMessageForSession(*message.message,
-                                              Messaging::kStoppedMessageFormat,
-                                              session_counter_)) {
-      // If this is a stop acknowledgement message, indicating that the last
-      // session was stopped, notify all sources that the sink is once again
-      // available.
-      if (active_bridge_) {
-        // Hmm...The Cast Provider was in a state that disagrees with this
-        // connector. Attempt to resolve this by shutting everything down to
-        // effectively reset to a known state.
-        LOG(WARNING) << "BUG: Cast Provider sent 'stopped' message during "
-                        "an active remoting session.";
-        StopRemoting(active_bridge_, RemotingStopReason::UNEXPECTED_FAILURE);
-      }
-      for (RemotingBridge* notifyee : bridges_)
-        notifyee->OnSinkAvailable(enabled_features_);
-    } else {
-      LOG(WARNING) << "BUG: Unexpected message from Cast Provider: "
-                   << *message.message;
-    }
-  }
-}
-
-void CastRemotingConnector::HandleSendMessageResult(bool success) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  // A single message send failure is treated as fatal to an active remoting
+  // The receiver's capabilities should be unchanged during an active remoting
   // session.
-  if (!success && active_bridge_)
-    StopRemoting(active_bridge_, RemotingStopReason::MESSAGE_SEND_FAILED);
+  if (active_bridge_) {
+    LOG(WARNING) << "Unexpected OnSinkAvailable() call during an active"
+                 << "remoting session.";
+    return;
+  }
+
+  // TODO(xjz): Pass the receiver's capabilities to the sources.
+  for (RemotingBridge* notifyee : bridges_)
+    notifyee->OnSinkAvailable(enabled_features_);
+}
+
+void CastRemotingConnector::OnError() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  VLOG(2) << __func__;
+
+  if (active_bridge_)
+    StopRemoting(active_bridge_, RemotingStopReason::UNEXPECTED_FAILURE);
 }
 
 void CastRemotingConnector::OnDataSendFailed() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  VLOG(2) << __func__;
+
   // A single data send failure is treated as fatal to an active remoting
   // session.
   if (active_bridge_)
     StopRemoting(active_bridge_, RemotingStopReason::DATA_SEND_FAILED);
 }
-
-void CastRemotingConnector::OnRoutesUpdated(
-    const std::vector<media_router::MediaRoute>& routes,
-    const std::vector<media_router::MediaRoute::Id>& joinable_route_ids) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
-  // If a remoting route has already been identified, check that it still
-  // exists. Otherwise, shut down messaging and any active remoting, and notify
-  // the sources that remoting is no longer available.
-  if (message_observer_) {
-    for (const media_router::MediaRoute& route : routes) {
-      if (message_observer_->route_id() == route.media_route_id())
-        return;  // Remoting route still exists. Take no further action.
-    }
-    message_observer_.reset();
-    if (active_bridge_)
-      StopRemoting(active_bridge_, RemotingStopReason::ROUTE_TERMINATED);
-    for (RemotingBridge* notifyee : bridges_)
-      notifyee->OnSinkGone();
-  }
-
-  // There shouldn't be an active RemotingBridge at this point, since there is
-  // currently no known remoting route.
-  DCHECK(!active_bridge_);
-
-  // Scan |routes| for a new remoting route. If one is found, begin processing
-  // messages on the route, and notify the sources that remoting is now
-  // available.
-  for (const media_router::MediaRoute& route : routes) {
-    if (route.media_source().id() != media_source_id_)
-      continue;
-    message_observer_.reset(new MessageObserver(
-        media_router::MediaRoutesObserver::router(), route.media_route_id(),
-        this));
-    // TODO(miu): In the future, scan the route ID for sink capabilities
-    // properties and pass these to the source in the OnSinkAvailable()
-    // notification.
-    for (RemotingBridge* notifyee : bridges_)
-      notifyee->OnSinkAvailable(enabled_features_);
-    break;
-  }
-}
diff --git a/chrome/browser/media/cast_remoting_connector.h b/chrome/browser/media/cast_remoting_connector.h
index d5bba5883..5156885 100644
--- a/chrome/browser/media/cast_remoting_connector.h
+++ b/chrome/browser/media/cast_remoting_connector.h
@@ -9,13 +9,17 @@
 
 #include "base/memory/weak_ptr.h"
 #include "base/supports_user_data.h"
-#include "chrome/browser/media/router/media_routes_observer.h"
+#include "media/mojo/interfaces/mirror_service_remoting.mojom.h"
 #include "media/mojo/interfaces/remoting.mojom.h"
+#include "mojo/public/cpp/bindings/binding.h"
 
 namespace content {
 class RenderFrameHost;
 class WebContents;
-struct PresentationConnectionMessage;
+}  // namespace content
+
+namespace media_router {
+class MediaRouter;
 }
 
 // CastRemotingConnector connects a single source (a media element in a render
@@ -23,7 +27,12 @@
 // instance of a CastRemotingConnector per source WebContents (representing a
 // collection of render frames), and it is created on-demand. The source in the
 // render process represents itself by providing a media::mojom::RemotingSource
-// service instance. The sink is represented by the Media Router Cast Provider.
+// service instance. The sink is represented by a MediaRemoter in the Cast Media
+// Router Provider that handles the communication with the remote device. The
+// CastRemotingConnector and the MediaRemoter can communicate with each other
+// through the media::mojom::MirrorServiceRemoter and
+// media::mojom::MirrorServiceRemotingSource interfaces when a sink that is
+// capable of remoting is available.
 //
 // Whenever a candidate media source is created in a render frame,
 // ChromeContentBrowserClient will call CreateMediaRemoter() to instantiate a
@@ -33,43 +42,41 @@
 // notify when a sink becomes available for remoting, and to pass binary
 // messages from the sink back to the source.
 //
-// At any time before or after the CastRemotingConnector is created, the
-// Media Router Cast Provider may create a tab remoting media route. This
-// indicates that the provider has found a sink that is capable of remoting and
-// is available for use. At this point, CastRemotingConnector notifies all
-// RemotingSources that a sink is available, and some time later a
-// RemotingSource can request the start of a remoting session. Once the sink is
-// no longer available, the provider terminates the route and
-// CastRemotingConnector notifies all RemotingSources that the sink is gone.
+// When the CastRemotingConnector is created, it registers itself in the
+// media_router::MediaRouter with a tab ID that uniquely identifies it. When a
+// mirroring route is created and available for remoting, the Cast MRP will
+// create a MediaRemoter and notify MediaRouter, which notifies the
+// CastRemotingConnector registered under the tab ID being remoted. At this
+// point, the CastRemotingConnector can communicate with the MediaRemoter. When
+// CastRemotingConnector gets notified that a sink is available, it notifies all
+// RemotingSources, and some time later a RemotingSource can request the start
+// of a remoting session.
 //
 // Note that only one RemotingSource can remote media at a time. Therefore,
 // CastRemotingConnector must mediate among simultaneous requests for media
 // remoting, and only allow one at a time. Currently, the policy is "first come,
 // first served."
 //
-// When starting a remoting session, the CastRemotingConnector sends control
-// messages to the Cast Provider. These control messages direct the provider to
-// start/stop remoting with the remote device (e.g., launching apps or services
-// on the remote device). Among its other duties, the provider will also set up
-// a Cast Streaming session to provide a bitstream transport for the media. Once
-// this is done, the provider sends notification messages back to
-// CastRemotingConnector to acknowledge this. Then, CastRemotingConnector knows
-// it can look-up and pass the mojo data pipe handles to CastRemotingSenders,
-// and the remoting session will be fully active. The CastRemotingConnector is
-// responsible for passing small binary messages between the source and sink,
-// while the CastRemotingSender handles the high-volume media data transfer.
+// When starting a remoting session, the Cast MRP will also set up a Cast
+// Streaming session to provide a bitstream transport for the media. Once this
+// is done, the MediaRemoter notifies the CastRemotingConnector. Then,
+// CastRemotingConnector knows it can look-up and pass the mojo data pipe
+// handles to CastRemotingSenders, and the remoting session will be fully active
+// The CastRemotingConnector is responsible for passing small binary messages
+// between the source and sink, while the CastRemotingSender handles the
+// high-volume media data transfer.
 //
 // Please see the unit tests in cast_remoting_connector_unittest.cc as a
-// reference for how CastRemotingConnector and a Cast Provider interact to
+// reference for how CastRemotingConnector and a MediaRemoter interact to
 // start/execute/stop remoting sessions.
-class CastRemotingConnector
-    : public base::SupportsUserData::Data,
-      public media_router::MediaRoutesObserver {
+class CastRemotingConnector : public base::SupportsUserData::Data,
+                              public media::mojom::MirrorServiceRemotingSource {
  public:
   ~CastRemotingConnector() final;
 
   // Returns the instance of the CastRemotingConnector associated with
-  // |source_contents|, creating a new instance if needed.
+  // |source_contents|, creating a new instance if needed. Returns nullptr if
+  // |source_contents| doesn't have a valid tab ID.
   static CastRemotingConnector* Get(content::WebContents* source_contents);
 
   // Used by ChromeContentBrowserClient to request a binding to a new
@@ -78,6 +85,13 @@
                                  media::mojom::RemotingSourcePtr source,
                                  media::mojom::RemoterRequest request);
 
+  // Called when a MediaRemoter is created and started in the Cast MRP. This
+  // call connects the CastRemotingConnector with the MediaRemoter. Remoting
+  // sessions can only be started after this is called.
+  void ConnectToService(
+      media::mojom::MirrorServiceRemotingSourceRequest source_request,
+      media::mojom::MirrorServiceRemoterPtr remoter);
+
  private:
   // Allow unit tests access to the private constructor and CreateBridge()
   // method, since unit tests don't have a complete browser (i.e., with a
@@ -91,15 +105,9 @@
   // mojo message pipe.
   class RemotingBridge;
 
-  // A RouteMessageObserver for the remoting route that passes messages from the
-  // Cast Provider back to this connector. An instance of this class only exists
-  // while a remoting route is available, and is owned by CastRemotingConnector.
-  class MessageObserver;
-
-  // Main constructor. |media_source_id| refers to any remoted content managed
+  // Main constructor. |tab_id| refers to any remoted content managed
   // by this instance (i.e., any remoted content from one tab/WebContents).
-  CastRemotingConnector(media_router::MediaRouter* router,
-                        const media_router::MediaSource::Id& media_source_id);
+  CastRemotingConnector(media_router::MediaRouter* router, int32_t tab_id);
 
   // Creates a RemotingBridge that implements the requested Remoter service, and
   // binds it to the interface |request|.
@@ -113,6 +121,12 @@
   void DeregisterBridge(RemotingBridge* bridge,
                         media::mojom::RemotingStopReason reason);
 
+  // media::mojom::MirrorServiceRemotingSource implementations.
+  void OnSinkAvailable(media::mojom::SinkCapabilitiesPtr capabilities) override;
+  void OnMessageFromSink(const std::vector<uint8_t>& message) override;
+  void OnStopped(media::mojom::RemotingStopReason reason) override;
+  void OnError() override;
+
   // These methods are called by RemotingBridge to forward media::mojom::Remoter
   // calls from a source through to this connector. They ensure that only one
   // source is allowed to be in a remoting session at a time, and that no source
@@ -129,35 +143,25 @@
   void SendMessageToSink(RemotingBridge* bridge,
                          const std::vector<uint8_t>& message);
 
-  // Send a control message to the Cast Provider. This may or may not succeed,
-  // with the success status reported later via HandleSendMessageResult().
-  void SendMessageToProvider(const std::string& message);
+  // Called when RTP streams are started.
+  void OnDataStreamsStarted(
+      mojo::ScopedDataPipeConsumerHandle audio_pipe,
+      mojo::ScopedDataPipeConsumerHandle video_pipe,
+      media::mojom::RemotingDataStreamSenderRequest audio_sender_request,
+      media::mojom::RemotingDataStreamSenderRequest video_sender_request,
+      int32_t audio_stream_id,
+      int32_t video_stream_id);
 
-  // Called by the current MessageObserver to process messages observed on the
-  // remoting route. There are two types of messages: 1) text notification
-  // messages from the Cast Provider, to report on the current state of a
-  // remoting session between Chrome and the remote device, and 2) binary
-  // messages, to be passed directly to the active remoting source during a
-  // remoting session.
-  void ProcessMessagesFromRoute(
-      const std::vector<content::PresentationConnectionMessage>& messages);
-
-  // Error handlers for message/data sending during an active remoting
-  // session. When a failure occurs, these immediately force-stop remoting.
-  void HandleSendMessageResult(bool success);
+  // Error handlers for message sending during an active remoting session. When
+  // a failure occurs, these immediately force-stop remoting.
   void OnDataSendFailed();
 
-  // MediaRoutesObserver implementation: Scans |routes| to check whether the
-  // existing remoting route has gone away and/or there is a new remoting route
-  // established, and take the necessary actions to notify sources and/or
-  // shutdown an active remoting session.
-  void OnRoutesUpdated(
-      const std::vector<media_router::MediaRoute>& routes,
-      const std::vector<media_router::MediaRoute::Id>& ignored) final;
+  // Called when any connection error/lost occurs with the MediaRemoter.
+  void OnMirrorServiceStopped();
 
-  // The MediaSource ID referring to any remoted content managed by this
-  // CastRemotingConnector.
-  const media_router::MediaSource::Id media_source_id_;
+  media_router::MediaRouter* const media_router_;
+
+  const int32_t tab_id_;
 
   // Describes the sink's capabilities according to what has been enabled via
   // |features::kMediaRemoting|. These are controlled manually via
@@ -169,28 +173,12 @@
   // set.
   std::set<RemotingBridge*> bridges_;
 
-  // Created when the Media Router Cast Provider has created a media remoting
-  // route to a sink that supports remoting and is available for use. This
-  // observer simply dispatches messages from the Cast Provider and sink back to
-  // this connector. Once the route is gone, this is reset to null.
-  std::unique_ptr<MessageObserver> message_observer_;
-
-  // Incremented each time StartRemoting() is called, and used as a "current
-  // session ID" to ensure that control messaging between this connector and the
-  // Cast Provider are referring to the same remoting session. This allows both
-  // this connector and the provider to ignore stale messages.
-  unsigned int session_counter_;
-
   // When non-null, an active remoting session is taking place, with this
   // pointing to the RemotingBridge being used to communicate with the source.
   RemotingBridge* active_bridge_;
 
-  // These temporarily hold the mojo data pipe handles and interface requests
-  // until hand-off to the CastRemotingSenders.
-  mojo::ScopedDataPipeConsumerHandle pending_audio_pipe_;
-  mojo::ScopedDataPipeConsumerHandle pending_video_pipe_;
-  media::mojom::RemotingDataStreamSenderRequest pending_audio_sender_request_;
-  media::mojom::RemotingDataStreamSenderRequest pending_video_sender_request_;
+  mojo::Binding<media::mojom::MirrorServiceRemotingSource> binding_;
+  media::mojom::MirrorServiceRemoterPtr remoter_;
 
   // Produces weak pointers that are only valid for the current remoting
   // session. This is used to cancel any outstanding callbacks when a remoting
diff --git a/chrome/browser/media/cast_remoting_connector_messaging.cc b/chrome/browser/media/cast_remoting_connector_messaging.cc
deleted file mode 100644
index d55d6a5..0000000
--- a/chrome/browser/media/cast_remoting_connector_messaging.cc
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/media/cast_remoting_connector_messaging.h"
-
-#include <stdio.h>
-
-#include <limits>
-
-#include "base/strings/string_number_conversions.h"
-
-const char CastRemotingConnectorMessaging::kMessageFieldSeparator = ':';
-const char CastRemotingConnectorMessaging::kStartRemotingMessageFormat[] =
-    "START_CAST_REMOTING:session=%x";
-const char CastRemotingConnectorMessaging::kStartStreamsMessageFormat[] =
-    "START_CAST_REMOTING_STREAMS:session=%x:audio=%c:video=%c";
-const char
-CastRemotingConnectorMessaging::kStartedStreamsMessageFormatPartial[] =
-    "STARTED_CAST_REMOTING_STREAMS:session=%x";
-const char
-CastRemotingConnectorMessaging::kStartedStreamsMessageAudioIdSpecifier[] =
-    ":audio_stream_id=";
-const char
-CastRemotingConnectorMessaging::kStartedStreamsMessageVideoIdSpecifier[] =
-    ":video_stream_id=";
-const char CastRemotingConnectorMessaging::kStopRemotingMessageFormat[] =
-    "STOP_CAST_REMOTING:session=%x";
-const char CastRemotingConnectorMessaging::kStoppedMessageFormat[] =
-    "STOPPED_CAST_REMOTING:session=%x";
-const char CastRemotingConnectorMessaging::kFailedMessageFormat[] =
-    "FAILED_CAST_REMOTING:session=%x";
-
-// static
-bool CastRemotingConnectorMessaging::IsMessageForSession(
-    const std::string& message, const char* format,
-    unsigned int expected_session_id) {
-  unsigned int session_id;
-  if (sscanf(message.c_str(), format, &session_id) == 1)
-    return session_id == expected_session_id;
-  return false;
-}
-
-// static
-int32_t CastRemotingConnectorMessaging::GetStreamIdFromStartedMessage(
-    base::StringPiece message, base::StringPiece specifier) {
-  auto start = message.find(specifier);
-  if (start == std::string::npos)
-    return -1;
-  start += specifier.size();
-  if (start + 1 >= message.size())
-    return -1; // Must be at least one hex digit following the specifier.
-  const auto length = message.find(kMessageFieldSeparator, start) - start;
-  int parsed_value;
-  if (!base::HexStringToInt(message.substr(start, length), &parsed_value) ||
-      parsed_value < 0 ||
-      parsed_value > std::numeric_limits<int32_t>::max()) {
-    return -1; // Non-hex digits, or outside valid range.
-  }
-  return static_cast<int32_t>(parsed_value);
-}
diff --git a/chrome/browser/media/cast_remoting_connector_messaging.h b/chrome/browser/media/cast_remoting_connector_messaging.h
deleted file mode 100644
index dbe6ae6..0000000
--- a/chrome/browser/media/cast_remoting_connector_messaging.h
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_MEDIA_CAST_REMOTING_CONNECTOR_MESSAGING_H_
-#define CHROME_BROWSER_MEDIA_CAST_REMOTING_CONNECTOR_MESSAGING_H_
-
-#include <stdint.h>
-
-#include <string>
-
-#include "base/strings/string_piece.h"
-
-// Utility functions for parsing messages from the Cast Provider to
-// CastRemotingConnector. These have been broken-out into this separate module
-// to allow for efficient building, linking and execution of these routines for
-// fuzzer testing.
-//
-// Note: If any additions/changes are made here, please update
-// cast_remoting_connector_fuzzertest.cc as well!
-class CastRemotingConnectorMessaging {
- public:
-  // Returns true if the given |message| from the Cast Provider matches the
-  // given |format| and the session ID in the |message| is equal to the
-  // |expected_session_id|.
-  static bool IsMessageForSession(const std::string& message,
-                                  const char* format,
-                                  unsigned int expected_session_id);
-
-  // Scans |message| for |specifier| and extracts the remoting stream ID that
-  // follows the specifier. Returns a negative value on error.
-  static int32_t GetStreamIdFromStartedMessage(base::StringPiece message,
-                                               base::StringPiece specifier);
-
-  // Simple command messages sent from/to the CastRemotingConnector to/from the
-  // Media Router Cast Provider to start/stop media remoting to a Cast device.
-  //
-  // Field separator (for tokenizing parts of messages).
-  static const char kMessageFieldSeparator;
-  // Message sent by CastRemotingConnector to Cast provider to start remoting.
-  // Example:
-  //   "START_CAST_REMOTING:session=1f"
-  static const char kStartRemotingMessageFormat[];
-  // Message sent by CastRemotingConnector to Cast provider to start the
-  // remoting RTP stream(s). Example:
-  //   "START_CAST_REMOTING_STREAMS:session=1f:audio=N:video=Y"
-  static const char kStartStreamsMessageFormat[];
-  // Start acknowledgement message sent by Cast provider to
-  // CastRemotingConnector once remoting RTP streams have been set up. Examples:
-  //   "STARTED_CAST_REMOTING_STREAMS:session=1f:audio_stream_id=2e:"
-  //                                                        "video_stream_id=3d"
-  //   "STARTED_CAST_REMOTING_STREAMS:session=1f:video_stream_id=b33f"
-  static const char kStartedStreamsMessageFormatPartial[];
-  static const char kStartedStreamsMessageAudioIdSpecifier[];
-  static const char kStartedStreamsMessageVideoIdSpecifier[];
-  // Stop message sent by CastRemotingConnector to Cast provider. Example:
-  //   "STOP_CAST_REMOTING:session=1f"
-  static const char kStopRemotingMessageFormat[];
-  // Stop acknowledgement message sent by Cast provider to CastRemotingConnector
-  // once remoting is available again after the last session ended. Example:
-  //   "STOPPED_CAST_REMOTING:session=1f"
-  static const char kStoppedMessageFormat[];
-  // Failure message sent by Cast provider to CastRemotingConnector any time
-  // there was a fatal error (e.g., the Cast provider failed to set up the RTP
-  // streams, or there was some unexpected external event). Example:
-  //   "FAILED_CAST_REMOTING:session=1f"
-  static const char kFailedMessageFormat[];
-};
-
-#endif  // CHROME_BROWSER_MEDIA_CAST_REMOTING_CONNECTOR_MESSAGING_H_
diff --git a/chrome/browser/media/cast_remoting_connector_unittest.cc b/chrome/browser/media/cast_remoting_connector_unittest.cc
index 17467c88..2a6b6385 100644
--- a/chrome/browser/media/cast_remoting_connector_unittest.cc
+++ b/chrome/browser/media/cast_remoting_connector_unittest.cc
@@ -8,24 +8,21 @@
 #include <vector>
 
 #include "base/callback.h"
+#include "base/memory/ptr_util.h"
 #include "base/memory/weak_ptr.h"
 #include "base/run_loop.h"
-#include "chrome/browser/media/router/media_routes_observer.h"
 #include "chrome/browser/media/router/mock_media_router.h"
-#include "chrome/browser/media/router/route_message_observer.h"
-#include "chrome/browser/media/router/test_helper.h"
 #include "chrome/common/media_router/media_route.h"
 #include "chrome/common/media_router/media_source.h"
 #include "content/public/browser/browser_thread.h"
-#include "content/public/common/presentation_connection_message.h"
 #include "content/public/test/test_browser_thread_bundle.h"
+#include "media/mojo/interfaces/mirror_service_remoting.mojom.h"
 #include "media/mojo/interfaces/remoting.mojom.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using content::BrowserThread;
-using content::PresentationConnectionMessage;
 
 using media::mojom::RemoterPtr;
 using media::mojom::RemoterRequest;
@@ -34,28 +31,17 @@
 using media::mojom::RemotingSourceRequest;
 using media::mojom::RemotingStartFailReason;
 using media::mojom::RemotingStopReason;
-
-using media_router::MediaRoutesObserver;
-using media_router::MediaRoute;
-using media_router::MediaSource;
-using media_router::PresentationConnectionMessageToString;
-using media_router::RouteMessageObserver;
+using media::mojom::MirrorServiceRemoterPtr;
+using media::mojom::MirrorServiceRemoterRequest;
+using media::mojom::MirrorServiceRemotingSourcePtr;
+using media::mojom::MirrorServiceRemotingSourceRequest;
 
 using ::testing::_;
 using ::testing::AtLeast;
 
 namespace {
 
-constexpr char kRemotingMediaSource[] =
-    "urn:x-org.chromium.media:source:tab_content_remoting:123";
-constexpr char kRemotingMediaSink[] = "wiggles";
-constexpr char kRemotingMediaRoute[] =
-    "urn:x-org.chromium:media:route:garbly_gook_ssi7m4oa8oma7rasd/cast-wiggles";
-
-constexpr char kTabMirroringMediaSource[] =
-    "urn:x-org.chromium.media:source:tab:123";
-constexpr char kTabMirroringMediaRoute[] =
-    "urn:x-org.chromium:media:route:bloopity_blop_ohun48i56nh9oid/cast-wiggles";
+constexpr int32_t kRemotingTabId = 2;
 
 constexpr RemotingSinkCapabilities kAllCapabilities =
     RemotingSinkCapabilities::CONTENT_DECRYPTION_AND_RENDERING;
@@ -65,146 +51,40 @@
 // if any methods were called that should not have been called.
 class FakeMediaRouter : public media_router::MockMediaRouter {
  public:
-  FakeMediaRouter()
-      : routes_observer_(nullptr), message_observer_(nullptr),
-        weak_factory_(this) {}
+  FakeMediaRouter() : weak_factory_(this) {}
   ~FakeMediaRouter() final {}
 
-  //
-  // These methods are called by test code to create/destroy a media route and
-  // pass messages in both directions.
-  //
-
-  void OnRemotingRouteExists(bool exists) {
-    routes_.clear();
-
-    // Always add a non-remoting route to make sure CastRemotingConnector
-    // ignores non-remoting routes.
-    routes_.push_back(MediaRoute(
-        kTabMirroringMediaRoute, MediaSource(kTabMirroringMediaSource),
-        kRemotingMediaSink, "Cast Tab Mirroring", false, "", false));
-
-    if (exists) {
-      routes_.push_back(MediaRoute(
-          kRemotingMediaRoute, MediaSource(kRemotingMediaSource),
-          kRemotingMediaSink, "Cast Media Remoting", false, "", false));
-    } else {
-      // Cancel delivery of all messages in both directions.
-      inbound_messages_.clear();
-      for (auto& entry : outbound_messages_) {
-        BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
-                                base::BindOnce(std::move(entry.second), false));
-      }
-      outbound_messages_.clear();
-    }
-
-    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
-                            base::BindOnce(&FakeMediaRouter::DoUpdateRoutes,
-                                           weak_factory_.GetWeakPtr()));
+  void RegisterRemotingSource(int32_t tab_id,
+                              CastRemotingConnector* remoting_source) final {
+    EXPECT_EQ(-1, tab_id_);
+    tab_id_ = tab_id;
+    connector_ = remoting_source;
   }
 
-  void OnMessageFromProvider(const std::string& message) {
-    inbound_messages_.emplace_back(message);
-    BrowserThread::PostTask(
-        BrowserThread::UI, FROM_HERE,
-        base::BindOnce(&FakeMediaRouter::DoDeliverInboundMessages,
-                       weak_factory_.GetWeakPtr()));
+  void UnregisterRemotingSource(int32_t tab_id) final {
+    EXPECT_EQ(tab_id, tab_id_);
+    tab_id_ = -1;
+    connector_ = nullptr;
   }
 
-  void OnBinaryMessageFromProvider(const std::vector<uint8_t>& message) {
-    inbound_messages_.emplace_back(message);
-    BrowserThread::PostTask(
-        BrowserThread::UI, FROM_HERE,
-        base::BindOnce(&FakeMediaRouter::DoDeliverInboundMessages,
-                       weak_factory_.GetWeakPtr()));
+  void OnMediaRemoterCreated(
+      int32_t tab_id,
+      MirrorServiceRemoterPtr remoter,
+      MirrorServiceRemotingSourceRequest remoting_source) {
+    if (tab_id != tab_id_)
+      return;
+
+    EXPECT_TRUE(connector_);
+    connector_->ConnectToService(std::move(remoting_source),
+                                 std::move(remoter));
   }
 
-  void TakeMessagesSentToProvider(
-      bool text,
-      std::vector<PresentationConnectionMessage>* messages) {
-    decltype(outbound_messages_) untaken_messages;
-    for (auto& entry : outbound_messages_) {
-      if (!entry.first.is_binary() == text) {
-        messages->push_back(entry.first);
-        BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
-                                base::BindOnce(std::move(entry.second), true));
-      } else {
-        untaken_messages.push_back(std::move(entry));
-      }
-    }
-    outbound_messages_.swap(untaken_messages);
-  }
-
- protected:
-  void RegisterMediaRoutesObserver(MediaRoutesObserver* observer) final {
-    CHECK(!routes_observer_);
-    routes_observer_ = observer;
-    CHECK(routes_observer_);
-    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
-                            base::BindOnce(&FakeMediaRouter::DoUpdateRoutes,
-                                           weak_factory_.GetWeakPtr()));
-  }
-
-  void UnregisterMediaRoutesObserver(MediaRoutesObserver* observer) final {
-    CHECK_EQ(routes_observer_, observer);
-    routes_observer_ = nullptr;
-  }
-
-  void RegisterRouteMessageObserver(RouteMessageObserver* observer) final {
-    CHECK(!message_observer_);
-    message_observer_ = observer;
-    CHECK(message_observer_);
-  }
-
-  void UnregisterRouteMessageObserver(RouteMessageObserver* observer) final {
-    CHECK_EQ(message_observer_, observer);
-    message_observer_ = nullptr;
-  }
-
-  void SendRouteMessage(const MediaRoute::Id& route_id,
-                        const std::string& text,
-                        SendRouteMessageCallback callback) final {
-    EXPECT_EQ(message_observer_->route_id(), route_id);
-    ASSERT_FALSE(callback.is_null());
-    PresentationConnectionMessage message(text);
-    outbound_messages_.emplace_back(std::move(message), std::move(callback));
-  }
-
-  void SendRouteBinaryMessage(const MediaRoute::Id& route_id,
-                              std::unique_ptr<std::vector<uint8_t>> data,
-                              SendRouteMessageCallback callback) final {
-    EXPECT_EQ(message_observer_->route_id(), route_id);
-    ASSERT_TRUE(!!data);
-    ASSERT_FALSE(callback.is_null());
-    PresentationConnectionMessage message(std::move(*data));
-    outbound_messages_.emplace_back(std::move(message), std::move(callback));
-  }
+  // Get the registered tab ID.
+  int32_t tab_id() const { return tab_id_; }
 
  private:
-  // Asynchronous callback to notify the MediaRoutesObserver of a change in
-  // routes.
-  void DoUpdateRoutes() {
-    if (routes_observer_)
-      routes_observer_->OnRoutesUpdated(routes_, std::vector<MediaRoute::Id>());
-  }
-
-  // Asynchronous callback to deliver messages to the RouteMessageObserver.
-  void DoDeliverInboundMessages() {
-    if (message_observer_)
-      message_observer_->OnMessagesReceived(inbound_messages_);
-    inbound_messages_.clear();
-  }
-
-  MediaRoutesObserver* routes_observer_;
-  RouteMessageObserver* message_observer_;
-
-  std::vector<MediaRoute> routes_;
-  // Messages from Cast Provider to the connector.
-  std::vector<PresentationConnectionMessage> inbound_messages_;
-  // Messages from the connector to the Cast Provider.
-  using OutboundMessageAndCallback =
-      std::pair<PresentationConnectionMessage, SendRouteMessageCallback>;
-  std::vector<OutboundMessageAndCallback> outbound_messages_;
+  int32_t tab_id_ = -1;
+  CastRemotingConnector* connector_ = nullptr;
 
   base::WeakPtrFactory<FakeMediaRouter> weak_factory_;
 };
@@ -229,15 +109,53 @@
   mojo::Binding<media::mojom::RemotingSource> binding_;
 };
 
+class MockMediaRemoter : public media::mojom::MirrorServiceRemoter {
+ public:
+  explicit MockMediaRemoter(FakeMediaRouter* media_router) : binding_(this) {
+    MirrorServiceRemoterPtr remoter;
+    binding_.Bind(mojo::MakeRequest(&remoter));
+    media_router->OnMediaRemoterCreated(kRemotingTabId, std::move(remoter),
+                                        mojo::MakeRequest(&source_));
+  }
+  ~MockMediaRemoter() final {}
+
+  void OnSinkAvailable() {
+    media::mojom::SinkCapabilitiesPtr capabilities =
+        media::mojom::SinkCapabilities::New();
+    source_->OnSinkAvailable(std::move(capabilities));
+  }
+
+  void SendMessageToSource(const std::vector<uint8_t>& message) {
+    source_->OnMessageFromSink(message);
+  }
+
+  void OnStopped(RemotingStopReason reason) { source_->OnStopped(reason); }
+
+  void OnError() { source_->OnError(); }
+
+  // media::mojom::MirrorServiceRemoter implementations.
+  void StartDataStreams(bool audio,
+                        bool video,
+                        StartDataStreamsCallback callback) override {}
+  MOCK_METHOD0(Start, void());
+  MOCK_METHOD1(Stop, void(RemotingStopReason));
+  MOCK_METHOD1(SendMessageToSink, void(const std::vector<uint8_t>&));
+
+ private:
+  mojo::Binding<media::mojom::MirrorServiceRemoter> binding_;
+  MirrorServiceRemotingSourcePtr source_;
+};
+
 }  // namespace
 
 class CastRemotingConnectorTest : public ::testing::Test {
  public:
-  CastRemotingConnectorTest()
-      : connector_(&media_router_, kRemotingMediaSource) {
+  CastRemotingConnectorTest() : connector_(&media_router_, kRemotingTabId) {
     // HACK: Override feature flags for testing.
     const_cast<RemotingSinkCapabilities&>(connector_.enabled_features_) =
         kAllCapabilities;
+
+    EXPECT_EQ(kRemotingTabId, media_router_.tab_id());
   }
 
   void TearDown() final {
@@ -257,77 +175,14 @@
     return remoter_ptr;
   }
 
-  void ProviderDiscoversSink() {
-    media_router_.OnRemotingRouteExists(true);
-  }
-
-  void ProviderLosesSink() {
-    media_router_.OnRemotingRouteExists(false);
-  }
-
-  void ConnectorSentMessageToProvider(const std::string& expected_message) {
-    std::vector<PresentationConnectionMessage> messages;
-    media_router_.TakeMessagesSentToProvider(true, &messages);
-    bool did_see_expected_message = false;
-    for (const auto& message : messages) {
-      if (expected_message == message.message) {
-        did_see_expected_message = true;
-      } else {
-        ADD_FAILURE() << "Unexpected message: "
-                      << PresentationConnectionMessageToString(message);
-      }
-    }
-    EXPECT_TRUE(did_see_expected_message);
-  }
-
-  void ConnectorSentMessageToSink(
-      const std::vector<uint8_t>& expected_message) {
-    std::vector<PresentationConnectionMessage> messages;
-    media_router_.TakeMessagesSentToProvider(false, &messages);
-    bool did_see_expected_message = false;
-    for (const auto& message : messages) {
-      if (expected_message == message.data) {
-        did_see_expected_message = true;
-      } else {
-        ADD_FAILURE() << "Unexpected message: "
-                      << PresentationConnectionMessageToString(message);
-      }
-    }
-    EXPECT_TRUE(did_see_expected_message);
-  }
-
-  void ConnectorSentNoMessagesToProvider() {
-    std::vector<PresentationConnectionMessage> messages;
-    media_router_.TakeMessagesSentToProvider(true, &messages);
-    EXPECT_TRUE(messages.empty());
-  }
-
-  void ConnectorSentNoMessagesToSink() {
-    std::vector<PresentationConnectionMessage> messages;
-    media_router_.TakeMessagesSentToProvider(false, &messages);
-    EXPECT_TRUE(messages.empty());
-  }
-
-  void ProviderPassesMessageFromSink(
-      const std::vector<uint8_t>& message) {
-    media_router_.OnBinaryMessageFromProvider(message);
-  }
-
-  void ProviderSaysToRemotingConnector(const std::string& message) {
-    media_router_.OnMessageFromProvider(message);
-  }
-
-  void MediaRouterTerminatesRoute() {
-    media_router_.OnRemotingRouteExists(false);
-  }
-
   static void RunUntilIdle() {
     base::RunLoop().RunUntilIdle();
   }
 
+  FakeMediaRouter media_router_;
+
  private:
   content::TestBrowserThreadBundle browser_thread_bundle_;
-  FakeMediaRouter media_router_;
   CastRemotingConnector connector_;
 };
 
@@ -344,12 +199,15 @@
   MockRemotingSource source;
   RemoterPtr remoter = CreateRemoter(&source);
 
+  std::unique_ptr<MockMediaRemoter> media_remoter =
+      base::MakeUnique<MockMediaRemoter>(&media_router_);
+
   EXPECT_CALL(source, OnSinkAvailable(kAllCapabilities)).Times(1);
-  ProviderDiscoversSink();
+  media_remoter->OnSinkAvailable();
   RunUntilIdle();
 
   EXPECT_CALL(source, OnSinkGone()).Times(AtLeast(1));
-  ProviderLosesSink();
+  media_remoter.reset();
   RunUntilIdle();
 }
 
@@ -360,14 +218,17 @@
   MockRemotingSource source2;
   RemoterPtr remoter2 = CreateRemoter(&source2);
 
+  std::unique_ptr<MockMediaRemoter> media_remoter =
+      base::MakeUnique<MockMediaRemoter>(&media_router_);
+
   EXPECT_CALL(source1, OnSinkAvailable(kAllCapabilities)).Times(1);
   EXPECT_CALL(source2, OnSinkAvailable(kAllCapabilities)).Times(1);
-  ProviderDiscoversSink();
+  media_remoter->OnSinkAvailable();
   RunUntilIdle();
 
   EXPECT_CALL(source1, OnSinkGone()).Times(AtLeast(1));
   EXPECT_CALL(source2, OnSinkGone()).Times(AtLeast(1));
-  ProviderLosesSink();
+  media_remoter.reset();
   RunUntilIdle();
 }
 
@@ -375,8 +236,11 @@
   std::unique_ptr<MockRemotingSource> source(new MockRemotingSource);
   RemoterPtr remoter = CreateRemoter(source.get());
 
+  std::unique_ptr<MockMediaRemoter> media_remoter =
+      base::MakeUnique<MockMediaRemoter>(&media_router_);
+
   EXPECT_CALL(*source, OnSinkAvailable(kAllCapabilities)).Times(1);
-  ProviderDiscoversSink();
+  media_remoter->OnSinkAvailable();
   RunUntilIdle();
 
   source.reset();
@@ -387,14 +251,28 @@
   MockRemotingSource source;
   RemoterPtr remoter = CreateRemoter(&source);
 
+  std::unique_ptr<MockMediaRemoter> media_remoter =
+      base::MakeUnique<MockMediaRemoter>(&media_router_);
+
   EXPECT_CALL(source, OnSinkAvailable(kAllCapabilities)).Times(1);
-  ProviderDiscoversSink();
+  media_remoter->OnSinkAvailable();
   RunUntilIdle();
 
   remoter.reset();
   RunUntilIdle();
 }
 
+TEST_F(CastRemotingConnectorTest, NoConnectedMediaRemoter) {
+  MockRemotingSource source;
+  RemoterPtr remoter = CreateRemoter(&source);
+
+  EXPECT_CALL(source,
+              OnStartFailed(RemotingStartFailReason::SERVICE_NOT_CONNECTED))
+      .Times(1);
+  remoter->Start();
+  RunUntilIdle();
+}
+
 namespace {
 
 // The possible ways a remoting session may be terminated in the "full
@@ -424,6 +302,8 @@
   RemoterPtr remoter = CreateRemoter(source.get());
   std::unique_ptr<MockRemotingSource> other_source(new MockRemotingSource());
   RemoterPtr other_remoter = CreateRemoter(other_source.get());
+  std::unique_ptr<MockMediaRemoter> media_remoter =
+      base::MakeUnique<MockMediaRemoter>(&media_router_);
 
   // Throughout this test |other_source| should not participate in the
   // remoting session, and so these method calls should never occur:
@@ -437,7 +317,7 @@
       .RetiresOnSaturation();
   EXPECT_CALL(*other_source, OnSinkAvailable(kAllCapabilities)).Times(1)
       .RetiresOnSaturation();
-  ProviderDiscoversSink();
+  media_remoter->OnSinkAvailable();
   RunUntilIdle();
 
   // When |source| starts a remoting session, |other_source| is notified the
@@ -445,24 +325,28 @@
   // and |source| is notified that its request was successful.
   EXPECT_CALL(*source, OnStarted()).Times(1).RetiresOnSaturation();
   EXPECT_CALL(*other_source, OnSinkGone()).Times(1).RetiresOnSaturation();
+  EXPECT_CALL(*media_remoter, Start()).Times(1).RetiresOnSaturation();
   remoter->Start();
   RunUntilIdle();
-  ConnectorSentMessageToProvider("START_CAST_REMOTING:session=1");
 
   // The |source| should now be able to send binary messages to the sink.
   // |other_source| should not!
   const std::vector<uint8_t> message_to_sink = { 3, 1, 4, 1, 5, 9 };
+  EXPECT_CALL(*media_remoter, SendMessageToSink(message_to_sink))
+      .Times(1)
+      .RetiresOnSaturation();
   remoter->SendMessageToSink(message_to_sink);
   const std::vector<uint8_t> ignored_message_to_sink = { 1, 2, 3 };
+  EXPECT_CALL(*media_remoter, SendMessageToSink(ignored_message_to_sink))
+      .Times(0);
   other_remoter->SendMessageToSink(ignored_message_to_sink);
   RunUntilIdle();
-  ConnectorSentMessageToSink(message_to_sink);
 
   // The sink should also be able to send binary messages to the |source|.
   const std::vector<uint8_t> message_to_source = { 2, 7, 1, 8, 2, 8 };
   EXPECT_CALL(*source, OnMessageFromSink(message_to_source)).Times(1)
       .RetiresOnSaturation();
-  ProviderPassesMessageFromSink(message_to_source);
+  media_remoter->SendMessageToSource(message_to_source);
   RunUntilIdle();
 
   // The |other_source| should not be allowed to start a remoting session.
@@ -471,7 +355,6 @@
       .Times(1).RetiresOnSaturation();
   other_remoter->Start();
   RunUntilIdle();
-  ConnectorSentNoMessagesToProvider();
 
   // What happens from here depends on how this remoting session will end...
   switch (how_it_ends()) {
@@ -482,19 +365,19 @@
       const RemotingStopReason reason = RemotingStopReason::LOCAL_PLAYBACK;
       EXPECT_CALL(*source, OnSinkGone()).Times(1).RetiresOnSaturation();
       EXPECT_CALL(*source, OnStopped(reason)).Times(1).RetiresOnSaturation();
+      EXPECT_CALL(*media_remoter, Stop(reason)).Times(1).RetiresOnSaturation();
       remoter->Stop(reason);
       RunUntilIdle();
-      ConnectorSentMessageToProvider("STOP_CAST_REMOTING:session=1");
 
       // Since remoting is stopped, any further messaging in either direction
       // must be dropped.
       const std::vector<uint8_t> message_to_sink = { 1, 6, 1, 8, 0, 3 };
       const std::vector<uint8_t> message_to_source = { 6, 2, 8, 3, 1, 8 };
       EXPECT_CALL(*source, OnMessageFromSink(_)).Times(0);
+      EXPECT_CALL(*media_remoter, SendMessageToSink(_)).Times(0);
       remoter->SendMessageToSink(message_to_sink);
-      ProviderPassesMessageFromSink(message_to_source);
+      media_remoter->SendMessageToSource(message_to_source);
       RunUntilIdle();
-      ConnectorSentNoMessagesToSink();
 
       // When the sink is ready, the Cast Provider sends a notification to the
       // connector. The connector will notify both sources that a sink is once
@@ -503,14 +386,14 @@
           .RetiresOnSaturation();
       EXPECT_CALL(*other_source, OnSinkAvailable(kAllCapabilities)).Times(1)
           .RetiresOnSaturation();
-      ProviderSaysToRemotingConnector("STOPPED_CAST_REMOTING:session=1");
+      media_remoter->OnSinkAvailable();
       RunUntilIdle();
 
       // When the sink is no longer available, the Cast Provider notifies the
       // connector, and both sources are then notified the sink is gone.
       EXPECT_CALL(*source, OnSinkGone()).Times(AtLeast(1));
       EXPECT_CALL(*other_source, OnSinkGone()).Times(AtLeast(1));
-      ProviderLosesSink();
+      media_remoter.reset();
       RunUntilIdle();
 
       break;
@@ -519,23 +402,17 @@
     case MOJO_PIPE_CLOSES:
       // When the Mojo pipes for |other_source| close, this should not affect
       // the current remoting session.
+      EXPECT_CALL(*media_remoter, Stop(_)).Times(0);
       other_source.reset();
       other_remoter.reset();
       RunUntilIdle();
-      ConnectorSentNoMessagesToProvider();
 
       // Now, when the Mojo pipes for |source| close, the Cast Provider will be
       // notified that the session has stopped.
+      EXPECT_CALL(*media_remoter, Stop(_)).Times(1).RetiresOnSaturation();
       source.reset();
       remoter.reset();
       RunUntilIdle();
-      ConnectorSentMessageToProvider("STOP_CAST_REMOTING:session=1");
-
-      // The Cast Provider will detect when the sink is ready for the next
-      // remoting session, and then notify the connector. However, there are no
-      // sources to propagate this notification to.
-      ProviderSaysToRemotingConnector("STOPPED_CAST_REMOTING:session=1");
-      RunUntilIdle();
 
       break;
 
@@ -544,20 +421,14 @@
       // terminated the route from the UI), the source and sink are immediately
       // cut off from one another.
       EXPECT_CALL(*source, OnSinkGone()).Times(AtLeast(1));
-      EXPECT_CALL(*source, OnStopped(RemotingStopReason::ROUTE_TERMINATED))
-          .Times(1).RetiresOnSaturation();
       EXPECT_CALL(*other_source, OnSinkGone()).Times(AtLeast(0));
-      MediaRouterTerminatesRoute();
-      RunUntilIdle();
-      ConnectorSentNoMessagesToProvider();
-
       // Furthermore, the connector and Cast Provider are also cut off from one
       // another and should not be able to exchange messages anymore. Therefore,
       // the connector will never try to notify the sources that the sink is
       // available again.
       EXPECT_CALL(*source, OnSinkAvailable(_)).Times(0);
       EXPECT_CALL(*other_source, OnSinkAvailable(_)).Times(0);
-      ProviderSaysToRemotingConnector("STOPPED_CAST_REMOTING:session=1");
+      media_remoter.reset();
       RunUntilIdle();
 
       break;
@@ -569,7 +440,10 @@
       EXPECT_CALL(*source, OnSinkGone()).Times(1).RetiresOnSaturation();
       EXPECT_CALL(*source, OnStopped(RemotingStopReason::UNEXPECTED_FAILURE))
           .Times(1).RetiresOnSaturation();
-      ProviderSaysToRemotingConnector("FAILED_CAST_REMOTING:session=1");
+      EXPECT_CALL(*media_remoter, Stop(RemotingStopReason::UNEXPECTED_FAILURE))
+          .Times(1)
+          .RetiresOnSaturation();
+      media_remoter->OnError();
       RunUntilIdle();
 
       // Since remoting is stopped, any further messaging in either direction
@@ -577,26 +451,16 @@
       const std::vector<uint8_t> message_to_sink = { 1, 6, 1, 8, 0, 3 };
       const std::vector<uint8_t> message_to_source = { 6, 2, 8, 3, 1, 8 };
       EXPECT_CALL(*source, OnMessageFromSink(_)).Times(0);
+      EXPECT_CALL(*media_remoter, SendMessageToSink(_)).Times(0);
       remoter->SendMessageToSink(message_to_sink);
-      ProviderPassesMessageFromSink(message_to_source);
-      RunUntilIdle();
-      ConnectorSentNoMessagesToSink();
-
-      // Later, if whatever caused the external failure has resolved, the Cast
-      // Provider will notify the connector that the sink is available one
-      // again.
-      EXPECT_CALL(*source, OnSinkAvailable(kAllCapabilities)).Times(1)
-          .RetiresOnSaturation();
-      EXPECT_CALL(*other_source, OnSinkAvailable(kAllCapabilities)).Times(1)
-          .RetiresOnSaturation();
-      ProviderSaysToRemotingConnector("STOPPED_CAST_REMOTING:session=1");
+      media_remoter->SendMessageToSource(message_to_source);
       RunUntilIdle();
 
       // When the sink is no longer available, the Cast Provider notifies the
       // connector, and both sources are then notified the sink is gone.
       EXPECT_CALL(*source, OnSinkGone()).Times(AtLeast(1));
       EXPECT_CALL(*other_source, OnSinkGone()).Times(AtLeast(1));
-      ProviderLosesSink();
+      media_remoter.reset();
       RunUntilIdle();
 
       break;
diff --git a/chrome/browser/media/router/media_router.h b/chrome/browser/media/router/media_router.h
index 00b583e..48b855cc 100644
--- a/chrome/browser/media/router/media_router.h
+++ b/chrome/browser/media/router/media_router.h
@@ -13,6 +13,7 @@
 #include "base/callback.h"
 #include "base/callback_list.h"
 #include "base/time/time.h"
+#include "chrome/browser/media/cast_remoting_connector.h"
 #include "chrome/browser/media/router/route_message_observer.h"
 #include "chrome/common/media_router/discovery/media_sink_internal.h"
 #include "chrome/common/media_router/issue.h"
@@ -199,6 +200,14 @@
   virtual scoped_refptr<MediaRouteController> GetRouteController(
       const MediaRoute::Id& route_id) = 0;
 
+  // Registers/Unregisters a CastRemotingConnector with the |tab_id|. For a
+  // given |tab_id|, only one CastRemotingConnector can be registered. The
+  // registered CastRemotingConnector should be removed before it is destroyed.
+  virtual void RegisterRemotingSource(
+      int32_t tab_id,
+      CastRemotingConnector* remoting_source) = 0;
+  virtual void UnregisterRemotingSource(int32_t tab_id) = 0;
+
  private:
   friend class IssuesObserver;
   friend class MediaSinksObserver;
diff --git a/chrome/browser/media/router/media_router_base.cc b/chrome/browser/media/router/media_router_base.cc
index d3ec5e6..e29be4c 100644
--- a/chrome/browser/media/router/media_router_base.cc
+++ b/chrome/browser/media/router/media_router_base.cc
@@ -155,4 +155,21 @@
 void MediaRouterBase::DetachRouteController(const MediaRoute::Id& route_id,
                                             MediaRouteController* controller) {}
 
+void MediaRouterBase::RegisterRemotingSource(
+    int32_t tab_id,
+    CastRemotingConnector* remoting_source) {
+  auto it = remoting_sources_.find(tab_id);
+  if (it != remoting_sources_.end()) {
+    DCHECK(remoting_source == it->second);
+    return;
+  }
+  remoting_sources_.emplace(tab_id, remoting_source);
+}
+
+void MediaRouterBase::UnregisterRemotingSource(int32_t tab_id) {
+  auto it = remoting_sources_.find(tab_id);
+  DCHECK(it != remoting_sources_.end());
+  remoting_sources_.erase(it);
+}
+
 }  // namespace media_router
diff --git a/chrome/browser/media/router/media_router_base.h b/chrome/browser/media/router/media_router_base.h
index c482662..807ac26 100644
--- a/chrome/browser/media/router/media_router_base.h
+++ b/chrome/browser/media/router/media_router_base.h
@@ -37,6 +37,10 @@
   scoped_refptr<MediaRouteController> GetRouteController(
       const MediaRoute::Id& route_id) override;
 
+  void RegisterRemotingSource(int32_t tab_id,
+                              CastRemotingConnector* remoting_source) override;
+  void UnregisterRemotingSource(int32_t tab_id) override;
+
  protected:
   FRIEND_TEST_ALL_PREFIXES(MediaRouterMojoImplTest,
                            PresentationConnectionStateChangedCallback);
@@ -73,6 +77,11 @@
       std::unique_ptr<PresentationConnectionStateChangedCallbacks>>
       presentation_connection_state_callbacks_;
 
+  // Stores CastRemotingConnectors that can be connected to the MediaRemoter
+  // for media remoting when MediaRemoter is started. The map uses the tab ID
+  // as the key.
+  std::unordered_map<int32_t, CastRemotingConnector*> remoting_sources_;
+
  private:
   friend class MediaRouterBaseTest;
   friend class MediaRouterFactory;
diff --git a/chrome/browser/media/router/mojo/media_router_mojo_impl.cc b/chrome/browser/media/router/mojo/media_router_mojo_impl.cc
index fb6ad4cd..ee3c4c6 100644
--- a/chrome/browser/media/router/mojo/media_router_mojo_impl.cc
+++ b/chrome/browser/media/router/mojo/media_router_mojo_impl.cc
@@ -1021,4 +1021,21 @@
   MediaRouterMojoMetrics::RecordMediaRouteControllerCreationResult(success);
 }
 
+void MediaRouterMojoImpl::OnMediaRemoterCreated(
+    int32_t tab_id,
+    media::mojom::MirrorServiceRemoterPtr remoter,
+    media::mojom::MirrorServiceRemotingSourceRequest source_request) {
+  DVLOG_WITH_INSTANCE(1) << __func__ << ": tab_id = " << tab_id;
+
+  auto it = remoting_sources_.find(tab_id);
+  if (it == remoting_sources_.end()) {
+    LOG(WARNING) << __func__
+                 << ": No registered remoting source for tab_id = " << tab_id;
+    return;
+  }
+
+  CastRemotingConnector* connector = it->second;
+  connector->ConnectToService(std::move(source_request), std::move(remoter));
+}
+
 }  // namespace media_router
diff --git a/chrome/browser/media/router/mojo/media_router_mojo_impl.h b/chrome/browser/media/router/mojo/media_router_mojo_impl.h
index 6dbf96e..01cb711 100644
--- a/chrome/browser/media/router/mojo/media_router_mojo_impl.h
+++ b/chrome/browser/media/router/mojo/media_router_mojo_impl.h
@@ -328,6 +328,10 @@
       const std::string& route_id,
       const std::vector<content::PresentationConnectionMessage>& messages)
       override;
+  void OnMediaRemoterCreated(
+      int32_t tab_id,
+      media::mojom::MirrorServiceRemoterPtr remoter,
+      media::mojom::MirrorServiceRemotingSourceRequest source_request) override;
 
   // Result callback when Mojo terminateRoute is invoked.  |route_id| is bound
   // to the ID of the route that was terminated.
diff --git a/chrome/browser/page_load_metrics/metrics_web_contents_observer.cc b/chrome/browser/page_load_metrics/metrics_web_contents_observer.cc
index cda6f282..0cf731de 100644
--- a/chrome/browser/page_load_metrics/metrics_web_contents_observer.cc
+++ b/chrome/browser/page_load_metrics/metrics_web_contents_observer.cc
@@ -218,6 +218,7 @@
 
 PageLoadTracker* MetricsWebContentsObserver::GetTrackerOrNullForRequest(
     const content::GlobalRequestID& request_id,
+    content::RenderFrameHost* render_frame_host_or_null,
     content::ResourceType resource_type,
     base::TimeTicks creation_time) {
   if (resource_type == content::RESOURCE_TYPE_MAIN_FRAME) {
@@ -240,23 +241,33 @@
     // Non main frame resources are always associated with the currently
     // committed load. If the resource request was started before this
     // navigation then it should be ignored.
+    if (!committed_load_ || creation_time < committed_load_->navigation_start())
+      return nullptr;
 
-    // TODO(jkarlin): There is a race here. Consider the following sequence:
-    // 1. renderer has a committed page A
-    // 2. navigation is initiated to page B
-    // 3. page A initiates URLRequests (e.g. in the unload handler)
-    // 4. page B commits
-    // 5. the URLRequests initiated by A complete
-    // In the above example, the URLRequests initiated by A will be attributed
-    // to page load B. This should be relatively rare but we may want to fix
-    // this at some point. We could fix this by comparing the URLRequest
-    // creation time against the committed load's commit time, however more
-    // investigation is needed to confirm that all cases would be handled
-    // correctly (for example Link: preloads).
-    if (committed_load_ &&
-        creation_time >= committed_load_->navigation_start()) {
+    // Sub-frame resources have a null RFH when browser-side navigation is
+    // enabled, so we can't perform the RFH check below for them.
+    //
+    // TODO(bmcquade): consider tracking GlobalRequestIDs for sub-frame
+    // navigations in each PageLoadTracker, and performing a lookup for
+    // sub-frames similar to the main-frame lookup above.
+    if (resource_type == content::RESOURCE_TYPE_SUB_FRAME)
       return committed_load_.get();
-    }
+
+    // There is a race here: a completed resource for the previously committed
+    // page can arrive after the new page has committed. In this case, we may
+    // attribute the resource to the wrong page load. We do our best to guard
+    // against this by verifying that the RFH for the resource matches the RFH
+    // for the currently committed load, however there are cases where the same
+    // RFH is used across page loads (same origin navigations, as well as some
+    // cross-origin render-initiated navigations).
+    //
+    // TODO(crbug.com/738577): use a DocumentId here instead, to eliminate this
+    // race.
+    DCHECK(render_frame_host_or_null != nullptr);
+    content::RenderFrameHost* main_frame_for_resource =
+        GetMainFrame(render_frame_host_or_null);
+    if (main_frame_for_resource == web_contents()->GetMainFrame())
+      return committed_load_.get();
   }
   return nullptr;
 }
@@ -266,6 +277,7 @@
     const net::HostPortPair& host_port_pair,
     int frame_tree_node_id,
     const content::GlobalRequestID& request_id,
+    content::RenderFrameHost* render_frame_host_or_null,
     content::ResourceType resource_type,
     bool was_cached,
     std::unique_ptr<data_reduction_proxy::DataReductionProxyData>
@@ -278,8 +290,8 @@
   if (!url.SchemeIsHTTPOrHTTPS())
     return;
 
-  PageLoadTracker* tracker =
-      GetTrackerOrNullForRequest(request_id, resource_type, creation_time);
+  PageLoadTracker* tracker = GetTrackerOrNullForRequest(
+      request_id, render_frame_host_or_null, resource_type, creation_time);
   if (tracker) {
     ExtraRequestCompleteInfo extra_request_complete_info(
         url, host_port_pair, frame_tree_node_id, was_cached, raw_body_bytes,
diff --git a/chrome/browser/page_load_metrics/metrics_web_contents_observer.h b/chrome/browser/page_load_metrics/metrics_web_contents_observer.h
index 69eedb2..5d35bf3 100644
--- a/chrome/browser/page_load_metrics/metrics_web_contents_observer.h
+++ b/chrome/browser/page_load_metrics/metrics_web_contents_observer.h
@@ -96,12 +96,14 @@
       content::NavigationHandle* navigation_handle);
 
   // A resource request completed on the IO thread. This method is invoked on
-  // the UI thread.
+  // the UI thread. |render_frame_host_or_null will| be null for main or sub
+  // frame requests when browser-side navigation is enabled.
   void OnRequestComplete(
       const GURL& url,
       const net::HostPortPair& host_port_pair,
       int frame_tree_node_id,
       const content::GlobalRequestID& request_id,
+      content::RenderFrameHost* render_frame_host_or_null,
       content::ResourceType resource_type,
       bool was_cached,
       std::unique_ptr<data_reduction_proxy::DataReductionProxyData>
@@ -156,6 +158,7 @@
   // PageLoadTrackers.
   PageLoadTracker* GetTrackerOrNullForRequest(
       const content::GlobalRequestID& request_id,
+      content::RenderFrameHost* render_frame_host_or_null,
       content::ResourceType resource_type,
       base::TimeTicks creation_time);
 
diff --git a/chrome/browser/page_load_metrics/metrics_web_contents_observer_unittest.cc b/chrome/browser/page_load_metrics/metrics_web_contents_observer_unittest.cc
index f7ffe1b9..712caed 100644
--- a/chrome/browser/page_load_metrics/metrics_web_contents_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/metrics_web_contents_observer_unittest.cc
@@ -983,6 +983,7 @@
 
   observer()->OnRequestComplete(
       main_resource_url, net::HostPortPair(), frame_tree_node_id, request_id,
+      web_contents()->GetMainFrame(),
       content::ResourceType::RESOURCE_TYPE_MAIN_FRAME, false, nullptr, 0, 0,
       base::TimeTicks::Now(), net::OK);
   EXPECT_EQ(1u, loaded_resources().size());
@@ -994,6 +995,7 @@
   // specified |request_id| is no longer associated with any tracked page loads.
   observer()->OnRequestComplete(
       main_resource_url, net::HostPortPair(), frame_tree_node_id, request_id,
+      web_contents()->GetMainFrame(),
       content::ResourceType::RESOURCE_TYPE_MAIN_FRAME, false, nullptr, 0, 0,
       base::TimeTicks::Now(), net::OK);
   EXPECT_EQ(1u, loaded_resources().size());
@@ -1008,14 +1010,40 @@
   observer()->OnRequestComplete(
       loaded_resource_url, net::HostPortPair(),
       web_contents()->GetMainFrame()->GetFrameTreeNodeId(),
-      content::GlobalRequestID(), content::RESOURCE_TYPE_SCRIPT, false, nullptr,
-      0, 0, base::TimeTicks::Now(), net::OK);
+      content::GlobalRequestID(), web_contents()->GetMainFrame(),
+      content::RESOURCE_TYPE_SCRIPT, false, nullptr, 0, 0,
+      base::TimeTicks::Now(), net::OK);
 
   EXPECT_EQ(1u, loaded_resources().size());
   EXPECT_EQ(loaded_resource_url, loaded_resources().back().url);
 }
 
 TEST_F(MetricsWebContentsObserverTest,
+       OnLoadedResource_ResourceFromOtherRFHIgnored) {
+  content::WebContentsTester* web_contents_tester =
+      content::WebContentsTester::For(web_contents());
+  web_contents_tester->NavigateAndCommit(GURL(kDefaultTestUrl));
+
+  // This is a bit of a hack. We want to simulate giving the
+  // MetricsWebContentsObserver a RenderFrameHost from a previously committed
+  // page, to verify that resources for RFHs that don't match the currently
+  // committed RFH are ignored. There isn't a way to hold on to an old RFH (it
+  // gets cleaned up soon after being navigated away from) so instead we use an
+  // RFH from another WebContents, as a way to simulate the desired behavior.
+  content::WebContents* other_web_contents =
+      content::WebContentsTester::CreateTestWebContents(browser_context(),
+                                                        nullptr);
+  observer()->OnRequestComplete(
+      GURL("http://www.other.com/"), net::HostPortPair(),
+      other_web_contents->GetMainFrame()->GetFrameTreeNodeId(),
+      content::GlobalRequestID(), other_web_contents->GetMainFrame(),
+      content::RESOURCE_TYPE_SCRIPT, false, nullptr, 0, 0,
+      base::TimeTicks::Now(), net::OK);
+
+  EXPECT_TRUE(loaded_resources().empty());
+}
+
+TEST_F(MetricsWebContentsObserverTest,
        OnLoadedResource_IgnoreNonHttpOrHttpsScheme) {
   content::WebContentsTester* web_contents_tester =
       content::WebContentsTester::For(web_contents());
@@ -1024,8 +1052,9 @@
   observer()->OnRequestComplete(
       loaded_resource_url, net::HostPortPair(),
       web_contents()->GetMainFrame()->GetFrameTreeNodeId(),
-      content::GlobalRequestID(), content::RESOURCE_TYPE_SCRIPT, false, nullptr,
-      0, 0, base::TimeTicks::Now(), net::OK);
+      content::GlobalRequestID(), web_contents()->GetMainFrame(),
+      content::RESOURCE_TYPE_SCRIPT, false, nullptr, 0, 0,
+      base::TimeTicks::Now(), net::OK);
 
   EXPECT_TRUE(loaded_resources().empty());
 }
diff --git a/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_unittest.cc
index f68af67..639ead34 100644
--- a/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_unittest.cc
@@ -95,6 +95,7 @@
         GURL(kNonAdUrl), net::HostPortPair(),
         navigation_handle->GetRenderFrameHost()->GetFrameTreeNodeId(),
         navigation_handle->GetGlobalRequestID(),
+        navigation_handle->GetRenderFrameHost(),
         content::RESOURCE_TYPE_MAIN_FRAME, false /* was_cached */,
         nullptr /* data_reduction_proxy */, 10 * 1024 /* raw_body_bytes */,
         0 /* original_network_content_length */, base::TimeTicks::Now(), 0);
diff --git a/chrome/browser/page_load_metrics/observers/local_network_requests_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/local_network_requests_page_load_metrics_observer.cc
new file mode 100644
index 0000000..11066e7
--- /dev/null
+++ b/chrome/browser/page_load_metrics/observers/local_network_requests_page_load_metrics_observer.cc
@@ -0,0 +1,599 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/page_load_metrics/observers/local_network_requests_page_load_metrics_observer.h"
+
+#include "base/lazy_instance.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_macros.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/page_load_metrics/page_load_metrics_util.h"
+#include "net/base/host_port_pair.h"
+#include "net/base/ip_address.h"
+#include "net/base/net_errors.h"
+#include "net/base/url_util.h"
+#include "services/metrics/public/cpp/ukm_entry_builder.h"
+#include "services/metrics/public/cpp/ukm_recorder.h"
+#include "url/gurl.h"
+
+namespace {
+// These constants are used to generate the UMA histograms for local network
+// request metrics. If the enums for |DomainType|, |ResourceType|, or |PortType|
+// change, the bitwise arithmetic below must also change.
+static const int kNumNonlocalhostHistograms =
+    internal::DOMAIN_TYPE_LOCALHOST << 6 |
+    internal::RESOURCE_TYPE_LOCALHOST << 1 | true + 1;
+static const int kNumLocalhostHistograms =
+    internal::DOMAIN_TYPE_LOCALHOST << 5 | internal::PORT_TYPE_DEV << 1 |
+    true + 1;
+
+// Generates a histogram for a non-localhost resource using the values stored in
+// |counts| given a combination of domain type, resource type, and status for
+// the resource.
+void CreateHistogram(std::map<const std::string, int> counts,
+                     internal::DomainType domain_type,
+                     internal::ResourceType resource_type,
+                     bool status) {
+  const std::string& histogram_name = internal::GetNonlocalhostHistogramNames()
+                                          .at(domain_type)
+                                          .at(resource_type)
+                                          .at(status);
+  // Because the macro requires a single value for the maximum histogram index,
+  // but we use three variables to distinguish histograms, we bitwise
+  // concatenate them here to uniquely distinguish each combination. If the
+  // enums for |DomainType| or |ResourceType| change, the bitwise arithmetic
+  // below must also change.
+  const int histogram_index =
+      domain_type << 6 | resource_type << 1 | (status ? 1 : 0);
+  STATIC_HISTOGRAM_POINTER_GROUP(
+      histogram_name, histogram_index, kNumNonlocalhostHistograms,
+      Add(counts[histogram_name]),
+      base::Histogram::FactoryGet(
+          histogram_name,
+          1,     // min value
+          1000,  // max value
+          50,    // number of buckets
+          base::HistogramBase::kUmaTargetedHistogramFlag));
+}
+
+// Generates a histogram for a localhost resource using the values stored in
+// |counts| given a combination of domain type, port type, and status for
+// the resource.
+void CreateHistogram(std::map<const std::string, int> counts,
+                     internal::DomainType domain_type,
+                     internal::PortType port_type,
+                     bool status) {
+  const std::string& histogram_name = internal::GetLocalhostHistogramNames()
+                                          .at(domain_type)
+                                          .at(port_type)
+                                          .at(status);
+  // Because the macro requires a single value for the maximum histogram index,
+  // but we use three variables to distinguish histograms, we bitwise
+  // concatenate them here to uniquely distinguish each combination. If the
+  // enums for |DomainType| or |PortType| change, the bitwise arithmetic
+  // below must also change.
+  const int histogram_index =
+      domain_type << 5 | port_type << 1 | (status ? 1 : 0);
+  STATIC_HISTOGRAM_POINTER_GROUP(
+      histogram_name, histogram_index, kNumLocalhostHistograms,
+      Add(counts[histogram_name]),
+      base::Histogram::FactoryGet(
+          histogram_name,
+          1,     // min value
+          1000,  // max value
+          50,    // number of buckets
+          base::HistogramBase::kUmaTargetedHistogramFlag));
+}
+
+// TODO(uthakore): Update router regex based on further study.
+// Returns true if the IP address matches the following regular expression for
+// common router IP addresses:
+// "^192\.168\.(0|10?|2)\.(1(00?)?)|^10\.(0|1)\.(0|10?)\.(1(00?)?|2)"
+bool IsLikelyRouterIP(net::IPAddress ip_address) {
+  return ip_address.IsIPv4() &&
+         ((ip_address.bytes()[0] == 192 && ip_address.bytes()[1] == 168 &&
+           (ip_address.bytes()[2] == 0 || ip_address.bytes()[2] == 1 ||
+            ip_address.bytes()[2] == 2 || ip_address.bytes()[2] == 10) &&
+           (ip_address.bytes()[3] == 1 || ip_address.bytes()[3] == 10 ||
+            ip_address.bytes()[3] == 100)) ||
+          (ip_address.bytes()[0] == 10 &&
+           (ip_address.bytes()[1] == 0 || ip_address.bytes()[1] == 1) &&
+           (ip_address.bytes()[2] == 0 || ip_address.bytes()[2] == 1 ||
+            ip_address.bytes()[2] == 10) &&
+           (ip_address.bytes()[3] == 1 || ip_address.bytes()[3] == 10 ||
+            ip_address.bytes()[3] == 100 || ip_address.bytes()[3] == 2)));
+}
+
+// Attempts to get the IP address of a resource request from
+// |extra_request_info.host_port_pair|, trying to get it from the URL string in
+// |extra_request_info.url| if that fails.
+// Sets the values of |resource_ip| and |port| with the extracted IP address and
+// port, respectively.
+// Returns true if a valid, nonempty IP address was extracted.
+bool GetIPAndPort(
+    const page_load_metrics::ExtraRequestCompleteInfo& extra_request_info,
+    net::IPAddress* resource_ip,
+    int* resource_port) {
+  // If the request was successful, then the IP address should be in
+  // |extra_request_info|.
+  bool ip_exists = net::ParseURLHostnameToAddress(
+      extra_request_info.host_port_pair.host(), resource_ip);
+  *resource_port = extra_request_info.host_port_pair.port();
+
+  // If the request failed, it's possible we didn't receive the IP address,
+  // possibly because domain resolution failed. As a backup, try getting the IP
+  // from the URL. If none was returned, try matching the hostname from the URL
+  // itself as it might be an IP address if it is a local network request, which
+  // is what we care about.
+  if (!ip_exists && extra_request_info.url.is_valid()) {
+    if (net::IsLocalhost(extra_request_info.url.HostNoBrackets())) {
+      *resource_ip = net::IPAddress::IPv4Localhost();
+      ip_exists = true;
+    } else {
+      ip_exists = net::ParseURLHostnameToAddress(extra_request_info.url.host(),
+                                                 resource_ip);
+    }
+    *resource_port = extra_request_info.url.EffectiveIntPort();
+  }
+
+  if (net::IsLocalhost(resource_ip->ToString())) {
+    *resource_ip = net::IPAddress::IPv4Localhost();
+    ip_exists = true;
+  }
+
+  return ip_exists;
+}
+
+// Getter for the list of mappings for localhost ports that belong to special
+// categories that we want to track.
+const std::map<uint16_t, internal::PortType>& GetLocalhostPortCategories() {
+  static base::LazyInstance<std::map<uint16_t, internal::PortType>>::Leaky
+      localhost_port_categories = LAZY_INSTANCE_INITIALIZER;
+  if (localhost_port_categories.Get().empty()) {
+    localhost_port_categories.Get() = {
+        {80, internal::PORT_TYPE_WEB},     {443, internal::PORT_TYPE_WEB},
+        {8000, internal::PORT_TYPE_WEB},   {8008, internal::PORT_TYPE_WEB},
+        {8080, internal::PORT_TYPE_WEB},   {8081, internal::PORT_TYPE_WEB},
+        {8088, internal::PORT_TYPE_WEB},   {8181, internal::PORT_TYPE_WEB},
+        {8888, internal::PORT_TYPE_WEB},   {3306, internal::PORT_TYPE_DB},
+        {5432, internal::PORT_TYPE_DB},    {27017, internal::PORT_TYPE_DB},
+        {427, internal::PORT_TYPE_PRINT},  {515, internal::PORT_TYPE_PRINT},
+        {631, internal::PORT_TYPE_PRINT},  {9100, internal::PORT_TYPE_PRINT},
+        {9220, internal::PORT_TYPE_PRINT}, {9500, internal::PORT_TYPE_PRINT},
+        {3000, internal::PORT_TYPE_DEV},   {5000, internal::PORT_TYPE_DEV},
+        {9000, internal::PORT_TYPE_DEV},
+        // TODO(uthakore): Add additional port mappings based on further study.
+    };
+  }
+
+  return localhost_port_categories.Get();
+}
+
+}  // namespace
+
+namespace internal {
+
+// UKM event names
+const char kUkmPageDomainEventName[] = "PageDomainInfo";
+const char kUkmLocalNetworkRequestsEventName[] = "LocalNetworkRequests";
+
+// UKM metric names
+const char kUkmDomainTypeName[] = "DomainType";
+const char kUkmResourceTypeName[] = "ResourceType";
+const char kUkmPortTypeName[] = "PortType";
+const char kUkmSuccessfulCountName[] = "Count.Successful";
+const char kUkmFailedCountName[] = "Count.Failed";
+
+// Definitions of getters for the histogram names maps.
+const std::map<internal::DomainType,
+               std::map<internal::ResourceType, std::map<bool, std::string>>>&
+GetNonlocalhostHistogramNames() {
+  static base::LazyInstance<std::map<
+      internal::DomainType,
+      std::map<internal::ResourceType, std::map<bool, std::string>>>>::Leaky
+      histogram_names = LAZY_INSTANCE_INITIALIZER;
+
+  if (histogram_names.Get().empty()) {
+    histogram_names.Get()[internal::DOMAIN_TYPE_PUBLIC]
+                         [internal::RESOURCE_TYPE_PRIVATE][true] =
+        "LocalNetworkRequests.PublicPage.PrivateRequestCount.Successful";
+    histogram_names.Get()[internal::DOMAIN_TYPE_PUBLIC]
+                         [internal::RESOURCE_TYPE_PRIVATE][false] =
+        "LocalNetworkRequests.PublicPage.PrivateRequestCount.Failed";
+    histogram_names.Get()[internal::DOMAIN_TYPE_PUBLIC]
+                         [internal::RESOURCE_TYPE_ROUTER][true] =
+        "LocalNetworkRequests.PublicPage.RouterRequestCount.Successful";
+    histogram_names.Get()[internal::DOMAIN_TYPE_PUBLIC]
+                         [internal::RESOURCE_TYPE_ROUTER][false] =
+        "LocalNetworkRequests.PublicPage.RouterRequestCount.Failed";
+
+    histogram_names.Get()[internal::DOMAIN_TYPE_PRIVATE]
+                         [internal::RESOURCE_TYPE_PUBLIC][true] =
+        "LocalNetworkRequests.PrivatePage.PublicRequestCount.Successful";
+    histogram_names.Get()[internal::DOMAIN_TYPE_PRIVATE]
+                         [internal::RESOURCE_TYPE_PUBLIC][false] =
+        "LocalNetworkRequests.PrivatePage.PublicRequestCount.Failed";
+    histogram_names.Get()[internal::DOMAIN_TYPE_PRIVATE]
+                         [internal::RESOURCE_TYPE_LOCAL_SAME_SUBNET][true] =
+        "LocalNetworkRequests.PrivatePage.SameSubnetRequestCount.Successful";
+    histogram_names.Get()[internal::DOMAIN_TYPE_PRIVATE]
+                         [internal::RESOURCE_TYPE_LOCAL_SAME_SUBNET][false] =
+        "LocalNetworkRequests.PrivatePage.SameSubnetRequestCount.Failed";
+    histogram_names.Get()[internal::DOMAIN_TYPE_PRIVATE]
+                         [internal::RESOURCE_TYPE_LOCAL_DIFF_SUBNET][true] =
+        "LocalNetworkRequests.PrivatePage."
+        "DifferentSubnetRequestCount.Successful";
+    histogram_names.Get()[internal::DOMAIN_TYPE_PRIVATE]
+                         [internal::RESOURCE_TYPE_LOCAL_DIFF_SUBNET][false] =
+        "LocalNetworkRequests.PrivatePage.DifferentSubnetRequestCount.Failed";
+  }
+
+  return histogram_names.Get();
+}
+
+const std::map<internal::DomainType,
+               std::map<internal::PortType, std::map<bool, std::string>>>&
+GetLocalhostHistogramNames() {
+  static base::LazyInstance<std::map<
+      internal::DomainType,
+      std::map<internal::PortType, std::map<bool, std::string>>>>::Leaky
+      histogram_names = LAZY_INSTANCE_INITIALIZER;
+
+  if (histogram_names.Get().empty()) {
+    histogram_names
+        .Get()[internal::DOMAIN_TYPE_PUBLIC][internal::PORT_TYPE_WEB][true] =
+        "LocalNetworkRequests.PublicPage.Localhost."
+        "WebRequestCount.Successful";
+    histogram_names
+        .Get()[internal::DOMAIN_TYPE_PUBLIC][internal::PORT_TYPE_WEB][false] =
+        "LocalNetworkRequests.PublicPage.Localhost."
+        "WebRequestCount.Failed";
+    histogram_names
+        .Get()[internal::DOMAIN_TYPE_PUBLIC][internal::PORT_TYPE_DB][true] =
+        "LocalNetworkRequests.PublicPage.Localhost."
+        "DatabaseRequestCount.Successful";
+    histogram_names
+        .Get()[internal::DOMAIN_TYPE_PUBLIC][internal::PORT_TYPE_DB][false] =
+        "LocalNetworkRequests.PublicPage.Localhost."
+        "DatabaseRequestCount.Failed";
+    histogram_names
+        .Get()[internal::DOMAIN_TYPE_PUBLIC][internal::PORT_TYPE_PRINT][true] =
+        "LocalNetworkRequests.PublicPage.Localhost."
+        "PrinterRequestCount.Successful";
+    histogram_names
+        .Get()[internal::DOMAIN_TYPE_PUBLIC][internal::PORT_TYPE_PRINT][false] =
+        "LocalNetworkRequests.PublicPage.Localhost."
+        "PrinterRequestCount.Failed";
+    histogram_names
+        .Get()[internal::DOMAIN_TYPE_PUBLIC][internal::PORT_TYPE_DEV][true] =
+        "LocalNetworkRequests.PublicPage.Localhost."
+        "DevelopmentRequestCount.Successful";
+    histogram_names
+        .Get()[internal::DOMAIN_TYPE_PUBLIC][internal::PORT_TYPE_DEV][false] =
+        "LocalNetworkRequests.PublicPage.Localhost."
+        "DevelopmentRequestCount.Failed";
+    histogram_names
+        .Get()[internal::DOMAIN_TYPE_PUBLIC][internal::PORT_TYPE_OTHER][true] =
+        "LocalNetworkRequests.PublicPage.Localhost."
+        "OtherRequestCount.Successful";
+    histogram_names
+        .Get()[internal::DOMAIN_TYPE_PUBLIC][internal::PORT_TYPE_OTHER][false] =
+        "LocalNetworkRequests.PublicPage.Localhost."
+        "OtherRequestCount.Failed";
+
+    histogram_names
+        .Get()[internal::DOMAIN_TYPE_PRIVATE][internal::PORT_TYPE_WEB][true] =
+        "LocalNetworkRequests.PrivatePage.Localhost."
+        "WebRequestCount.Successful";
+    histogram_names
+        .Get()[internal::DOMAIN_TYPE_PRIVATE][internal::PORT_TYPE_WEB][false] =
+        "LocalNetworkRequests.PrivatePage.Localhost."
+        "WebRequestCount.Failed";
+    histogram_names
+        .Get()[internal::DOMAIN_TYPE_PRIVATE][internal::PORT_TYPE_DB][true] =
+        "LocalNetworkRequests.PrivatePage.Localhost."
+        "DatabaseRequestCount.Successful";
+    histogram_names
+        .Get()[internal::DOMAIN_TYPE_PRIVATE][internal::PORT_TYPE_DB][false] =
+        "LocalNetworkRequests.PrivatePage.Localhost."
+        "DatabaseRequestCount.Failed";
+    histogram_names
+        .Get()[internal::DOMAIN_TYPE_PRIVATE][internal::PORT_TYPE_PRINT][true] =
+        "LocalNetworkRequests.PrivatePage.Localhost."
+        "PrinterRequestCount.Successful";
+    histogram_names.Get()[internal::DOMAIN_TYPE_PRIVATE]
+                         [internal::PORT_TYPE_PRINT][false] =
+        "LocalNetworkRequests.PrivatePage.Localhost."
+        "PrinterRequestCount.Failed";
+    histogram_names
+        .Get()[internal::DOMAIN_TYPE_PRIVATE][internal::PORT_TYPE_DEV][true] =
+        "LocalNetworkRequests.PrivatePage.Localhost."
+        "DevelopmentRequestCount.Successful";
+    histogram_names
+        .Get()[internal::DOMAIN_TYPE_PRIVATE][internal::PORT_TYPE_DEV][false] =
+        "LocalNetworkRequests.PrivatePage.Localhost."
+        "DevelopmentRequestCount.Failed";
+    histogram_names
+        .Get()[internal::DOMAIN_TYPE_PRIVATE][internal::PORT_TYPE_OTHER][true] =
+        "LocalNetworkRequests.PrivatePage.Localhost."
+        "OtherRequestCount.Successful";
+    histogram_names.Get()[internal::DOMAIN_TYPE_PRIVATE]
+                         [internal::PORT_TYPE_OTHER][false] =
+        "LocalNetworkRequests.PrivatePage.Localhost."
+        "OtherRequestCount.Failed";
+  }
+
+  return histogram_names.Get();
+}
+
+}  // namespace internal
+
+LocalNetworkRequestsPageLoadMetricsObserver::
+    LocalNetworkRequestsPageLoadMetricsObserver() {}
+LocalNetworkRequestsPageLoadMetricsObserver::
+    ~LocalNetworkRequestsPageLoadMetricsObserver() {}
+
+page_load_metrics::PageLoadMetricsObserver::ObservePolicy
+LocalNetworkRequestsPageLoadMetricsObserver::OnCommit(
+    content::NavigationHandle* navigation_handle,
+    ukm::SourceId source_id) {
+  // Upon page load, we want to determine whether the page loaded was a public
+  // domain or private domain and generate an event describing the domain type.
+  net::HostPortPair address = navigation_handle->GetSocketAddress();
+
+  // In cases where the page loaded does not have a socket address or was not a
+  // network resource, we don't want to track the page load. Such resources will
+  // fail to parse or return an empty IP address.
+  if (!net::ParseURLHostnameToAddress(address.host(), &page_ip_address_) ||
+      page_ip_address_.IsZero()) {
+    return STOP_OBSERVING;
+  }
+
+  // |IsLocalhost| assumes (and doesn't verify) that any IPv6 address passed
+  // to it does not have square brackets around it, but |HostPortPair::host|
+  // retains the brackets, so we need to separately check for IPv6 localhost
+  // here.
+  if (net::IsLocalhost(address.host()) ||
+      page_ip_address_ == net::IPAddress::IPv6Localhost()) {
+    page_domain_type_ = internal::DOMAIN_TYPE_LOCALHOST;
+  } else if (page_ip_address_.IsReserved()) {
+    page_domain_type_ = internal::DOMAIN_TYPE_PRIVATE;
+    // Maps from first byte of an IPv4 address to the number of bits in the
+    // reserved prefix. This table contains the subset of prefixes defined in
+    // |IPAddress::IsReservedIPv4| from which we would expect a page load.
+    // TODO: Refactor this code to remove the table and retrieve the prefix from
+    // net::IPAddress as per bug 739856.
+    static const uint8_t kReservedIPv4Prefixes[][2] = {
+        {10, 8}, {100, 10}, {169, 16}, {172, 12}, {192, 16}, {198, 15}};
+
+    for (const auto& entry : kReservedIPv4Prefixes) {
+      // A reserved IP will always be a valid IPv4 or IPv6 address and will
+      // thus have at least 4 bytes, so [0] is safe here.
+      if (page_ip_address_.bytes()[0] == entry[0]) {
+        page_ip_prefix_length_ = entry[1];
+      }
+    }
+  } else {
+    page_domain_type_ = internal::DOMAIN_TYPE_PUBLIC;
+  }
+
+  RecordUkmDomainType(source_id);
+
+  // If the load was localhost, we don't track it because it isn't meaningful
+  // for our purposes.
+  return (page_domain_type_ == internal::DOMAIN_TYPE_LOCALHOST)
+             ? STOP_OBSERVING
+             : CONTINUE_OBSERVING;
+}
+
+page_load_metrics::PageLoadMetricsObserver::ObservePolicy
+LocalNetworkRequestsPageLoadMetricsObserver::FlushMetricsOnAppEnterBackground(
+    const page_load_metrics::mojom::PageLoadTiming& timing,
+    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+  // The browser may come back, but there is no guarantee. To be safe, we record
+  // what we have now and treat changes to this navigation as new page loads.
+  if (extra_info.did_commit) {
+    RecordHistograms();
+    RecordUkmMetrics(extra_info.source_id);
+    ClearLocalState();
+  }
+
+  return CONTINUE_OBSERVING;
+}
+
+void LocalNetworkRequestsPageLoadMetricsObserver::OnLoadedResource(
+    const page_load_metrics::ExtraRequestCompleteInfo& extra_request_info) {
+  net::IPAddress resource_ip;
+  int resource_port;
+
+  // We can't track anything if we don't have an IP address for the resource.
+  // We also don't want to track any requests to the page's IP address itself.
+  if (!GetIPAndPort(extra_request_info, &resource_ip, &resource_port) ||
+      resource_ip.IsZero() || resource_ip == page_ip_address_) {
+    return;
+  }
+
+  // We monitor localhost resource requests for both public and private page
+  // loads.
+  if (resource_ip == net::IPAddress::IPv4Localhost()) {
+    if (extra_request_info.net_error != net::OK) {
+      localhost_request_counts_[resource_port].second++;
+    } else {
+      localhost_request_counts_[resource_port].first++;
+    }
+  }
+  // We only track public resource requests for private pages.
+  else if (resource_ip.IsReserved() ||
+           page_domain_type_ == internal::DOMAIN_TYPE_PRIVATE) {
+    if (extra_request_info.net_error != net::OK) {
+      resource_request_counts_[resource_ip].second++;
+    } else {
+      resource_request_counts_[resource_ip].first++;
+    }
+  }
+}
+
+void LocalNetworkRequestsPageLoadMetricsObserver::OnComplete(
+    const page_load_metrics::mojom::PageLoadTiming& timing,
+    const page_load_metrics::PageLoadExtraInfo& info) {
+  if (info.did_commit) {
+    RecordHistograms();
+    RecordUkmMetrics(info.source_id);
+  }
+}
+
+void LocalNetworkRequestsPageLoadMetricsObserver::ClearLocalState() {
+  localhost_request_counts_.clear();
+  resource_request_counts_.clear();
+  requested_resource_types_.reset();
+}
+
+void LocalNetworkRequestsPageLoadMetricsObserver::RecordHistograms() {
+  if (page_domain_type_ == internal::DOMAIN_TYPE_LOCALHOST) {
+    return;
+  }
+  ResolveResourceTypes();
+
+  // Compute the number of requests of each resource type for the loaded page.
+  std::map<const std::string, int> counts;
+  for (const auto& entry : resource_request_counts_) {
+    counts[internal::GetNonlocalhostHistogramNames()
+               .at(page_domain_type_)
+               .at(requested_resource_types_->at(entry.first))
+               .at(true)] += entry.second.first;
+    counts[internal::GetNonlocalhostHistogramNames()
+               .at(page_domain_type_)
+               .at(requested_resource_types_->at(entry.first))
+               .at(false)] += entry.second.second;
+  }
+
+  for (const auto& entry : localhost_request_counts_) {
+    counts[internal::GetLocalhostHistogramNames()
+               .at(page_domain_type_)
+               .at(DeterminePortType(entry.first))
+               .at(true)] += entry.second.first;
+    counts[internal::GetLocalhostHistogramNames()
+               .at(page_domain_type_)
+               .at(DeterminePortType(entry.first))
+               .at(false)] += entry.second.second;
+  }
+
+  // Log a histogram for each type of resource depending on the domain type of
+  // the page load.
+  if (page_domain_type_ == internal::DOMAIN_TYPE_PUBLIC) {
+    for (auto resource_type :
+         {internal::RESOURCE_TYPE_PRIVATE, internal::RESOURCE_TYPE_ROUTER}) {
+      CreateHistogram(counts, page_domain_type_, resource_type, true);
+      CreateHistogram(counts, page_domain_type_, resource_type, false);
+    }
+  } else {
+    DCHECK_EQ(page_domain_type_, internal::DOMAIN_TYPE_PRIVATE);
+    for (auto resource_type : {internal::RESOURCE_TYPE_PUBLIC,
+                               internal::RESOURCE_TYPE_LOCAL_SAME_SUBNET,
+                               internal::RESOURCE_TYPE_LOCAL_DIFF_SUBNET}) {
+      CreateHistogram(counts, page_domain_type_, resource_type, true);
+      CreateHistogram(counts, page_domain_type_, resource_type, false);
+    }
+  }
+  for (auto port_type : {internal::PORT_TYPE_WEB, internal::PORT_TYPE_DB,
+                         internal::PORT_TYPE_PRINT, internal::PORT_TYPE_DEV,
+                         internal::PORT_TYPE_OTHER}) {
+    CreateHistogram(counts, page_domain_type_, port_type, true);
+    CreateHistogram(counts, page_domain_type_, port_type, false);
+  }
+}
+
+internal::ResourceType
+LocalNetworkRequestsPageLoadMetricsObserver::DetermineResourceType(
+    net::IPAddress resource_ip) {
+  if (page_domain_type_ == internal::DOMAIN_TYPE_PUBLIC) {
+    DCHECK(resource_ip.IsReserved());
+    return IsLikelyRouterIP(resource_ip) ? internal::RESOURCE_TYPE_ROUTER
+                                         : internal::RESOURCE_TYPE_PRIVATE;
+  }
+
+  DCHECK_EQ(internal::DOMAIN_TYPE_PRIVATE, page_domain_type_);
+  if (resource_ip.IsReserved()) {  // PRIVATE
+    const bool is_same_subnet =
+        net::CommonPrefixLength(page_ip_address_, resource_ip) >=
+        page_ip_prefix_length_;
+    return is_same_subnet ? internal::RESOURCE_TYPE_LOCAL_SAME_SUBNET
+                          : internal::RESOURCE_TYPE_LOCAL_DIFF_SUBNET;
+  }
+  return internal::RESOURCE_TYPE_PUBLIC;  // PUBLIC
+}
+
+internal::PortType
+LocalNetworkRequestsPageLoadMetricsObserver::DeterminePortType(int port) {
+  auto lookup = GetLocalhostPortCategories().find(port);
+  if (lookup == GetLocalhostPortCategories().end()) {
+    return internal::PORT_TYPE_OTHER;
+  } else {
+    return lookup->second;
+  }
+}
+
+void LocalNetworkRequestsPageLoadMetricsObserver::ResolveResourceTypes() {
+  // Lazy instantiation.
+  if (requested_resource_types_) {
+    return;
+  }
+
+  requested_resource_types_ =
+      base::MakeUnique<std::map<net::IPAddress, internal::ResourceType>>();
+  for (const auto& entry : resource_request_counts_) {
+    requested_resource_types_->insert(
+        {entry.first, DetermineResourceType(entry.first)});
+  }
+}
+
+void LocalNetworkRequestsPageLoadMetricsObserver::RecordUkmMetrics(
+    ukm::SourceId source_id) {
+  if (page_domain_type_ == internal::DOMAIN_TYPE_LOCALHOST ||
+      g_browser_process->ukm_recorder() == nullptr) {
+    return;
+  }
+
+  ResolveResourceTypes();
+
+  // Log an entry for each non-localhost resource (one per IP address).
+  for (const auto& entry : resource_request_counts_) {
+    ukm::UkmRecorder* ukm_recorder = g_browser_process->ukm_recorder();
+    std::unique_ptr<ukm::UkmEntryBuilder> builder =
+        ukm_recorder->GetEntryBuilder(
+            source_id, internal::kUkmLocalNetworkRequestsEventName);
+    builder->AddMetric(
+        internal::kUkmResourceTypeName,
+        static_cast<int>(requested_resource_types_->at(entry.first)));
+    builder->AddMetric(internal::kUkmSuccessfulCountName, entry.second.first);
+    builder->AddMetric(internal::kUkmFailedCountName, entry.second.second);
+  }
+
+  // Log an entry for each localhost resource (one per port).
+  for (const auto& entry : localhost_request_counts_) {
+    ukm::UkmRecorder* ukm_recorder = g_browser_process->ukm_recorder();
+    std::unique_ptr<ukm::UkmEntryBuilder> builder =
+        ukm_recorder->GetEntryBuilder(
+            source_id, internal::kUkmLocalNetworkRequestsEventName);
+    builder->AddMetric(internal::kUkmResourceTypeName,
+                       static_cast<int>(internal::RESOURCE_TYPE_LOCALHOST));
+    builder->AddMetric(internal::kUkmPortTypeName,
+                       static_cast<int>(DeterminePortType(entry.first)));
+    builder->AddMetric(internal::kUkmSuccessfulCountName, entry.second.first);
+    builder->AddMetric(internal::kUkmFailedCountName, entry.second.second);
+  }
+}
+
+void LocalNetworkRequestsPageLoadMetricsObserver::RecordUkmDomainType(
+    ukm::SourceId source_id) {
+  ukm::UkmRecorder* ukm_recorder = g_browser_process->ukm_recorder();
+  if (!ukm_recorder) {
+    return;
+  }
+
+  std::unique_ptr<ukm::UkmEntryBuilder> builder = ukm_recorder->GetEntryBuilder(
+      source_id, internal::kUkmPageDomainEventName);
+  builder->AddMetric(internal::kUkmDomainTypeName,
+                     static_cast<int>(page_domain_type_));
+}
diff --git a/chrome/browser/page_load_metrics/observers/local_network_requests_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/local_network_requests_page_load_metrics_observer.h
new file mode 100644
index 0000000..e503c64
--- /dev/null
+++ b/chrome/browser/page_load_metrics/observers/local_network_requests_page_load_metrics_observer.h
@@ -0,0 +1,134 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_LOCAL_NETWORK_REQUESTS_PAGE_LOAD_METRICS_OBSERVER_H_
+#define CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_LOCAL_NETWORK_REQUESTS_PAGE_LOAD_METRICS_OBSERVER_H_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "chrome/browser/page_load_metrics/page_load_metrics_observer.h"
+#include "net/base/ip_address.h"
+
+namespace internal {
+
+// The domain type of the IP address of the loaded page. We use these to
+// determine what classes of resource request metrics to collect.
+enum DomainType {
+  DOMAIN_TYPE_UNKNOWN = 0,
+  DOMAIN_TYPE_PUBLIC = 1,
+  DOMAIN_TYPE_PRIVATE = 2,
+  DOMAIN_TYPE_LOCALHOST = 4,
+};
+
+// The type of the IP address of the loaded resource.
+enum ResourceType {
+  RESOURCE_TYPE_PUBLIC = 0,
+  RESOURCE_TYPE_PRIVATE = 1,
+  RESOURCE_TYPE_LOCAL_SAME_SUBNET = 2,
+  RESOURCE_TYPE_LOCAL_DIFF_SUBNET = 4,
+  RESOURCE_TYPE_ROUTER = 8,
+  RESOURCE_TYPE_LOCALHOST = 16,
+};
+
+// The types of services to distinguish between when collecting local network
+// request metrics.
+enum PortType {
+  PORT_TYPE_WEB = 1,
+  PORT_TYPE_DB = 2,
+  PORT_TYPE_PRINT = 4,
+  PORT_TYPE_DEV = 8,
+  PORT_TYPE_OTHER = 0,
+};
+
+// Exposed for tests.
+extern const char kUkmPageDomainEventName[];
+extern const char kUkmLocalNetworkRequestsEventName[];
+
+extern const char kUkmDomainTypeName[];
+extern const char kUkmResourceTypeName[];
+extern const char kUkmPortTypeName[];
+extern const char kUkmSuccessfulCountName[];
+extern const char kUkmFailedCountName[];
+
+// For simple access during UMA histogram logging, the names are in a
+// multidimensional map indexed by [DomainType][ResourceType][Status].
+const std::map<DomainType, std::map<ResourceType, std::map<bool, std::string>>>&
+GetNonlocalhostHistogramNames();
+// For localhost histogram names, the map is indexed by
+// [DomainType][PortType][Status].
+const std::map<DomainType, std::map<PortType, std::map<bool, std::string>>>&
+GetLocalhostHistogramNames();
+
+}  // namespace internal
+
+// This observer is for observing local network requests.
+// TODO(uthakore): Add description.
+class LocalNetworkRequestsPageLoadMetricsObserver
+    : public page_load_metrics::PageLoadMetricsObserver {
+  using SuccessFailCounts = std::pair<uint32_t, uint32_t>;
+
+ public:
+  LocalNetworkRequestsPageLoadMetricsObserver();
+  ~LocalNetworkRequestsPageLoadMetricsObserver() override;
+
+  // page_load_metrics::PageLoadMetricsObserver
+  ObservePolicy OnCommit(content::NavigationHandle* navigation_handle,
+                         ukm::SourceId source_id) override;
+  ObservePolicy FlushMetricsOnAppEnterBackground(
+      const page_load_metrics::mojom::PageLoadTiming& timing,
+      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+  void OnLoadedResource(const page_load_metrics::ExtraRequestCompleteInfo&
+                            extra_request_info) override;
+  void OnComplete(const page_load_metrics::mojom::PageLoadTiming& timing,
+                  const page_load_metrics::PageLoadExtraInfo& info) override;
+
+ private:
+  // Clears all local resource request counts. Only used if we decide to log
+  // metrics but the observer may stay in scope and capture additional resource
+  // requests.
+  void ClearLocalState();
+  // Determines the resource type for the |ip_address| based on the page load
+  // type.
+  internal::ResourceType DetermineResourceType(net::IPAddress ip_address);
+  // Determines the port type for the localhost |port|.
+  internal::PortType DeterminePortType(int port);
+  // Resolves the resource types to report for all IP addresses in
+  // |resource_request_counts_|.
+  void ResolveResourceTypes();
+
+  void RecordUkmDomainType(ukm::SourceId source_id);
+  void RecordHistograms();
+  void RecordUkmMetrics(ukm::SourceId source_id);
+
+  // Stores the counts of resource requests for each non-localhost IP address as
+  // pairs of (successful, failed) request counts.
+  std::map<net::IPAddress, SuccessFailCounts> resource_request_counts_;
+
+  std::unique_ptr<std::map<net::IPAddress, internal::ResourceType>>
+      requested_resource_types_;
+
+  // Stores the counts of resource requests for each localhost port as
+  // pairs of (successful, failed) request counts.
+  std::map<int, SuccessFailCounts> localhost_request_counts_;
+
+  // The page load type. This is used to determine what resource requests to
+  // monitor while the page is committed and to determine the UMA histogram name
+  // to use.
+  internal::DomainType page_domain_type_ = internal::DOMAIN_TYPE_UNKNOWN;
+
+  // The IP address of the page that was loaded.
+  net::IPAddress page_ip_address_;
+
+  // For private page loads, the IP prefix defining the largest reserved subnet
+  // the page could belong to. Used to distinguish between same subnet and
+  // different subnet private network queries.
+  size_t page_ip_prefix_length_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(LocalNetworkRequestsPageLoadMetricsObserver);
+};
+
+#endif  // CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_LOCAL_NETWORK_REQUESTS_PAGE_LOAD_METRICS_OBSERVER_H_
diff --git a/chrome/browser/page_load_metrics/observers/local_network_requests_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/local_network_requests_page_load_metrics_observer_unittest.cc
new file mode 100644
index 0000000..41a8ef0
--- /dev/null
+++ b/chrome/browser/page_load_metrics/observers/local_network_requests_page_load_metrics_observer_unittest.cc
@@ -0,0 +1,893 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/page_load_metrics/observers/local_network_requests_page_load_metrics_observer.h"
+
+#include <vector>
+
+#include "base/memory/ptr_util.h"
+#include "base/test/histogram_tester.h"
+#include "chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.h"
+#include "chrome/browser/page_load_metrics/page_load_metrics_observer.h"
+#include "chrome/browser/page_load_metrics/page_load_metrics_util.h"
+#include "components/ukm/ukm_source.h"
+#include "content/public/browser/global_request_id.h"
+#include "content/public/common/resource_type.h"
+#include "content/public/test/navigation_simulator.h"
+#include "net/base/host_port_pair.h"
+#include "net/base/ip_address.h"
+#include "net/base/net_errors.h"
+#include "url/gurl.h"
+
+namespace internal {
+
+typedef struct {
+  char* url;
+  char* host_ip;
+  uint16_t port;
+} PageAddressInfo;
+
+typedef struct {
+  internal::ResourceType resource_type;
+  internal::PortType port_type;
+  int success_count, failed_count;
+} UkmMetricInfo;
+
+static const PageAddressInfo
+    kPublicPage = {(char*)"https://foo.com/", (char*)"216.58.195.78", 443},
+    kPublicPageIPv6 = {(char*)"https://google.com/",
+                       (char*)"[2607:f8b0:4005:809::200e]", 443},
+    kPrivatePage = {(char*)"http://test.local/", (char*)"192.168.10.123", 80},
+    kLocalhostPage = {(char*)"http://localhost/", (char*)"127.0.0.1", 80},
+    kLocalhostPageIPv6 = {(char*)"http://[::1]/", (char*)"[::1]", 80},
+    kPublicRequest1 = {(char*)"http://bar.com/", (char*)"100.150.200.250", 80},
+    kPublicRequest2 = {(char*)"https://www.baz.com/", (char*)"192.10.20.30",
+                       443},
+    kSameSubnetRequest1 = {(char*)"http://test2.local:9000/",
+                           (char*)"192.168.10.200", 9000},
+    kSameSubnetRequest2 = {(char*)"http://test2.local:8000/index.html",
+                           (char*)"192.168.10.200", 8000},
+    kSameSubnetRequest3 = {(char*)"http://test2.local:8000/bar.html",
+                           (char*)"192.168.10.200", 8000},
+    kDiffSubnetRequest1 = {(char*)"http://10.0.10.200/", (char*)"10.0.10.200",
+                           80},
+    kDiffSubnetRequest2 = {(char*)"http://172.16.0.85:8181/",
+                           (char*)"172.16.0.85", 8181},
+    kDiffSubnetRequest3 = {(char*)"http://10.15.20.25:12345/",
+                           (char*)"10.15.20.25", 12345},
+    kDiffSubnetRequest4 = {(char*)"http://172.31.100.20:515/",
+                           (char*)"172.31.100.20", 515},
+    kLocalhostRequest1 = {(char*)"http://localhost:8080/", (char*)"127.0.0.1",
+                          8080},  // WEB
+    kLocalhostRequest2 = {(char*)"http://127.0.1.1:3306/", (char*)"127.0.1.1",
+                          3306},  // DB
+    kLocalhostRequest3 = {(char*)"http://localhost:515/", (char*)"127.0.2.1",
+                          515},  // PRINT
+    kLocalhostRequest4 = {(char*)"http://127.100.150.200:9000/",
+                          (char*)"127.100.150.200", 9000},  // DEV
+    kLocalhostRequest5 = {(char*)"http://127.0.0.1:9876/", (char*)"127.0.0.1",
+                          9876},  // OTHER
+    kRouterRequest1 = {(char*)"http://10.0.0.1/", (char*)"10.0.0.1", 80},
+    kRouterRequest2 = {(char*)"https://192.168.10.1/", (char*)"192.168.10.1",
+                       443};
+
+}  // namespace internal
+
+class LocalNetworkRequestsPageLoadMetricsObserverTest
+    : public page_load_metrics::PageLoadMetricsObserverTestHarness {
+ protected:
+  void RegisterObservers(page_load_metrics::PageLoadTracker* tracker) override {
+    tracker->AddObserver(
+        base::MakeUnique<LocalNetworkRequestsPageLoadMetricsObserver>());
+  }
+
+  void SetUp() override {
+    page_load_metrics::PageLoadMetricsObserverTestHarness::SetUp();
+  }
+
+  void SimulateNavigateAndCommit(const internal::PageAddressInfo& page) {
+    GURL url(page.url);
+    net::HostPortPair socket_address(page.host_ip, page.port);
+
+    navigation_simulator_ =
+        content::NavigationSimulator::CreateRendererInitiated(url, main_rfh());
+    navigation_simulator_->Start();
+    navigation_simulator_->SetSocketAddress(socket_address);
+    navigation_simulator_->Commit();
+  }
+
+  void SimulateLoadedSuccessfulResource(
+      const internal::PageAddressInfo& resource) {
+    SimulateLoadedResource(resource, 0);
+  }
+
+  void SimulateLoadedFailedResource(const internal::PageAddressInfo& resource) {
+    SimulateLoadedResource(resource, net::ERR_CONNECTION_REFUSED);
+  }
+
+  void SimulateLoadedResource(const internal::PageAddressInfo& resource,
+                              const int net_error) {
+    page_load_metrics::ExtraRequestCompleteInfo request_info(
+        GURL(resource.url), net::HostPortPair(resource.host_ip, resource.port),
+        -1 /* frame_tree_node_id */, !net_error /* was_cached */,
+        (net_error ? 1024 * 20 : 0) /* raw_body_bytes */,
+        0 /* original_network_content_length */,
+        nullptr /* data_reduction_proxy_data */,
+        content::ResourceType::RESOURCE_TYPE_MAIN_FRAME, net_error);
+
+    PageLoadMetricsObserverTestHarness::SimulateLoadedResource(
+        request_info, navigation_simulator_->GetGlobalRequestID());
+  }
+
+  void NavigateToPageAndLoadResources(
+      const internal::PageAddressInfo& page,
+      const std::vector<std::pair<internal::PageAddressInfo, bool>>&
+          resources_and_statuses) {
+    SimulateNavigateAndCommit(page);
+    for (size_t i = 0; i < resources_and_statuses.size(); ++i) {
+      if (resources_and_statuses[i].second) {
+        SimulateLoadedSuccessfulResource(resources_and_statuses[i].first);
+      } else {
+        SimulateLoadedFailedResource(resources_and_statuses[i].first);
+      }
+    }
+    DeleteContents();
+  }
+
+  const content::GlobalRequestID GetGlobalRequestID() {
+    DCHECK(navigation_simulator_);
+    return navigation_simulator_->GetGlobalRequestID();
+  }
+
+  // Helper functions to verify that particular slices of UMA histograms are
+  // empty.
+  void ExpectEmptyHistograms(internal::DomainType domain_type) {
+    for (const auto& port :
+         internal::GetLocalhostHistogramNames().at(domain_type)) {
+      for (const auto& histogramName : port.second) {
+        histogram_tester().ExpectUniqueSample(histogramName.second, 0, 1);
+      }
+    }
+    for (const auto& resource :
+         internal::GetNonlocalhostHistogramNames().at(domain_type)) {
+      for (const auto& histogramName : resource.second) {
+        histogram_tester().ExpectUniqueSample(histogramName.second, 0, 1);
+      }
+    }
+  }
+
+  void ExpectNoHistograms() {
+    for (const auto& domain : internal::GetLocalhostHistogramNames()) {
+      for (const auto& port : domain.second) {
+        for (const auto& status : port.second) {
+          histogram_tester().ExpectTotalCount(status.second, 0);
+        }
+      }
+    }
+    for (const auto& domain : internal::GetNonlocalhostHistogramNames()) {
+      for (const auto& resource : domain.second) {
+        for (const auto& status : resource.second) {
+          histogram_tester().ExpectTotalCount(status.second, 0);
+        }
+      }
+    }
+  }
+
+  void ExpectUkmPageDomainMetric(const internal::PageAddressInfo& page,
+                                 const internal::DomainType domain_type) {
+    EXPECT_EQ(1ul, ukm_tester().sources_count());
+    const ukm::UkmSource* source = ukm_tester().GetSourceForUrl(page.url);
+    EXPECT_EQ(GURL(page.url), source->url());
+
+    ukm_tester().ExpectEntry(
+        *source, internal::kUkmPageDomainEventName,
+        {{internal::kUkmDomainTypeName, static_cast<int>(domain_type)}});
+  }
+
+  void ExpectMetricsAndHistograms(
+      const internal::PageAddressInfo& page,
+      const std::vector<internal::UkmMetricInfo>& expected_metrics,
+      const std::map<std::string, int>& expected_histograms) {
+    // The page domain info UKM entry will always be created, so we expect that
+    // there should be one more UKM entry than the expected number of metrics
+    // entries.
+    EXPECT_EQ(expected_metrics.size() + 1, ukm_tester().entries_count());
+
+    const ukm::UkmSource* source = ukm_tester().GetSourceForUrl(page.url);
+    for (auto entry : expected_metrics) {
+      std::vector<std::pair<const char*, int64_t>> metric_values = {
+          {internal::kUkmResourceTypeName, entry.resource_type},
+          {internal::kUkmSuccessfulCountName, entry.success_count},
+          {internal::kUkmFailedCountName, entry.failed_count}};
+      if (entry.resource_type == internal::RESOURCE_TYPE_LOCALHOST) {
+        // Localhost page load
+        metric_values.push_back(
+            {internal::kUkmPortTypeName, static_cast<int>(entry.port_type)});
+      }
+      ukm_tester().ExpectEntry(
+          *source, internal::kUkmLocalNetworkRequestsEventName, metric_values);
+    }
+
+    // Should have generated UMA histograms for all requests made.
+    for (auto hist : expected_histograms) {
+      histogram_tester().ExpectUniqueSample(hist.first, hist.second, 1);
+    }
+  }
+
+ private:
+  std::unique_ptr<content::NavigationSimulator> navigation_simulator_;
+};
+
+TEST_F(LocalNetworkRequestsPageLoadMetricsObserverTest, NoMetrics) {
+  EXPECT_EQ(0ul, ukm_tester().sources_count());
+  EXPECT_EQ(0ul, ukm_tester().entries_count());
+
+  // Sanity check
+  ExpectNoHistograms();
+}
+
+TEST_F(LocalNetworkRequestsPageLoadMetricsObserverTest,
+       PublicPageIPv6PublicRequests) {
+  // Navigate to a public page and make only public resource requests.
+  const internal::PageAddressInfo& page = internal::kPublicPageIPv6;
+  NavigateToPageAndLoadResources(page, {{internal::kPublicRequest1, true},
+                                        {internal::kPublicPageIPv6, true}});
+
+  // Should generate only a domain type UKM entry and nothing else.
+  ExpectUkmPageDomainMetric(page, internal::DOMAIN_TYPE_PUBLIC);
+  ExpectEmptyHistograms(internal::DOMAIN_TYPE_PUBLIC);
+}
+
+TEST_F(LocalNetworkRequestsPageLoadMetricsObserverTest,
+       PublicPagePublicRequests) {
+  // Navigate to a public page and make only public resource requests.
+  const internal::PageAddressInfo& page = internal::kPublicPage;
+  NavigateToPageAndLoadResources(page, {{internal::kPublicRequest1, true},
+                                        {internal::kPublicRequest2, true},
+                                        {internal::kPublicPageIPv6, true}});
+
+  // Should generate only a domain type UKM entry and nothing else.
+  ExpectUkmPageDomainMetric(page, internal::DOMAIN_TYPE_PUBLIC);
+  ExpectEmptyHistograms(internal::DOMAIN_TYPE_PUBLIC);
+}
+
+TEST_F(LocalNetworkRequestsPageLoadMetricsObserverTest,
+       PrivatePageSelfRequests) {
+  // Navigate to a private page and make resource requests only to the page
+  // itself.
+  const internal::PageAddressInfo& page = internal::kSameSubnetRequest1;
+  NavigateToPageAndLoadResources(page, {{internal::kSameSubnetRequest2, true},
+                                        {internal::kSameSubnetRequest3, false},
+                                        {page, true}});
+
+  // Should generate only a domain type UKM entry and nothing else.
+  ExpectUkmPageDomainMetric(page, internal::DOMAIN_TYPE_PRIVATE);
+  ExpectEmptyHistograms(internal::DOMAIN_TYPE_PRIVATE);
+}
+
+TEST_F(LocalNetworkRequestsPageLoadMetricsObserverTest, PrivatePageNoRequests) {
+  // Navigate to a private page and make no resource requests.
+  const internal::PageAddressInfo& page = internal::kPrivatePage;
+  NavigateToPageAndLoadResources(
+      page, std::vector<std::pair<internal::PageAddressInfo, bool>>{});
+
+  // Should generate only a domain type UKM entry and nothing else.
+  ExpectUkmPageDomainMetric(page, internal::DOMAIN_TYPE_PRIVATE);
+  ExpectEmptyHistograms(internal::DOMAIN_TYPE_PRIVATE);
+}
+
+TEST_F(LocalNetworkRequestsPageLoadMetricsObserverTest, LocalhostPage) {
+  // Navigate to a localhost page and make some arbitrary resource requests.
+  const internal::PageAddressInfo& page = internal::kLocalhostPage;
+  NavigateToPageAndLoadResources(page, {{internal::kPublicRequest1, true},
+                                        {internal::kPublicRequest2, false},
+                                        {internal::kSameSubnetRequest1, true},
+                                        {internal::kLocalhostRequest5, true}});
+
+  // Should generate only a domain type UKM entry and nothing else.
+  ExpectUkmPageDomainMetric(page, internal::DOMAIN_TYPE_LOCALHOST);
+  ExpectNoHistograms();
+}
+
+TEST_F(LocalNetworkRequestsPageLoadMetricsObserverTest, LocalhostPageIPv6) {
+  // Navigate to a localhost page with an IPv6 address and make some arbitrary
+  // resource requests.
+  const internal::PageAddressInfo& page = internal::kLocalhostPageIPv6;
+  NavigateToPageAndLoadResources(page, {{internal::kPublicRequest1, false},
+                                        {internal::kLocalhostRequest2, true},
+                                        {internal::kDiffSubnetRequest1, false},
+                                        {internal::kLocalhostRequest4, true}});
+
+  // Should generate only a domain type UKM entry and nothing else.
+  ExpectUkmPageDomainMetric(page, internal::DOMAIN_TYPE_LOCALHOST);
+  ExpectNoHistograms();
+}
+
+TEST_F(LocalNetworkRequestsPageLoadMetricsObserverTest,
+       PublicPageAllSuccessfulRequests) {
+  // Navigate to a public page and make successful resource requests to all
+  // resource types.
+  const internal::PageAddressInfo& page = internal::kPublicPage;
+  NavigateToPageAndLoadResources(page, {{internal::kPublicPage, true},
+                                        {internal::kPublicPageIPv6, true},
+                                        {internal::kPrivatePage, true},
+                                        {internal::kLocalhostPage, true},
+                                        {internal::kLocalhostPageIPv6, true},
+                                        {internal::kPublicRequest1, true},
+                                        {internal::kPublicRequest2, true},
+                                        {internal::kSameSubnetRequest1, true},
+                                        {internal::kSameSubnetRequest2, true},
+                                        {internal::kSameSubnetRequest3, true},
+                                        {internal::kDiffSubnetRequest1, true},
+                                        {internal::kDiffSubnetRequest2, true},
+                                        {internal::kLocalhostRequest1, true},
+                                        {internal::kLocalhostRequest2, true},
+                                        {internal::kLocalhostRequest3, true},
+                                        {internal::kLocalhostRequest4, true},
+                                        {internal::kLocalhostRequest5, true},
+                                        {internal::kRouterRequest1, true},
+                                        {internal::kRouterRequest2, true}});
+
+  // Should now have generated UKM entries for each of the types of resources
+  // requested except for the public resources.
+  ExpectUkmPageDomainMetric(page, internal::DOMAIN_TYPE_PUBLIC);
+
+  // We should now see UKM entries and UMA histograms for each of the types of
+  // resources requested except for public resources.
+  ExpectMetricsAndHistograms(
+      page,
+      // List of expected UKM metric values.
+      {
+          {internal::RESOURCE_TYPE_ROUTER, internal::PORT_TYPE_WEB, 1,
+           0},  // 10.0.0.1:80
+          {internal::RESOURCE_TYPE_PRIVATE, internal::PORT_TYPE_WEB, 1,
+           0},  // 10.0.10.200:80
+          {internal::RESOURCE_TYPE_PRIVATE, internal::PORT_TYPE_WEB, 1,
+           0},  // 172.16.0.85:8181
+          {internal::RESOURCE_TYPE_ROUTER, internal::PORT_TYPE_WEB, 1,
+           0},  // 192.168.10.1:443
+          {internal::RESOURCE_TYPE_PRIVATE, internal::PORT_TYPE_WEB, 1,
+           0},  // 192.168.10.123:80
+          {internal::RESOURCE_TYPE_PRIVATE, internal::PORT_TYPE_WEB, 3,
+           0},  // 192.168.10.200:8000
+          {internal::RESOURCE_TYPE_LOCALHOST, internal::PORT_TYPE_WEB, 2,
+           0},  // 127.0.0.1:80
+          {internal::RESOURCE_TYPE_LOCALHOST, internal::PORT_TYPE_PRINT, 1,
+           0},  // 127.0.2.1:515
+          {internal::RESOURCE_TYPE_LOCALHOST, internal::PORT_TYPE_DB, 1,
+           0},  // 127.0.1.1:3306
+          {internal::RESOURCE_TYPE_LOCALHOST, internal::PORT_TYPE_WEB, 1,
+           0},  // 127.0.0.1:8080
+          {internal::RESOURCE_TYPE_LOCALHOST, internal::PORT_TYPE_DEV, 1,
+           0},  // 127.100.150.200:9000
+          {internal::RESOURCE_TYPE_LOCALHOST, internal::PORT_TYPE_OTHER, 1,
+           0},  // 127.0.0.1:9876
+      },
+      // List of expected nonzero UMA histogram values.
+      {
+          {internal::GetNonlocalhostHistogramNames()
+               .at(internal::DOMAIN_TYPE_PUBLIC)
+               .at(internal::RESOURCE_TYPE_ROUTER)
+               .at(true),
+           2},
+          {internal::GetNonlocalhostHistogramNames()
+               .at(internal::DOMAIN_TYPE_PUBLIC)
+               .at(internal::RESOURCE_TYPE_PRIVATE)
+               .at(true),
+           6},
+          {internal::GetLocalhostHistogramNames()
+               .at(internal::DOMAIN_TYPE_PUBLIC)
+               .at(internal::PORT_TYPE_WEB)
+               .at(true),
+           3},
+          {internal::GetLocalhostHistogramNames()
+               .at(internal::DOMAIN_TYPE_PUBLIC)
+               .at(internal::PORT_TYPE_PRINT)
+               .at(true),
+           1},
+          {internal::GetLocalhostHistogramNames()
+               .at(internal::DOMAIN_TYPE_PUBLIC)
+               .at(internal::PORT_TYPE_DB)
+               .at(true),
+           1},
+          {internal::GetLocalhostHistogramNames()
+               .at(internal::DOMAIN_TYPE_PUBLIC)
+               .at(internal::PORT_TYPE_DEV)
+               .at(true),
+           1},
+          {internal::GetLocalhostHistogramNames()
+               .at(internal::DOMAIN_TYPE_PUBLIC)
+               .at(internal::PORT_TYPE_OTHER)
+               .at(true),
+           1},
+      });
+}
+
+TEST_F(LocalNetworkRequestsPageLoadMetricsObserverTest,
+       PrivatePageAllSuccessfulRequests) {
+  // Navigate to a private page and make successful resource requests to all
+  // resource types.
+  const internal::PageAddressInfo& page = internal::kPrivatePage;
+  NavigateToPageAndLoadResources(page, {{internal::kPublicPage, true},
+                                        {internal::kPublicPageIPv6, true},
+                                        {internal::kPrivatePage, true},
+                                        {internal::kLocalhostPage, true},
+                                        {internal::kLocalhostPageIPv6, true},
+                                        {internal::kPublicRequest1, true},
+                                        {internal::kPublicRequest2, true},
+                                        {internal::kSameSubnetRequest1, true},
+                                        {internal::kSameSubnetRequest2, true},
+                                        {internal::kSameSubnetRequest3, true},
+                                        {internal::kDiffSubnetRequest1, true},
+                                        {internal::kDiffSubnetRequest2, true},
+                                        {internal::kLocalhostRequest1, true},
+                                        {internal::kLocalhostRequest2, true},
+                                        {internal::kLocalhostRequest3, true},
+                                        {internal::kLocalhostRequest4, true},
+                                        {internal::kLocalhostRequest5, true},
+                                        {internal::kRouterRequest1, true},
+                                        {internal::kRouterRequest2, true}});
+
+  // Should now have generated UKM entries for each of the types of resources
+  // requested except for the public resources.
+  ExpectUkmPageDomainMetric(page, internal::DOMAIN_TYPE_PRIVATE);
+
+  // We should now see UKM entries and UMA histograms for each of the types of
+  // resources requested except for the request to the page itself.
+  ExpectMetricsAndHistograms(
+      page,
+      // List of expected UKM metric values.
+      {
+          {internal::RESOURCE_TYPE_LOCAL_DIFF_SUBNET, internal::PORT_TYPE_WEB,
+           1, 0},  // 10.0.0.1:80
+          {internal::RESOURCE_TYPE_LOCAL_DIFF_SUBNET, internal::PORT_TYPE_WEB,
+           1, 0},  // 10.0.10.200:80
+          {internal::RESOURCE_TYPE_PUBLIC, internal::PORT_TYPE_WEB, 1,
+           0},  // 100.150.200.250:80
+          {internal::RESOURCE_TYPE_LOCAL_DIFF_SUBNET, internal::PORT_TYPE_WEB,
+           1, 0},  // 172.16.0.85:8181
+          {internal::RESOURCE_TYPE_PUBLIC, internal::PORT_TYPE_WEB, 1,
+           0},  // 192.10.20.30:443
+          {internal::RESOURCE_TYPE_LOCAL_SAME_SUBNET, internal::PORT_TYPE_WEB,
+           1, 0},  // 192.168.10.1:443
+          {internal::RESOURCE_TYPE_LOCAL_SAME_SUBNET, internal::PORT_TYPE_WEB,
+           3, 0},  // 192.168.10.200:8000
+          {internal::RESOURCE_TYPE_PUBLIC, internal::PORT_TYPE_WEB, 1,
+           0},  // 216.58.195.78:443
+          {internal::RESOURCE_TYPE_PUBLIC, internal::PORT_TYPE_WEB, 1,
+           0},  // [2607:f8b0:4005:809::200e]:443
+          {internal::RESOURCE_TYPE_LOCALHOST, internal::PORT_TYPE_WEB, 2,
+           0},  // 127.0.0.1:80
+          {internal::RESOURCE_TYPE_LOCALHOST, internal::PORT_TYPE_PRINT, 1,
+           0},  // 127.0.2.1:515
+          {internal::RESOURCE_TYPE_LOCALHOST, internal::PORT_TYPE_DB, 1,
+           0},  // 127.0.1.1:3306
+          {internal::RESOURCE_TYPE_LOCALHOST, internal::PORT_TYPE_WEB, 1,
+           0},  // 127.0.0.1:8080
+          {internal::RESOURCE_TYPE_LOCALHOST, internal::PORT_TYPE_DEV, 1,
+           0},  // 127.100.150.200:9000
+          {internal::RESOURCE_TYPE_LOCALHOST, internal::PORT_TYPE_OTHER, 1,
+           0},  // 127.0.0.1:9876
+      },
+      // List of expected nonzero UMA histogram values.
+      {
+          {internal::GetNonlocalhostHistogramNames()
+               .at(internal::DOMAIN_TYPE_PRIVATE)
+               .at(internal::RESOURCE_TYPE_PUBLIC)
+               .at(true),
+           4},
+          {internal::GetNonlocalhostHistogramNames()
+               .at(internal::DOMAIN_TYPE_PRIVATE)
+               .at(internal::RESOURCE_TYPE_LOCAL_DIFF_SUBNET)
+               .at(true),
+           3},
+          {internal::GetNonlocalhostHistogramNames()
+               .at(internal::DOMAIN_TYPE_PRIVATE)
+               .at(internal::RESOURCE_TYPE_LOCAL_SAME_SUBNET)
+               .at(true),
+           4},
+          {internal::GetLocalhostHistogramNames()
+               .at(internal::DOMAIN_TYPE_PRIVATE)
+               .at(internal::PORT_TYPE_WEB)
+               .at(true),
+           3},
+          {internal::GetLocalhostHistogramNames()
+               .at(internal::DOMAIN_TYPE_PRIVATE)
+               .at(internal::PORT_TYPE_PRINT)
+               .at(true),
+           1},
+          {internal::GetLocalhostHistogramNames()
+               .at(internal::DOMAIN_TYPE_PRIVATE)
+               .at(internal::PORT_TYPE_DB)
+               .at(true),
+           1},
+          {internal::GetLocalhostHistogramNames()
+               .at(internal::DOMAIN_TYPE_PRIVATE)
+               .at(internal::PORT_TYPE_DEV)
+               .at(true),
+           1},
+          {internal::GetLocalhostHistogramNames()
+               .at(internal::DOMAIN_TYPE_PRIVATE)
+               .at(internal::PORT_TYPE_OTHER)
+               .at(true),
+           1},
+      });
+}
+
+TEST_F(LocalNetworkRequestsPageLoadMetricsObserverTest,
+       PrivatePageAllFailedRequests) {
+  // Navigate to a private page and make successful resource requests to all
+  // resource types.
+  const internal::PageAddressInfo& page = internal::kPrivatePage;
+  NavigateToPageAndLoadResources(page, {{internal::kPublicPage, false},
+                                        {internal::kPublicPageIPv6, false},
+                                        {internal::kPrivatePage, false},
+                                        {internal::kLocalhostPage, false},
+                                        {internal::kLocalhostPageIPv6, false},
+                                        {internal::kPublicRequest1, false},
+                                        {internal::kPublicRequest2, false},
+                                        {internal::kSameSubnetRequest1, false},
+                                        {internal::kSameSubnetRequest2, false},
+                                        {internal::kSameSubnetRequest3, false},
+                                        {internal::kDiffSubnetRequest1, false},
+                                        {internal::kDiffSubnetRequest2, false},
+                                        {internal::kLocalhostRequest1, false},
+                                        {internal::kLocalhostRequest2, false},
+                                        {internal::kLocalhostRequest3, false},
+                                        {internal::kLocalhostRequest4, false},
+                                        {internal::kLocalhostRequest5, false},
+                                        {internal::kRouterRequest1, false},
+                                        {internal::kRouterRequest2, false}});
+
+  // Should now have generated UKM entries for each of the types of resources
+  // requested except for the public resources.
+  ExpectUkmPageDomainMetric(page, internal::DOMAIN_TYPE_PRIVATE);
+
+  ExpectMetricsAndHistograms(
+      page,
+      // List of expected UKM metric values.
+      {
+          {internal::RESOURCE_TYPE_LOCAL_DIFF_SUBNET, internal::PORT_TYPE_WEB,
+           0, 1},  // 10.0.0.1:80
+          {internal::RESOURCE_TYPE_LOCAL_DIFF_SUBNET, internal::PORT_TYPE_WEB,
+           0, 1},  // 10.0.10.200:80
+          {internal::RESOURCE_TYPE_PUBLIC, internal::PORT_TYPE_WEB, 0,
+           1},  // 100.150.200.250:80
+          {internal::RESOURCE_TYPE_LOCAL_DIFF_SUBNET, internal::PORT_TYPE_WEB,
+           0, 1},  // 172.16.0.85:8181
+          {internal::RESOURCE_TYPE_PUBLIC, internal::PORT_TYPE_WEB, 0,
+           1},  // 192.10.20.30:443
+          {internal::RESOURCE_TYPE_LOCAL_SAME_SUBNET, internal::PORT_TYPE_WEB,
+           0, 1},  // 192.168.10.1:443
+          {internal::RESOURCE_TYPE_LOCAL_SAME_SUBNET, internal::PORT_TYPE_WEB,
+           0, 3},  // 192.168.10.200:8000
+          {internal::RESOURCE_TYPE_PUBLIC, internal::PORT_TYPE_WEB, 0,
+           1},  // 216.58.195.78:443
+          {internal::RESOURCE_TYPE_PUBLIC, internal::PORT_TYPE_WEB, 0,
+           1},  // [2607:f8b0:4005:809::200e]:443
+          {internal::RESOURCE_TYPE_LOCALHOST, internal::PORT_TYPE_WEB, 0,
+           2},  // 127.0.0.1:80
+          {internal::RESOURCE_TYPE_LOCALHOST, internal::PORT_TYPE_PRINT, 0,
+           1},  // 127.0.2.1:515
+          {internal::RESOURCE_TYPE_LOCALHOST, internal::PORT_TYPE_DB, 0,
+           1},  // 127.0.1.1:3306
+          {internal::RESOURCE_TYPE_LOCALHOST, internal::PORT_TYPE_WEB, 0,
+           1},  // 127.0.0.1:8080
+          {internal::RESOURCE_TYPE_LOCALHOST, internal::PORT_TYPE_DEV, 0,
+           1},  // 127.100.150.200:9000
+          {internal::RESOURCE_TYPE_LOCALHOST, internal::PORT_TYPE_OTHER, 0,
+           1},  // 127.0.0.1:9876
+      },
+      // List of expected nonzero UMA histogram values.
+      {
+          {internal::GetNonlocalhostHistogramNames()
+               .at(internal::DOMAIN_TYPE_PRIVATE)
+               .at(internal::RESOURCE_TYPE_PUBLIC)
+               .at(false),
+           4},
+          {internal::GetNonlocalhostHistogramNames()
+               .at(internal::DOMAIN_TYPE_PRIVATE)
+               .at(internal::RESOURCE_TYPE_LOCAL_DIFF_SUBNET)
+               .at(false),
+           3},
+          {internal::GetNonlocalhostHistogramNames()
+               .at(internal::DOMAIN_TYPE_PRIVATE)
+               .at(internal::RESOURCE_TYPE_LOCAL_SAME_SUBNET)
+               .at(false),
+           4},
+          {internal::GetLocalhostHistogramNames()
+               .at(internal::DOMAIN_TYPE_PRIVATE)
+               .at(internal::PORT_TYPE_WEB)
+               .at(false),
+           3},
+          {internal::GetLocalhostHistogramNames()
+               .at(internal::DOMAIN_TYPE_PRIVATE)
+               .at(internal::PORT_TYPE_PRINT)
+               .at(false),
+           1},
+          {internal::GetLocalhostHistogramNames()
+               .at(internal::DOMAIN_TYPE_PRIVATE)
+               .at(internal::PORT_TYPE_DB)
+               .at(false),
+           1},
+          {internal::GetLocalhostHistogramNames()
+               .at(internal::DOMAIN_TYPE_PRIVATE)
+               .at(internal::PORT_TYPE_DEV)
+               .at(false),
+           1},
+          {internal::GetLocalhostHistogramNames()
+               .at(internal::DOMAIN_TYPE_PRIVATE)
+               .at(internal::PORT_TYPE_OTHER)
+               .at(false),
+           1},
+      });
+}
+
+TEST_F(LocalNetworkRequestsPageLoadMetricsObserverTest,
+       PublicPageMixedStatusRequests) {
+  // Navigate to a public page and make mixed status resource requests.
+  const internal::PageAddressInfo& page = internal::kPublicPage;
+  NavigateToPageAndLoadResources(page, {{internal::kPublicRequest1, true},
+                                        {internal::kSameSubnetRequest1, true},
+                                        {internal::kLocalhostRequest2, false},
+                                        {internal::kDiffSubnetRequest2, true},
+                                        {internal::kLocalhostRequest5, false},
+                                        {internal::kDiffSubnetRequest2, false},
+                                        {internal::kRouterRequest1, true}});
+
+  // Should now have generated UKM entries for each of the types of resources
+  // requested except for the public resources.
+  ExpectUkmPageDomainMetric(page, internal::DOMAIN_TYPE_PUBLIC);
+
+  ExpectMetricsAndHistograms(
+      page,
+      // List of expected UKM metric values.
+      {
+          {internal::RESOURCE_TYPE_ROUTER, internal::PORT_TYPE_WEB, 1,
+           0},  // 10.0.0.1:80
+          {internal::RESOURCE_TYPE_PRIVATE, internal::PORT_TYPE_WEB, 1,
+           1},  // 172.16.0.85:8181
+          {internal::RESOURCE_TYPE_PRIVATE, internal::PORT_TYPE_DEV, 1,
+           0},  // 192.168.10.200:8000
+          {internal::RESOURCE_TYPE_LOCALHOST, internal::PORT_TYPE_DB, 0,
+           1},  // 127.0.1.1:3306
+          {internal::RESOURCE_TYPE_LOCALHOST, internal::PORT_TYPE_OTHER, 0,
+           1},  // 127.0.0.1:9876
+      },
+      // List of expected nonzero UMA histogram values.
+      {
+          {internal::GetNonlocalhostHistogramNames()
+               .at(internal::DOMAIN_TYPE_PUBLIC)
+               .at(internal::RESOURCE_TYPE_ROUTER)
+               .at(true),
+           1},
+          {internal::GetNonlocalhostHistogramNames()
+               .at(internal::DOMAIN_TYPE_PUBLIC)
+               .at(internal::RESOURCE_TYPE_PRIVATE)
+               .at(true),
+           2},
+          {internal::GetNonlocalhostHistogramNames()
+               .at(internal::DOMAIN_TYPE_PUBLIC)
+               .at(internal::RESOURCE_TYPE_PRIVATE)
+               .at(false),
+           1},
+          {internal::GetLocalhostHistogramNames()
+               .at(internal::DOMAIN_TYPE_PUBLIC)
+               .at(internal::PORT_TYPE_DB)
+               .at(false),
+           1},
+          {internal::GetLocalhostHistogramNames()
+               .at(internal::DOMAIN_TYPE_PUBLIC)
+               .at(internal::PORT_TYPE_OTHER)
+               .at(false),
+           1},
+      });
+}
+
+TEST_F(LocalNetworkRequestsPageLoadMetricsObserverTest,
+       PublicPageLargeNumberOfRequests) {
+  // This test also verifies the sequence and timing of UKM metric generation.
+
+  // Navigate to a public page with an IPv6 address.
+  const internal::PageAddressInfo& page = internal::kPublicPageIPv6;
+  SimulateNavigateAndCommit(page);
+
+  // Should generate only a domain type UKM entry by this point.
+  ExpectUkmPageDomainMetric(page, internal::DOMAIN_TYPE_PUBLIC);
+
+  // Make 100 each of many different types of requests, with 1000 of a single
+  // type.
+  std::vector<std::pair<internal::PageAddressInfo, bool>> requests = {
+      {internal::kPublicRequest1, true},
+      {internal::kLocalhostPage, true},
+      {internal::kLocalhostPageIPv6, false},
+      {internal::kSameSubnetRequest1, false},
+      {internal::kDiffSubnetRequest2, false},
+  };
+  for (auto request : requests) {
+    for (int i = 0; i < 100; ++i) {
+      SimulateLoadedResource(request.first, (request.second ? 0 : -1));
+    }
+  }
+  for (int i = 0; i < 1000; ++i) {
+    SimulateLoadedSuccessfulResource(internal::kDiffSubnetRequest1);
+  }
+
+  // At this point, we should still only see the domain type UKM entry.
+  EXPECT_EQ(1ul, ukm_tester().entries_count());
+
+  // Close the page.
+  DeleteContents();
+
+  ExpectMetricsAndHistograms(
+      page,
+      // List of expected UKM metric values.
+      {
+          {internal::RESOURCE_TYPE_PRIVATE, internal::PORT_TYPE_WEB, 1000,
+           0},  // 10.0.10.200:80
+          {internal::RESOURCE_TYPE_PRIVATE, internal::PORT_TYPE_WEB, 0,
+           100},  // 172.16.0.85:8181
+          {internal::RESOURCE_TYPE_PRIVATE, internal::PORT_TYPE_DEV, 0,
+           100},  // 192.168.10.200:9000
+          {internal::RESOURCE_TYPE_LOCALHOST, internal::PORT_TYPE_WEB, 100,
+           100},  // 127.0.0.1:80
+      },
+      // List of expected nonzero UMA histogram values.
+      {
+          {internal::GetNonlocalhostHistogramNames()
+               .at(internal::DOMAIN_TYPE_PUBLIC)
+               .at(internal::RESOURCE_TYPE_PRIVATE)
+               .at(true),
+           1000},
+          {internal::GetNonlocalhostHistogramNames()
+               .at(internal::DOMAIN_TYPE_PUBLIC)
+               .at(internal::RESOURCE_TYPE_PRIVATE)
+               .at(false),
+           200},
+          {internal::GetLocalhostHistogramNames()
+               .at(internal::DOMAIN_TYPE_PUBLIC)
+               .at(internal::PORT_TYPE_WEB)
+               .at(true),
+           100},
+          {internal::GetLocalhostHistogramNames()
+               .at(internal::DOMAIN_TYPE_PUBLIC)
+               .at(internal::PORT_TYPE_WEB)
+               .at(false),
+           100},
+      });
+}
+
+TEST_F(LocalNetworkRequestsPageLoadMetricsObserverTest,
+       PublicPageRequestIpInUrlOnly) {
+  const internal::PageAddressInfo& page = internal::kPublicPage;
+  SimulateNavigateAndCommit(page);
+
+  // Load a resource that has the IP address in the URL but returned an empty
+  // socket address for some reason.
+  PageLoadMetricsObserverTestHarness::SimulateLoadedResource(
+      {
+          GURL(internal::kDiffSubnetRequest2.url), net::HostPortPair(),
+          -1 /* frame_tree_node_id */, true /* was_cached */,
+          1024 * 20 /* raw_body_bytes */,
+          0 /* original_network_content_length */,
+          nullptr /* data_reduction_proxy_data */,
+          content::ResourceType::RESOURCE_TYPE_MAIN_FRAME, 0,
+      },
+      GetGlobalRequestID());
+  DeleteContents();
+
+  // We should still see a UKM entry and UMA histogram for the resource request.
+  ExpectUkmPageDomainMetric(page, internal::DOMAIN_TYPE_PUBLIC);
+  ExpectMetricsAndHistograms(
+      page, {{internal::RESOURCE_TYPE_PRIVATE, internal::PORT_TYPE_WEB, 1, 0}},
+      {{internal::GetNonlocalhostHistogramNames()
+            .at(internal::DOMAIN_TYPE_PUBLIC)
+            .at(internal::RESOURCE_TYPE_PRIVATE)
+            .at(true),
+        1}});
+}
+
+TEST_F(LocalNetworkRequestsPageLoadMetricsObserverTest,
+       PublicPageRequestIpNotPresent) {
+  const internal::PageAddressInfo& page = internal::kPublicPage;
+  SimulateNavigateAndCommit(page);
+
+  // Load a resource that doesn't have the IP address in the URL and returned an
+  // empty socket address (e.g., failed DNS resolution).
+  PageLoadMetricsObserverTestHarness::SimulateLoadedResource(
+      {
+          GURL(internal::kPrivatePage.url), net::HostPortPair(),
+          -1 /* frame_tree_node_id */, false /* was_cached */,
+          0 /* raw_body_bytes */, 0 /* original_network_content_length */,
+          nullptr /* data_reduction_proxy_data */,
+          content::ResourceType::RESOURCE_TYPE_MAIN_FRAME, -20,
+      },
+      GetGlobalRequestID());
+  DeleteContents();
+
+  // We shouldn't see any UKM entries or UMA histograms this time.
+  ExpectUkmPageDomainMetric(page, internal::DOMAIN_TYPE_PUBLIC);
+  ExpectEmptyHistograms(internal::DOMAIN_TYPE_PUBLIC);
+}
+
+TEST_F(LocalNetworkRequestsPageLoadMetricsObserverTest, PrivatePageSubnet10) {
+  // Navigate to a private page on the 10.0.0.0/8 subnet and make requests to
+  // other 10.0.0.0/8 subnet resources.
+  const internal::PageAddressInfo& page = internal::kRouterRequest1;
+  NavigateToPageAndLoadResources(page, {{internal::kDiffSubnetRequest1, false},
+                                        {internal::kDiffSubnetRequest3, false},
+                                        {internal::kRouterRequest2, false}});
+  ExpectUkmPageDomainMetric(page, internal::DOMAIN_TYPE_PRIVATE);
+
+  // The first two requests should be on the same subnet and the last request
+  // should be on a different subnet.
+  ExpectMetricsAndHistograms(
+      page,
+      {
+          {internal::RESOURCE_TYPE_LOCAL_SAME_SUBNET, internal::PORT_TYPE_WEB,
+           0, 1},  // 10.0.10.200:80
+          {internal::RESOURCE_TYPE_LOCAL_SAME_SUBNET, internal::PORT_TYPE_OTHER,
+           0, 1},  // 10.15.20.25:12345
+          {internal::RESOURCE_TYPE_LOCAL_DIFF_SUBNET, internal::PORT_TYPE_WEB,
+           0, 1},  // 192.168.10.1:443
+      },
+      {
+          {internal::GetNonlocalhostHistogramNames()
+               .at(internal::DOMAIN_TYPE_PRIVATE)
+               .at(internal::RESOURCE_TYPE_LOCAL_SAME_SUBNET)
+               .at(false),
+           2},
+          {internal::GetNonlocalhostHistogramNames()
+               .at(internal::DOMAIN_TYPE_PRIVATE)
+               .at(internal::RESOURCE_TYPE_LOCAL_DIFF_SUBNET)
+               .at(false),
+           1},
+      });
+}
+
+TEST_F(LocalNetworkRequestsPageLoadMetricsObserverTest, PrivatePageSubnet172) {
+  // Navigate to a private page on the 10.0.0.0/8 subnet and make requests to
+  // other 10.0.0.0/8 subnet resources.
+  const internal::PageAddressInfo& page = internal::kDiffSubnetRequest2;
+  NavigateToPageAndLoadResources(page, {{internal::kDiffSubnetRequest4, false},
+                                        {internal::kRouterRequest1, false}});
+  ExpectUkmPageDomainMetric(page, internal::DOMAIN_TYPE_PRIVATE);
+
+  // The first two requests should be on the same subnet and the last request
+  // should be on a different subnet.
+  ExpectMetricsAndHistograms(
+      page,
+      {
+          {internal::RESOURCE_TYPE_LOCAL_DIFF_SUBNET, internal::PORT_TYPE_WEB,
+           0, 1},  // 10.0.10.200:80
+          {internal::RESOURCE_TYPE_LOCAL_SAME_SUBNET, internal::PORT_TYPE_PRINT,
+           0, 1},  // 172.31.100.20:515
+      },
+      {
+          {internal::GetNonlocalhostHistogramNames()
+               .at(internal::DOMAIN_TYPE_PRIVATE)
+               .at(internal::RESOURCE_TYPE_LOCAL_SAME_SUBNET)
+               .at(false),
+           1},
+          {internal::GetNonlocalhostHistogramNames()
+               .at(internal::DOMAIN_TYPE_PRIVATE)
+               .at(internal::RESOURCE_TYPE_LOCAL_DIFF_SUBNET)
+               .at(false),
+           1},
+      });
+}
+
+TEST_F(LocalNetworkRequestsPageLoadMetricsObserverTest, PrivatePageFailedLoad) {
+  GURL url(internal::kPrivatePage.url);
+  auto navigation_simulator =
+      content::NavigationSimulator::CreateRendererInitiated(url, main_rfh());
+  navigation_simulator->Start();
+  navigation_simulator->Fail(-20);
+  navigation_simulator->CommitErrorPage();
+
+  // Nothing should have been generated.
+  EXPECT_EQ(0ul, ukm_tester().sources_count());
+  EXPECT_EQ(0ul, ukm_tester().entries_count());
+  ExpectNoHistograms();
+}
diff --git a/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.cc b/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.cc
index 87eb3d8..60f99b82 100644
--- a/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.cc
+++ b/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.cc
@@ -15,6 +15,7 @@
 #include "chrome/test/base/testing_browser_process.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_data.h"
 #include "content/public/browser/navigation_entry.h"
+#include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/test/web_contents_tester.h"
@@ -100,14 +101,22 @@
         << "Main frame resources must have a GlobalRequestID.";
   }
 
+  // For consistency with browser-side navigation, we provide a null RFH for
+  // main frame and sub frame resources.
+  content::RenderFrameHost* render_frame_host_or_null =
+      (info.resource_type == content::RESOURCE_TYPE_MAIN_FRAME ||
+       info.resource_type == content::RESOURCE_TYPE_SUB_FRAME)
+          ? nullptr
+          : web_contents()->GetMainFrame();
+
   observer_->OnRequestComplete(
       info.url, info.host_port_pair, info.frame_tree_node_id, request_id,
-      info.resource_type, info.was_cached,
+      render_frame_host_or_null, info.resource_type, info.was_cached,
       info.data_reduction_proxy_data
           ? info.data_reduction_proxy_data->DeepCopy()
           : nullptr,
       info.raw_body_bytes, info.original_network_content_length,
-      base::TimeTicks::Now(), 0);
+      base::TimeTicks::Now(), info.net_error);
 }
 
 void PageLoadMetricsObserverTestHarness::SimulateInputEvent(
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc b/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc
index 4d5a1ca1..c81c149 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc
+++ b/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc
@@ -1023,6 +1023,27 @@
   histogram_tester_.ExpectBucketCount(internal::kHistogramTotalBytes, 10, 1);
 }
 
+IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, PayloadSizeChildFrame) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  auto waiter = CreatePageLoadMetricsWaiter();
+  waiter->AddPageExpectation(TimingField::LOAD_EVENT);
+  ui_test_utils::NavigateToURL(
+      browser(),
+      embedded_test_server()->GetURL("/page_load_metrics/large_iframe.html"));
+  waiter->Wait();
+
+  // Payload histograms are only logged when a page load terminates, so force
+  // navigation to another page.
+  NavigateToUntrackedUrl();
+
+  histogram_tester_.ExpectTotalCount(internal::kHistogramTotalBytes, 1);
+
+  // Verify that there is a single sample recorded in the 10kB bucket (the size
+  // of the iframe response).
+  histogram_tester_.ExpectBucketCount(internal::kHistogramTotalBytes, 10, 1);
+}
+
 IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest,
                        PayloadSizeIgnoresDownloads) {
   ASSERT_TRUE(embedded_test_server()->Start());
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc b/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc
index ce05d70..1c0c832 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc
+++ b/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc
@@ -26,6 +26,7 @@
 #include "chrome/browser/page_load_metrics/observers/google_captcha_observer.h"
 #include "chrome/browser/page_load_metrics/observers/https_engagement_metrics/https_engagement_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/loading_predictor_page_load_metrics_observer.h"
+#include "chrome/browser/page_load_metrics/observers/local_network_requests_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/lofi_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/media_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/multi_tab_loading_page_load_metrics_observer.h"
@@ -132,6 +133,8 @@
                 web_contents_);
     if (loading_predictor_observer)
       tracker->AddObserver(std::move(loading_predictor_observer));
+    tracker->AddObserver(
+        base::MakeUnique<LocalNetworkRequestsPageLoadMetricsObserver>());
   } else {
     std::unique_ptr<page_load_metrics::PageLoadMetricsObserver>
         prerender_observer =
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_observer.h b/chrome/browser/page_load_metrics/page_load_metrics_observer.h
index 122ccbc..3f964a2 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/page_load_metrics_observer.h
@@ -255,7 +255,7 @@
   // The network error encountered by the request, as defined by
   // net/base/net_error_list.h. If no error was encountered, this value will be
   // 0.
-  int net_error;
+  const int net_error;
 };
 
 // Interface for PageLoadMetrics observers. All instances of this class are
diff --git a/chrome/browser/payments/android/journey_logger_android.cc b/chrome/browser/payments/android/journey_logger_android.cc
index 8ee841e..6f3540b 100644
--- a/chrome/browser/payments/android/journey_logger_android.cc
+++ b/chrome/browser/payments/android/journey_logger_android.cc
@@ -145,6 +145,12 @@
       static_cast<JourneyLogger::NotShownReason>(jreason));
 }
 
+void JourneyLoggerAndroid::SetUserHadInitialFormOfPayment(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& jcaller) {
+  journey_logger_.SetUserHadInitialFormOfPayment();
+}
+
 static jlong InitJourneyLoggerAndroid(
     JNIEnv* env,
     const JavaParamRef<jobject>& jcaller,
diff --git a/chrome/browser/payments/android/journey_logger_android.h b/chrome/browser/payments/android/journey_logger_android.h
index d6167d6..95afb75 100644
--- a/chrome/browser/payments/android/journey_logger_android.h
+++ b/chrome/browser/payments/android/journey_logger_android.h
@@ -71,6 +71,9 @@
   void SetNotShown(JNIEnv* env,
                    const base::android::JavaParamRef<jobject>& jcaller,
                    jint jreason);
+  void SetUserHadInitialFormOfPayment(
+      JNIEnv* env,
+      const base::android::JavaParamRef<jobject>& jcaller);
 
  private:
   JourneyLogger journey_logger_;
diff --git a/chrome/browser/predictors/resource_prefetch_predictor_tab_helper.cc b/chrome/browser/predictors/resource_prefetch_predictor_tab_helper.cc
index 015a06d..e814cae5 100644
--- a/chrome/browser/predictors/resource_prefetch_predictor_tab_helper.cc
+++ b/chrome/browser/predictors/resource_prefetch_predictor_tab_helper.cc
@@ -53,6 +53,7 @@
   ResourcePrefetchPredictor::URLRequestSummary summary;
   summary.navigation_id = NavigationID(web_contents());
   summary.resource_url = url;
+  summary.request_url = url;
   summary.mime_type = mime_type;
   summary.resource_type =
       ResourcePrefetchPredictor::GetResourceTypeFromMimeType(
diff --git a/chrome/browser/resources/chromeos/chromevox/BUILD.gn b/chrome/browser/resources/chromeos/chromevox/BUILD.gn
index 913079e..5f75f2a 100644
--- a/chrome/browser/resources/chromeos/chromevox/BUILD.gn
+++ b/chrome/browser/resources/chromeos/chromevox/BUILD.gn
@@ -146,6 +146,7 @@
   "cvox2/background/panel_command.js",
   "cvox2/background/panel_menu.js",
   "cvox2/background/panel_menu_item.js",
+  "cvox2/background/recovery_strategy.js",
   "cvox2/background/tabs_automation_handler.js",
   "cvox2/background/tree_walker.js",
   "cvox2/background/tutorial.js",
@@ -679,6 +680,7 @@
     "cvox2/background/live_regions_test.extjs",
     "cvox2/background/output_test.extjs",
     "cvox2/background/panel_test.extjs",
+    "cvox2/background/recovery_strategy_test.extjs",
     "cvox2/background/tree_walker_test.extjs",
     "host/chrome/tts_background_test.extjs",
   ]
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors.js
index 1f3f614f..d544e6a 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors.js
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors.js
@@ -12,8 +12,10 @@
 goog.provide('cursors.Range');
 goog.provide('cursors.Unit');
 
+goog.require('AncestryRecoveryStrategy');
 goog.require('AutomationPredicate');
 goog.require('AutomationUtil');
+goog.require('RecoveryStrategy');
 goog.require('StringUtil');
 goog.require('constants');
 
@@ -84,7 +86,7 @@
 
     // The exception is when a user types at the end of a line. In that case,
     // staying on the current node is appropriate.
-    if (node && node.nextOnLine && nextNode) {
+    if (node && node.nextOnLine && node.nextOnLine.role && nextNode) {
       node = nextNode;
       index = 0;
     }
@@ -99,15 +101,8 @@
 
   /** @type {number} @private */
   this.index_ = index;
-  /** @type {Array<AutomationNode>} @private */
-  this.ancestry_ = [];
-  var nodeWalker = node;
-  while (nodeWalker) {
-    this.ancestry_.push(nodeWalker);
-    nodeWalker = nodeWalker.parent;
-    if (nodeWalker && nodeWalker.role == RoleType.WINDOW)
-      break;
-  }
+  /** @type {RecoveryStrategy} */
+  this.recovery_ = new AncestryRecoveryStrategy(node);
 };
 
 /**
@@ -177,16 +172,11 @@
    * @return {AutomationNode}
    */
   get node() {
-    for (var i = 0; i < this.ancestry_.length; i++) {
-      var firstValidNode = this.ancestry_[i];
-      if (firstValidNode != null && firstValidNode.role !== undefined &&
-          firstValidNode.root != undefined) {
-        return firstValidNode;
-      }
-      // If we have to walk up to an ancestor, reset the index to NODE_INDEX.
+    if (this.recovery_.requiresRecovery()) {
+      // If we need to recover, the index is no longer valid.
       this.index_ = cursors.NODE_INDEX;
     }
-    return null;
+    return this.recovery_.node;
   },
 
   /**
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/editing.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/editing.js
index 99cbad3..7c8335d 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/editing.js
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/editing.js
@@ -13,6 +13,7 @@
 goog.require('AutomationUtil');
 goog.require('Output');
 goog.require('Output.EventType');
+goog.require('TreePathRecoveryStrategy');
 goog.require('cursors.Cursor');
 goog.require('cursors.Range');
 goog.require('cvox.BrailleBackground');
@@ -655,6 +656,8 @@
   this.lineEndContainer_;
   /** @private {number} */
   this.localLineEndContainerOffset_ = 0;
+  /** @type {RecoveryStrategy} */
+  this.lineStartContainerRecovery_;
 
   this.computeLineData_(opt_baseLineOnStart);
 };
@@ -760,6 +763,11 @@
     }
     this.localLineStartContainerOffset_ = textCountBeforeLineStart;
 
+    if (this.lineStartContainer_) {
+      this.lineStartContainerRecovery_ =
+          new TreePathRecoveryStrategy(this.lineStartContainer_);
+    }
+
     finder = this.lineEnd_;
     while (finder.nextSibling) {
       finder = finder.nextSibling;
@@ -902,7 +910,11 @@
                 this.localLineStartContainerOffset_) ||
         (otherLine.lineEndContainer_ == this.lineEndContainer_ &&
          otherLine.localLineEndContainerOffset_ ==
-             this.localLineEndContainerOffset_);
+             this.localLineEndContainerOffset_) ||
+        (otherLine.lineStartContainerRecovery_.node ==
+             this.lineStartContainerRecovery_.node &&
+         otherLine.localLineStartContainerOffset_ ==
+             this.localLineStartContainerOffset_);
   },
 
   /**
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/recovery_strategy.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/recovery_strategy.js
new file mode 100644
index 0000000..f3409ba
--- /dev/null
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/recovery_strategy.js
@@ -0,0 +1,133 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Defines various strategies for recovering automation nodes.
+ */
+
+goog.provide('AncestryRecoveryStrategy');
+goog.provide('RecoveryStrategy');
+goog.provide('TreePathRecoveryStrategy');
+
+goog.scope(function() {
+var AutomationNode = chrome.automation.AutomationNode;
+var RoleType = chrome.automation.RoleType;
+
+/**
+ * @param {!AutomationNode} node
+ * @constructor
+ */
+RecoveryStrategy = function(node) {
+  /** @private {!AutomationNode} */
+  this.node_ = node;
+};
+
+RecoveryStrategy.prototype = {
+  /** @return {!AutomationNode} */
+  get node() {
+    if (this.requiresRecovery())
+      this.node_ = this.recover() || this.node_;
+
+    return this.node_;
+  },
+
+  /** @return {boolean} */
+  requiresRecovery: function() {
+    return !this.node_ || !this.node_.role;
+  },
+
+  /**
+   * @return {AutomationNode}
+   * @protected
+   */
+  recover: function() {
+    return null;
+  }
+};
+
+/**
+ * A recovery strategy that uses the node's ancestors.
+ * @constructor
+ * @extends {RecoveryStrategy}
+ */
+AncestryRecoveryStrategy = function(node) {
+  RecoveryStrategy.call(this, node);
+
+  /** @type {!Array<AutomationNode>} @private */
+  this.ancestry_ = [];
+  var nodeWalker = node;
+  while (nodeWalker) {
+    this.ancestry_.push(nodeWalker);
+    nodeWalker = nodeWalker.parent;
+    if (nodeWalker && nodeWalker.role == RoleType.WINDOW)
+      break;
+  }
+};
+
+AncestryRecoveryStrategy.prototype = {
+  __proto__: RecoveryStrategy.prototype,
+
+  /** @override */
+  recover: function() {
+    return this.ancestry_[this.getFirstValidNodeIndex_()];
+  },
+
+  /**
+   * @return {number}
+   * @protected
+   */
+  getFirstValidNodeIndex_: function() {
+    for (var i = 0; i < this.ancestry_.length; i++) {
+      var firstValidNode = this.ancestry_[i];
+      if (firstValidNode != null && firstValidNode.role !== undefined &&
+          firstValidNode.root != undefined) {
+        return i;
+      }
+    }
+    return 0;
+  }
+};
+
+/**
+ * A recovery strategy that uses the node's tree path.
+ * @constructor
+ * @extends {AncestryRecoveryStrategy}
+ */
+TreePathRecoveryStrategy = function(node) {
+  AncestryRecoveryStrategy.call(this, node);
+
+  /** @type {!Array<number>} @private */
+  this.recoveryChildIndex_ = [];
+  var nodeWalker = node;
+  while (nodeWalker) {
+    this.recoveryChildIndex_.push(nodeWalker.indexInParent);
+    nodeWalker = nodeWalker.parent;
+    if (nodeWalker && nodeWalker.role == RoleType.WINDOW)
+      break;
+  }
+};
+
+TreePathRecoveryStrategy.prototype = {
+  __proto__: AncestryRecoveryStrategy.prototype,
+
+  /** @override */
+  recover: function() {
+    var index = this.getFirstValidNodeIndex_();
+    if (index == 0)
+      return this.ancestry_[index];
+
+    // Otherwise, attempt to recover.
+    var node = this.ancestry_[index];
+    for (var j = index - 1; j >= 0; j--) {
+      var childIndex = this.recoveryChildIndex_[j];
+      var children = node.children;
+      if (!children[childIndex])
+        return node;
+      node = children[childIndex];
+    }
+    return node;
+  }
+};
+
+});  // goog.scope
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/recovery_strategy_test.extjs b/chrome/browser/resources/chromeos/chromevox/cvox2/background/recovery_strategy_test.extjs
new file mode 100644
index 0000000..41d70a1
--- /dev/null
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/recovery_strategy_test.extjs
@@ -0,0 +1,71 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Include test fixture.
+GEN_INCLUDE(['../../testing/chromevox_next_e2e_test_base.js',
+             '../../testing/assert_additions.js']);
+
+/**
+ * Test fixture for recovery strategy tests.
+ * @constructor
+ * @extends {ChromeVoxNextE2ETest}
+ */
+function RecoveryStrategyTest() {
+  ChromeVoxNextE2ETest.call(this);
+  window.RoleType = chrome.automation.RoleType;
+}
+
+RecoveryStrategyTest.prototype = {
+  __proto__: ChromeVoxNextE2ETest.prototype,
+};
+
+TEST_F('RecoveryStrategyTest', 'ReparentedRecovery', function() {
+  this.runWithLoadedTree(function() {/*!
+    <input type="text"></input>
+    <p id="p">hi</p>
+    <button id="go"</button>
+    <script>
+      document.getElementById('go').addEventListener('click', function() {
+        var p = document.getElementById('p');
+        p.remove();
+        document.body.appendChild(p);
+      });
+    </script>
+  */}, function(root) {
+    var p = root.find({role: RoleType.PARAGRAPH});
+    var s = root.find({role: RoleType.STATIC_TEXT});
+    var b = root.find({role: RoleType.BUTTON});
+    var bAncestryRecovery = new AncestryRecoveryStrategy(b);
+    var pAncestryRecovery = new AncestryRecoveryStrategy(p);
+    var sAncestryRecovery = new AncestryRecoveryStrategy(s);
+    var bTreePathRecovery = new TreePathRecoveryStrategy(b);
+    var pTreePathRecovery = new TreePathRecoveryStrategy(p);
+    var sTreePathRecovery = new TreePathRecoveryStrategy(s);
+    this.listenOnce(b, 'clicked', function() {
+      assertFalse(bAncestryRecovery.requiresRecovery());
+      assertTrue(pAncestryRecovery.requiresRecovery());
+      assertTrue(sAncestryRecovery.requiresRecovery());
+      assertFalse(bTreePathRecovery.requiresRecovery());
+      assertTrue(pTreePathRecovery.requiresRecovery());
+      assertTrue(sTreePathRecovery.requiresRecovery());
+
+      assertEquals(RoleType.BUTTON, bAncestryRecovery.node.role);
+      assertEquals(root, pAncestryRecovery.node);
+      assertEquals(root, sAncestryRecovery.node);
+
+      assertEquals(b, bTreePathRecovery.node);
+      assertEquals(b, pTreePathRecovery.node);
+      assertEquals(b, sTreePathRecovery.node);
+
+      assertFalse(bAncestryRecovery.requiresRecovery());
+      assertFalse(pAncestryRecovery.requiresRecovery());
+      assertFalse(sAncestryRecovery.requiresRecovery());
+      assertFalse(bTreePathRecovery.requiresRecovery());
+      assertFalse(pTreePathRecovery.requiresRecovery());
+      assertFalse(sTreePathRecovery.requiresRecovery());
+    });
+    // Trigger the change.
+    b.doDefault();
+  });
+});
diff --git a/chrome/browser/resources/print_preview/previewarea/preview_area.js b/chrome/browser/resources/print_preview/previewarea/preview_area.js
index 24b14be..0e2ff3c 100644
--- a/chrome/browser/resources/print_preview/previewarea/preview_area.js
+++ b/chrome/browser/resources/print_preview/previewarea/preview_area.js
@@ -172,11 +172,6 @@
      */
     this.openSystemDialogButton_ = null;
 
-    /**
-     * If this is in a browser test (fake plugin).
-     * @private {boolean}
-     */
-    this.isBrowserTest_ = false;
   }
 
   /**
@@ -605,8 +600,6 @@
      */
     onDocumentReady_: function(event) {
       this.isDocumentReady_ = true;
-      if (this.isBrowserTest_)
-        this.isPluginReloaded_ = true;
       this.dispatchPreviewGenerationDoneIfReady_();
     },
 
@@ -675,11 +668,6 @@
       // being draggable.
       this.plugin_.style.pointerEvents = isDragging ? 'none' : 'auto';
     },
-
-    /** @param {boolean} isTest Whether this instance is in a browser test. */
-    setIsBrowserTest: function(isTest) {
-      this.isBrowserTest_ = isTest;
-    }
   };
 
   // Export
diff --git a/chrome/browser/resources/settings/chrome_cleanup_page/chrome_cleanup_page.html b/chrome/browser/resources/settings/chrome_cleanup_page/chrome_cleanup_page.html
index d8bee09..b9ab3dc5 100644
--- a/chrome/browser/resources/settings/chrome_cleanup_page/chrome_cleanup_page.html
+++ b/chrome/browser/resources/settings/chrome_cleanup_page/chrome_cleanup_page.html
@@ -19,8 +19,10 @@
       }
 
       #show-files-button {
-        color: var(--google-blue-700);
         --paper-button-ink-color: white;
+        /* Left-align the text of the button with the rest of the card's text */
+        -webkit-margin-start: calc(var(--settings-button-edge-spacing) * -1);
+        color: var(--google-blue-700);
         text-transform: inherit;
       }
 
@@ -30,8 +32,8 @@
       }
 
       .status-icon-container {
+        -webkit-padding-end: 12px;
         min-width: 28px;
-        padding-right: 12px;
       }
 
       .status-icon-remove {
@@ -67,9 +69,22 @@
     </div>
     <div id="details-container"
          class="settings-box two-line" hidden="[[!showDetails_]]">
-      <div>[[detailsDescription]]</div>
+      <div>
+        $i18n{chromeCleanupExplanation}
+        <a href="$i18n{chromeCleanupLearnMoreUrl}" target="_blank">
+          $i18n{learnMore}
+        </a>
+      </div>
     </div>
-    <div class="settings-box continuation" hidden="[[!showDetails_]]">
+    <div class="settings-box continuation" hidden="[[!showLogsPermission_]]">
+      <settings-toggle-button class="start"
+              id="chromeCleanupLogsUploadControl"
+              label="$i18n{chromeCleanupLogsUploadPermission}"
+              pref="[[logsUploadPref_]]"
+              on-settings-boolean-control-change="changeLogsPermission_">
+      </settings-toggle-button>
+    </div>
+    <div class="settings-box" hidden="[[!showDetails_]]">
       <div class="show-files-container start" hidden="[[showFilesToRemove_]]">
         <paper-button id="show-files-button" on-tap="showFiles_">
           $i18n{chromeCleanupLinkShowFiles}
diff --git a/chrome/browser/resources/settings/chrome_cleanup_page/chrome_cleanup_page.js b/chrome/browser/resources/settings/chrome_cleanup_page/chrome_cleanup_page.js
index 89dd30b..25637213 100644
--- a/chrome/browser/resources/settings/chrome_cleanup_page/chrome_cleanup_page.js
+++ b/chrome/browser/resources/settings/chrome_cleanup_page/chrome_cleanup_page.js
@@ -66,9 +66,9 @@
     },
 
     /** @private */
-    detailsDescription: {
-      type: String,
-      value: '',
+    showLogsPermission_: {
+      type: Boolean,
+      value: false,
     },
 
     /** @private */
@@ -94,6 +94,14 @@
       type: String,
       value: '',
     },
+
+    /** @private {chrome.settingsPrivate.PrefObject} */
+    logsUploadPref_: {
+      type: Object,
+      value: function() {
+        return /** @type {chrome.settingsPrivate.PrefObject} */ ({});
+      },
+    },
   },
 
   /** @private {?settings.ChromeCleanupProxy} */
@@ -123,6 +131,9 @@
         'chrome-cleanup-on-reboot-required', this.onRebootRequired_.bind(this));
     this.addWebUIListener(
         'chrome-cleanup-on-dismiss', this.onDismiss_.bind(this));
+    this.addWebUIListener(
+        'chrome-cleanup-upload-permission-change',
+        this.onUploadPermissionChange_.bind(this));
 
     this.browserProxy_.registerChromeCleanerObserver();
   },
@@ -155,15 +166,13 @@
       this.enableActionButton_(
           this.i18n('chromeCleanupDoneButtonLabel'), this.dismiss_.bind(this));
       this.setIconDone_();
-    } else if (idleReason == settings.ChromeCleanupIdleReason.CLEANING_FAILED) {
+    } else {
+      // Scanning-related idle reasons are unexpected. Show an error message for
+      // all reasons other than |CLEANING_SUCCEEDED|.
       this.title_ = this.i18n('chromeCleanupTitleErrorCantRemove');
       this.enableActionButton_(
           this.i18n('chromeCleanupDoneButtonLabel'), this.dismiss_.bind(this));
       this.setIconWarning_();
-    } else {
-      // TODO(proberge): Handle other cases.
-      this.title_ = '';
-      this.disableActionButton_();
     }
 
     this.isRemoving_ = false;
@@ -214,6 +223,7 @@
     this.resetIcon_();
     this.disableActionButton_();
     this.enableDetails_(files);
+    this.showLogsPermission_ = false;
   },
 
   /**
@@ -242,6 +252,27 @@
   },
 
   /**
+   * @param {boolean} enabled Whether logs upload is enabled.
+   * @private
+   */
+  onUploadPermissionChange_: function(enabled) {
+    this.logsUploadPref_ = {
+      key: '',
+      type: chrome.settingsPrivate.PrefType.BOOLEAN,
+      value: enabled,
+    };
+  },
+
+  /**
+   * @param {boolean} enabled Whether to enable logs upload.
+   * @private
+   */
+  changeLogsPermission_: function(enabled) {
+    var enabled = this.$.chromeCleanupLogsUploadControl.checked;
+    this.browserProxy_.setLogsUploadPermission(enabled);
+  },
+
+  /**
    * Dismiss the card.
    * @private
    */
@@ -278,7 +309,7 @@
    */
   disableDetails_: function() {
     this.showDetails_ = false;
-    this.detailsDescription = '';
+    this.showLogsPermission_ = false;
     this.showFilesToRemove_ = false;
     this.filesToRemove_ = [];
   },
@@ -290,7 +321,7 @@
    */
   enableDetails_: function(files) {
     this.showDetails_ = true;
-    this.detailsDescription = this.i18n('chromeCleanupExplanation');
+    this.showLogsPermission_ = true;
     // Note: doesn't change the state of this.showFilesToRemove_.
     this.filesToRemove_ = files;
   },
@@ -300,7 +331,8 @@
    * @private
    */
   startCleanup_: function() {
-    this.browserProxy_.startCleanup();
+    this.browserProxy_.startCleanup(
+        this.$.chromeCleanupLogsUploadControl.checked);
   },
 
   /**
diff --git a/chrome/browser/resources/settings/chrome_cleanup_page/chrome_cleanup_proxy.js b/chrome/browser/resources/settings/chrome_cleanup_page/chrome_cleanup_proxy.js
index b032a5cd..135379a 100644
--- a/chrome/browser/resources/settings/chrome_cleanup_page/chrome_cleanup_proxy.js
+++ b/chrome/browser/resources/settings/chrome_cleanup_page/chrome_cleanup_proxy.js
@@ -13,8 +13,9 @@
 
     /**
      * Starts a cleanup on the user's computer.
+     * @param {boolean} logsUploadEnabled
      */
-    startCleanup() {}
+    startCleanup(logsUploadEnabled) {}
 
     /**
      * Restarts the user's computer.
@@ -25,6 +26,12 @@
      * Hides the Cleanup page from the settings menu.
      */
     dismissCleanupPage() {}
+
+    /**
+     * Updates the cleanup logs upload permission status.
+     * @param {boolean} enabled
+     */
+    setLogsUploadPermission(enabled) {}
   }
 
   /**
@@ -37,8 +44,8 @@
     }
 
     /** @override */
-    startCleanup() {
-      chrome.send('startCleanup');
+    startCleanup(logsUploadEnabled) {
+      chrome.send('startCleanup', [logsUploadEnabled]);
     }
 
     /** @override */
@@ -50,6 +57,11 @@
     dismissCleanupPage() {
       chrome.send('dismissCleanupPage');
     }
+
+    /** @override */
+    setLogsUploadPermission(enabled) {
+      chrome.send('setLogsUploadPermission', [enabled]);
+    }
   }
 
   cr.addSingletonGetter(ChromeCleanupProxyImpl);
diff --git a/chrome/browser/resources/settings/chrome_cleanup_page/compiled_resources2.gyp b/chrome/browser/resources/settings/chrome_cleanup_page/compiled_resources2.gyp
index 1d6d6fc..d6c2e9b 100644
--- a/chrome/browser/resources/settings/chrome_cleanup_page/compiled_resources2.gyp
+++ b/chrome/browser/resources/settings/chrome_cleanup_page/compiled_resources2.gyp
@@ -19,6 +19,7 @@
         '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior',
         '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data',
         '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:web_ui_listener_behavior',
+        '<(EXTERNS_GYP):settings_private',
         'chrome_cleanup_proxy',
       ],
       'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
diff --git a/chrome/browser/resources/settings/settings_shared_css.html b/chrome/browser/resources/settings/settings_shared_css.html
index 1cc949ea..0591b4d2 100644
--- a/chrome/browser/resources/settings/settings_shared_css.html
+++ b/chrome/browser/resources/settings/settings_shared_css.html
@@ -376,25 +376,28 @@
       }
 
       .search-bubble {
+        /* RGB value matches var(--paper-yellow-500). */
+        --search-bubble-color: rgba(255, 235, 59, 0.9);
         position: absolute;
         z-index: 1;
       }
 
       .search-bubble-innards {
         align-items: center;
-        background-color: var(--paper-yellow-500);
+        background-color: var(--search-bubble-color);
         border-radius: 2px;
+        max-width: 100px;
+        min-width: 64px;
         padding: 4px 10px;
         text-align: center;
-        width: 100px;
       }
 
       /* Provides the arrow which points at the anchor element. */
       .search-bubble-innards::after {
-        background-color: var(--paper-yellow-500);
+        background-color: var(--search-bubble-color);
         content: '';
         height: 10px;
-        left: 55px;
+        left: calc(50% - 5px);
         position: absolute;
         top: -5px;
         transform: rotate(-45deg);
diff --git a/chrome/browser/safe_browsing/download_protection_service_unittest.cc b/chrome/browser/safe_browsing/download_protection_service_unittest.cc
index 021fac4..d42213e 100644
--- a/chrome/browser/safe_browsing/download_protection_service_unittest.cc
+++ b/chrome/browser/safe_browsing/download_protection_service_unittest.cc
@@ -1409,7 +1409,7 @@
                           base::Unretained(this), run_loop.QuitClosure()));
     run_loop.Run();
     ASSERT_TRUE(HasClientDownloadRequest());
-    EXPECT_EQ(1, GetClientDownloadRequest()->archived_binary_size());
+    EXPECT_EQ(2, GetClientDownloadRequest()->archived_binary_size());
     EXPECT_TRUE(GetClientDownloadRequest()->has_download_type());
     EXPECT_EQ(ClientDownloadRequest_DownloadType_ZIPPED_EXECUTABLE,
               GetClientDownloadRequest()->download_type());
diff --git a/chrome/browser/safe_browsing/safe_browsing_navigation_observer_browsertest.cc b/chrome/browser/safe_browsing/safe_browsing_navigation_observer_browsertest.cc
index 346b630a..dcba4d2 100644
--- a/chrome/browser/safe_browsing/safe_browsing_navigation_observer_browsertest.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_navigation_observer_browsertest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/strings/stringprintf.h"
+#include "build/build_config.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/download/download_prefs.h"
 #include "chrome/browser/profiles/profile.h"
@@ -1271,9 +1272,14 @@
                            referrer_chain.Get(3));
 }
 
+#if defined(OS_WIN)
+#define MAYBE_SubFrameNewTabDownload DISABLED_SubFrameNewTabDownload
+#else
+#define MAYBE_SubFrameNewTabDownload SubFrameNewTabDownload
+#endif
 // Click a link in a subframe and open download in a new tab.
 IN_PROC_BROWSER_TEST_F(SBNavigationObserverBrowserTest,
-                       SubFrameNewTabDownload) {
+                       MAYBE_SubFrameNewTabDownload) {
   GURL initial_url = embedded_test_server()->GetURL(kSingleFrameTestURL);
   ClickTestLink("sub_frame_download_attribution", 1, initial_url);
   std::string test_name =
diff --git a/chrome/browser/safe_browsing/sandboxed_zip_analyzer_unittest.cc b/chrome/browser/safe_browsing/sandboxed_zip_analyzer_unittest.cc
index 5bb3bff..4b434d8 100644
--- a/chrome/browser/safe_browsing/sandboxed_zip_analyzer_unittest.cc
+++ b/chrome/browser/safe_browsing/sandboxed_zip_analyzer_unittest.cc
@@ -216,7 +216,7 @@
   ASSERT_TRUE(results.success);
   EXPECT_FALSE(results.has_executable);
   EXPECT_TRUE(results.has_archive);
-  EXPECT_EQ(0, results.archived_binary.size());
+  EXPECT_EQ(1, results.archived_binary.size());
   ASSERT_EQ(1u, results.archived_archive_filenames.size());
   EXPECT_EQ(FILE_PATH_LITERAL("hello.zip"),
             results.archived_archive_filenames[0].value());
@@ -229,7 +229,7 @@
   ASSERT_TRUE(results.success);
   EXPECT_FALSE(results.has_executable);
   EXPECT_TRUE(results.has_archive);
-  EXPECT_EQ(0, results.archived_binary.size());
+  EXPECT_EQ(1, results.archived_binary.size());
   ASSERT_EQ(1u, results.archived_archive_filenames.size());
   EXPECT_EQ(FILE_PATH_LITERAL("hello.rar"),
             results.archived_archive_filenames[0].value());
@@ -242,7 +242,7 @@
   ASSERT_TRUE(results.success);
   EXPECT_TRUE(results.has_executable);
   EXPECT_TRUE(results.has_archive);
-  ASSERT_EQ(1, results.archived_binary.size());
+  ASSERT_EQ(2, results.archived_binary.size());
   ExpectBinary(kSignedExe, results.archived_binary.Get(0));
   ASSERT_EQ(1u, results.archived_archive_filenames.size());
   EXPECT_EQ(FILE_PATH_LITERAL("hello.7z"),
@@ -258,7 +258,7 @@
   ASSERT_TRUE(results.success);
   EXPECT_TRUE(results.has_executable);
   EXPECT_TRUE(results.has_archive);
-  ASSERT_EQ(2, results.archived_binary.size());
+  ASSERT_EQ(3, results.archived_binary.size());
 
   BinaryData SignedExe = kSignedExe;
   SignedExe.file_basename = "signed.exe ";
diff --git a/chrome/browser/sync/test/integration/single_client_user_events_sync_test.cc b/chrome/browser/sync/test/integration/single_client_user_events_sync_test.cc
index 8a7564c..d5f6929 100644
--- a/chrome/browser/sync/test/integration/single_client_user_events_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_user_events_sync_test.cc
@@ -11,12 +11,44 @@
 #include "components/sync/protocol/user_event_specifics.pb.h"
 #include "components/sync/user_events/user_event_service.h"
 
+using base::Time;
+using base::TimeDelta;
 using fake_server::FakeServer;
 using sync_pb::UserEventSpecifics;
 using sync_pb::SyncEntity;
+using sync_pb::CommitResponse;
 
 namespace {
 
+UserEventSpecifics CreateEvent(int minutes_ago) {
+  UserEventSpecifics specifics;
+  specifics.set_event_time_usec(
+      (Time::Now() - TimeDelta::FromMinutes(minutes_ago)).ToInternalValue());
+  specifics.mutable_test_event();
+  return specifics;
+}
+
+CommitResponse::ResponseType BounceType(
+    CommitResponse::ResponseType type,
+    const fake_server::FakeServerEntity& entity) {
+  return type;
+}
+
+CommitResponse::ResponseType TransientErrorFirst(
+    bool* first,
+    UserEventSpecifics* retry_specifics,
+    const fake_server::FakeServerEntity& entity) {
+  if (*first) {
+    *first = false;
+    SyncEntity sync_entity;
+    entity.SerializeAsProto(&sync_entity);
+    *retry_specifics = sync_entity.specifics().user_event();
+    return CommitResponse::TRANSIENT_ERROR;
+  } else {
+    return CommitResponse::SUCCESS;
+  }
+}
+
 class UserEventEqualityChecker : public SingleClientStatusChangeChecker {
  public:
   UserEventEqualityChecker(browser_sync::ProfileSyncService* service,
@@ -24,7 +56,8 @@
                            std::vector<UserEventSpecifics> expected_specifics)
       : SingleClientStatusChangeChecker(service), fake_server_(fake_server) {
     for (const UserEventSpecifics& specifics : expected_specifics) {
-      expected_specifics_[specifics.event_time_usec()] = specifics;
+      expected_specifics_.insert(std::pair<int64_t, UserEventSpecifics>(
+          specifics.event_time_usec(), specifics));
     }
   }
 
@@ -40,17 +73,24 @@
       return false;
     }
 
+    // Because we have a multimap, we cannot just check counts and equality of
+    // items, but we need to make sure 2 of A and 1 of B is not the same as 1 of
+    // A and 2 of B. So to make this easy, copy the multimap and remove items.
+    std::multimap<int64_t, UserEventSpecifics> copied_expected_(
+        expected_specifics_.begin(), expected_specifics_.end());
     for (const SyncEntity& entity : entities) {
       UserEventSpecifics server_specifics = entity.specifics().user_event();
-      auto iter = expected_specifics_.find(server_specifics.event_time_usec());
+      auto iter = copied_expected_.find(server_specifics.event_time_usec());
       // We don't expect to encounter id matching events with different values,
       // this isn't going to recover so fail the test case now.
-      CHECK(expected_specifics_.end() != iter);
+      CHECK(copied_expected_.end() != iter);
       // TODO(skym): This may need to change if we start updating navigation_id
       // based on what sessions data is committed, and end up committing the
       // same event multiple times.
       EXPECT_EQ(iter->second.navigation_id(), server_specifics.navigation_id());
       EXPECT_EQ(iter->second.event_case(), server_specifics.event_case());
+
+      copied_expected_.erase(iter);
     }
 
     return true;
@@ -62,7 +102,7 @@
 
  private:
   FakeServer* fake_server_;
-  std::map<int64_t, UserEventSpecifics> expected_specifics_;
+  std::multimap<int64_t, UserEventSpecifics> expected_specifics_;
 };
 
 class SingleClientUserEventsSyncTest : public SyncTest {
@@ -82,12 +122,67 @@
       GetFakeServer()->GetSyncEntitiesByModelType(syncer::USER_EVENTS).size());
   syncer::UserEventService* event_service =
       browser_sync::UserEventServiceFactory::GetForProfile(GetProfile(0));
-  UserEventSpecifics specifics1;
-  specifics1.set_event_time_usec(base::Time::Now().ToInternalValue());
-  specifics1.mutable_test_event();
+  UserEventSpecifics specifics = CreateEvent(0);
+  event_service->RecordUserEvent(specifics);
+  UserEventEqualityChecker(GetSyncService(0), GetFakeServer(), {specifics})
+      .Wait();
+}
+
+IN_PROC_BROWSER_TEST_F(SingleClientUserEventsSyncTest, RetrySequential) {
+  ASSERT_TRUE(SetupSync());
+  UserEventSpecifics specifics1 = CreateEvent(1);
+  UserEventSpecifics specifics2 = CreateEvent(2);
+  syncer::UserEventService* event_service =
+      browser_sync::UserEventServiceFactory::GetForProfile(GetProfile(0));
+
+  GetFakeServer()->OverrideResponseType(
+      base::Bind(&BounceType, CommitResponse::TRANSIENT_ERROR));
   event_service->RecordUserEvent(specifics1);
+
+  // This will block until we hit a TRANSIENT_ERROR, at which point we will
+  // regain control and can switch back to SUCCESS.
   UserEventEqualityChecker(GetSyncService(0), GetFakeServer(), {specifics1})
       .Wait();
+  GetFakeServer()->OverrideResponseType(
+      base::Bind(&BounceType, CommitResponse::SUCCESS));
+  // Because the fake server records commits even on failure, we are able to
+  // verify that the commit for this event reached the server twice.
+  UserEventEqualityChecker(GetSyncService(0), GetFakeServer(),
+                           {specifics1, specifics1})
+      .Wait();
+
+  // Only record |specifics2| after |specifics1| was successful to avoid race
+  // conditions.
+  event_service->RecordUserEvent(specifics2);
+  UserEventEqualityChecker(GetSyncService(0), GetFakeServer(),
+                           {specifics1, specifics1, specifics2})
+      .Wait();
+}
+
+IN_PROC_BROWSER_TEST_F(SingleClientUserEventsSyncTest, RetryParallel) {
+  ASSERT_TRUE(SetupSync());
+  bool first = true;
+  UserEventSpecifics specifics1 = CreateEvent(1);
+  UserEventSpecifics specifics2 = CreateEvent(2);
+  UserEventSpecifics retry_specifics;
+
+  syncer::UserEventService* event_service =
+      browser_sync::UserEventServiceFactory::GetForProfile(GetProfile(0));
+
+  // We're not really sure if |specifics1| or |specifics2| is going to see the
+  // error, so record the one that does into |retry_specifics| and use it in
+  // expectations.
+  GetFakeServer()->OverrideResponseType(
+      base::Bind(&TransientErrorFirst, &first, &retry_specifics));
+
+  event_service->RecordUserEvent(specifics2);
+  event_service->RecordUserEvent(specifics1);
+  UserEventEqualityChecker(GetSyncService(0), GetFakeServer(),
+                           {specifics1, specifics2})
+      .Wait();
+  UserEventEqualityChecker(GetSyncService(0), GetFakeServer(),
+                           {specifics1, specifics2, retry_specifics})
+      .Wait();
 }
 
 }  // namespace
diff --git a/chrome/browser/ui/autofill/popup_constants.h b/chrome/browser/ui/autofill/popup_constants.h
index ca3a74a24..c2f6ed0 100644
--- a/chrome/browser/ui/autofill/popup_constants.h
+++ b/chrome/browser/ui/autofill/popup_constants.h
@@ -5,10 +5,17 @@
 #ifndef CHROME_BROWSER_UI_AUTOFILL_POPUP_CONSTANTS_H_
 #define CHROME_BROWSER_UI_AUTOFILL_POPUP_CONSTANTS_H_
 
+#include "build/build_config.h"
+
 namespace autofill {
 
+#if defined(TOOLKIT_VIEWS)
+// In views, the implementation takes care of the border itself.
+const int kPopupBorderThickness = 0;
+#else
 // TODO(crbug.com/676221): Change this to pixels
 const int kPopupBorderThickness = 1;
+#endif
 
 }  // namespace autofill
 
diff --git a/chrome/browser/ui/chrome_pages.cc b/chrome/browser/ui/chrome_pages.cc
index f04527b..44ab1e39 100644
--- a/chrome/browser/ui/chrome_pages.cc
+++ b/chrome/browser/ui/chrome_pages.cc
@@ -216,16 +216,9 @@
 
 void ShowConflicts(Browser* browser) {
 #if defined(OS_WIN)
-  EnumerateModulesModel* model = EnumerateModulesModel::GetInstance();
-  GURL conflict_url = model->GetConflictUrl();
-  if (conflict_url.is_valid()) {
-    ShowSingletonTab(browser, conflict_url);
-    model->AcknowledgeConflictNotification();
-    return;
-  }
+  EnumerateModulesModel::GetInstance()->AcknowledgeConflictNotification();
 #endif
 
-  base::RecordAction(UserMetricsAction("AboutConflicts"));
   ShowSingletonTab(browser, GURL(kChromeUIConflictsURL));
 }
 
diff --git a/chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.mm b/chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.mm
index f6123f0b..c38ea78 100644
--- a/chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.mm
+++ b/chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.mm
@@ -248,8 +248,25 @@
 
 void OmniboxViewMac::Update() {
   if (model()->UpdatePermanentText()) {
+    const bool was_select_all = IsSelectAll();
+    NSTextView* text_view =
+        base::mac::ObjCCastStrict<NSTextView>([field_ currentEditor]);
+    const bool was_reversed =
+        [text_view selectionAffinity] == NSSelectionAffinityUpstream;
+
     // Restore everything to the baseline look.
     RevertAll();
+
+    // Only select all when we have focus.  If we don't have focus, selecting
+    // all is unnecessary since the selection will change on regaining focus,
+    // and can in fact cause artifacts, e.g. if the user is on the NTP and
+    // clicks a link to navigate, causing |was_select_all| to be vacuously true
+    // for the empty omnibox, and we then select all here, leading to the
+    // trailing portion of a long URL being scrolled into view.  We could try
+    // and address cases like this, but it seems better to just not muck with
+    // things when the omnibox isn't focused to begin with.
+    if (was_select_all && model()->has_focus())
+      SelectAll(was_reversed);
   } else {
     // TODO(shess): This corresponds to _win and _gtk, except those
     // guard it with a test for whether the security level changed.
diff --git a/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc b/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc
index a3704890..454864c4 100644
--- a/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc
+++ b/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc
@@ -866,18 +866,11 @@
 
   // Check if RevertAll() resets the text and preserves the cursor position.
   omnibox_view->RevertAll();
-  EXPECT_TRUE(omnibox_view->IsSelectAll());
+  EXPECT_FALSE(omnibox_view->IsSelectAll());
   EXPECT_EQ(old_text, omnibox_view->GetText());
   omnibox_view->GetSelectionBounds(&start, &end);
-#if defined(OS_MACOSX)
-  // Cocoa doesn't choose a direction until the user interacts with it,
-  // so it will have a default direction here.
-  std::swap(start, end);
-#endif
-  // When |Revert()| calls |SelectAll()|, it requests a reverse selection
-  // so that the start of a long URL is in view.
-  EXPECT_EQ(11U, start);
-  EXPECT_EQ(0U, end);
+  EXPECT_EQ(3U, start);
+  EXPECT_EQ(3U, end);
 
   // Check that reverting clamps the cursor to the bounds of the new text.
   // Move the cursor to the end.
@@ -893,11 +886,8 @@
   omnibox_view->RevertAll();
   // Cursor should be no further than original text.
   omnibox_view->GetSelectionBounds(&start, &end);
-#if defined(OS_MACOSX)
-  std::swap(start, end);
-#endif
   EXPECT_EQ(11U, start);
-  EXPECT_EQ(0U, end);
+  EXPECT_EQ(11U, end);
 }
 
 // Make sure the cursor position doesn't get set past the last character of
@@ -1966,13 +1956,13 @@
   test_toolbar_model->set_text(url_a);
   omnibox_view->Update();
   EXPECT_EQ(url_a, omnibox_view->GetText());
-  EXPECT_TRUE(omnibox_view->IsSelectAll());
+  EXPECT_FALSE(omnibox_view->IsSelectAll());
 
   // Test behavior of the "reversed" attribute of OmniboxView::SelectAll().
   test_toolbar_model->set_text(ASCIIToUTF16("AB"));
   omnibox_view->Update();
   // Should be at the end already. Shift+Left to select "reversed".
-  EXPECT_EQ(2u, GetSelectionSize(omnibox_view));
+  EXPECT_EQ(0u, GetSelectionSize(omnibox_view));
   ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_LEFT, ui::EF_SHIFT_DOWN));
   ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_LEFT, ui::EF_SHIFT_DOWN));
   EXPECT_EQ(2u, GetSelectionSize(omnibox_view));
@@ -1999,8 +1989,6 @@
   omnibox_view->Update();
   EXPECT_EQ(2u, GetSelectionSize(omnibox_view));
 
-  // Force a forwards select-all because Cocoa will anyways.
-  omnibox_view->SelectAll(false);
   // Now Shift+Right should do nothing, and Shift+Left should reduce.
   // At the end, so Shift+Right should do nothing.
   ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_RIGHT, ui::EF_SHIFT_DOWN));
diff --git a/chrome/browser/ui/passwords/manage_passwords_bubble_model.cc b/chrome/browser/ui/passwords/manage_passwords_bubble_model.cc
index d5cd143..eab9341 100644
--- a/chrome/browser/ui/passwords/manage_passwords_bubble_model.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_bubble_model.cc
@@ -340,6 +340,11 @@
   delegate_->NeverSavePassword();
 }
 
+void ManagePasswordsBubbleModel::OnUsernameEdited(base::string16 new_username) {
+  DCHECK_EQ(password_manager::ui::PENDING_PASSWORD_STATE, state_);
+  pending_password_.username_value = std::move(new_username);
+}
+
 void ManagePasswordsBubbleModel::OnSaveClicked() {
   DCHECK_EQ(password_manager::ui::PENDING_PASSWORD_STATE, state_);
   interaction_keeper_->set_dismissal_reason(metrics_util::CLICKED_SAVE);
diff --git a/chrome/browser/ui/passwords/manage_passwords_bubble_model.h b/chrome/browser/ui/passwords/manage_passwords_bubble_model.h
index 0ff5bfa..03c9268 100644
--- a/chrome/browser/ui/passwords/manage_passwords_bubble_model.h
+++ b/chrome/browser/ui/passwords/manage_passwords_bubble_model.h
@@ -46,6 +46,10 @@
   // by the user.
   void OnNeverForThisSiteClicked();
 
+  // Called by the view code when username is corrected using the edit button
+  // in PendingView.
+  void OnUsernameEdited(base::string16 new_username);
+
   // Called by the view code when the save button is clicked by the user.
   void OnSaveClicked();
 
diff --git a/chrome/browser/ui/toolbar/app_menu_icon_controller.cc b/chrome/browser/ui/toolbar/app_menu_icon_controller.cc
index 79b6acb..21b95c0 100644
--- a/chrome/browser/ui/toolbar/app_menu_icon_controller.cc
+++ b/chrome/browser/ui/toolbar/app_menu_icon_controller.cc
@@ -12,8 +12,10 @@
 #include "chrome/browser/upgrade_detector.h"
 
 #if defined(OS_WIN)
+#include "base/feature_list.h"
 #include "base/win/windows_version.h"
 #include "chrome/browser/win/enumerate_modules_model.h"
+#include "chrome/common/chrome_features.h"
 #endif
 
 namespace {
@@ -57,7 +59,8 @@
 // Returns true if we should show the warning for incompatible software.
 bool ShouldShowIncompatibilityWarning() {
 #if defined(OS_WIN)
-  return EnumerateModulesModel::GetInstance()->ShouldShowConflictWarning();
+  return !base::FeatureList::IsEnabled(features::kModuleDatabase) &&
+         EnumerateModulesModel::GetInstance()->ShouldShowConflictWarning();
 #else
   return false;
 #endif
@@ -77,9 +80,11 @@
   UpgradeDetector::GetInstance()->AddObserver(this);
 
 #if defined(OS_WIN)
-  auto* modules = EnumerateModulesModel::GetInstance();
-  modules->AddObserver(this);
-  modules->MaybePostScanningTask();
+  if (!base::FeatureList::IsEnabled(features::kModuleDatabase)) {
+    auto* modules = EnumerateModulesModel::GetInstance();
+    modules->AddObserver(this);
+    modules->MaybePostScanningTask();
+  }
 #endif
 }
 
@@ -87,7 +92,8 @@
   UpgradeDetector::GetInstance()->RemoveObserver(this);
 
 #if defined(OS_WIN)
-  EnumerateModulesModel::GetInstance()->RemoveObserver(this);
+  if (!base::FeatureList::IsEnabled(features::kModuleDatabase))
+    EnumerateModulesModel::GetInstance()->RemoveObserver(this);
 #endif
 }
 
diff --git a/chrome/browser/ui/toolbar/app_menu_icon_controller.h b/chrome/browser/ui/toolbar/app_menu_icon_controller.h
index 2e7a98e5..cf3fe474 100644
--- a/chrome/browser/ui/toolbar/app_menu_icon_controller.h
+++ b/chrome/browser/ui/toolbar/app_menu_icon_controller.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_UI_TOOLBAR_APP_MENU_ICON_CONTROLLER_H_
 
 #include "base/macros.h"
+#include "build/build_config.h"
 #include "chrome/browser/upgrade_observer.h"
 #include "content/public/browser/notification_details.h"
 #include "content/public/browser/notification_observer.h"
@@ -66,20 +67,20 @@
   void UpdateDelegate();
 
  private:
+#if defined(OS_WIN)
+  // EnumerateModulesModel::Observer:
+  void OnScanCompleted() override;
+  void OnConflictsAcknowledged() override;
+#endif
+
   // content::NotificationObserver:
   void Observe(int type,
                const content::NotificationSource& source,
                const content::NotificationDetails& details) override;
 
-  // UpgradeObserver implementation.
+  // UpgradeObserver:
   void OnUpgradeRecommended() override;
 
-#if defined(OS_WIN)
-  // EnumerateModulesModel:
-  void OnScanCompleted() override;
-  void OnConflictsAcknowledged() override;
-#endif
-
   Profile* profile_;
   Delegate* delegate_;
   content::NotificationRegistrar registrar_;
diff --git a/chrome/browser/ui/views/autofill/autofill_popup_base_view.cc b/chrome/browser/ui/views/autofill/autofill_popup_base_view.cc
index 4540466..3c31853 100644
--- a/chrome/browser/ui/views/autofill/autofill_popup_base_view.cc
+++ b/chrome/browser/ui/views/autofill/autofill_popup_base_view.cc
@@ -9,14 +9,27 @@
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
-#include "chrome/browser/ui/autofill/popup_constants.h"
 #include "ui/native_theme/native_theme.h"
 #include "ui/views/border.h"
+#include "ui/views/controls/scroll_view.h"
 #include "ui/views/focus/focus_manager.h"
 #include "ui/views/widget/widget.h"
 
 namespace autofill {
 
+namespace {
+
+// The minimum vertical space between the bottom of the autofill popup and the
+// bottom of the Chrome frame.
+// TODO(crbug.com/739978): Investigate if we should compute this distance
+// programmatically. 10dp may not be enough for windows with thick borders.
+const int kPopupBottomMargin = 10;
+
+// The thickness of the border for the autofill popup in dp.
+const int kPopupBorderThicknessDp = 1;
+
+}  // namespace
+
 AutofillPopupBaseView::AutofillPopupBaseView(
     AutofillPopupViewDelegate* delegate,
     views::Widget* parent_widget)
@@ -55,14 +68,20 @@
     params.parent = parent_widget_->GetNativeView();
     widget->Init(params);
 
+    scroll_view_ = new views::ScrollView;
+    scroll_view_->set_hide_horizontal_scrollbar(true);
+    scroll_view_->SetContents(this);
+
+    widget->SetContentsView(scroll_view_);
+
     // No animation for popup appearance (too distracting).
     widget->SetVisibilityAnimationTransition(views::Widget::ANIMATE_HIDE);
 
     show_time_ = base::Time::Now();
   }
 
-  SetBorder(views::CreateSolidBorder(
-      kPopupBorderThickness,
+  GetWidget()->GetRootView()->SetBorder(views::CreateSolidBorder(
+      kPopupBorderThicknessDp,
       GetNativeTheme()->GetSystemColor(
           ui::NativeTheme::kColorId_UnfocusedBorderColor)));
 
@@ -105,7 +124,32 @@
 }
 
 void AutofillPopupBaseView::DoUpdateBoundsAndRedrawPopup() {
-  GetWidget()->SetBounds(delegate_->popup_bounds());
+  gfx::Rect bounds = delegate_->popup_bounds();
+
+  // |bounds| is in screen space and we want the bounds relative to the parent
+  // view. Since the parent is the scroll container, this will always be at
+  // position 0, 0 with dimensions specified by |bounds|.
+  SetSize(bounds.size());
+
+  // Compute the space available for the popup. It's the space between its top
+  // and the bottom of its parent view, minus some margin space.
+  int available_vertical_space =
+      parent_widget_->GetClientAreaBoundsInScreen().height() -
+      (bounds.y() - parent_widget_->GetClientAreaBoundsInScreen().y()) -
+      kPopupBottomMargin;
+
+  if (available_vertical_space < bounds.height()) {
+    // The available space is not enough for the full popup so clamp the widget
+    // to what's available. Since the scroll view will show a scroll bar,
+    // increase the width so that the content isn't partially hidden.
+    bounds.set_width(bounds.width() + scroll_view_->GetScrollBarLayoutWidth());
+    bounds.set_height(available_vertical_space);
+  }
+
+  // Account for the scroll view's border so that the content has enough space.
+  bounds.set_height(bounds.height() + 2 * kPopupBorderThicknessDp);
+  bounds.set_width(bounds.width() + 2 * kPopupBorderThicknessDp);
+  GetWidget()->SetBounds(bounds);
   SchedulePaint();
 }
 
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 181b142..f407b96 100644
--- a/chrome/browser/ui/views/autofill/autofill_popup_base_view.h
+++ b/chrome/browser/ui/views/autofill/autofill_popup_base_view.h
@@ -78,6 +78,8 @@
   // The widget of the window that triggered this popup. Weak reference.
   views::Widget* parent_widget_;
 
+  views::ScrollView* scroll_view_;
+
   // The time when the popup was shown.
   base::Time show_time_;
 
diff --git a/chrome/browser/ui/views/autofill/autofill_popup_base_view_browsertest.cc b/chrome/browser/ui/views/autofill/autofill_popup_base_view_browsertest.cc
index d23716a..94e9985b 100644
--- a/chrome/browser/ui/views/autofill/autofill_popup_base_view_browsertest.cc
+++ b/chrome/browser/ui/views/autofill/autofill_popup_base_view_browsertest.cc
@@ -143,8 +143,10 @@
 
   ShowView();
 
-  gfx::Point display_point =
-      static_cast<views::View*>(view_)->GetBoundsInScreen().origin();
+  gfx::Point display_point = static_cast<views::View*>(view_)
+                                 ->GetWidget()
+                                 ->GetClientAreaBoundsInScreen()
+                                 .origin();
   gfx::Point expected_point = bounds.origin();
   EXPECT_EQ(expected_point, display_point);
 }
diff --git a/chrome/browser/ui/views/autofill/password_generation_popup_view_views.cc b/chrome/browser/ui/views/autofill/password_generation_popup_view_views.cc
index 1deca5aa..a4ee391 100644
--- a/chrome/browser/ui/views/autofill/password_generation_popup_view_views.cc
+++ b/chrome/browser/ui/views/autofill/password_generation_popup_view_views.cc
@@ -169,8 +169,7 @@
   password_view_->Init(controller_->password(),
                        controller_->SuggestedText(),
                        font_list_);
-  password_view_->SetPosition(gfx::Point(kPopupBorderThickness,
-                                         kPopupBorderThickness));
+  password_view_->SetPosition(gfx::Point());
   password_view_->SizeToPreferredSize();
   AddChildView(password_view_);
 }
@@ -185,8 +184,7 @@
     height +=
         PasswordGenerationPopupController::kPopupPasswordSectionHeight + 1;
   }
-  return gfx::Size(width + 2 * kPopupBorderThickness,
-                   height + 2 * kPopupBorderThickness);
+  return gfx::Size(width, height);
 }
 
 void PasswordGenerationPopupViewViews::Show() {
@@ -220,25 +218,22 @@
 
 void PasswordGenerationPopupViewViews::Layout() {
   // Need to leave room for the border.
-  int y = kPopupBorderThickness;
-  int popup_width = bounds().width() - 2 * kPopupBorderThickness;
+  int y = 0;
+  int popup_width = bounds().width();
   if (controller_->display_password()) {
     // Currently the UI can change from not offering a password to offering
     // a password (e.g. the user is editing a generated password and deletes
     // it), but it can't change the other way around.
     CreatePasswordView();
     password_view_->SetBounds(
-        kPopupBorderThickness,
-        y,
-        popup_width,
+        0, 0, popup_width,
         PasswordGenerationPopupController::kPopupPasswordSectionHeight);
     divider_bounds_ =
-        gfx::Rect(kPopupBorderThickness, password_view_->bounds().bottom(),
-                  popup_width, 1);
+        gfx::Rect(0, password_view_->bounds().bottom(), popup_width, 1);
     y = divider_bounds_.bottom();
   }
 
-  help_label_->SetBounds(kPopupBorderThickness, y, popup_width,
+  help_label_->SetBounds(0, y, popup_width,
                          help_label_->GetHeightForWidth(popup_width));
 }
 
diff --git a/chrome/browser/ui/views/conflicting_module_view_win.cc b/chrome/browser/ui/views/conflicting_module_view_win.cc
index 3b5d671..7fbd051 100644
--- a/chrome/browser/ui/views/conflicting_module_view_win.cc
+++ b/chrome/browser/ui/views/conflicting_module_view_win.cc
@@ -13,6 +13,7 @@
 #include "chrome/browser/ui/layout_constants.h"
 #include "chrome/browser/ui/views/harmony/chrome_layout_provider.h"
 #include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/grit/locale_settings.h"
@@ -65,13 +66,6 @@
   if (done_checking)
     return;  // Only show the bubble once per launch.
 
-  auto* model = EnumerateModulesModel::GetInstance();
-  GURL url = model->GetConflictUrl();
-  if (!url.is_valid()) {
-    done_checking = true;
-    return;
-  }
-
   // A pref that counts how often the Sideload Wipeout bubble has been shown.
   IntegerPrefMember bubble_shown;
   bubble_shown.Init(prefs::kModuleConflictBubbleShown,
@@ -88,8 +82,8 @@
   DCHECK(anchor_view);
   DCHECK(anchor_view->GetWidget());
 
-  ConflictingModuleView* bubble_delegate =
-      new ConflictingModuleView(anchor_view, browser, url);
+  ConflictingModuleView* bubble_delegate = new ConflictingModuleView(
+      anchor_view, browser, GURL(chrome::kChromeUIConflictsURL));
   views::BubbleDialogDelegateView::CreateBubble(bubble_delegate);
   bubble_delegate->ShowBubble();
 
diff --git a/chrome/browser/ui/views/conflicting_module_view_win.h b/chrome/browser/ui/views/conflicting_module_view_win.h
index 5cb6b90..721188e1 100644
--- a/chrome/browser/ui/views/conflicting_module_view_win.h
+++ b/chrome/browser/ui/views/conflicting_module_view_win.h
@@ -15,6 +15,7 @@
 
 // This is the class that implements the UI for the bubble showing that there
 // is a 3rd party module loaded that conflicts with Chrome.
+// TODO(pmonette): Delete this view when EnumerateModulesModel gets removed.
 class ConflictingModuleView : public views::BubbleDialogDelegateView,
                               public EnumerateModulesModel::Observer {
  public:
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
index f87cf715..b77191b 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
@@ -203,10 +203,31 @@
 void OmniboxViewViews::Update() {
   const security_state::SecurityLevel old_security_level = security_level_;
   UpdateSecurityLevel();
-  if (model()->UpdatePermanentText())
+  if (model()->UpdatePermanentText()) {
+    // Select all the new text if the user had all the old text selected, or if
+    // there was no previous text (for new tab page URL replacement extensions).
+    // This makes one particular case better: the user clicks in the box to
+    // change it right before the permanent URL is changed.  Since the new URL
+    // is still fully selected, the user's typing will replace the edit contents
+    // as they'd intended.
+    const bool was_select_all = IsSelectAll();
+    const bool was_reversed = GetSelectedRange().is_reversed();
+
     RevertAll();
-  else if (old_security_level != security_level_)
+
+    // Only select all when we have focus.  If we don't have focus, selecting
+    // all is unnecessary since the selection will change on regaining focus,
+    // and can in fact cause artifacts, e.g. if the user is on the NTP and
+    // clicks a link to navigate, causing |was_select_all| to be vacuously true
+    // for the empty omnibox, and we then select all here, leading to the
+    // trailing portion of a long URL being scrolled into view.  We could try
+    // and address cases like this, but it seems better to just not muck with
+    // things when the omnibox isn't focused to begin with.
+    if (was_select_all && model()->has_focus())
+      SelectAll(was_reversed);
+  } else if (old_security_level != security_level_) {
     EmphasizeURLComponents();
+  }
 }
 
 base::string16 OmniboxViewViews::GetText() const {
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views_browsertest.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views_browsertest.cc
index 7435951..c1955e5c 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views_browsertest.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views_browsertest.cc
@@ -286,7 +286,7 @@
   // RevertAll after navigation to invalidate the selection range saved on blur.
   omnibox_view->RevertAll();
   EXPECT_FALSE(ui_test_utils::IsViewFocused(browser(), VIEW_ID_OMNIBOX));
-  EXPECT_TRUE(omnibox_view->IsSelectAll());
+  EXPECT_FALSE(omnibox_view->IsSelectAll());
 
   // Pressing tab to focus the omnibox should select all text.
   while (!ui_test_utils::IsViewFocused(browser(), VIEW_ID_OMNIBOX)) {
diff --git a/chrome/browser/ui/views/passwords/account_chooser_dialog_view.cc b/chrome/browser/ui/views/passwords/account_chooser_dialog_view.cc
index 2ca04e95..902f85d1 100644
--- a/chrome/browser/ui/views/passwords/account_chooser_dialog_view.cc
+++ b/chrome/browser/ui/views/passwords/account_chooser_dialog_view.cc
@@ -176,10 +176,6 @@
   return l10n_util::GetStringUTF16(message_id);
 }
 
-gfx::Size AccountChooserDialogView::CalculatePreferredSize() const {
-  return gfx::Size(kDesiredWidth, GetHeightForWidth(kDesiredWidth));
-}
-
 void AccountChooserDialogView::StyledLabelLinkClicked(views::StyledLabel* label,
                                                       const gfx::Range& range,
                                                       int event_flags) {
diff --git a/chrome/browser/ui/views/passwords/account_chooser_dialog_view.h b/chrome/browser/ui/views/passwords/account_chooser_dialog_view.h
index 0241a6b..63f2b0c 100644
--- a/chrome/browser/ui/views/passwords/account_chooser_dialog_view.h
+++ b/chrome/browser/ui/views/passwords/account_chooser_dialog_view.h
@@ -43,9 +43,6 @@
   int GetDialogButtons() const override;
   base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
 
-  // views::View
-  gfx::Size CalculatePreferredSize() const override;
-
   // StyledLabelListener:
   void StyledLabelLinkClicked(views::StyledLabel* label,
                               const gfx::Range& range,
diff --git a/chrome/browser/ui/views/passwords/manage_password_items_view.cc b/chrome/browser/ui/views/passwords/manage_password_items_view.cc
index 8d0661b7..cb6285d1 100644
--- a/chrome/browser/ui/views/passwords/manage_password_items_view.cc
+++ b/chrome/browser/ui/views/passwords/manage_password_items_view.cc
@@ -217,24 +217,17 @@
 void ManagePasswordItemsView::PasswordFormRow::AddCredentialsRow(
     views::GridLayout* layout) {
   ResetControls();
-  int column_set_id =
-      host_->model_->state() == password_manager::ui::MANAGE_STATE
-          ? THREE_COLUMN_SET
-          : TWO_COLUMN_SET;
-  BuildColumnSetIfNeeded(layout, column_set_id);
-  layout->StartRow(0, column_set_id);
+  BuildColumnSetIfNeeded(layout, THREE_COLUMN_SET);
+  layout->StartRow(0, THREE_COLUMN_SET);
   layout->AddView(GenerateUsernameLabel(*password_form_).release(), 1, 1,
                   views::GridLayout::FILL, views::GridLayout::FILL,
                   0, fixed_height_);
   layout->AddView(GeneratePasswordLabel(*password_form_).release(), 1, 1,
                   views::GridLayout::FILL, views::GridLayout::FILL,
                   0, fixed_height_);
-  if (column_set_id == THREE_COLUMN_SET) {
-    delete_button_ = GenerateDeleteButton(this).release();
-    layout->AddView(delete_button_, 1, 1,
-                    views::GridLayout::TRAILING, views::GridLayout::FILL,
-                    0, fixed_height_);
-  }
+  delete_button_ = GenerateDeleteButton(this).release();
+  layout->AddView(delete_button_, 1, 1, views::GridLayout::TRAILING,
+                  views::GridLayout::FILL, 0, fixed_height_);
 }
 
 void ManagePasswordItemsView::PasswordFormRow::AddUndoRow(
@@ -277,6 +270,7 @@
     ManagePasswordsBubbleModel* manage_passwords_bubble_model,
     const std::vector<autofill::PasswordForm>* password_forms)
     : model_(manage_passwords_bubble_model) {
+  DCHECK_EQ(password_manager::ui::MANAGE_STATE, model_->state());
   int fixed_height = PasswordFormRow::GetFixedHeight(model_->state());
   for (const auto& password_form : *password_forms) {
     password_forms_rows_.push_back(base::MakeUnique<PasswordFormRow>(
@@ -285,15 +279,6 @@
   AddRows();
 }
 
-ManagePasswordItemsView::ManagePasswordItemsView(
-    ManagePasswordsBubbleModel* manage_passwords_bubble_model,
-    const autofill::PasswordForm* password_form)
-    : model_(manage_passwords_bubble_model) {
-  password_forms_rows_.push_back(
-      base::MakeUnique<PasswordFormRow>(this, password_form, 0));
-  AddRows();
-}
-
 ManagePasswordItemsView::~ManagePasswordItemsView() = default;
 
 void ManagePasswordItemsView::AddRows() {
@@ -321,7 +306,6 @@
 }
 
 void ManagePasswordItemsView::Refresh() {
-  DCHECK_NE(password_manager::ui::PENDING_PASSWORD_STATE, model_->state());
   RemoveAllChildViews(true);
   AddRows();
 }
diff --git a/chrome/browser/ui/views/passwords/manage_password_items_view.h b/chrome/browser/ui/views/passwords/manage_password_items_view.h
index 3cecaf3..85662b4 100644
--- a/chrome/browser/ui/views/passwords/manage_password_items_view.h
+++ b/chrome/browser/ui/views/passwords/manage_password_items_view.h
@@ -28,18 +28,12 @@
     const autofill::PasswordForm& form);
 
 // A custom view of individual credentials. The view is represented as a table
-// where each row can be in three distinct states:
-//
-// * Present already-saved credentials to the user for management.
-// * Offer the user the ability to undo a deletion action.
+// where each row can offer the user the ability to undo a deletion action.
 class ManagePasswordItemsView : public views::View {
  public:
   ManagePasswordItemsView(
       ManagePasswordsBubbleModel* manage_passwords_bubble_model,
       const std::vector<autofill::PasswordForm>* password_forms);
-  ManagePasswordItemsView(
-      ManagePasswordsBubbleModel* manage_passwords_bubble_model,
-      const autofill::PasswordForm* password_form);
 
  private:
   class PasswordFormRow;
diff --git a/chrome/browser/ui/views/passwords/manage_passwords_bubble_view.cc b/chrome/browser/ui/views/passwords/manage_passwords_bubble_view.cc
index 89cc567..9de3a026 100644
--- a/chrome/browser/ui/views/passwords/manage_passwords_bubble_view.cc
+++ b/chrome/browser/ui/views/passwords/manage_passwords_bubble_view.cc
@@ -300,7 +300,7 @@
   // views::View:
   bool OnKeyPressed(const ui::KeyEvent& event) override;
 
-  void ToggleEditingState();
+  void ToggleEditingState(bool accept_changes);
 
   ManagePasswordsBubbleView* parent_;
 
@@ -393,7 +393,7 @@
     const ui::Event& event) {
   // TODO(https://crbug.com/734965): Implement edit button logic.
   if (sender == edit_button_) {
-    ToggleEditingState();
+    ToggleEditingState(false);
     return;
   }
   if (sender == save_button_) {
@@ -421,7 +421,7 @@
     View* focused_before,
     View* focused_now) {
   if (editing_ && focused_before == username_field_) {
-    ToggleEditingState();
+    ToggleEditingState(true);
   }
 }
 
@@ -429,13 +429,18 @@
     const ui::KeyEvent& event) {
   if (editing_ && (event.key_code() == ui::KeyboardCode::VKEY_RETURN ||
                    event.key_code() == ui::KeyboardCode::VKEY_ESCAPE)) {
-    ToggleEditingState();
+    ToggleEditingState(event.key_code() == ui::KeyboardCode::VKEY_RETURN);
     return true;
   }
   return false;
 }
 
-void ManagePasswordsBubbleView::PendingView::ToggleEditingState() {
+void ManagePasswordsBubbleView::PendingView::ToggleEditingState(
+    bool accept_changes) {
+  if (editing_ && accept_changes) {
+    parent_->model()->OnUsernameEdited(
+        static_cast<views::Textfield*>(username_field_)->text());
+  }
   editing_ = !editing_;
   edit_button_->SetEnabled(!editing_);
   RemoveChildView(username_field_);
@@ -719,31 +724,29 @@
   layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0));
   SetLayoutManager(layout);
 
-  // Create the pending credential item, update button.
-  View* item = nullptr;
-  if (parent->model()->ShouldShowMultipleAccountUpdateUI()) {
-    selection_view_ = new CredentialsSelectionView(parent->model());
-    item = selection_view_;
-  } else {
-    item = new ManagePasswordItemsView(parent_->model(),
-                                       &parent->model()->pending_password());
-  }
-  nope_button_ = views::MdTextButton::CreateSecondaryUiButton(
-      this, l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_CANCEL_BUTTON));
-
-  update_button_ = views::MdTextButton::CreateSecondaryUiBlueButton(
-      this, l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_UPDATE_BUTTON));
-
-  BuildColumnSet(layout, SINGLE_VIEW_COLUMN_SET);
-
   // Credential row.
-  layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
-  layout->AddView(item);
+  if (parent->model()->ShouldShowMultipleAccountUpdateUI()) {
+    BuildColumnSet(layout, SINGLE_VIEW_COLUMN_SET);
+    layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
+    layout->AddView(new CredentialsSelectionView(parent->model()));
+  } else {
+    BuildColumnSet(layout, DOUBLE_VIEW_COLUMN_SET);
+    layout->StartRow(0, DOUBLE_VIEW_COLUMN_SET);
+    const autofill::PasswordForm* password_form =
+        &parent_->model()->pending_password();
+    layout->AddView(GenerateUsernameLabel(*password_form).release());
+    layout->AddView(GeneratePasswordLabel(*password_form).release());
+  }
   layout->AddPaddingRow(
       0,
       layout_provider->GetInsetsMetric(views::INSETS_DIALOG_CONTENTS).bottom());
 
   // Button row.
+  nope_button_ = views::MdTextButton::CreateSecondaryUiButton(
+      this, l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_CANCEL_BUTTON));
+
+  update_button_ = views::MdTextButton::CreateSecondaryUiBlueButton(
+      this, l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_UPDATE_BUTTON));
   BuildColumnSet(layout, DOUBLE_BUTTON_COLUMN_SET);
   layout->StartRowWithPadding(
       0, DOUBLE_BUTTON_COLUMN_SET, 0,
diff --git a/chrome/browser/ui/views/passwords/password_dialog_view_browsertest.cc b/chrome/browser/ui/views/passwords/password_dialog_view_browsertest.cc
index ac7d985..73c7068d 100644
--- a/chrome/browser/ui/views/passwords/password_dialog_view_browsertest.cc
+++ b/chrome/browser/ui/views/passwords/password_dialog_view_browsertest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/memory/ptr_util.h"
+#include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/password_manager/chrome_password_manager_client.h"
 #include "chrome/browser/profiles/profile.h"
@@ -446,19 +447,40 @@
   std::vector<std::unique_ptr<autofill::PasswordForm>> local_credentials;
   autofill::PasswordForm form;
   form.origin = origin;
-  form.display_name = base::ASCIIToUTF16("Peter");
+  form.display_name = base::ASCIIToUTF16("Peter Pan");
   form.username_value = base::ASCIIToUTF16("peter@pan.test");
   if (name == "PopupAutoSigninPrompt") {
     form.icon_url = GURL("broken url");
     local_credentials.push_back(base::MakeUnique<autofill::PasswordForm>(form));
     form.icon_url = GURL("https://google.com/icon.png");
-    form.display_name = base::ASCIIToUTF16("Peter Pan");
+    form.display_name = base::ASCIIToUTF16("Peter");
     form.federation_origin = url::Origin(GURL("https://google.com/federation"));
     local_credentials.push_back(base::MakeUnique<autofill::PasswordForm>(form));
     SetupChooseCredentials(std::move(local_credentials), origin);
     ASSERT_TRUE(controller()->current_account_chooser());
-  } else if (name == "PopupAccountChooserWithSingleCredentialClickSignIn") {
+  } else if (base::StartsWith(name, "PopupAccountChooserWith",
+                              base::CompareCase::SENSITIVE)) {
     local_credentials.push_back(base::MakeUnique<autofill::PasswordForm>(form));
+    if (name == "PopupAccountChooserWithMultipleCredentialClickSignIn") {
+      form.icon_url = GURL("https://google.com/icon.png");
+      form.display_name = base::ASCIIToUTF16("Tinkerbell");
+      form.username_value = base::ASCIIToUTF16("tinkerbell@pan.test");
+      form.federation_origin =
+          url::Origin(GURL("https://google.com/neverland"));
+      local_credentials.push_back(
+          base::MakeUnique<autofill::PasswordForm>(form));
+      form.display_name = base::ASCIIToUTF16("James Hook");
+      form.username_value = base::ASCIIToUTF16("james@pan.test");
+      form.federation_origin =
+          url::Origin(GURL("https://google.com/jollyroger"));
+      local_credentials.push_back(
+          base::MakeUnique<autofill::PasswordForm>(form));
+      form.display_name = base::ASCIIToUTF16("Wendy Darling");
+      form.username_value = base::ASCIIToUTF16("wendy@pan.test");
+      form.federation_origin = url::Origin(GURL("https://google.com/london"));
+      local_credentials.push_back(
+          base::MakeUnique<autofill::PasswordForm>(form));
+    }
     SetupChooseCredentials(std::move(local_credentials), origin);
   } else {
     ADD_FAILURE() << "Unknown dialog type";
@@ -477,6 +499,12 @@
   RunDialog();
 }
 
+IN_PROC_BROWSER_TEST_F(
+    PasswordDialogViewTest,
+    InvokeDialog_PopupAccountChooserWithMultipleCredentialClickSignIn) {
+  RunDialog();
+}
+
 IN_PROC_BROWSER_TEST_F(PasswordDialogViewTest,
                        InvokeDialog_AutoSigninFirstRun) {
   RunDialog();
diff --git a/chrome/browser/ui/views/payments/modifiers_browsertest.cc b/chrome/browser/ui/views/payments/modifiers_browsertest.cc
new file mode 100644
index 0000000..5482872
--- /dev/null
+++ b/chrome/browser/ui/views/payments/modifiers_browsertest.cc
@@ -0,0 +1,239 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <vector>
+
+#include "base/macros.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/ui/views/payments/payment_request_browsertest_base.h"
+#include "chrome/browser/ui/views/payments/payment_request_dialog_view_ids.h"
+#include "components/autofill/core/browser/autofill_test_utils.h"
+#include "components/web_modal/web_contents_modal_dialog_manager.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/test/browser_test_utils.h"
+
+namespace payments {
+
+class PaymentRequestModifiersTest : public PaymentRequestBrowserTestBase {
+ protected:
+  PaymentRequestModifiersTest()
+      : PaymentRequestBrowserTestBase(
+            "/payment_request_bobpay_and_basic_card_with_basic_card_modifiers_"
+            "test.html") {}
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    PaymentRequestBrowserTestBase::SetUpCommandLine(command_line);
+    command_line->AppendSwitch(
+        switches::kEnableExperimentalWebPlatformFeatures);
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(PaymentRequestModifiersTest);
+};
+
+IN_PROC_BROWSER_TEST_F(PaymentRequestModifiersTest,
+                       NoModifierAppliedIfNoSelectedInstrument) {
+  InvokePaymentRequestUI();
+  OpenOrderSummaryScreen();
+
+  EXPECT_EQ(base::ASCIIToUTF16("$5.00"),
+            GetLabelText(DialogViewID::ORDER_SUMMARY_TOTAL_AMOUNT_LABEL));
+  // There's only the total line.
+  EXPECT_EQ(1, dialog_view()
+                   ->view_stack_for_testing()
+                   ->top()
+                   ->GetViewByID(static_cast<int>(DialogViewID::CONTENT_VIEW))
+                   ->child_count());
+}
+
+IN_PROC_BROWSER_TEST_F(
+    PaymentRequestModifiersTest,
+    ModifierAppliedIfApplicableSelectedInstrumentWithoutTypeOrNetwork) {
+  autofill::AutofillProfile profile(autofill::test::GetFullProfile());
+  AddAutofillProfile(profile);
+  autofill::CreditCard card(
+      autofill::test::GetMaskedServerCard());  // Mastercard card.
+  card.set_billing_address_id(profile.guid());
+  card.set_card_type(autofill::CreditCard::CardType::CARD_TYPE_CREDIT);
+  AddCreditCard(card);
+
+  InvokePaymentRequestUI();
+  OpenOrderSummaryScreen();
+
+  EXPECT_EQ(base::ASCIIToUTF16("$4.00"),
+            GetLabelText(DialogViewID::ORDER_SUMMARY_TOTAL_AMOUNT_LABEL));
+  // A line for the discount and one for the total.
+  EXPECT_EQ(2, dialog_view()
+                   ->view_stack_for_testing()
+                   ->top()
+                   ->GetViewByID(static_cast<int>(DialogViewID::CONTENT_VIEW))
+                   ->child_count());
+}
+
+IN_PROC_BROWSER_TEST_F(
+    PaymentRequestModifiersTest,
+    ModifierAppliedIfApplicableSelectedInstrumentWithCreditSupportedType) {
+  autofill::AutofillProfile profile(autofill::test::GetFullProfile());
+  AddAutofillProfile(profile);
+  autofill::CreditCard card(
+      autofill::test::GetMaskedServerCard());  // Mastercard card.
+  card.set_billing_address_id(profile.guid());
+  card.set_card_type(autofill::CreditCard::CardType::CARD_TYPE_CREDIT);
+  AddCreditCard(card);
+
+  ResetEventObserver(DialogEvent::DIALOG_OPENED);
+  content::WebContents* web_contents = GetActiveWebContents();
+  const std::string click_buy_button_js =
+      "(function() { "
+      "document.getElementById('credit_supported_type')."
+      "click(); })();";
+  ASSERT_TRUE(content::ExecuteScript(web_contents, click_buy_button_js));
+  WaitForObservedEvent();
+  // The web-modal dialog should be open.
+  web_modal::WebContentsModalDialogManager* web_contents_modal_dialog_manager =
+      web_modal::WebContentsModalDialogManager::FromWebContents(web_contents);
+  EXPECT_TRUE(web_contents_modal_dialog_manager->IsDialogActive());
+
+  OpenOrderSummaryScreen();
+
+  EXPECT_EQ(base::ASCIIToUTF16("$4.00"),
+            GetLabelText(DialogViewID::ORDER_SUMMARY_TOTAL_AMOUNT_LABEL));
+  // A line for the discount and one for the total.
+  EXPECT_EQ(2, dialog_view()
+                   ->view_stack_for_testing()
+                   ->top()
+                   ->GetViewByID(static_cast<int>(DialogViewID::CONTENT_VIEW))
+                   ->child_count());
+}
+
+IN_PROC_BROWSER_TEST_F(
+    PaymentRequestModifiersTest,
+    ModifierNotAppliedIfSelectedInstrumentWithDebitSupportedType) {
+  autofill::AutofillProfile profile(autofill::test::GetFullProfile());
+  AddAutofillProfile(profile);
+  autofill::CreditCard card(
+      autofill::test::GetMaskedServerCard());  // Mastercard card.
+  card.set_billing_address_id(profile.guid());
+  card.set_card_type(autofill::CreditCard::CardType::CARD_TYPE_CREDIT);
+  AddCreditCard(card);
+
+  ResetEventObserver(DialogEvent::DIALOG_OPENED);
+  content::WebContents* web_contents = GetActiveWebContents();
+  const std::string click_buy_button_js =
+      "(function() { "
+      "document.getElementById('debit_supported_type').click("
+      "); })();";
+  ASSERT_TRUE(content::ExecuteScript(web_contents, click_buy_button_js));
+  WaitForObservedEvent();
+  // The web-modal dialog should be open.
+  web_modal::WebContentsModalDialogManager* web_contents_modal_dialog_manager =
+      web_modal::WebContentsModalDialogManager::FromWebContents(web_contents);
+  EXPECT_TRUE(web_contents_modal_dialog_manager->IsDialogActive());
+
+  OpenOrderSummaryScreen();
+
+  EXPECT_EQ(base::ASCIIToUTF16("$5.00"),
+            GetLabelText(DialogViewID::ORDER_SUMMARY_TOTAL_AMOUNT_LABEL));
+  // There's only the total line.
+  EXPECT_EQ(1, dialog_view()
+                   ->view_stack_for_testing()
+                   ->top()
+                   ->GetViewByID(static_cast<int>(DialogViewID::CONTENT_VIEW))
+                   ->child_count());
+}
+
+IN_PROC_BROWSER_TEST_F(
+    PaymentRequestModifiersTest,
+    ModifierAppliedIfApplicableSelectedInstrumentWithMatchingNetwork) {
+  autofill::AutofillProfile profile(autofill::test::GetFullProfile());
+  AddAutofillProfile(profile);
+  autofill::CreditCard card(
+      autofill::test::GetMaskedServerCard());  // Mastercard card.
+  card.set_billing_address_id(profile.guid());
+  card.set_card_type(autofill::CreditCard::CardType::CARD_TYPE_CREDIT);
+  AddCreditCard(card);
+
+  ResetEventObserver(DialogEvent::DIALOG_OPENED);
+  content::WebContents* web_contents = GetActiveWebContents();
+  const std::string click_buy_button_js =
+      "(function() { "
+      "document.getElementById('mastercard_supported_network'"
+      ").click(); })();";
+  ASSERT_TRUE(content::ExecuteScript(web_contents, click_buy_button_js));
+  WaitForObservedEvent();
+  // The web-modal dialog should be open.
+  web_modal::WebContentsModalDialogManager* web_contents_modal_dialog_manager =
+      web_modal::WebContentsModalDialogManager::FromWebContents(web_contents);
+  EXPECT_TRUE(web_contents_modal_dialog_manager->IsDialogActive());
+
+  OpenOrderSummaryScreen();
+
+  EXPECT_EQ(base::ASCIIToUTF16("$4.00"),
+            GetLabelText(DialogViewID::ORDER_SUMMARY_TOTAL_AMOUNT_LABEL));
+  // A line for the discount and one for the total.
+  EXPECT_EQ(2, dialog_view()
+                   ->view_stack_for_testing()
+                   ->top()
+                   ->GetViewByID(static_cast<int>(DialogViewID::CONTENT_VIEW))
+                   ->child_count());
+}
+
+IN_PROC_BROWSER_TEST_F(
+    PaymentRequestModifiersTest,
+    ModifierNotAppliedIfSelectedInstrumentWithoutMatchingNetwork) {
+  autofill::AutofillProfile profile(autofill::test::GetFullProfile());
+  AddAutofillProfile(profile);
+  autofill::CreditCard card(
+      autofill::test::GetMaskedServerCard());  // Mastercard card.
+  card.set_billing_address_id(profile.guid());
+  card.set_card_type(autofill::CreditCard::CardType::CARD_TYPE_CREDIT);
+  AddCreditCard(card);
+
+  ResetEventObserver(DialogEvent::DIALOG_OPENED);
+  content::WebContents* web_contents = GetActiveWebContents();
+  const std::string click_buy_button_js =
+      "(function() { "
+      "document.getElementById('visa_supported_network')."
+      "click(); })();";
+  ASSERT_TRUE(content::ExecuteScript(web_contents, click_buy_button_js));
+  WaitForObservedEvent();
+  // The web-modal dialog should be open.
+  web_modal::WebContentsModalDialogManager* web_contents_modal_dialog_manager =
+      web_modal::WebContentsModalDialogManager::FromWebContents(web_contents);
+  EXPECT_TRUE(web_contents_modal_dialog_manager->IsDialogActive());
+
+  OpenOrderSummaryScreen();
+
+  EXPECT_EQ(base::ASCIIToUTF16("$5.00"),
+            GetLabelText(DialogViewID::ORDER_SUMMARY_TOTAL_AMOUNT_LABEL));
+  // There's only the total line.
+  EXPECT_EQ(1, dialog_view()
+                   ->view_stack_for_testing()
+                   ->top()
+                   ->GetViewByID(static_cast<int>(DialogViewID::CONTENT_VIEW))
+                   ->child_count());
+}
+
+IN_PROC_BROWSER_TEST_F(PaymentRequestModifiersTest,
+                       ModifierNotAppliedToUnknownType) {
+  autofill::AutofillProfile profile(autofill::test::GetFullProfile());
+  AddAutofillProfile(profile);
+  autofill::CreditCard card(autofill::test::GetCreditCard());  // Visa card.
+  card.set_billing_address_id(profile.guid());
+  AddCreditCard(card);
+
+  InvokePaymentRequestUI();
+  OpenOrderSummaryScreen();
+
+  EXPECT_EQ(base::ASCIIToUTF16("$5.00"),
+            GetLabelText(DialogViewID::ORDER_SUMMARY_TOTAL_AMOUNT_LABEL));
+  // There's only the total line.
+  EXPECT_EQ(1, dialog_view()
+                   ->view_stack_for_testing()
+                   ->top()
+                   ->GetViewByID(static_cast<int>(DialogViewID::CONTENT_VIEW))
+                   ->child_count());
+}
+
+}  // namespace payments
diff --git a/chrome/browser/ui/views/payments/order_summary_view_controller.cc b/chrome/browser/ui/views/payments/order_summary_view_controller.cc
index 890788af..0c16806 100644
--- a/chrome/browser/ui/views/payments/order_summary_view_controller.cc
+++ b/chrome/browser/ui/views/payments/order_summary_view_controller.cc
@@ -170,35 +170,40 @@
       DialogViewID::ORDER_SUMMARY_LINE_ITEM_1,
       DialogViewID::ORDER_SUMMARY_LINE_ITEM_2,
       DialogViewID::ORDER_SUMMARY_LINE_ITEM_3};
-  for (size_t i = 0; i < spec()->details().display_items.size(); i++) {
+  const auto& display_items =
+      spec()->GetDisplayItems(state()->selected_instrument());
+  for (size_t i = 0; i < display_items.size(); i++) {
     DialogViewID view_id =
         i < line_items.size() ? line_items[i] : DialogViewID::VIEW_ID_NONE;
     base::string16 currency = base::UTF8ToUTF16("");
     if (is_mixed_currency) {
-      currency = base::UTF8ToUTF16(
-          spec()->details().display_items[i]->amount->currency);
+      currency = base::UTF8ToUTF16((*display_items[i])->amount->currency);
     }
 
     content_view->AddChildView(
         CreateLineItemView(
-            base::UTF8ToUTF16(spec()->details().display_items[i]->label),
-            currency,
-            spec()->GetFormattedCurrencyAmount(
-                spec()->details().display_items[i]->amount),
+            base::UTF8ToUTF16((*display_items[i])->label), currency,
+            spec()->GetFormattedCurrencyAmount((*display_items[i])->amount),
             false, DialogViewID::VIEW_ID_NONE, view_id)
             .release());
   }
 
   base::string16 total_label_value = l10n_util::GetStringFUTF16(
       IDS_PAYMENT_REQUEST_ORDER_SUMMARY_SHEET_TOTAL_FORMAT,
-      base::UTF8ToUTF16(spec()->details().total->amount->currency),
-      spec()->GetFormattedCurrencyAmount(spec()->details().total->amount));
+      base::UTF8ToUTF16(
+          spec()->GetTotal(state()->selected_instrument())->amount->currency),
+      spec()->GetFormattedCurrencyAmount(
+          spec()->GetTotal(state()->selected_instrument())->amount));
 
   content_view->AddChildView(
       CreateLineItemView(
-          base::UTF8ToUTF16(spec()->details().total->label),
-          base::UTF8ToUTF16(spec()->details().total->amount->currency),
-          spec()->GetFormattedCurrencyAmount(spec()->details().total->amount),
+          base::UTF8ToUTF16(
+              spec()->GetTotal(state()->selected_instrument())->label),
+          base::UTF8ToUTF16(spec()
+                                ->GetTotal(state()->selected_instrument())
+                                ->amount->currency),
+          spec()->GetFormattedCurrencyAmount(
+              spec()->GetTotal(state()->selected_instrument())->amount),
           true, DialogViewID::ORDER_SUMMARY_TOTAL_CURRENCY_LABEL,
           DialogViewID::ORDER_SUMMARY_TOTAL_AMOUNT_LABEL)
           .release());
diff --git a/chrome/browser/ui/views/payments/payment_request_dialog_view_ids.h b/chrome/browser/ui/views/payments/payment_request_dialog_view_ids.h
index cea42989..e0211d8f7 100644
--- a/chrome/browser/ui/views/payments/payment_request_dialog_view_ids.h
+++ b/chrome/browser/ui/views/payments/payment_request_dialog_view_ids.h
@@ -14,6 +14,7 @@
 
 enum class DialogViewID : int {
   VIEW_ID_NONE = autofill::MAX_VALID_FIELD_TYPE,
+  CONTENT_VIEW,  // The main content view filled by each sheet
 
   // The following are views::Button (clickable).
   PAYMENT_SHEET_CONTACT_INFO_SECTION,
diff --git a/chrome/browser/ui/views/payments/payment_request_journey_logger_browsertest.cc b/chrome/browser/ui/views/payments/payment_request_journey_logger_browsertest.cc
index ff960d9f..b7fdae6 100644
--- a/chrome/browser/ui/views/payments/payment_request_journey_logger_browsertest.cc
+++ b/chrome/browser/ui/views/payments/payment_request_journey_logger_browsertest.cc
@@ -434,8 +434,8 @@
   NavigateTo("/payment_request_email_test.html");
 
   // Initiated should be logged.
-  histogram_tester.ExpectBucketCount("PaymentRequest.CheckoutFunnel.Initiated",
-                                     1, 1);
+  histogram_tester.ExpectUniqueSample("PaymentRequest.CheckoutFunnel.Initiated",
+                                      1, 1);
   // Show should not be logged.
   histogram_tester.ExpectTotalCount("PaymentRequest.CheckoutFunnel.Shown", 0);
   // Abort should not be logged.
@@ -455,6 +455,91 @@
       "PaymentRequest.UserHadSuggestionsForEverything", 0);
   histogram_tester.ExpectTotalCount(
       "PaymentRequest.UserDidNotHaveSuggestionsForEverything", 0);
+  histogram_tester.ExpectTotalCount(
+      "PaymentRequest.UserHadInitialFormOfPayment", 0);
+  histogram_tester.ExpectTotalCount(
+      "PaymentRequest.UserDidNotHaveInitialFormOfPayment", 0);
+}
+
+class PaymentRequestInitialFormOfPaymentTest
+    : public PaymentRequestBrowserTestBase {
+ protected:
+  PaymentRequestInitialFormOfPaymentTest()
+      : PaymentRequestBrowserTestBase("/payment_request_email_test.html") {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(PaymentRequestInitialFormOfPaymentTest);
+};
+
+IN_PROC_BROWSER_TEST_F(PaymentRequestInitialFormOfPaymentTest,
+                       UserHadInitialFormOfPayment) {
+  base::HistogramTester histogram_tester;
+
+  // Add an address and a credit card on file.
+  autofill::AutofillProfile billing_address = autofill::test::GetFullProfile();
+  AddAutofillProfile(billing_address);
+  autofill::CreditCard card = autofill::test::GetCreditCard();
+  card.set_billing_address_id(billing_address.guid());
+  AddCreditCard(card);  // Visa.
+
+  // Show a Payment Request.
+  InvokePaymentRequestUI();
+
+  // Navigate away to abort the Payment Request and trigger the logs.
+  NavigateTo("/payment_request_email_test.html");
+
+  // The fact that the user had a form of payment on file should be recorded.
+  histogram_tester.ExpectUniqueSample(
+      "PaymentRequest.UserHadInitialFormOfPayment.EffectOnCompletion",
+      JourneyLogger::COMPLETION_STATUS_USER_ABORTED, 1);
+  histogram_tester.ExpectTotalCount(
+      "PaymentRequest.UserDidNotHaveInitialFormOfPayment.EffectOnCompletion",
+      0);
+}
+
+IN_PROC_BROWSER_TEST_F(PaymentRequestInitialFormOfPaymentTest,
+                       UserDidNotHaveInitialFormOfPayment_NoCard) {
+  base::HistogramTester histogram_tester;
+
+  // Show a Payment Request. The user has no form of payment on file.
+  InvokePaymentRequestUI();
+
+  // Navigate away to abort the Payment Request and trigger the logs.
+  NavigateTo("/payment_request_email_test.html");
+
+  // The fact that the user had no form of payment on file should be recorded.
+  histogram_tester.ExpectUniqueSample(
+      "PaymentRequest.UserDidNotHaveInitialFormOfPayment.EffectOnCompletion",
+      JourneyLogger::COMPLETION_STATUS_USER_ABORTED, 1);
+  histogram_tester.ExpectTotalCount(
+      "PaymentRequest.UserHadInitialFormOfPayment.EffectOnCompletion", 0);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    PaymentRequestInitialFormOfPaymentTest,
+    UserDidNotHaveInitialFormOfPayment_CardNetworkNotSupported) {
+  base::HistogramTester histogram_tester;
+
+  // Add an address and an AMEX credit card on file. AMEX is not supported by
+  // the merchant.
+  autofill::AutofillProfile billing_address = autofill::test::GetFullProfile();
+  AddAutofillProfile(billing_address);
+  autofill::CreditCard card = autofill::test::GetCreditCard2();
+  card.set_billing_address_id(billing_address.guid());
+  AddCreditCard(card);  // AMEX.
+
+  // Show a Payment Request.
+  InvokePaymentRequestUI();
+
+  // Navigate away to abort the Payment Request and trigger the logs.
+  NavigateTo("/payment_request_email_test.html");
+
+  // The fact that the user had no form of payment on file should be recorded.
+  histogram_tester.ExpectUniqueSample(
+      "PaymentRequest.UserDidNotHaveInitialFormOfPayment.EffectOnCompletion",
+      JourneyLogger::COMPLETION_STATUS_USER_ABORTED, 1);
+  histogram_tester.ExpectTotalCount(
+      "PaymentRequest.UserHadInitialFormOfPayment.EffectOnCompletion", 0);
 }
 
 }  // namespace payments
diff --git a/chrome/browser/ui/views/payments/payment_request_sheet_controller.cc b/chrome/browser/ui/views/payments/payment_request_sheet_controller.cc
index 17c4ceb..07436a9 100644
--- a/chrome/browser/ui/views/payments/payment_request_sheet_controller.cc
+++ b/chrome/browser/ui/views/payments/payment_request_sheet_controller.cc
@@ -250,6 +250,7 @@
   content_view_->SetPaintToLayer();
   content_view_->layer()->SetFillsBoundsOpaquely(true);
   content_view_->SetBackground(views::CreateSolidBackground(SK_ColorWHITE));
+  content_view_->set_id(static_cast<int>(DialogViewID::CONTENT_VIEW));
   pane_layout->AddView(content_view_);
   pane_->SizeToPreferredSize();
 
diff --git a/chrome/browser/ui/views/payments/payment_sheet_view_controller.cc b/chrome/browser/ui/views/payments/payment_sheet_view_controller.cc
index a9c1cf5..8e759e6b 100644
--- a/chrome/browser/ui/views/payments/payment_sheet_view_controller.cc
+++ b/chrome/browser/ui/views/payments/payment_sheet_view_controller.cc
@@ -555,8 +555,8 @@
                      views::GridLayout::FIXED, kItemSummaryPriceFixedWidth,
                      kItemSummaryPriceFixedWidth);
 
-  const std::vector<mojom::PaymentItemPtr>& items =
-      spec()->details().display_items;
+  const std::vector<const mojom::PaymentItemPtr*>& items =
+      spec()->GetDisplayItems(state()->selected_instrument());
 
   bool is_mixed_currency = spec()->IsMixedCurrency();
   // The inline items section contains the first 2 display items of the
@@ -572,7 +572,7 @@
   for (size_t i = 0; i < items.size() && i < displayed_items; ++i) {
     layout->StartRow(0, 0);
     std::unique_ptr<views::Label> summary =
-        base::MakeUnique<views::Label>(base::UTF8ToUTF16(items[i]->label));
+        base::MakeUnique<views::Label>(base::UTF8ToUTF16((*items[i])->label));
     summary->SetHorizontalAlignment(gfx::ALIGN_LEFT);
     layout->AddView(summary.release());
 
@@ -580,9 +580,10 @@
         CreateInlineCurrencyAmountItem(
             is_mixed_currency
                 ? base::UTF8ToUTF16(
-                      spec()->GetFormattedCurrencyCode(items[i]->amount))
+                      spec()->GetFormattedCurrencyCode((*items[i])->amount))
                 : base::string16(),
-            spec()->GetFormattedCurrencyAmount(items[i]->amount), true, false)
+            spec()->GetFormattedCurrencyAmount((*items[i])->amount), true,
+            false)
             .release());
   }
 
@@ -603,14 +604,17 @@
 
   layout->StartRow(0, 0);
   layout->AddView(
-      CreateBoldLabel(base::UTF8ToUTF16(spec()->details().total->label))
+      CreateBoldLabel(
+          base::UTF8ToUTF16(
+              spec()->GetTotal(state()->selected_instrument())->label))
           .release());
 
   layout->AddView(
       CreateInlineCurrencyAmountItem(
           base::UTF8ToUTF16(spec()->GetFormattedCurrencyCode(
-              spec()->details().total->amount)),
-          spec()->GetFormattedCurrencyAmount(spec()->details().total->amount),
+              spec()->GetTotal(state()->selected_instrument())->amount)),
+          spec()->GetFormattedCurrencyAmount(
+              spec()->GetTotal(state()->selected_instrument())->amount),
           false, true)
           .release());
 
@@ -849,7 +853,7 @@
   builder.Tag(PaymentSheetViewControllerTags::SHOW_SHIPPING_OPTION_BUTTON);
 
   if (state()->selected_shipping_profile()) {
-    if (spec()->details().shipping_options.empty()) {
+    if (spec()->GetShippingOptions().empty()) {
       // 1.1 No shipping options, do not display the row.
       return nullptr;
     }
@@ -868,16 +872,16 @@
     } else {
       // 1.3 There are options, none are selected: show the enabled Choose
       // button.
+      const auto& shipping_options = spec()->GetShippingOptions();
       return builder
           .Id(DialogViewID::PAYMENT_SHEET_SHIPPING_OPTION_SECTION_BUTTON)
-          .CreateWithButton(
-              base::UTF8ToUTF16(spec()->details().shipping_options[0]->label),
-              l10n_util::GetPluralStringFUTF16(
-                  IDS_PAYMENT_REQUEST_SHIPPING_OPTIONS_PREVIEW,
-                  spec()->details().shipping_options.size() - 1),
-              spec()->details().shipping_options.size() - 1,
-              l10n_util::GetStringUTF16(IDS_CHOOSE),
-              /*button_enabled=*/true);
+          .CreateWithButton(base::UTF8ToUTF16(shipping_options[0]->label),
+                            l10n_util::GetPluralStringFUTF16(
+                                IDS_PAYMENT_REQUEST_SHIPPING_OPTIONS_PREVIEW,
+                                shipping_options.size() - 1),
+                            shipping_options.size() - 1,
+                            l10n_util::GetStringUTF16(IDS_CHOOSE),
+                            /*button_enabled=*/true);
     }
   } else {
     // 2. There is no selected address: do not show the shipping option section.
diff --git a/chrome/browser/ui/views/payments/profile_list_view_controller.cc b/chrome/browser/ui/views/payments/profile_list_view_controller.cc
index dcd0b80..1da06e3 100644
--- a/chrome/browser/ui/views/payments/profile_list_view_controller.cc
+++ b/chrome/browser/ui/views/payments/profile_list_view_controller.cc
@@ -172,7 +172,7 @@
   // | Warning icon | Warning message            |
   // ---------------------------------------------
   std::unique_ptr<views::View> CreateHeaderView() override {
-    if (!spec()->details().shipping_options.empty())
+    if (!spec()->GetShippingOptions().empty())
       return nullptr;
 
     auto header_view = base::MakeUnique<views::View>();
diff --git a/chrome/browser/ui/views/payments/shipping_option_view_controller.cc b/chrome/browser/ui/views/payments/shipping_option_view_controller.cc
index 79385cc..d8ed2739 100644
--- a/chrome/browser/ui/views/payments/shipping_option_view_controller.cc
+++ b/chrome/browser/ui/views/payments/shipping_option_view_controller.cc
@@ -80,7 +80,7 @@
     PaymentRequestDialogView* dialog)
     : PaymentRequestSheetController(spec, state, dialog) {
   spec->AddObserver(this);
-  for (const auto& option : spec->details().shipping_options) {
+  for (const auto& option : spec->GetShippingOptions()) {
     shipping_option_list_.AddItem(base::MakeUnique<ShippingOptionItem>(
         option.get(), spec, state, &shipping_option_list_, dialog,
         option.get() == spec->selected_shipping_option()));
diff --git a/chrome/browser/ui/views/payments/view_stack.h b/chrome/browser/ui/views/payments/view_stack.h
index 0472c6c..9b51d1b1 100644
--- a/chrome/browser/ui/views/payments/view_stack.h
+++ b/chrome/browser/ui/views/payments/view_stack.h
@@ -48,6 +48,9 @@
   void Layout() override;
   void RequestFocus() override;
 
+  // Returns the top state of the stack.
+  views::View* top() { return stack_.back().get(); }
+
  private:
   FRIEND_TEST_ALL_PREFIXES(
       ViewStackTest, TestPopStateRemovesChildViewAndCleansUpState);
@@ -58,9 +61,6 @@
   friend class ViewStackTest;
   friend class payments::PaymentRequestBrowserTestBase;
 
-  // Returns the top state of the stack, used in tests.
-  views::View* top() { return stack_.back().get(); }
-
   // Marks all views, except the topmost, as invisible.
   void HideCoveredViews();
 
diff --git a/chrome/browser/ui/webui/chromeos/login/oobe_display_chooser.cc b/chrome/browser/ui/webui/chromeos/login/oobe_display_chooser.cc
index c15ebea..7118b9f 100644
--- a/chrome/browser/ui/webui/chromeos/login/oobe_display_chooser.cc
+++ b/chrome/browser/ui/webui/chromeos/login/oobe_display_chooser.cc
@@ -4,13 +4,17 @@
 
 #include "chrome/browser/ui/webui/chromeos/login/oobe_display_chooser.h"
 
+#include <stdint.h>
+
 #include "ash/display/window_tree_host_manager.h"
 #include "ash/shell.h"
+#include "base/stl_util.h"
 #include "content/public/browser/browser_thread.h"
 #include "ui/display/display.h"
-#include "ui/display/display_layout.h"
 #include "ui/display/manager/display_manager.h"
 #include "ui/display/screen.h"
+#include "ui/events/devices/device_data_manager.h"
+#include "ui/events/devices/touchscreen_device.h"
 
 using content::BrowserThread;
 
@@ -23,6 +27,9 @@
          display::Display::TouchSupport::TOUCH_SUPPORT_AVAILABLE;
 }
 
+// TODO(felixe): More context at crbug.com/738885
+const uint16_t kDeviceIds[] = {0x0457, 0x266e};
+
 }  // namespace
 
 OobeDisplayChooser::OobeDisplayChooser() : weak_ptr_factory_(this) {}
@@ -51,18 +58,21 @@
 void OobeDisplayChooser::MoveToTouchDisplay() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-  const display::Displays& displays =
-      ash::Shell::Get()->display_manager()->active_only_display_list();
+  const ui::DeviceDataManager* device_manager =
+      ui::DeviceDataManager::GetInstance();
+  for (const ui::TouchscreenDevice& device :
+       device_manager->GetTouchscreenDevices()) {
+    if (!base::ContainsValue(kDeviceIds, device.vendor_id))
+      continue;
 
-  if (displays.size() <= 1)
-    return;
+    int64_t display_id =
+        device_manager->GetTargetDisplayForTouchDevice(device.id);
+    if (display_id == display::kInvalidDisplayId)
+      continue;
 
-  for (const display::Display& display : displays) {
-    if (TouchSupportAvailable(display)) {
-      ash::Shell::Get()->window_tree_host_manager()->SetPrimaryDisplayId(
-          display.id());
-      break;
-    }
+    ash::Shell::Get()->window_tree_host_manager()->SetPrimaryDisplayId(
+        display_id);
+    break;
   }
 }
 
diff --git a/chrome/browser/ui/webui/chromeos/login/oobe_display_chooser_unittest.cc b/chrome/browser/ui/webui/chromeos/login/oobe_display_chooser_unittest.cc
index acf074ae..528fd1c 100644
--- a/chrome/browser/ui/webui/chromeos/login/oobe_display_chooser_unittest.cc
+++ b/chrome/browser/ui/webui/chromeos/login/oobe_display_chooser_unittest.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ui/webui/chromeos/login/oobe_display_chooser.h"
 
 #include <memory>
+#include <vector>
 
 #include "ash/display/display_configuration_controller.h"
 #include "ash/shell.h"
@@ -13,9 +14,12 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/display/display.h"
 #include "ui/display/display_observer.h"
+#include "ui/display/manager/chromeos/touchscreen_util.h"
 #include "ui/display/manager/display_manager.h"
 #include "ui/display/screen.h"
 #include "ui/display/test/display_manager_test_api.h"
+#include "ui/events/devices/device_data_manager.h"
+#include "ui/events/devices/touchscreen_device.h"
 
 namespace chromeos {
 
@@ -25,63 +29,93 @@
  public:
   OobeDisplayChooserTest() : ash::test::AshTestBase() {}
 
-  void SetUp() override {
-    ash::test::AshTestBase::SetUp();
-    display_manager_test_api_.reset(
-        new display::test::DisplayManagerTestApi(display_manager()));
-  }
-
-  void EnableTouch(int64_t id) {
-    display_manager_test_api_->SetTouchSupport(
-        id, display::Display::TouchSupport::TOUCH_SUPPORT_AVAILABLE);
-  }
-
-  void DisableTouch(int64_t id) {
-    display_manager_test_api_->SetTouchSupport(
-        id, display::Display::TouchSupport::TOUCH_SUPPORT_UNAVAILABLE);
-  }
-
   int64_t GetPrimaryDisplay() {
     return display::Screen::GetScreen()->GetPrimaryDisplay().id();
   }
 
- private:
-  std::unique_ptr<display::test::DisplayManagerTestApi>
-      display_manager_test_api_;
+  void UpdateTouchscreenDevices(const ui::TouchscreenDevice& touchscreen) {
+    std::vector<ui::TouchscreenDevice> devices{touchscreen};
 
+    ui::DeviceHotplugEventObserver* manager =
+        ui::DeviceDataManager::GetInstance();
+    manager->OnTouchscreenDevicesUpdated(devices);
+  }
+
+ private:
   DISALLOW_COPY_AND_ASSIGN(OobeDisplayChooserTest);
 };
 
+const uint16_t kWhitelistedId = 0x266e;
+
 }  // namespace
 
 TEST_F(OobeDisplayChooserTest, PreferTouchAsPrimary) {
-  OobeDisplayChooser display_chooser;
-
-  UpdateDisplay("3000x2000,800x600");
-  display::DisplayIdList ids = display_manager()->GetCurrentDisplayIdList();
-  DisableTouch(ids[0]);
-  EnableTouch(ids[1]);
-
-  EXPECT_EQ(ids[0], GetPrimaryDisplay());
-  display_chooser.TryToPlaceUiOnTouchDisplay();
+  // Setup 2 displays, second one is intended to be a touch display
+  std::vector<display::ManagedDisplayInfo> display_info;
+  display_info.push_back(
+      display::ManagedDisplayInfo::CreateFromSpecWithID("0+0-3000x2000", 1));
+  display_info.push_back(
+      display::ManagedDisplayInfo::CreateFromSpecWithID("3000+0-800x600", 2));
+  display_manager()->OnNativeDisplaysChanged(display_info);
   base::RunLoop().RunUntilIdle();
 
-  EXPECT_EQ(ids[1], GetPrimaryDisplay());
+  // Make sure the non-touch display is primary
+  ash::Shell::Get()->window_tree_host_manager()->SetPrimaryDisplayId(1);
+
+  // Setup corresponding TouchscreenDevice object
+  ui::TouchscreenDevice touchscreen =
+      ui::TouchscreenDevice(1, ui::InputDeviceType::INPUT_DEVICE_EXTERNAL,
+                            "Touchscreen", gfx::Size(800, 600), 1);
+  touchscreen.vendor_id = kWhitelistedId;
+  UpdateTouchscreenDevices(touchscreen);
+  base::RunLoop().RunUntilIdle();
+
+  // Associate touchscreen device with display
+  display_info[1].AddInputDevice(touchscreen.id);
+  display_info[1].set_touch_support(display::Display::TOUCH_SUPPORT_AVAILABLE);
+  display_manager()->OnNativeDisplaysChanged(display_info);
+  base::RunLoop().RunUntilIdle();
+
+  OobeDisplayChooser display_chooser;
+  EXPECT_EQ(1, GetPrimaryDisplay());
+  display_chooser.TryToPlaceUiOnTouchDisplay();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(2, GetPrimaryDisplay());
 }
 
-TEST_F(OobeDisplayChooserTest, AddingSecondTouchDisplayShouldbeNOP) {
-  OobeDisplayChooser display_chooser;
-
-  UpdateDisplay("3000x2000,800x600");
-  display::DisplayIdList ids = display_manager()->GetCurrentDisplayIdList();
-  EnableTouch(ids[0]);
-  EnableTouch(ids[1]);
-
-  EXPECT_EQ(ids[0], GetPrimaryDisplay());
-  display_chooser.TryToPlaceUiOnTouchDisplay();
+TEST_F(OobeDisplayChooserTest, DontSwitchFromTouch) {
+  // Setup 2 displays, second one is intended to be a touch display
+  std::vector<display::ManagedDisplayInfo> display_info;
+  display_info.push_back(
+      display::ManagedDisplayInfo::CreateFromSpecWithID("0+0-3000x2000", 1));
+  display_info.push_back(
+      display::ManagedDisplayInfo::CreateFromSpecWithID("3000+0-800x600", 2));
+  display_info[0].set_touch_support(display::Display::TOUCH_SUPPORT_AVAILABLE);
+  display_manager()->OnNativeDisplaysChanged(display_info);
   base::RunLoop().RunUntilIdle();
 
-  EXPECT_EQ(ids[0], GetPrimaryDisplay());
+  // Make sure the non-touch display is primary
+  ash::Shell::Get()->window_tree_host_manager()->SetPrimaryDisplayId(1);
+
+  // Setup corresponding TouchscreenDevice object
+  ui::TouchscreenDevice touchscreen =
+      ui::TouchscreenDevice(1, ui::InputDeviceType::INPUT_DEVICE_EXTERNAL,
+                            "Touchscreen", gfx::Size(800, 600), 1);
+  touchscreen.vendor_id = kWhitelistedId;
+  UpdateTouchscreenDevices(touchscreen);
+  base::RunLoop().RunUntilIdle();
+
+  // Associate touchscreen device with display
+  display_info[1].AddInputDevice(touchscreen.id);
+  display_info[1].set_touch_support(display::Display::TOUCH_SUPPORT_AVAILABLE);
+  display_manager()->OnNativeDisplaysChanged(display_info);
+  base::RunLoop().RunUntilIdle();
+
+  OobeDisplayChooser display_chooser;
+  EXPECT_EQ(1, GetPrimaryDisplay());
+  display_chooser.TryToPlaceUiOnTouchDisplay();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1, GetPrimaryDisplay());
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc b/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc
index d2db7e5..94f7115 100644
--- a/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc
@@ -226,17 +226,12 @@
   return path;
 }
 
-bool IsKeyboardConnected() {
-  const std::vector<ui::InputDevice>& keyboards =
-      ui::InputDeviceManager::GetInstance()->GetKeyboardDevices();
-  for (const ui::InputDevice& keyboard : keyboards) {
-    if (keyboard.type == ui::INPUT_DEVICE_INTERNAL ||
-        keyboard.type == ui::INPUT_DEVICE_EXTERNAL) {
-      return true;
-    }
-  }
-
-  return false;
+bool IsRemoraRequisitioned() {
+  policy::DeviceCloudPolicyManagerChromeOS* policy_manager =
+      g_browser_process->platform_part()
+          ->browser_policy_connector_chromeos()
+          ->GetDeviceCloudPolicyManager();
+  return policy_manager && policy_manager->IsRemoraRequisition();
 }
 
 }  // namespace
@@ -376,7 +371,7 @@
 
   // TODO(felixe): Display iteration and primary display selection not supported
   // in Mash. See http://crbug.com/720917.
-  if (!ash_util::IsRunningInMash() && !IsKeyboardConnected())
+  if (!ash_util::IsRunningInMash() && IsRemoraRequisitioned())
     oobe_display_chooser_ = base::MakeUnique<OobeDisplayChooser>();
 }
 
diff --git a/chrome/browser/ui/webui/conflicts_ui.cc b/chrome/browser/ui/webui/conflicts_ui.cc
index 1789f0c..bf8f50a0c4 100644
--- a/chrome/browser/ui/webui/conflicts_ui.cc
+++ b/chrome/browser/ui/webui/conflicts_ui.cc
@@ -6,7 +6,6 @@
 
 #include "base/memory/ptr_util.h"
 #include "base/memory/ref_counted_memory.h"
-#include "base/metrics/user_metrics.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/webui/conflicts_handler.h"
 #include "chrome/browser/ui/webui/module_database_conflicts_handler.h"
@@ -60,8 +59,6 @@
 
 ConflictsUI::ConflictsUI(content::WebUI* web_ui)
     : content::WebUIController(web_ui) {
-  base::RecordAction(base::UserMetricsAction("ViewAboutConflicts"));
-
   if (base::FeatureList::IsEnabled(features::kModuleDatabase)) {
     web_ui->AddMessageHandler(
         base::MakeUnique<ModuleDatabaseConflictsHandler>());
diff --git a/chrome/browser/ui/webui/conflicts_ui.h b/chrome/browser/ui/webui/conflicts_ui.h
index 0618ae5..c13d471e 100644
--- a/chrome/browser/ui/webui/conflicts_ui.h
+++ b/chrome/browser/ui/webui/conflicts_ui.h
@@ -6,7 +6,6 @@
 #define CHROME_BROWSER_UI_WEBUI_CONFLICTS_UI_H_
 
 #include "base/macros.h"
-#include "build/build_config.h"
 #include "content/public/browser/web_ui_controller.h"
 #include "ui/base/layout.h"
 
diff --git a/chrome/browser/ui/webui/extensions/extension_settings_browsertest.js b/chrome/browser/ui/webui/extensions/extension_settings_browsertest.js
index b54503f..32a910b4 100644
--- a/chrome/browser/ui/webui/extensions/extension_settings_browsertest.js
+++ b/chrome/browser/ui/webui/extensions/extension_settings_browsertest.js
@@ -516,6 +516,14 @@
 OptionsDialogExtensionSettingsWebUITest.prototype = {
   __proto__: InstallGoodExtensionSettingsWebUITest.prototype,
 
+  setUp() {
+    InstallGoodExtensionSettingsWebUITest.prototype.setUp.call(this);
+
+    // False positive on iframe hosting the <extensionoptions> guest view.
+    this.accessibilityAuditConfig.ignoreSelectors(
+        'focusableElementNotVisibleAndNotAriaHidden', 'iframe');
+  },
+
   /** @override */
   browsePreload: ExtensionSettingsWebUITest.prototype.browsePreload +
       '?options=' + GOOD_EXTENSION_ID,
diff --git a/chrome/browser/ui/webui/settings/chrome_cleanup_handler.cc b/chrome/browser/ui/webui/settings/chrome_cleanup_handler.cc
index 8965daf..e786d4c 100644
--- a/chrome/browser/ui/webui/settings/chrome_cleanup_handler.cc
+++ b/chrome/browser/ui/webui/settings/chrome_cleanup_handler.cc
@@ -75,6 +75,10 @@
       base::Bind(&ChromeCleanupHandler::HandleRestartComputer,
                  base::Unretained(this)));
   web_ui()->RegisterMessageCallback(
+      "setLogsUploadPermission",
+      base::Bind(&ChromeCleanupHandler::HandleSetLogsUploadPermission,
+                 base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
       "startCleanup", base::Bind(&ChromeCleanupHandler::HandleStartCleanup,
                                  base::Unretained(this)));
 }
@@ -116,6 +120,12 @@
                          base::Value("chrome-cleanup-on-reboot-required"));
 }
 
+void ChromeCleanupHandler::OnLogsEnabledChanged(bool logs_enabled) {
+  CallJavascriptFunction("cr.webUIListenerCallback",
+                         base::Value("chrome-cleanup-upload-permission-change"),
+                         base::Value(logs_enabled));
+}
+
 void ChromeCleanupHandler::HandleDismiss(const base::ListValue* args) {
   DCHECK_EQ(0U, args->GetSize());
 
@@ -134,6 +144,9 @@
       base::FeatureList::IsEnabled(safe_browsing::kInBrowserCleanerUIFeature));
 
   AllowJavascript();
+
+  // Send the current logs upload state.
+  OnLogsEnabledChanged(controller_->logs_enabled());
 }
 
 void ChromeCleanupHandler::HandleRestartComputer(const base::ListValue* args) {
@@ -141,16 +154,32 @@
 
   CallJavascriptFunction("cr.webUIListenerCallback",
                          base::Value("chrome-cleanup-on-dismiss"));
-  // TODO(proberge): Show a prompt to reboot the system.
+
+  controller_->Reboot();
+}
+
+void ChromeCleanupHandler::HandleSetLogsUploadPermission(
+    const base::ListValue* args) {
+  CHECK_EQ(1U, args->GetSize());
+  bool allow_logs_upload = false;
+  args->GetBoolean(0, &allow_logs_upload);
+
+  controller_->SetLogsEnabled(allow_logs_upload);
 }
 
 void ChromeCleanupHandler::HandleStartCleanup(const base::ListValue* args) {
-  DCHECK_EQ(0U, args->GetSize());
+  CHECK_EQ(1U, args->GetSize());
+  bool allow_logs_upload = false;
+  args->GetBoolean(0, &allow_logs_upload);
+
+  // The state is propagated to all open tabs and should be consistent.
+  DCHECK_EQ(controller_->logs_enabled(), allow_logs_upload);
 
   controller_->ReplyWithUserResponse(
-      // TODO(proberge): Send kAcceptedWithLogs or kAcceptedWithoutLogs based on
-      // the state of a logs upload permissions checkbox.
-      profile_, ChromeCleanerController::UserResponse::kAcceptedWithoutLogs);
+      profile_,
+      allow_logs_upload
+          ? ChromeCleanerController::UserResponse::kAcceptedWithLogs
+          : ChromeCleanerController::UserResponse::kAcceptedWithoutLogs);
 }
 
 }  // namespace settings
diff --git a/chrome/browser/ui/webui/settings/chrome_cleanup_handler.h b/chrome/browser/ui/webui/settings/chrome_cleanup_handler.h
index e32b273..59f4493 100644
--- a/chrome/browser/ui/webui/settings/chrome_cleanup_handler.h
+++ b/chrome/browser/ui/webui/settings/chrome_cleanup_handler.h
@@ -36,6 +36,7 @@
   void OnInfected(const std::set<base::FilePath>& files) override;
   void OnCleaning(const std::set<base::FilePath>& files) override;
   void OnRebootRequired() override;
+  void OnLogsEnabledChanged(bool logs_enabled) override;
 
  private:
   // Callback for the "dismissCleanupPage" message to hide the Cleanup page
@@ -51,6 +52,10 @@
   // system restart.
   void HandleRestartComputer(const base::ListValue* args);
 
+  // Callback for the "setLogsUploadPermission" message to keep track of
+  // whether the user opted-out of logs upload or not.
+  void HandleSetLogsUploadPermission(const base::ListValue* args);
+
   // Callback for the "startCleanup" message to start removing unwanted
   // software from the user's computer.
   void HandleStartCleanup(const base::ListValue* args);
diff --git a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
index dfb283f30..5c9dce3 100644
--- a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
@@ -788,6 +788,7 @@
       {"chromeCleanupDoneButtonLabel",
        IDS_CHROME_CLEANUP_WEBUI_DONE_BUTTON_LABEL},
       {"chromeCleanupLinkShowFiles", IDS_CHROME_CLEANUP_WEBUI_LINK_SHOW_FILES},
+      {"chromeCleanupLogsUploadPermission", IDS_CHROME_CLEANUP_LOGS_PERMISSION},
       {"chromeCleanupRemoveButtonLabel",
        IDS_CHROME_CLEANUP_WEBUI_REMOVE_BUTTON_LABEL},
       {"chromeCleanupRestartButtonLabel",
@@ -801,6 +802,12 @@
   };
   AddLocalizedStringsBulk(html_source, localized_strings,
                           arraysize(localized_strings));
+  const std::string cleanup_learn_more_url =
+      google_util::AppendGoogleLocaleParam(
+          GURL(chrome::kChromeCleanerLearnMoreURL),
+          g_browser_process->GetApplicationLocale())
+          .spec();
+  html_source->AddString("chromeCleanupLearnMoreUrl", cleanup_learn_more_url);
 }
 #endif  // defined(OS_WIN)
 
diff --git a/chrome/browser/ui/webui/signin/signin_error_handler.cc b/chrome/browser/ui/webui/signin/signin_error_handler.cc
index 88e1f53..6824d82 100644
--- a/chrome/browser/ui/webui/signin/signin_error_handler.cc
+++ b/chrome/browser/ui/webui/signin/signin_error_handler.cc
@@ -20,13 +20,11 @@
   // |browser_| must not be null when this dialog is presented from the
   // user manager.
   DCHECK(browser_ || is_system_profile_);
-  if (!is_system_profile_)
-    BrowserList::AddObserver(this);
+  BrowserList::AddObserver(this);
 }
 
 SigninErrorHandler::~SigninErrorHandler() {
-  if (!is_system_profile_)
-    BrowserList::RemoveObserver(this);
+  BrowserList::RemoveObserver(this);
 }
 
 void SigninErrorHandler::OnBrowserRemoved(Browser* browser) {
@@ -103,11 +101,16 @@
 
 void SigninErrorHandler::CloseDialog() {
   if (is_system_profile_) {
-    // Avoid closing the user manager window when the error message is displayed
-    // without browser window.
-    UserManagerProfileDialog::HideDialog();
-  } else {
-    if (browser_)
-      browser_->signin_view_controller()->CloseModalSignin();
+    CloseUserManagerProfileDialog();
+  } else if (browser_){
+    CloseBrowserModalSigninDialog();
   }
 }
+
+void SigninErrorHandler::CloseBrowserModalSigninDialog() {
+  browser_->signin_view_controller()->CloseModalSignin();
+}
+
+void SigninErrorHandler::CloseUserManagerProfileDialog() {
+  UserManagerProfileDialog::HideDialog();
+}
diff --git a/chrome/browser/ui/webui/signin/signin_error_handler.h b/chrome/browser/ui/webui/signin/signin_error_handler.h
index 84e101f98..d6e21d7 100644
--- a/chrome/browser/ui/webui/signin/signin_error_handler.h
+++ b/chrome/browser/ui/webui/signin/signin_error_handler.h
@@ -66,6 +66,16 @@
   // its members after this call.
   void CloseDialog();
 
+  // Closes the modal sign-in view dialog.
+  //
+  // Virtual, so that it can be overriden from unit tests.
+  virtual void CloseBrowserModalSigninDialog();
+
+  // Closes the user manager profile dialog.
+  //
+  // Virtual, so that it can be overriden from unit tests.
+  virtual void CloseUserManagerProfileDialog();
+
  private:
   // Weak reference to the browser that showed the sign-in error dialog.
   // This is null when this sign-in error dialog is presented from the user
diff --git a/chrome/browser/ui/webui/signin/signin_error_handler_unittest.cc b/chrome/browser/ui/webui/signin/signin_error_handler_unittest.cc
new file mode 100644
index 0000000..a6687c9
--- /dev/null
+++ b/chrome/browser/ui/webui/signin/signin_error_handler_unittest.cc
@@ -0,0 +1,179 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/signin/signin_error_handler.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/values.h"
+#include "chrome/browser/ui/browser_commands.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/webui/signin/signin_error_ui.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/test/base/browser_with_test_window_test.h"
+#include "chrome/test/base/dialog_test_browser_window.h"
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/test/test_web_ui.h"
+
+namespace {
+const char kSigninErrorLearnMoreUrl[] =
+    "https://support.google.com/chrome/answer/1181420?";
+
+class TestingSigninErrorHandler : public SigninErrorHandler {
+ public:
+  TestingSigninErrorHandler(Browser* browser,
+                            bool is_system_profile,
+                            content::WebUI* web_ui)
+      : SigninErrorHandler(browser, is_system_profile),
+        browser_modal_dialog_did_close_(false),
+        user_manager_profile_dialog_did_close_(false) {
+    set_web_ui(web_ui);
+  }
+
+  void CloseBrowserModalSigninDialog() override {
+    browser_modal_dialog_did_close_ = true;
+    SigninErrorHandler::CloseBrowserModalSigninDialog();
+  }
+
+  void CloseUserManagerProfileDialog() override {
+    user_manager_profile_dialog_did_close_ = true;
+    SigninErrorHandler::CloseUserManagerProfileDialog();
+  }
+
+  using SigninErrorHandler::HandleSwitchToExistingProfile;
+  using SigninErrorHandler::HandleConfirm;
+  using SigninErrorHandler::HandleLearnMore;
+  using SigninErrorHandler::HandleInitializedWithSize;
+
+  bool browser_modal_dialog_did_close() {
+    return browser_modal_dialog_did_close_;
+  }
+
+  bool user_manager_profile_dialog_did_close() {
+    return user_manager_profile_dialog_did_close_;
+  }
+
+ private:
+  bool browser_modal_dialog_did_close_;
+  bool user_manager_profile_dialog_did_close_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestingSigninErrorHandler);
+};
+
+class SigninErrorHandlerTest : public BrowserWithTestWindowTest {
+ public:
+  SigninErrorHandlerTest()
+      : web_ui_(new content::TestWebUI), handler_(nullptr) {}
+
+  void SetUp() override {
+    BrowserWithTestWindowTest::SetUp();
+    chrome::NewTab(browser());
+    web_ui()->set_web_contents(
+        browser()->tab_strip_model()->GetActiveWebContents());
+    signin_error_ui_.reset(new SigninErrorUI(web_ui()));
+  }
+
+  void TearDown() override {
+    signin_error_ui_.reset();
+    web_ui_.reset();
+    BrowserWithTestWindowTest::TearDown();
+  }
+
+  void CreateHandlerInBrowser() {
+    DCHECK(!handler_);
+    auto handler = base::MakeUnique<TestingSigninErrorHandler>(
+        browser(), false /* is_system_profile */, web_ui());
+    handler_ = handler.get();
+    signin_error_ui_.reset(new SigninErrorUI(web_ui()));
+    web_ui()->AddMessageHandler(std::move(handler));
+  }
+
+  void CreateHandlerInUserManager() {
+    DCHECK(!handler_);
+    auto handler = base::MakeUnique<TestingSigninErrorHandler>(
+        nullptr /* browser */, true /* is_system_profile */, web_ui());
+    handler_ = handler.get();
+    web_ui()->AddMessageHandler(std::move(handler));
+  }
+
+  TestingSigninErrorHandler* handler() { return handler_; }
+
+  content::TestWebUI* web_ui() { return web_ui_.get(); }
+
+  // BrowserWithTestWindowTest
+  BrowserWindow* CreateBrowserWindow() override {
+    return new DialogTestBrowserWindow;
+  }
+
+ private:
+  std::unique_ptr<content::TestWebUI> web_ui_;
+  std::unique_ptr<SigninErrorUI> signin_error_ui_;
+  TestingSigninErrorHandler* handler_;  // Not owned.
+
+  DISALLOW_COPY_AND_ASSIGN(SigninErrorHandlerTest);
+};
+
+TEST_F(SigninErrorHandlerTest, InBrowserHandleLearnMore) {
+  // Before the test, there is only one new tab opened.
+  TabStripModel* tab_strip_model = browser()->tab_strip_model();
+  EXPECT_EQ(1, tab_strip_model->count());
+  EXPECT_EQ(GURL(chrome::kChromeUINewTabURL),
+            tab_strip_model->GetActiveWebContents()->GetURL());
+
+  // Open learn more
+  CreateHandlerInBrowser();
+  base::ListValue args;
+  handler()->HandleLearnMore(&args);
+
+  // Dialog should be closed now.
+  EXPECT_TRUE(handler()->browser_modal_dialog_did_close());
+
+  // Verify that the learn more URL was opened.
+  EXPECT_EQ(2, tab_strip_model->count());
+  EXPECT_EQ(GURL(kSigninErrorLearnMoreUrl),
+            tab_strip_model->GetActiveWebContents()->GetURL());
+}
+
+TEST_F(SigninErrorHandlerTest, InBrowserHandleLearnMoreAfterBrowserRemoved) {
+  // Before the test, there is only one new tab opened.
+  TabStripModel* tab_strip_model = browser()->tab_strip_model();
+  EXPECT_EQ(1, tab_strip_model->count());
+  EXPECT_EQ(GURL(chrome::kChromeUINewTabURL),
+            tab_strip_model->GetActiveWebContents()->GetURL());
+
+  // Inform the handler that the browser was removed;
+  CreateHandlerInBrowser();
+  handler()->OnBrowserRemoved(browser());
+
+  // Open learn more
+  base::ListValue args;
+  handler()->HandleLearnMore(&args);
+
+  // Dialog is not closed if the browser was removed.
+  EXPECT_FALSE(handler()->browser_modal_dialog_did_close());
+
+  // Verify that the learn more URL was not opened as the browser was removed.
+  EXPECT_EQ(1, tab_strip_model->count());
+  EXPECT_EQ(GURL(chrome::kChromeUINewTabURL),
+            tab_strip_model->GetActiveWebContents()->GetURL());
+}
+
+TEST_F(SigninErrorHandlerTest, InBrowserTestConfirm) {
+  CreateHandlerInBrowser();
+  base::ListValue args;
+  handler()->HandleConfirm(&args);
+
+  // Confirm simply closes the dialog.
+  EXPECT_TRUE(handler()->browser_modal_dialog_did_close());
+}
+
+TEST_F(SigninErrorHandlerTest, InUserManagerTestConfirm) {
+  CreateHandlerInUserManager();
+  base::ListValue args;
+  handler()->HandleConfirm(&args);
+
+  // Confirm simply closes the dialog.
+  EXPECT_TRUE(handler()->user_manager_profile_dialog_did_close());
+}
+
+}  // namespace
diff --git a/chrome/browser/win/enumerate_modules_model.cc b/chrome/browser/win/enumerate_modules_model.cc
index 84e8df2b..4b819277 100644
--- a/chrome/browser/win/enumerate_modules_model.cc
+++ b/chrome/browser/win/enumerate_modules_model.cc
@@ -655,14 +655,6 @@
   return list;
 }
 
-GURL EnumerateModulesModel::GetConflictUrl() {
-  // For now, simply bring up the chrome://conflicts page, which has detailed
-  // information about each module.
-  if (ShouldShowConflictWarning())
-    return GURL(L"chrome://conflicts");
-  return GURL();
-}
-
 EnumerateModulesModel::EnumerateModulesModel()
     : conflict_notification_acknowledged_(false),
       confirmed_bad_modules_detected_(0),
diff --git a/chrome/browser/win/enumerate_modules_model.h b/chrome/browser/win/enumerate_modules_model.h
index 12a5041..cd2b1e0a 100644
--- a/chrome/browser/win/enumerate_modules_model.h
+++ b/chrome/browser/win/enumerate_modules_model.h
@@ -302,12 +302,6 @@
   // Gets the whole module list as a ListValue.
   std::unique_ptr<base::ListValue> GetModuleList();
 
-  // Returns the site to which the user should be taken when the conflict bubble
-  // or app menu item is clicked. For now this is simply chrome://conflicts,
-  // which contains detailed information about conflicts. Returns an empty URL
-  // if there are no conficts. May only be called on UI thread.
-  GURL GetConflictUrl();
-
  private:
   friend class ModuleEnumerator;
 
diff --git a/chrome/common/media_router/mojo/BUILD.gn b/chrome/common/media_router/mojo/BUILD.gn
index 3a24977..4fda4230 100644
--- a/chrome/common/media_router/mojo/BUILD.gn
+++ b/chrome/common/media_router/mojo/BUILD.gn
@@ -35,6 +35,7 @@
 
   public_deps = [
     ":media_controller",
+    "//media/mojo/interfaces:mirror_service_remoting",
     "//mojo/common:common_custom_types",
     "//net/interfaces:interfaces",
     "//url/mojo:url_mojom_gurl",
diff --git a/chrome/common/media_router/mojo/media_router.mojom b/chrome/common/media_router/mojo/media_router.mojom
index ce99ec3..5f53bc1 100644
--- a/chrome/common/media_router/mojo/media_router.mojom
+++ b/chrome/common/media_router/mojo/media_router.mojom
@@ -6,6 +6,7 @@
 
 import "chrome/common/media_router/mojo/media_controller.mojom";
 import "chrome/common/media_router/mojo/media_status.mojom";
+import "media/mojo/interfaces/mirror_service_remoting.mojom";
 import "mojo/common/time.mojom";
 import "net/interfaces/ip_address.mojom";
 import "url/mojo/origin.mojom";
@@ -474,4 +475,11 @@
   // |messages|: A non-empty list of messages received.
   OnRouteMessagesReceived(string route_id,
                           array<RouteMessage> messages);
+
+  // Called when a MediaRemoter for a tab with |tab_id| is started. |remoter|
+  // can be used to access the MediaRemoter to control a media remoting session
+  // and send RPC messages to the remote device. |remoting_source| is bound to
+  // receive the updates/messages from MediaRemoter.
+  OnMediaRemoterCreated(int32 tab_id, media.mojom.MirrorServiceRemoter remoter,
+      media.mojom.MirrorServiceRemotingSource& remoting_source);
 };
diff --git a/chrome/common/safe_browsing/zip_analyzer.cc b/chrome/common/safe_browsing/zip_analyzer.cc
index 5577157..50f3f25 100644
--- a/chrome/common/safe_browsing/zip_analyzer.cc
+++ b/chrome/common/safe_browsing/zip_analyzer.cc
@@ -65,7 +65,7 @@
     const base::FilePath& file_path,
     zip::ZipReader* reader,
     base::File* temp_file,
-    ClientDownloadRequest_ArchivedBinary* archived_binary) {
+    ClientDownloadRequest::ArchivedBinary* archived_binary) {
   std::string file_basename(file_path.BaseName().AsUTF8Unsafe());
   if (base::StreamingUtf8Validator::Validate(file_basename))
     archived_binary->set_file_basename(file_basename);
@@ -121,6 +121,13 @@
       DVLOG(2) << "Downloaded a zipped archive: " << file.value();
       results->has_archive = true;
       archived_archive_filenames.insert(file.BaseName());
+      ClientDownloadRequest::ArchivedBinary* archived_archive =
+          results->archived_binary.Add();
+      std::string file_basename_utf8(file.BaseName().AsUTF8Unsafe());
+      if (base::StreamingUtf8Validator::Validate(file_basename_utf8))
+        archived_archive->set_file_basename(file_basename_utf8);
+      archived_archive->set_download_type(
+          ClientDownloadRequest::ZIPPED_ARCHIVE);
     } else if (FileTypePolicies::GetInstance()->IsCheckedBinaryFile(file)) {
       DVLOG(2) << "Downloaded a zipped executable: " << file.value();
       results->has_executable = true;
diff --git a/chrome/common/url_constants.cc b/chrome/common/url_constants.cc
index 1a4b7422..5303894 100644
--- a/chrome/common/url_constants.cc
+++ b/chrome/common/url_constants.cc
@@ -633,6 +633,10 @@
 #if defined(OS_WIN)
 const char kNotificationsHelpURL[] =
     "https://support.google.com/chrome/?p=ui_notifications";
+
+// TODO(proberge): Change this URL to a p-link.
+const char kChromeCleanerLearnMoreURL[] =
+    "https://support.google.com/chrome/answer/6086368";
 #endif
 
 const char kNotificationWelcomeLearnMoreURL[] =
diff --git a/chrome/common/url_constants.h b/chrome/common/url_constants.h
index 8df1386c..38e1188 100644
--- a/chrome/common/url_constants.h
+++ b/chrome/common/url_constants.h
@@ -526,6 +526,9 @@
 
 #if defined(OS_WIN)
 extern const char kNotificationsHelpURL[];
+
+// The URL for the Learn More link in the Chrome Cleanup settings card.
+extern const char kChromeCleanerLearnMoreURL[];
 #endif
 
 // The Welcome Notification More Info URL.
diff --git a/chrome/renderer/extensions/chrome_extensions_dispatcher_delegate.cc b/chrome/renderer/extensions/chrome_extensions_dispatcher_delegate.cc
index 881f5252..288de6377 100644
--- a/chrome/renderer/extensions/chrome_extensions_dispatcher_delegate.cc
+++ b/chrome/renderer/extensions/chrome_extensions_dispatcher_delegate.cc
@@ -275,6 +275,11 @@
                              IDR_MOJO_IP_ADDRESS_MOJOM_JS);
   source_map->RegisterSource("url/mojo/origin.mojom", IDR_ORIGIN_MOJOM_JS);
   source_map->RegisterSource("url/mojo/url.mojom", IDR_MOJO_URL_MOJOM_JS);
+  source_map->RegisterSource("media/mojo/interfaces/remoting_common.mojom",
+                             IDR_REMOTING_COMMON_JS);
+  source_map->RegisterSource(
+      "media/mojo/interfaces/mirror_service_remoting.mojom",
+      IDR_MEDIA_REMOTING_JS);
 
   // These bindings are unnecessary with native bindings enabled.
   if (!extensions::FeatureSwitch::native_crx_bindings()->IsEnabled()) {
diff --git a/chrome/renderer/resources/extensions/media_router_bindings.js b/chrome/renderer/resources/extensions/media_router_bindings.js
index f001ed3f..263b5f70 100644
--- a/chrome/renderer/resources/extensions/media_router_bindings.js
+++ b/chrome/renderer/resources/extensions/media_router_bindings.js
@@ -10,6 +10,8 @@
     'chrome/common/media_router/mojo/media_status.mojom',
     'content/public/renderer/frame_interfaces',
     'extensions/common/mojo/keep_alive.mojom',
+    'media/mojo/interfaces/mirror_service_remoting.mojom',
+    'media/mojo/interfaces/remoting_common.mojom',
     'mojo/common/time.mojom',
     'mojo/public/js/bindings',
     'net/interfaces/ip_address.mojom',
@@ -20,6 +22,8 @@
             mediaStatusMojom,
             frameInterfaces,
             keepAliveMojom,
+            remotingMojom,
+            remotingCommonMojom,
             timeMojom,
             bindings,
             ipAddressMojom,
@@ -279,11 +283,24 @@
       MediaController: mediaControllerMojom.MediaController,
       MediaStatus: mediaStatusMojom.MediaStatus,
       MediaStatusObserverPtr: mediaStatusMojom.MediaStatusObserverPtr,
+      MirrorServiceRemoter: remotingMojom.MirrorServiceRemoter,
+      MirrorServiceRemoterPtr: remotingMojom.MirrorServiceRemoterPtr,
+      MirrorServiceRemotingSourcePtr:
+          remotingMojom.MirrorServiceRemotingSourcePtr,
+      RemotingStopReason: remotingCommonMojom.RemotingStopReason,
+      RemotingStartFailReason: remotingCommonMojom.RemotingStartFailReason,
+      RemotingSinkFeatures: remotingCommonMojom.RemotingSinkFeatures,
+      RemotingSinkAudioCapabilities:
+          remotingCommonMojom.RemotingSinkAudioCapabilities,
+      RemotingSinkVideoCapabilities:
+          remotingCommonMojom.RemotingSinkVideoCapabilities,
+      SinkCapabilities: remotingCommonMojom.SinkCapabilities,
       Origin: originMojom.Origin,
       Sink: mediaRouterMojom.MediaSink,
       SinkExtraData: mediaRouterMojom.MediaSinkExtraData,
       TimeDelta: timeMojom.TimeDelta,
       Url: urlMojom.Url,
+      makeRequest: bindings.makeRequest,
     };
   };
 
@@ -465,6 +482,16 @@
   };
 
   /**
+   * @param {number} tabId
+   * @param {!remotingMojom.MirrorServiceRemoterPtr} remoter
+   * @param {!remotingMojom.MirrorServiceRemotingSourcePtr} remotingSource
+   */
+  MediaRouter.prototype.onMediaRemoterCreated = function(tabId, remoter,
+      remotingSource) {
+    this.service_.onMediaRemoterCreated(tabId, remoter, remotingSource);
+  }
+
+  /**
    * Object containing callbacks set by the provider manager.
    *
    * @constructor
diff --git a/chrome/renderer/resources/renderer_resources.grd b/chrome/renderer/resources/renderer_resources.grd
index fef051c..67382cb7 100644
--- a/chrome/renderer/resources/renderer_resources.grd
+++ b/chrome/renderer/resources/renderer_resources.grd
@@ -93,6 +93,8 @@
         <include name="IDR_MOJO_IP_ADDRESS_MOJOM_JS" file="${mojom_root}\net\interfaces\ip_address.mojom.js" use_base_dir="false" type="BINDATA" />
         <include name="IDR_ORIGIN_MOJOM_JS" file="${mojom_root}\url\mojo\origin.mojom.js" use_base_dir="false" type="BINDATA" />
         <include name="IDR_MOJO_URL_MOJOM_JS" file="${mojom_root}\url\mojo\url.mojom.js" use_base_dir="false" type="BINDATA" />
+        <include name="IDR_MEDIA_REMOTING_JS" file="${mojom_root}\media\mojo\interfaces\mirror_service_remoting.mojom.js" use_base_dir="false" type="BINDATA" />
+        <include name="IDR_REMOTING_COMMON_JS" file="${mojom_root}\media\mojo\interfaces\remoting_common.mojom.js" use_base_dir="false" type="BINDATA" />
       </if>
     </includes>
   </release>
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index dec3005..22226b8 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -2072,6 +2072,7 @@
         "../browser/ui/views/payments/credit_card_editor_view_controller_browsertest.cc",
         "../browser/ui/views/payments/cvc_unmask_view_controller_browsertest.cc",
         "../browser/ui/views/payments/error_message_view_controller_browsertest.cc",
+        "../browser/ui/views/payments/modifiers_browsertest.cc",
         "../browser/ui/views/payments/order_summary_view_controller_browsertest.cc",
         "../browser/ui/views/payments/payment_method_view_controller_browsertest.cc",
         "../browser/ui/views/payments/payment_request_blob_url_browsertest.cc",
@@ -3189,6 +3190,7 @@
     "../browser/page_load_metrics/observers/from_gws_page_load_metrics_observer_unittest.cc",
     "../browser/page_load_metrics/observers/google_captcha_observer_unittest.cc",
     "../browser/page_load_metrics/observers/loading_predictor_page_load_metrics_observer_unittest.cc",
+    "../browser/page_load_metrics/observers/local_network_requests_page_load_metrics_observer_unittest.cc",
     "../browser/page_load_metrics/observers/lofi_page_load_metrics_observer_unittest.cc",
     "../browser/page_load_metrics/observers/media_page_load_metrics_observer_unittest.cc",
     "../browser/page_load_metrics/observers/multi_tab_loading_page_load_metrics_observer_unittest.cc",
@@ -4797,6 +4799,7 @@
       "../browser/signin/signin_global_error_unittest.cc",
       "../browser/signin/signin_util_unittest.cc",
       "../browser/ui/webui/signin/signin_create_profile_handler_unittest.cc",
+      "../browser/ui/webui/signin/signin_error_handler_unittest.cc",
       "../browser/ui/webui/signin/sync_confirmation_handler_unittest.cc",
       "../browser/upgrade_detector_impl_unittest.cc",
     ]
diff --git a/chrome/test/data/page_load_metrics/large_iframe.html b/chrome/test/data/page_load_metrics/large_iframe.html
new file mode 100644
index 0000000..c6efc1a7
--- /dev/null
+++ b/chrome/test/data/page_load_metrics/large_iframe.html
@@ -0,0 +1,5 @@
+<html>
+  <body>
+    <iframe src="large.html"></iframe>
+  </body>
+</html>
diff --git a/chrome/test/data/payments/app.json b/chrome/test/data/payments/app.json
new file mode 100644
index 0000000..bcb1ae68
--- /dev/null
+++ b/chrome/test/data/payments/app.json
@@ -0,0 +1,12 @@
+{
+  "name": "BobPay",
+  "related_applications": [{
+    "platform": "play",
+    "id": "com.bobpay",
+    "min_version": "1",
+    "fingerprints": [{
+      "type": "sha256_cert",
+      "value": "59:5C:88:65:FF:C4:E8:20:CF:F7:3E:C8:64:D0:95:F0:06:19:2E:A6:7B:20:04:D1:03:07:92:E2:A5:31:67:66"
+    }]
+  }]
+}
diff --git a/chrome/test/data/payments/bobpay_and_basic_card_with_basic_card_modifiers.js b/chrome/test/data/payments/bobpay_and_basic_card_with_basic_card_modifiers.js
new file mode 100644
index 0000000..de2ee33
--- /dev/null
+++ b/chrome/test/data/payments/bobpay_and_basic_card_with_basic_card_modifiers.js
@@ -0,0 +1,237 @@
+/*
+ * Copyright 2017 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* global PaymentRequest:false */
+
+/**
+ * Launches the PaymentRequest UI with Bob Pay and basic-card as payment
+ * methods and a modifier for basic-card
+ */
+function buy() {  // eslint-disable-line no-unused-vars
+  try {
+    new PaymentRequest(
+        [{supportedMethods: ['https://bobpay.com', 'basic-card']}],
+        {
+          total: {label: 'Total', amount: {currency: 'USD', value: '5.00'}},
+          modifiers: [{
+            supportedMethods: ['basic-card'],
+            total: {
+              label: 'Total',
+              amount: {currency: 'USD', value: '4.00'},
+            },
+            additionalDisplayItems: [{
+              label: 'basic-card discount',
+              amount: {currency: 'USD', value: '-1.00'},
+            }],
+            data: {discountProgramParticipantId: '86328764873265'},
+          }],
+        })
+        .show()
+        .then(function(resp) {
+          resp.complete('success')
+              .then(function() {
+                print(
+                    resp.methodName + '<br>' +
+                    JSON.stringify(resp.details, undefined, 2));
+              })
+              .catch(function(error) {
+                print(error.message);
+              });
+        })
+        .catch(function(error) {
+          print(error.message);
+        });
+  } catch (error) {
+    print(error.message);
+  }
+}
+
+/**
+ * Launches the PaymentRequest UI with Bob Pay and basic-card as payment
+ * methods and a modifier for basic-card with "credit" type
+ */
+function creditSupportedType() {  // eslint-disable-line no-unused-vars
+  try {
+    new PaymentRequest(
+        [{supportedMethods: ['https://bobpay.com', 'basic-card']}],
+        {
+          total: {label: 'Total', amount: {currency: 'USD', value: '5.00'}},
+          modifiers: [{
+            supportedMethods: ['basic-card'],
+            total: {
+              label: 'Total',
+              amount: {currency: 'USD', value: '4.00'},
+            },
+            additionalDisplayItems: [{
+              label: 'basic-card discount',
+              amount: {currency: 'USD', value: '-1.00'},
+            }],
+            data: {
+              discountProgramParticipantId: '86328764873265',
+              supportedTypes: ['credit'],
+            },
+          }],
+        })
+        .show()
+        .then(function(resp) {
+          resp.complete('success')
+              .then(function() {
+                print(
+                    resp.methodName + '<br>' +
+                    JSON.stringify(resp.details, undefined, 2));
+              })
+              .catch(function(error) {
+                print(error.message);
+              });
+        })
+        .catch(function(error) {
+          print(error.message);
+        });
+  } catch (error) {
+    print(error.message);
+  }
+}
+
+/**
+ * Launches the PaymentRequest UI with Bob Pay and basic-card as payment
+ * methods and a modifier for basic-card with "debit" type
+ */
+function debitSupportedType() {  // eslint-disable-line no-unused-vars
+  try {
+    new PaymentRequest(
+        [{supportedMethods: ['https://bobpay.com', 'basic-card']}],
+        {
+          total: {label: 'Total', amount: {currency: 'USD', value: '5.00'}},
+          modifiers: [{
+            supportedMethods: ['basic-card'],
+            total: {
+              label: 'Total',
+              amount: {currency: 'USD', value: '4.00'},
+            },
+            additionalDisplayItems: [{
+              label: 'basic-card discount',
+              amount: {currency: 'USD', value: '-1.00'},
+            }],
+            data: {
+              discountProgramParticipantId: '86328764873265',
+              supportedTypes: ['debit'],
+            },
+          }],
+        })
+        .show()
+        .then(function(resp) {
+          resp.complete('success')
+              .then(function() {
+                print(
+                    resp.methodName + '<br>' +
+                    JSON.stringify(resp.details, undefined, 2));
+              })
+              .catch(function(error) {
+                print(error.message);
+              });
+        })
+        .catch(function(error) {
+          print(error.message);
+        });
+  } catch (error) {
+    print(error.message);
+  }
+}
+
+/**
+ * Launches the PaymentRequest UI with Bob Pay and basic-card as payment
+ * methods and a modifier for basic-card with "credit" type and "visa" network
+ */
+function visaSupportedNetwork() {  // eslint-disable-line no-unused-vars
+  try {
+    new PaymentRequest(
+        [{supportedMethods: ['https://bobpay.com', 'basic-card']}],
+        {
+          total: {label: 'Total', amount: {currency: 'USD', value: '5.00'}},
+          modifiers: [{
+            supportedMethods: ['basic-card'],
+            total: {
+              label: 'Total',
+              amount: {currency: 'USD', value: '4.00'},
+            },
+            additionalDisplayItems: [{
+              label: 'basic-card discount',
+              amount: {currency: 'USD', value: '-1.00'},
+            }],
+            data: {
+              discountProgramParticipantId: '86328764873265',
+              supportedTypes: ['credit'],
+              supportedNetworks: ['visa'],
+            },
+          }],
+        })
+        .show()
+        .then(function(resp) {
+          resp.complete('success')
+              .then(function() {
+                print(
+                    resp.methodName + '<br>' +
+                    JSON.stringify(resp.details, undefined, 2));
+              })
+              .catch(function(error) {
+                print(error.message);
+              });
+        })
+        .catch(function(error) {
+          print(error.message);
+        });
+  } catch (error) {
+    print(error.message);
+  }
+}
+
+/**
+ * Launches the PaymentRequest UI with Bob Pay and basic-card as payment
+ * methods and a modifier for basic-card with "credit" type and " mastercard"
+ * network
+ */
+function mastercardSupportedNetwork() {  // eslint-disable-line no-unused-vars
+  try {
+    new PaymentRequest(
+        [{supportedMethods: ['https://bobpay.com', 'basic-card']}],
+        {
+          total: {label: 'Total', amount: {currency: 'USD', value: '5.00'}},
+          modifiers: [{
+            supportedMethods: ['basic-card'],
+            total: {
+              label: 'Total',
+              amount: {currency: 'USD', value: '4.00'},
+            },
+            additionalDisplayItems: [{
+              label: 'basic-card discount',
+              amount: {currency: 'USD', value: '-1.00'},
+            }],
+            data: {
+              discountProgramParticipantId: '86328764873265',
+              supportedTypes: ['credit'],
+              supportedNetworks: ['mastercard'],
+            },
+          }],
+        })
+        .show()
+        .then(function(resp) {
+          resp.complete('success')
+              .then(function() {
+                print(
+                    resp.methodName + '<br>' +
+                    JSON.stringify(resp.details, undefined, 2));
+              })
+              .catch(function(error) {
+                print(error.message);
+              });
+        })
+        .catch(function(error) {
+          print(error.message);
+        });
+  } catch (error) {
+    print(error.message);
+  }
+}
diff --git a/chrome/test/data/payments/metrics.js b/chrome/test/data/payments/metrics.js
index 0edaa6a..753e387 100644
--- a/chrome/test/data/payments/metrics.js
+++ b/chrome/test/data/payments/metrics.js
@@ -188,6 +188,47 @@
 }
 
 /**
+ * Launches the PaymentRequest UI which accepts credit cards and Bob Pay.
+ */
+function cardsAndBobPayBuy() { // eslint-disable-line no-unused-vars
+  try {
+    request = new PaymentRequest(
+      [{
+        supportedMethods: ['visa', 'https://bobpay.com'],
+      }], {
+        total: {
+          label: 'Total',
+          amount: {
+            currency: 'USD',
+            value: '5.00',
+          },
+        },
+        shippingOptions: [{
+          id: 'freeShippingOption',
+          label: 'Free global shipping',
+          amount: {
+            currency: 'USD',
+            value: '0',
+          },
+          selected: true,
+        }],
+      }, {
+        requestShipping: true,
+      });
+    request.show()
+      .then(function(resp) {
+        return resp.complete('success');
+      }).then(function() {
+        print(JSON.stringify(resp, undefined, 2));
+      }).catch(function(error) {
+        print(error);
+      });
+  } catch (error) {
+    print(error.message);
+  }
+}
+
+/**
  * Aborts the current PaymentRequest.
  */
 function abort() { // eslint-disable-line no-unused-vars
diff --git a/chrome/test/data/payments/payment-manifest.json b/chrome/test/data/payments/payment-manifest.json
new file mode 100644
index 0000000..cf1ba1ec
--- /dev/null
+++ b/chrome/test/data/payments/payment-manifest.json
@@ -0,0 +1,4 @@
+{
+  "default_applications": ["app.json"],
+  "supported_origins": ["https://alicepay.com"]
+}
diff --git a/chrome/test/data/payments/payment_request_bobpay_and_basic_card_with_basic_card_modifiers_test.html b/chrome/test/data/payments/payment_request_bobpay_and_basic_card_with_basic_card_modifiers_test.html
new file mode 100644
index 0000000..624910d
--- /dev/null
+++ b/chrome/test/data/payments/payment_request_bobpay_and_basic_card_with_basic_card_modifiers_test.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<!--
+Copyright 2017 The Chromium Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+<html>
+<head>
+<title>Bob Pay and basic-card with Basic-Card modifiers Test</title>
+<meta charset="utf-8">
+<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
+<link rel="stylesheet" type="text/css" href="style.css">
+</head>
+<body>
+<button onclick="buy()" id="buy">Bob Pay and Basic-Card with Basic-Card modifiers Test</button>
+<button onclick="creditSupportedType()" id="credit_supported_type">Bob Pay and Basic-Card with Basic-Card modifiers Test with credit supported type</button>
+<button onclick="debitSupportedType()" id="debit_supported_type">Bob Pay and Basic-Card with Basic-Card modifiers Test with debit supported type</button>
+<button onclick="visaSupportedNetwork()" id="visa_supported_network">Bob Pay and Basic-Card with Basic-Card modifiers Test with visa supported network</button>
+<button onclick="mastercardSupportedNetwork()" id="mastercard_supported_network">Bob Pay and Basic-Card with Basic-Card modifiers Test with mastercard supported network</button>
+<pre id="result"></pre>
+<script src="util.js"></script>
+<script src="bobpay_and_basic_card_with_basic_card_modifiers.js"></script>
+</body>
+</html>
diff --git a/chrome/test/data/payments/payment_request_metrics_test.html b/chrome/test/data/payments/payment_request_metrics_test.html
index d567b4a..b8a8b14f 100644
--- a/chrome/test/data/payments/payment_request_metrics_test.html
+++ b/chrome/test/data/payments/payment_request_metrics_test.html
@@ -11,10 +11,11 @@
 <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
 </head>
 <body>
-<button onclick="ccBuy()" id="ccBuy">CC Buy Test</button><br>
-<button onclick="androidPayBuy()" id="androidPayBuy">Android Pay Buy Test</button><br>
-<button onclick="androidPaySkipUiBuy()" id="androidPaySkipUiBuy">Android Pay Skip UI Buy Test</button><br>
-<button onclick="noSupported()" id="noSupported">No Supported Method Test</button><br>
+<div> <button onclick="ccBuy()" id="ccBuy">CC Buy Test</button> </div>
+<div> <button onclick="androidPayBuy()" id="androidPayBuy">Android Pay Buy Test</button> </div>
+<div> <button onclick="androidPaySkipUiBuy()" id="androidPaySkipUiBuy">Android Pay Skip UI Buy Test</button> </div>
+<div> <button onclick="noSupported()" id="noSupported">No Supported Method Test</button> </div>
+<div> <button onclick="cardsAndBobPayBuy()" id="cardsAndBobPayBuy">Android Pay Buy Test</button> </div>
 <button onclick="abort()" id="abort">Abort</button>
 <pre id="result"></pre>
 <script src="util.js"></script>
diff --git a/chrome/test/data/payments/webpay b/chrome/test/data/payments/webpay
new file mode 100644
index 0000000..e0dfe7d
--- /dev/null
+++ b/chrome/test/data/payments/webpay
@@ -0,0 +1 @@
+<!-- Intentionally blank. -->
diff --git a/chrome/test/data/payments/webpay.mock-http-headers b/chrome/test/data/payments/webpay.mock-http-headers
new file mode 100644
index 0000000..44e5a707
--- /dev/null
+++ b/chrome/test/data/payments/webpay.mock-http-headers
@@ -0,0 +1,2 @@
+HTTP/1.1 200 OK
+Link: <payment-manifest.json>; rel="payment-method-manifest"
diff --git a/chrome/test/data/webui/print_preview/plugin_stub.js b/chrome/test/data/webui/print_preview/plugin_stub.js
new file mode 100644
index 0000000..1c141b9
--- /dev/null
+++ b/chrome/test/data/webui/print_preview/plugin_stub.js
@@ -0,0 +1,55 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+cr.define('print_preview', function() {
+  /**
+   * Test version of the print preview PDF plugin
+   */
+  class PDFPluginStub {
+    /**
+     * @param {!print_preview.PreviewArea} The PreviewArea that owns this
+     *     plugin.
+     */
+    constructor(area) {
+      /**
+       * @private {?Function} The callback to run when the plugin has loaded.
+       */
+      this.loadCallback_ = null;
+
+      /** @private {!EventTracker} The plugin stub's event tracker. */
+      this.tracker_ = new EventTracker();
+
+      // Call documentLoadComplete as soon as the preview area starts the
+      // preview.
+      this.tracker_.add(
+          area,
+          print_preview.PreviewArea.EventType.PREVIEW_GENERATION_IN_PROGRESS,
+          this.documentLoadComplete.bind(this));
+    }
+
+    /**
+     * @param {!Function} callback The callback to run when the plugin has
+     *     loaded.
+     */
+    setLoadCallback(callback) {
+      this.loadCallback_ = callback;
+    }
+
+    documentLoadComplete() {
+      if (this.loadCallback_)
+        this.loadCallback_();
+    }
+
+    /**
+     * Stubbed out since some tests result in a call.
+     * @param {string} url The url to initialize the plugin to.
+     * @param {boolean} color Whether the preview should be in color.
+     * @param {!Array<number>} pages The pages to preview.
+     * @param {boolean} modifiable Whether the source document is modifiable.
+     */
+    resetPrintPreviewMode(url, color, pages, modifiable) {}
+  }
+
+  return {PDFPluginStub: PDFPluginStub};
+});
diff --git a/chrome/test/data/webui/print_preview/print_preview_tests.js b/chrome/test/data/webui/print_preview/print_preview_tests.js
index e874df6..b38e99f6 100644
--- a/chrome/test/data/webui/print_preview/print_preview_tests.js
+++ b/chrome/test/data/webui/print_preview/print_preview_tests.js
@@ -302,7 +302,9 @@
       print_preview.NativeLayer.setInstance(nativeLayer);
       printPreview = new print_preview.PrintPreview();
       previewArea = printPreview.getPreviewArea();
-      previewArea.setIsBrowserTest(true);
+      previewArea.plugin_ = new print_preview.PDFPluginStub(previewArea);
+      previewArea.plugin_.setLoadCallback(
+          previewArea.onPluginLoad_.bind(previewArea));
     });
 
     // Test some basic assumptions about the print preview WebUI.
diff --git a/chrome/test/data/webui/print_preview/print_preview_ui_browsertest.js b/chrome/test/data/webui/print_preview/print_preview_ui_browsertest.js
index 929f107..af6eb5c 100644
--- a/chrome/test/data/webui/print_preview/print_preview_ui_browsertest.js
+++ b/chrome/test/data/webui/print_preview/print_preview_ui_browsertest.js
@@ -58,6 +58,7 @@
     ROOT_PATH + 'chrome/test/data/webui/test_browser_proxy.js',
     'print_preview_tests.js',
     'native_layer_stub.js',
+    'plugin_stub.js',
   ],
 };
 
diff --git a/chrome/test/data/webui/settings/chrome_cleanup_page_test.js b/chrome/test/data/webui/settings/chrome_cleanup_page_test.js
index 9af4c29..6472b7c4 100644
--- a/chrome/test/data/webui/settings/chrome_cleanup_page_test.js
+++ b/chrome/test/data/webui/settings/chrome_cleanup_page_test.js
@@ -12,6 +12,7 @@
     'dismissCleanupPage',
     'registerChromeCleanerObserver',
     'restartComputer',
+    'setLogsUploadPermission',
     'startCleanup',
   ]);
 };
@@ -35,9 +36,14 @@
   },
 
   /** @override */
-  startCleanup: function() {
-    this.methodCalled('startCleanup');
+  setLogsUploadPermission: function(enabled) {
+    this.methodCalled('setLogsUploadPermission', enabled);
   },
+
+  /** @override */
+  startCleanup: function(logsUploadEnabled) {
+    this.methodCalled('startCleanup', logsUploadEnabled);
+  }
 };
 
 var chromeCleanupPage = null;
@@ -61,6 +67,7 @@
   });
 
   test('startCleanupFromInfected', function() {
+    cr.webUIListenerCallback('chrome-cleanup-upload-permission-change', false);
     cr.webUIListenerCallback(
         'chrome-cleanup-on-infected', ['file 1', 'file 2', 'file 3']);
     Polymer.dom.flush();
@@ -75,13 +82,15 @@
     var actionButton = chromeCleanupPage.$$('#action-button');
     assertTrue(!!actionButton);
     MockInteractions.tap(actionButton);
-    ChromeCleanupProxy.whenCalled('startCleanup').then(function() {
-      cr.webUIListenerCallback('chrome-cleanup-on-cleaning', false);
-      Polymer.dom.flush();
+    ChromeCleanupProxy.whenCalled('startCleanup').then(
+      function(logsUploadEnabled) {
+        assertFalse(logsUploadEnabled);
+        cr.webUIListenerCallback('chrome-cleanup-on-cleaning', false);
+        Polymer.dom.flush();
 
-      var spinner = chromeCleanupPage.$$('#cleaning-spinner');
-      assertTrue(spinner.active);
-    })
+        var spinner = chromeCleanupPage.$$('#cleaning-spinner');
+        assertTrue(spinner.active);
+      })
   });
 
   test('rebootFromRebootRequired', function() {
@@ -119,4 +128,24 @@
     MockInteractions.tap(actionButton);
     return ChromeCleanupProxy.whenCalled('dismissCleanupPage');
   });
+
+  test('setLogsUploadPermission', function() {
+    cr.webUIListenerCallback(
+        'chrome-cleanup-on-infected', ['file 1', 'file 2', 'file 3']);
+    Polymer.dom.flush();
+
+    var control = chromeCleanupPage.$$('#chromeCleanupLogsUploadControl');
+    assertTrue(!!control);
+
+    cr.webUIListenerCallback('chrome-cleanup-upload-permission-change', true);
+    Polymer.dom.flush();
+    assertTrue(control.checked);
+
+    cr.webUIListenerCallback('chrome-cleanup-upload-permission-change', false);
+    Polymer.dom.flush();
+    assertFalse(control.checked);
+
+    // TODO(proberge): Mock tapping on |control| and verify that
+    // |setLogsUploadPermission| is called with the right argument.
+  });
 });
diff --git a/chromecast/BUILD.gn b/chromecast/BUILD.gn
index 3d10c9cd..cd88338 100644
--- a/chromecast/BUILD.gn
+++ b/chromecast/BUILD.gn
@@ -61,11 +61,7 @@
   }
 
   if (!is_android && use_alsa) {
-    tests += [
-      "//chromecast/media/cma/backend/alsa:cast_alsa_cma_backend_unittests",
-      "//chromecast/media/cma/backend/alsa:libcast_governor_unittests",
-      "//chromecast/media/cma/backend/alsa:slew_volume_unittests",
-    ]
+    tests += [ "//chromecast/media/cma/backend/alsa:cast_alsa_cma_backend_unittests" ]
   }
 
   if (!is_android) {
diff --git a/chromecast/media/cma/backend/alsa/BUILD.gn b/chromecast/media/cma/backend/alsa/BUILD.gn
index b87f10a..ea6399b1 100644
--- a/chromecast/media/cma/backend/alsa/BUILD.gn
+++ b/chromecast/media/cma/backend/alsa/BUILD.gn
@@ -27,6 +27,7 @@
 
   deps = [
     ":alsa_cma_backend",
+    ":post_processor_factory",
     "//base",
     "//chromecast/base",
     "//chromecast/public",
@@ -64,6 +65,7 @@
   deps = [
     ":alsa_features",
     ":slew_volume",
+    ":volume_map",
     "//base",
     "//chromecast/base",
     "//chromecast/media/base",
@@ -76,6 +78,19 @@
   ]
 }
 
+source_set("volume_map") {
+  sources = [
+    "cast_audio_json.cc",
+    "cast_audio_json.h",
+    "volume_map.cc",
+    "volume_map.h",
+  ]
+  deps = [
+    "//base",
+    "//chromecast/base",
+  ]
+}
+
 source_set("slew_volume") {
   sources = [
     "slew_volume.cc",
@@ -101,14 +116,24 @@
 test("cast_alsa_cma_backend_unittests") {
   sources = [
     "filter_group_unittest.cc",
+    "post_processors/governor_unittest.cc",
+    "post_processors/saturated_gain_unittest.cc",
+    "slew_volume_unittests.cc",
     "stream_mixer_alsa_unittest.cc",
   ]
 
   deps = [
+    ":governor",
+    ":libcast_governor_1.0",
+    ":libcast_saturated_gain_1.0",
+    ":post_processor_factory",
+    ":post_processor_test_support",
+    ":slew_volume",
     ":test_support",
     "//base",
     "//base/test:run_all_unittests",
     "//chromecast/media",
+    "//chromecast/public",
     "//media",
     "//media:shared_memory_support",
     "//testing/gmock",
@@ -135,36 +160,15 @@
   ]
 }
 
-test("slew_volume_unittests") {
-  sources = [
-    "slew_volume_unittests.cc",
-  ]
-  deps = [
-    ":slew_volume",
-    "//base",
-    "//base/test:run_all_unittests",
-    "//media",
-    "//testing/gtest",
-  ]
-}
-
+# This is separate from ":governor" because the associated unittest needs
+# to create a Governor directly (to use test functions).
 shared_library("libcast_governor_1.0") {
-  deps = [
-    ":governor",
-  ]
-}
-
-test("libcast_governor_unittests") {
   sources = [
-    "post_processors/governor_unittest.cc",
+    "post_processors/governor_create.cc",
   ]
   deps = [
     ":governor",
-    ":post_processor_test_support",
-    "//base",
-    "//base/test:run_all_unittests",
-    "//media",
-    "//testing/gtest",
+    "//chromecast/public/media",
   ]
 }
 
@@ -182,6 +186,20 @@
   public_configs = [ "//chromecast/public:public_config" ]
 }
 
+shared_library("libcast_saturated_gain_1.0") {
+  sources = [
+    "post_processors/saturated_gain.cc",
+    "post_processors/saturated_gain.h",
+  ]
+  deps = [
+    ":slew_volume",
+    ":volume_map",
+    "//base",
+    "//chromecast/base",
+    "//chromecast/public/media",
+  ]
+}
+
 source_set("post_processor_test_support") {
   testonly = true
   sources = [
@@ -194,3 +212,14 @@
     "//testing/gtest",
   ]
 }
+
+source_set("post_processor_factory") {
+  sources = [
+    "post_processor_factory.cc",
+    "post_processor_factory.h",
+  ]
+  deps = [
+    "//base",
+    "//chromecast/public/media",
+  ]
+}
diff --git a/chromecast/media/cma/backend/alsa/cast_audio_json.cc b/chromecast/media/cma/backend/alsa/cast_audio_json.cc
new file mode 100644
index 0000000..57ad7bb
--- /dev/null
+++ b/chromecast/media/cma/backend/alsa/cast_audio_json.cc
@@ -0,0 +1,13 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromecast/media/cma/backend/alsa/cast_audio_json.h"
+
+namespace chromecast {
+namespace media {
+
+const char kCastAudioJsonFilePath[] = "/etc/cast_audio.json";
+
+}  // namespace media
+}  // namespace chromecast
diff --git a/chromecast/media/cma/backend/alsa/cast_audio_json.h b/chromecast/media/cma/backend/alsa/cast_audio_json.h
new file mode 100644
index 0000000..abfaacb
--- /dev/null
+++ b/chromecast/media/cma/backend/alsa/cast_audio_json.h
@@ -0,0 +1,16 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMECAST_MEDIA_CMA_BACKEND_ALSA_CAST_AUDIO_JSON_H_
+#define CHROMECAST_MEDIA_CMA_BACKEND_ALSA_CAST_AUDIO_JSON_H_
+
+namespace chromecast {
+namespace media {
+
+extern const char kCastAudioJsonFilePath[];
+
+}  // namespace media
+}  // namespace chromecast
+
+#endif  // CHROMECAST_MEDIA_CMA_BACKEND_ALSA_CAST_AUDIO_JSON_H_
diff --git a/chromecast/media/cma/backend/alsa/cast_media_shlib.cc b/chromecast/media/cma/backend/alsa/cast_media_shlib.cc
index 4a2d49c3..6e2bda6 100644
--- a/chromecast/media/cma/backend/alsa/cast_media_shlib.cc
+++ b/chromecast/media/cma/backend/alsa/cast_media_shlib.cc
@@ -198,8 +198,9 @@
     LoopbackAudioObserver* observer) {
   StreamMixerAlsa::Get()->RemoveLoopbackAudioObserver(observer);
 }
-void SetPostProcessorConfig(const std::string& name,
-                            const std::string& config) {
+
+void CastMediaShlib::SetPostProcessorConfig(const std::string& name,
+                                            const std::string& config) {
   StreamMixerAlsa::Get()->SetPostProcessorConfig(name, config);
 }
 
diff --git a/chromecast/media/cma/backend/alsa/post_processing_pipeline_impl.cc b/chromecast/media/cma/backend/alsa/post_processing_pipeline_impl.cc
index 61a91772..c465d8d 100644
--- a/chromecast/media/cma/backend/alsa/post_processing_pipeline_impl.cc
+++ b/chromecast/media/cma/backend/alsa/post_processing_pipeline_impl.cc
@@ -21,13 +21,10 @@
 namespace {
 
 const int kNoSampleRate = -1;
-const char kSoCreateFunction[] = "AudioPostProcessorShlib_Create";
 const char kProcessorKey[] = "processor";
 const char kNameKey[] = "name";
 }  // namespace
 
-using CreatePostProcessor = AudioPostProcessor* (*)(const std::string&, int);
-
 std::unique_ptr<PostProcessingPipeline> PostProcessingPipeline::Create(
     const std::string& name,
     const base::ListValue* filter_description_list,
@@ -67,22 +64,16 @@
     const base::Value* processor_config_val;
     CHECK(processor_description_dict->Get("config", &processor_config_val));
     CHECK(processor_config_val->is_dict() || processor_config_val->is_string());
-    auto processor_config_string = SerializeToJson(*processor_config_val);
+    std::unique_ptr<std::string> processor_config_string =
+        SerializeToJson(*processor_config_val);
 
     LOG(INFO) << "Creating an instance of " << library_path << "("
               << *processor_config_string << ")";
-    libraries_.push_back(base::MakeUnique<base::ScopedNativeLibrary>(
-        base::FilePath(library_path)));
-    CHECK(libraries_.back()->is_valid())
-        << "Could not open post processing library " << library_path;
-    CreatePostProcessor create = reinterpret_cast<CreatePostProcessor>(
-        libraries_.back()->GetFunctionPointer(kSoCreateFunction));
 
-    CHECK(create) << "Could not find " << kSoCreateFunction << "() in "
-                  << library_path;
-    processors_.emplace_back(PostProcessorInfo{
-        base::WrapUnique(create(*processor_config_string, channels)),
-        processor_name});
+    processors_.emplace_back(
+        PostProcessorInfo{factory_.CreatePostProcessor(
+                              library_path, *processor_config_string, channels),
+                          processor_name});
   }
 }
 
diff --git a/chromecast/media/cma/backend/alsa/post_processing_pipeline_impl.h b/chromecast/media/cma/backend/alsa/post_processing_pipeline_impl.h
index 75a9463..43b4374 100644
--- a/chromecast/media/cma/backend/alsa/post_processing_pipeline_impl.h
+++ b/chromecast/media/cma/backend/alsa/post_processing_pipeline_impl.h
@@ -11,10 +11,10 @@
 
 #include "base/macros.h"
 #include "chromecast/media/cma/backend/alsa/post_processing_pipeline.h"
+#include "chromecast/media/cma/backend/alsa/post_processor_factory.h"
 
 namespace base {
 class ListValue;
-class ScopedNativeLibrary;
 }  // namespace base
 
 namespace chromecast {
@@ -44,6 +44,13 @@
                               const std::string& config) override;
 
  private:
+  // Note: typedef is used to silence chromium-style mandatory constructor in
+  // structs.
+  typedef struct {
+    std::unique_ptr<AudioPostProcessor> ptr;
+    std::string name;
+  } PostProcessorInfo;
+
   int GetRingingTimeInFrames();
   void UpdateCastVolume(float multiplier);
 
@@ -55,17 +62,8 @@
   float current_multiplier_;
   float cast_volume_;
 
-  // Contains all libraries in use;
-  // Functions in shared objects cannot be used once library is closed.
-  std::vector<std::unique_ptr<base::ScopedNativeLibrary>> libraries_;
-
-  // Must be after libraries_
-  // Note: typedef is used to silence chromium-style mandatory constructor in
-  // structs.
-  typedef struct {
-    std::unique_ptr<AudioPostProcessor> ptr;
-    std::string name;
-  } PostProcessorInfo;
+  // factory_ keeps shared libraries open, so it must outlive processors_.
+  PostProcessorFactory factory_;
 
   std::vector<PostProcessorInfo> processors_;
 
diff --git a/chromecast/media/cma/backend/alsa/post_processing_pipeline_parser.cc b/chromecast/media/cma/backend/alsa/post_processing_pipeline_parser.cc
index d4128e1..2ad80d6 100644
--- a/chromecast/media/cma/backend/alsa/post_processing_pipeline_parser.cc
+++ b/chromecast/media/cma/backend/alsa/post_processing_pipeline_parser.cc
@@ -10,6 +10,7 @@
 #include "base/values.h"
 #include "chromecast/base/serializers.h"
 #include "chromecast/media/base/audio_device_ids.h"
+#include "chromecast/media/cma/backend/alsa/cast_audio_json.h"
 #include "media/audio/audio_device_description.h"
 
 namespace chromecast {
@@ -26,8 +27,6 @@
 
 }  // namespace
 
-const char kCastAudioConfigFilePath[] = "/etc/cast_audio.json";
-
 StreamPipelineDescriptor::StreamPipelineDescriptor(
     const base::ListValue* pipeline_in,
     const std::unordered_set<std::string>& stream_types_in)
@@ -43,20 +42,20 @@
     const std::string& json)
     : postprocessor_config_(nullptr) {
   if (json.empty() &&
-      !base::PathExists(base::FilePath(kCastAudioConfigFilePath))) {
+      !base::PathExists(base::FilePath(kCastAudioJsonFilePath))) {
     LOG(WARNING) << "Could not open post-processing config in "
-                 << kCastAudioConfigFilePath << ".";
+                 << kCastAudioJsonFilePath << ".";
     return;
   }
 
   if (json.empty()) {
     config_dict_ = base::DictionaryValue::From(
-        DeserializeJsonFromFile(base::FilePath(kCastAudioConfigFilePath)));
+        DeserializeJsonFromFile(base::FilePath(kCastAudioJsonFilePath)));
   } else {
     config_dict_ = base::DictionaryValue::From(DeserializeFromJson(json));
   }
 
-  CHECK(config_dict_) << "Invalid JSON in " << kCastAudioConfigFilePath;
+  CHECK(config_dict_) << "Invalid JSON in " << kCastAudioJsonFilePath;
   if (!config_dict_->GetDictionary(kPostProcessorsKey,
                                    &postprocessor_config_)) {
     LOG(WARNING) << "No post-processor config found.";
@@ -98,10 +97,6 @@
   return descriptors;
 }
 
-std::string PostProcessingPipelineParser::GetFilePath() {
-  return kCastAudioConfigFilePath;
-}
-
 const base::ListValue* PostProcessingPipelineParser::GetMixPipeline() {
   return GetPipelineByKey(kMixPipelineKey);
 }
@@ -116,7 +111,7 @@
   if (!postprocessor_config_ ||
       !postprocessor_config_->GetDictionary(key, &stream_dict)) {
     LOG(WARNING) << "No post-processor description found for \"" << key
-                 << "\" in " << kCastAudioConfigFilePath
+                 << "\" in " << kCastAudioJsonFilePath
                  << ". Using passthrough.";
     return nullptr;
   }
diff --git a/chromecast/media/cma/backend/alsa/post_processing_pipeline_parser.h b/chromecast/media/cma/backend/alsa/post_processing_pipeline_parser.h
index 76f11bbf1..e75f872df 100644
--- a/chromecast/media/cma/backend/alsa/post_processing_pipeline_parser.h
+++ b/chromecast/media/cma/backend/alsa/post_processing_pipeline_parser.h
@@ -55,8 +55,6 @@
   const base::ListValue* GetMixPipeline();
   const base::ListValue* GetLinearizePipeline();
 
-  static std::string GetFilePath();
-
  private:
   const base::ListValue* GetPipelineByKey(const std::string& key);
 
diff --git a/chromecast/media/cma/backend/alsa/post_processor_factory.cc b/chromecast/media/cma/backend/alsa/post_processor_factory.cc
new file mode 100644
index 0000000..c2f5379
--- /dev/null
+++ b/chromecast/media/cma/backend/alsa/post_processor_factory.cc
@@ -0,0 +1,44 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromecast/media/cma/backend/alsa/post_processor_factory.h"
+
+#include "base/files/file_path.h"
+#include "base/memory/ptr_util.h"
+#include "base/scoped_native_library.h"
+#include "chromecast/public/media/audio_post_processor_shlib.h"
+
+namespace chromecast {
+namespace media {
+
+namespace {
+
+const char kSoCreateFunction[] = "AudioPostProcessorShlib_Create";
+
+}  // namespace
+
+using CreatePostProcessorFunction = AudioPostProcessor* (*)(const std::string&,
+                                                            int);
+
+PostProcessorFactory::PostProcessorFactory() = default;
+PostProcessorFactory::~PostProcessorFactory() = default;
+
+std::unique_ptr<AudioPostProcessor> PostProcessorFactory::CreatePostProcessor(
+    const std::string& library_path,
+    const std::string& config,
+    int channels) {
+  libraries_.push_back(base::MakeUnique<base::ScopedNativeLibrary>(
+      base::FilePath(library_path)));
+  CHECK(libraries_.back()->is_valid())
+      << "Could not open post processing library " << library_path;
+  auto create = reinterpret_cast<CreatePostProcessorFunction>(
+      libraries_.back()->GetFunctionPointer(kSoCreateFunction));
+
+  CHECK(create) << "Could not find " << kSoCreateFunction << "() in "
+                << library_path;
+  return base::WrapUnique(create(config, channels));
+}
+
+}  // namespace media
+}  // namespace chromecast
diff --git a/chromecast/media/cma/backend/alsa/post_processor_factory.h b/chromecast/media/cma/backend/alsa/post_processor_factory.h
new file mode 100644
index 0000000..b21352c
--- /dev/null
+++ b/chromecast/media/cma/backend/alsa/post_processor_factory.h
@@ -0,0 +1,44 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMECAST_MEDIA_CMA_BACKEND_ALSA_POST_PROCESSOR_FACTORY_H_
+#define CHROMECAST_MEDIA_CMA_BACKEND_ALSA_POST_PROCESSOR_FACTORY_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+
+namespace base {
+class ScopedNativeLibrary;
+}  // namespace base
+
+namespace chromecast {
+namespace media {
+
+class AudioPostProcessor;
+
+class PostProcessorFactory {
+ public:
+  PostProcessorFactory();
+  ~PostProcessorFactory();
+
+  std::unique_ptr<AudioPostProcessor> CreatePostProcessor(
+      const std::string& so_name,
+      const std::string& config,
+      int channels);
+
+ private:
+  // Contains all libraries in use;
+  // Functions in shared objects cannot be used once library is closed.
+  std::vector<std::unique_ptr<base::ScopedNativeLibrary>> libraries_;
+
+  DISALLOW_COPY_AND_ASSIGN(PostProcessorFactory);
+};
+
+}  // namespace media
+}  // namespace chromecast
+
+#endif  // CHROMECAST_MEDIA_CMA_BACKEND_ALSA_POST_PROCESSOR_FACTORY_H_
diff --git a/chromecast/media/cma/backend/alsa/post_processors/governor.cc b/chromecast/media/cma/backend/alsa/post_processors/governor.cc
index 83299966..1718ca8 100644
--- a/chromecast/media/cma/backend/alsa/post_processors/governor.cc
+++ b/chromecast/media/cma/backend/alsa/post_processors/governor.cc
@@ -75,10 +75,3 @@
 
 }  // namespace media
 }  // namespace chromecast
-
-chromecast::media::AudioPostProcessor* AudioPostProcessorShlib_Create(
-    const std::string& config,
-    int channels) {
-  return static_cast<chromecast::media::AudioPostProcessor*>(
-      new chromecast::media::Governor(config, channels));
-}
diff --git a/chromecast/media/cma/backend/alsa/post_processors/governor_create.cc b/chromecast/media/cma/backend/alsa/post_processors/governor_create.cc
new file mode 100644
index 0000000..358a0eea
--- /dev/null
+++ b/chromecast/media/cma/backend/alsa/post_processors/governor_create.cc
@@ -0,0 +1,13 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromecast/media/cma/backend/alsa/post_processors/governor.h"
+#include "chromecast/public/media/audio_post_processor_shlib.h"
+
+chromecast::media::AudioPostProcessor* AudioPostProcessorShlib_Create(
+    const std::string& config,
+    int channels) {
+  return static_cast<chromecast::media::AudioPostProcessor*>(
+      new chromecast::media::Governor(config, channels));
+}
diff --git a/chromecast/media/cma/backend/alsa/post_processors/governor_unittest.cc b/chromecast/media/cma/backend/alsa/post_processors/governor_unittest.cc
index 38cac53f..7acf623 100644
--- a/chromecast/media/cma/backend/alsa/post_processors/governor_unittest.cc
+++ b/chromecast/media/cma/backend/alsa/post_processors/governor_unittest.cc
@@ -11,10 +11,9 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/stringprintf.h"
+#include "chromecast/media/cma/backend/alsa/post_processor_factory.h"
 #include "chromecast/media/cma/backend/alsa/post_processors/governor.h"
 #include "chromecast/media/cma/backend/alsa/post_processors/post_processor_unittest.h"
-#include "media/base/audio_bus.h"
-#include "media/base/audio_sample_types.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace chromecast {
@@ -26,6 +25,7 @@
 const char* kConfigTemplate =
     R"config({"onset_volume": %f, "clamp_multiplier": %f})config";
 
+const char kLibraryPath[] = "libcast_governor_1.0.so";
 const float kDefaultClamp = 0.6f;
 const int kNumFrames = 100;
 const int kFrequency = 2000;
@@ -111,24 +111,24 @@
                         ::testing::Values(0.0f, 0.1f, 0.5f, 0.9f, 1.0f, 1.1f));
 
 // Default tests from post_processor_test
-TEST_P(PostProcessorTest, TestDelay) {
+TEST_P(PostProcessorTest, GovernorDelay) {
   std::string config = MakeConfigString(1.0, 1.0);
-  auto pp =
-      base::WrapUnique(AudioPostProcessorShlib_Create(config, kNumChannels));
+  PostProcessorFactory factory;
+  auto pp = factory.CreatePostProcessor(kLibraryPath, config, kNumChannels);
   TestDelay(pp.get(), sample_rate_);
 }
 
-TEST_P(PostProcessorTest, TestRinging) {
+TEST_P(PostProcessorTest, GovernorRinging) {
   std::string config = MakeConfigString(1.0, 1.0);
-  auto pp =
-      base::WrapUnique(AudioPostProcessorShlib_Create(config, kNumChannels));
+  PostProcessorFactory factory;
+  auto pp = factory.CreatePostProcessor(kLibraryPath, config, kNumChannels);
   TestRingingTime(pp.get(), sample_rate_);
 }
 
-TEST_P(PostProcessorTest, TestPassthrough) {
+TEST_P(PostProcessorTest, GovernorPassthrough) {
   std::string config = MakeConfigString(1.0, 1.0);
-  auto pp =
-      base::WrapUnique(AudioPostProcessorShlib_Create(config, kNumChannels));
+  PostProcessorFactory factory;
+  auto pp = factory.CreatePostProcessor(kLibraryPath, config, kNumChannels);
   TestPassthrough(pp.get(), sample_rate_);
 }
 
diff --git a/chromecast/media/cma/backend/alsa/post_processors/saturated_gain.cc b/chromecast/media/cma/backend/alsa/post_processors/saturated_gain.cc
new file mode 100644
index 0000000..a2d15e8
--- /dev/null
+++ b/chromecast/media/cma/backend/alsa/post_processors/saturated_gain.cc
@@ -0,0 +1,73 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromecast/media/cma/backend/alsa/post_processors/saturated_gain.h"
+
+#include <algorithm>
+#include <cmath>
+
+#include "base/logging.h"
+#include "base/values.h"
+#include "chromecast/base/serializers.h"
+#include "chromecast/media/cma/backend/alsa/slew_volume.h"
+
+namespace chromecast {
+namespace media {
+
+namespace {
+const int kNoSampleRate = -1;
+
+// Configuration strings:
+const char kGainKey[] = "gain_db";
+
+float DbFsToScale(float db) {
+  return std::pow(10, db / 20);
+}
+
+}  // namespace
+
+SaturatedGain::SaturatedGain(const std::string& config, int channels)
+    : channels_(channels), sample_rate_(kNoSampleRate), last_volume_(-1) {
+  auto config_dict = base::DictionaryValue::From(DeserializeFromJson(config));
+  CHECK(config_dict) << "SaturatedGain config is not valid json: " << config;
+  double gain_db;
+  CHECK(config_dict->GetDouble(kGainKey, &gain_db)) << config;
+  gain_ = DbFsToScale(gain_db);
+  LOG(INFO) << "Created a SaturatedGain: gain = " << gain_db;
+}
+
+SaturatedGain::~SaturatedGain() = default;
+
+bool SaturatedGain::SetSampleRate(int sample_rate) {
+  sample_rate_ = sample_rate;
+  slew_volume_.SetSampleRate(sample_rate);
+  return true;
+}
+
+int SaturatedGain::ProcessFrames(float* data, int frames, float volume) {
+  if (volume != last_volume_) {
+    last_volume_ = volume;
+    // Don't apply more gain than attenuation.
+    float effective_gain = std::min(
+        1.0f / DbFsToScale(volume_map_.VolumeToDbFS(last_volume_)), gain_);
+    slew_volume_.SetVolume(effective_gain);
+  }
+
+  slew_volume_.ProcessFMUL(false, data, frames, channels_, data);
+
+  return 0;  // No delay in this pipeline.
+}
+
+int SaturatedGain::GetRingingTimeInFrames() {
+  return 0;
+}
+
+}  // namespace media
+}  // namespace chromecast
+
+chromecast::media::AudioPostProcessor* AudioPostProcessorShlib_Create(
+    const std::string& config,
+    int channels) {
+  return new chromecast::media::SaturatedGain(config, channels);
+}
diff --git a/chromecast/media/cma/backend/alsa/post_processors/saturated_gain.h b/chromecast/media/cma/backend/alsa/post_processors/saturated_gain.h
new file mode 100644
index 0000000..51e195d
--- /dev/null
+++ b/chromecast/media/cma/backend/alsa/post_processors/saturated_gain.h
@@ -0,0 +1,43 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMECAST_MEDIA_CMA_BACKEND_ALSA_POST_PROCESSORS_SATURATED_GAIN_H_
+#define CHROMECAST_MEDIA_CMA_BACKEND_ALSA_POST_PROCESSORS_SATURATED_GAIN_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "chromecast/media/cma/backend/alsa/slew_volume.h"
+#include "chromecast/media/cma/backend/alsa/volume_map.h"
+#include "chromecast/public/media/audio_post_processor_shlib.h"
+
+namespace chromecast {
+namespace media {
+
+// Provides a simple gain, avoiding overflow.
+class SaturatedGain : public AudioPostProcessor {
+ public:
+  SaturatedGain(const std::string& config, int channels);
+  ~SaturatedGain() override;
+
+  // AudioPostProcessor implementation:
+  bool SetSampleRate(int sample_rate) override;
+  int ProcessFrames(float* data, int frames, float volume) override;
+  int GetRingingTimeInFrames() override;
+
+ private:
+  int channels_;
+  int sample_rate_;
+  float last_volume_;
+  VolumeMap volume_map_;
+  SlewVolume slew_volume_;
+  float gain_;
+
+  DISALLOW_COPY_AND_ASSIGN(SaturatedGain);
+};
+
+}  // namespace media
+}  // namespace chromecast
+
+#endif  // CHROMECAST_MEDIA_CMA_BACKEND_ALSA_POST_PROCESSORS_SATURATED_GAIN_H_
diff --git a/chromecast/media/cma/backend/alsa/post_processors/saturated_gain_unittest.cc b/chromecast/media/cma/backend/alsa/post_processors/saturated_gain_unittest.cc
new file mode 100644
index 0000000..bad7b96e
--- /dev/null
+++ b/chromecast/media/cma/backend/alsa/post_processors/saturated_gain_unittest.cc
@@ -0,0 +1,54 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+#include <vector>
+
+#include "base/strings/stringprintf.h"
+#include "chromecast/media/cma/backend/alsa/post_processor_factory.h"
+#include "chromecast/media/cma/backend/alsa/post_processors/post_processor_unittest.h"
+
+namespace chromecast {
+namespace media {
+namespace post_processor_test {
+
+namespace {
+
+const char kConfigTemplate[] =
+    R"config({"gain_db": %f})config";
+
+const char kLibraryPath[] = "libcast_saturated_gain_1.0.so";
+
+std::string MakeConfigString(float gain_db) {
+  return base::StringPrintf(kConfigTemplate, gain_db);
+}
+
+}  // namespace
+
+// Default tests from post_processor_test
+TEST_P(PostProcessorTest, SaturatedGainDelay) {
+  PostProcessorFactory factory;
+  std::string config = MakeConfigString(0.0);
+  auto pp = factory.CreatePostProcessor(kLibraryPath, config, kNumChannels);
+  TestDelay(pp.get(), sample_rate_);
+}
+
+TEST_P(PostProcessorTest, SaturatedGainRinging) {
+  PostProcessorFactory factory;
+  std::string config = MakeConfigString(0.0);
+  auto pp = factory.CreatePostProcessor(kLibraryPath, config, kNumChannels);
+  TestRingingTime(pp.get(), sample_rate_);
+}
+
+// Also tests clipping (by attempting to set gain way too high).
+TEST_P(PostProcessorTest, SaturatedGainPassthrough) {
+  PostProcessorFactory factory;
+  std::string config = MakeConfigString(100.0);
+  auto pp = factory.CreatePostProcessor(kLibraryPath, config, kNumChannels);
+  TestPassthrough(pp.get(), sample_rate_);
+}
+
+}  // namespace post_processor_test
+}  // namespace media
+}  // namespace chromecast
diff --git a/chromecast/media/cma/backend/alsa/stream_mixer_alsa.cc b/chromecast/media/cma/backend/alsa/stream_mixer_alsa.cc
index daab40ef..a9f38bc 100644
--- a/chromecast/media/cma/backend/alsa/stream_mixer_alsa.cc
+++ b/chromecast/media/cma/backend/alsa/stream_mixer_alsa.cc
@@ -24,6 +24,7 @@
 #include "chromecast/base/chromecast_switches.h"
 #include "chromecast/media/base/audio_device_ids.h"
 #include "chromecast/media/cma/backend/alsa/alsa_wrapper.h"
+#include "chromecast/media/cma/backend/alsa/cast_audio_json.h"
 #include "chromecast/media/cma/backend/alsa/filter_group.h"
 #include "chromecast/media/cma/backend/alsa/post_processing_pipeline_parser.h"
 #include "chromecast/media/cma/backend/alsa/stream_mixer_alsa_input_impl.h"
@@ -277,7 +278,7 @@
           << "media/audio/audio_device_description.cc";
       CHECK(used_streams.insert(stream_type).second)
           << "Multiple instances of stream type '" << stream_type << "' in "
-          << pipeline_parser->GetFilePath() << ".";
+          << kCastAudioJsonFilePath << ".";
     }
     filter_groups_.push_back(base::MakeUnique<FilterGroup>(
         kNumInputChannels, false /* mono_mixer */,
@@ -1086,10 +1087,10 @@
 }
 
 void StreamMixerAlsa::SetPostProcessorConfig(const std::string& name,
-                                             const std::string& message) {
-  RUN_ON_MIXER_THREAD(&StreamMixerAlsa::SetPostProcessorConfig, name, message);
+                                             const std::string& config) {
+  RUN_ON_MIXER_THREAD(&StreamMixerAlsa::SetPostProcessorConfig, name, config);
   for (auto&& filter_group : filter_groups_) {
-    filter_group->SetPostProcessorConfig(name, message);
+    filter_group->SetPostProcessorConfig(name, config);
   }
 }
 
diff --git a/chromecast/media/cma/backend/alsa/volume_control.cc b/chromecast/media/cma/backend/alsa/volume_control.cc
index 7ab9875..4146847 100644
--- a/chromecast/media/cma/backend/alsa/volume_control.cc
+++ b/chromecast/media/cma/backend/alsa/volume_control.cc
@@ -30,8 +30,10 @@
 #include "chromecast/base/serializers.h"
 #include "chromecast/media/cma/backend/alsa/alsa_features.h"
 #include "chromecast/media/cma/backend/alsa/alsa_volume_control.h"
+#include "chromecast/media/cma/backend/alsa/cast_audio_json.h"
 #include "chromecast/media/cma/backend/alsa/post_processing_pipeline_parser.h"
 #include "chromecast/media/cma/backend/alsa/stream_mixer_alsa.h"
+#include "chromecast/media/cma/backend/alsa/volume_map.h"
 
 namespace chromecast {
 namespace media {
@@ -47,22 +49,8 @@
 constexpr char kKeyMediaDbFS[] = "dbfs.media";
 constexpr char kKeyAlarmDbFS[] = "dbfs.alarm";
 constexpr char kKeyCommunicationDbFS[] = "dbfs.communication";
-constexpr char kKeyVolumeMap[] = "volume_map";
-constexpr char kKeyLevel[] = "level";
-constexpr char kKeyDb[] = "db";
 constexpr char kKeyDefaultVolume[] = "default_volume";
 
-struct LevelToDb {
-  float level;
-  float db;
-};
-
-const LevelToDb kDefaultVolumeMap[] = {{0.0f, kMinDbFS},
-                                       {0.01f, -58.0f},
-                                       {0.090909f, -48.0f},
-                                       {0.818182f, -8.0f},
-                                       {1.0f, 0.0f}};
-
 float DbFsToScale(float db) {
   if (db <= kMinDbFS) {
     return 0.0f;
@@ -81,103 +69,6 @@
   }
 }
 
-class VolumeMap {
- public:
-  VolumeMap() {
-    auto cast_audio_config = DeserializeJsonFromFile(
-        base::FilePath(PostProcessingPipelineParser::GetFilePath()));
-    const base::DictionaryValue* cast_audio_dict;
-    if (!cast_audio_config ||
-        !cast_audio_config->GetAsDictionary(&cast_audio_dict)) {
-      LOG(WARNING) << "No cast audio config found; using default volume map.";
-      volume_map_.insert(volume_map_.end(), kDefaultVolumeMap,
-                         kDefaultVolumeMap + arraysize(kDefaultVolumeMap));
-      return;
-    }
-
-    const base::ListValue* volume_map_list;
-    if (!cast_audio_dict->GetList(kKeyVolumeMap, &volume_map_list)) {
-      LOG(WARNING) << "No volume map found; using default volume map.";
-      volume_map_.insert(volume_map_.end(), kDefaultVolumeMap,
-                         kDefaultVolumeMap + arraysize(kDefaultVolumeMap));
-      return;
-    }
-
-    double prev_level = -1.0;
-    for (size_t i = 0; i < volume_map_list->GetSize(); ++i) {
-      const base::DictionaryValue* volume_map_entry;
-      CHECK(volume_map_list->GetDictionary(i, &volume_map_entry));
-
-      double level;
-      CHECK(volume_map_entry->GetDouble(kKeyLevel, &level));
-      CHECK_GE(level, 0.0);
-      CHECK_LE(level, 1.0);
-      CHECK_GT(level, prev_level);
-      prev_level = level;
-
-      double db;
-      CHECK(volume_map_entry->GetDouble(kKeyDb, &db));
-      CHECK_LE(db, 0.0);
-      if (level == 1.0) {
-        CHECK_EQ(db, 0.0);
-      }
-
-      volume_map_.push_back({level, db});
-    }
-
-    if (volume_map_.empty()) {
-      LOG(FATAL) << "No entries in volume map.";
-      return;
-    }
-
-    if (volume_map_[0].level > 0.0) {
-      volume_map_.insert(volume_map_.begin(), {0.0, kMinDbFS});
-    }
-
-    if (volume_map_.rbegin()->level < 1.0) {
-      volume_map_.push_back({1.0, 0.0});
-    }
-  }
-
-  float VolumeToDbFS(float volume) {
-    if (volume <= volume_map_[0].level) {
-      return volume_map_[0].db;
-    }
-    for (size_t i = 1; i < volume_map_.size(); ++i) {
-      if (volume < volume_map_[i].level) {
-        const float x_range = volume_map_[i].level - volume_map_[i - 1].level;
-        const float y_range = volume_map_[i].db - volume_map_[i - 1].db;
-        const float x_pos = volume - volume_map_[i - 1].level;
-
-        return volume_map_[i - 1].db + x_pos * y_range / x_range;
-      }
-    }
-    return volume_map_[volume_map_.size() - 1].db;
-  }
-
-  // static
-  float DbFSToVolume(float db) {
-    if (db <= volume_map_[0].db) {
-      return volume_map_[0].level;
-    }
-    for (size_t i = 1; i < volume_map_.size(); ++i) {
-      if (db < volume_map_[i].db) {
-        const float x_range = volume_map_[i].db - volume_map_[i - 1].db;
-        const float y_range = volume_map_[i].level - volume_map_[i - 1].level;
-        const float x_pos = db - volume_map_[i - 1].db;
-
-        return volume_map_[i - 1].level + x_pos * y_range / x_range;
-      }
-    }
-    return volume_map_[volume_map_.size() - 1].level;
-  }
-
- private:
-  std::vector<LevelToDb> volume_map_;
-
-  DISALLOW_COPY_AND_ASSIGN(VolumeMap);
-};
-
 base::LazyInstance<VolumeMap>::Leaky g_volume_map = LAZY_INSTANCE_INITIALIZER;
 
 class VolumeControlInternal : public AlsaVolumeControl::Delegate {
@@ -209,8 +100,8 @@
       }
     } else {
       // If saved_volumes does not exist, use per device default if it exists.
-      auto cast_audio_config = DeserializeJsonFromFile(
-          base::FilePath(PostProcessingPipelineParser::GetFilePath()));
+      auto cast_audio_config =
+          DeserializeJsonFromFile(base::FilePath(kCastAudioJsonFilePath));
       const base::DictionaryValue* cast_audio_dict;
       if (cast_audio_config &&
           cast_audio_config->GetAsDictionary(&cast_audio_dict)) {
diff --git a/chromecast/media/cma/backend/alsa/volume_map.cc b/chromecast/media/cma/backend/alsa/volume_map.cc
new file mode 100644
index 0000000..a8c3f34
--- /dev/null
+++ b/chromecast/media/cma/backend/alsa/volume_map.cc
@@ -0,0 +1,123 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromecast/media/cma/backend/alsa/volume_map.h"
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/values.h"
+#include "chromecast/base/serializers.h"
+#include "chromecast/media/cma/backend/alsa/cast_audio_json.h"
+
+namespace chromecast {
+namespace media {
+
+namespace {
+constexpr char kKeyVolumeMap[] = "volume_map";
+constexpr char kKeyLevel[] = "level";
+constexpr char kKeyDb[] = "db";
+constexpr float kMinDbFS = -120.0f;
+
+}  // namespace
+
+VolumeMap::VolumeMap() {
+  auto cast_audio_config =
+      DeserializeJsonFromFile(base::FilePath(kCastAudioJsonFilePath));
+  const base::DictionaryValue* cast_audio_dict;
+  if (!cast_audio_config ||
+      !cast_audio_config->GetAsDictionary(&cast_audio_dict)) {
+    LOG(WARNING) << "No cast audio config found; using default volume map.";
+    UseDefaultVolumeMap();
+    return;
+  }
+
+  const base::ListValue* volume_map_list;
+  if (!cast_audio_dict->GetList(kKeyVolumeMap, &volume_map_list)) {
+    LOG(WARNING) << "No volume map found; using default volume map.";
+    UseDefaultVolumeMap();
+    return;
+  }
+
+  double prev_level = -1.0;
+  for (size_t i = 0; i < volume_map_list->GetSize(); ++i) {
+    const base::DictionaryValue* volume_map_entry;
+    CHECK(volume_map_list->GetDictionary(i, &volume_map_entry));
+
+    double level;
+    CHECK(volume_map_entry->GetDouble(kKeyLevel, &level));
+    CHECK_GE(level, 0.0);
+    CHECK_LE(level, 1.0);
+    CHECK_GT(level, prev_level);
+    prev_level = level;
+
+    double db;
+    CHECK(volume_map_entry->GetDouble(kKeyDb, &db));
+    CHECK_LE(db, 0.0);
+    if (level == 1.0) {
+      CHECK_EQ(db, 0.0);
+    }
+
+    volume_map_.push_back({level, db});
+  }
+
+  if (volume_map_.empty()) {
+    LOG(FATAL) << "No entries in volume map.";
+    return;
+  }
+
+  if (volume_map_[0].level > 0.0) {
+    volume_map_.insert(volume_map_.begin(), {0.0, kMinDbFS});
+  }
+
+  if (volume_map_.rbegin()->level < 1.0) {
+    volume_map_.push_back({1.0, 0.0});
+  }
+}
+
+VolumeMap::~VolumeMap() = default;
+
+float VolumeMap::VolumeToDbFS(float volume) {
+  if (volume <= volume_map_[0].level) {
+    return volume_map_[0].db;
+  }
+  for (size_t i = 1; i < volume_map_.size(); ++i) {
+    if (volume < volume_map_[i].level) {
+      const float x_range = volume_map_[i].level - volume_map_[i - 1].level;
+      const float y_range = volume_map_[i].db - volume_map_[i - 1].db;
+      const float x_pos = volume - volume_map_[i - 1].level;
+
+      return volume_map_[i - 1].db + x_pos * y_range / x_range;
+    }
+  }
+  return volume_map_[volume_map_.size() - 1].db;
+}
+
+float VolumeMap::DbFSToVolume(float db) {
+  if (db <= volume_map_[0].db) {
+    return volume_map_[0].level;
+  }
+  for (size_t i = 1; i < volume_map_.size(); ++i) {
+    if (db < volume_map_[i].db) {
+      const float x_range = volume_map_[i].db - volume_map_[i - 1].db;
+      const float y_range = volume_map_[i].level - volume_map_[i - 1].level;
+      const float x_pos = db - volume_map_[i - 1].db;
+
+      return volume_map_[i - 1].level + x_pos * y_range / x_range;
+    }
+  }
+  return volume_map_[volume_map_.size() - 1].level;
+}
+
+// static
+void VolumeMap::UseDefaultVolumeMap() {
+  volume_map_ = {{0.0f, kMinDbFS},
+                 {0.01f, -58.0f},
+                 {0.090909f, -48.0f},
+                 {0.818182f, -8.0f},
+                 {1.0f, 0.0f}};
+}
+
+}  // namespace media
+}  // namespace chromecast
diff --git a/chromecast/media/cma/backend/alsa/volume_map.h b/chromecast/media/cma/backend/alsa/volume_map.h
new file mode 100644
index 0000000..493599dd
--- /dev/null
+++ b/chromecast/media/cma/backend/alsa/volume_map.h
@@ -0,0 +1,40 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMECAST_MEDIA_CMA_BACKEND_ALSA_VOLUME_MAP_H_
+#define CHROMECAST_MEDIA_CMA_BACKEND_ALSA_VOLUME_MAP_H_
+
+#include <vector>
+
+#include "base/macros.h"
+
+namespace chromecast {
+namespace media {
+
+class VolumeMap {
+ public:
+  VolumeMap();
+  ~VolumeMap();
+
+  float VolumeToDbFS(float volume);
+
+  float DbFSToVolume(float db);
+
+ private:
+  struct LevelToDb {
+    float level;
+    float db;
+  };
+
+  void UseDefaultVolumeMap();
+
+  std::vector<LevelToDb> volume_map_;
+
+  DISALLOW_COPY_AND_ASSIGN(VolumeMap);
+};
+
+}  // namespace media
+}  // namespace chromecast
+
+#endif  // CHROMECAST_MEDIA_CMA_BACKEND_ALSA_VOLUME_MAP_H_
diff --git a/chromecast/media/service/cast_renderer.cc b/chromecast/media/service/cast_renderer.cc
index 372c18b..8e6a01a3a 100644
--- a/chromecast/media/service/cast_renderer.cc
+++ b/chromecast/media/service/cast_renderer.cc
@@ -101,7 +101,9 @@
   AudioContentType content_type;
   if (audio_device_id_ == kAlarmAudioDeviceId) {
     content_type = AudioContentType::kAlarm;
-  } else if (audio_device_id_ == kTtsAudioDeviceId) {
+  } else if (audio_device_id_ == kTtsAudioDeviceId ||
+             audio_device_id_ ==
+                 ::media::AudioDeviceDescription::kCommunicationsDeviceId) {
     content_type = AudioContentType::kCommunication;
   } else {
     content_type = AudioContentType::kMedia;
diff --git a/components/arc/test/fake_bluetooth_instance.h b/components/arc/test/fake_bluetooth_instance.h
index 2121120..dd4bb9e1 100644
--- a/components/arc/test/fake_bluetooth_instance.h
+++ b/components/arc/test/fake_bluetooth_instance.h
@@ -9,7 +9,6 @@
 #include <vector>
 
 #include "base/macros.h"
-#include "base/memory/scoped_vector.h"
 #include "components/arc/common/bluetooth.mojom.h"
 #include "mojo/public/cpp/bindings/binding.h"
 
diff --git a/components/autofill/content/renderer/test_password_generation_agent.h b/components/autofill/content/renderer/test_password_generation_agent.h
index 11aef9d..6ab5509d 100644
--- a/components/autofill/content/renderer/test_password_generation_agent.h
+++ b/components/autofill/content/renderer/test_password_generation_agent.h
@@ -8,7 +8,6 @@
 #include <vector>
 
 #include "base/macros.h"
-#include "base/memory/scoped_vector.h"
 #include "components/autofill/content/renderer/password_generation_agent.h"
 
 namespace autofill {
diff --git a/components/autofill/core/browser/autofill_manager_unittest.cc b/components/autofill/core/browser/autofill_manager_unittest.cc
index dd8e02b..310b22c 100644
--- a/components/autofill/core/browser/autofill_manager_unittest.cc
+++ b/components/autofill/core/browser/autofill_manager_unittest.cc
@@ -17,7 +17,6 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/ref_counted.h"
-#include "base/memory/scoped_vector.h"
 #include "base/metrics/field_trial.h"
 #include "base/metrics/metrics_hashes.h"
 #include "base/run_loop.h"
diff --git a/components/autofill/core/browser/webdata/autofill_table_unittest.cc b/components/autofill/core/browser/webdata/autofill_table_unittest.cc
index 4e92e5c7..931f8b4 100644
--- a/components/autofill/core/browser/webdata/autofill_table_unittest.cc
+++ b/components/autofill/core/browser/webdata/autofill_table_unittest.cc
@@ -14,7 +14,6 @@
 #include "base/files/scoped_temp_dir.h"
 #include "base/guid.h"
 #include "base/macros.h"
-#include "base/memory/scoped_vector.h"
 #include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
diff --git a/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.cc b/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.cc
index 261f63b..7d47212 100644
--- a/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.cc
+++ b/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.cc
@@ -13,7 +13,6 @@
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
-#include "base/memory/scoped_vector.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/time/time.h"
 #include "components/autofill/core/browser/autofill_data_model.h"
diff --git a/components/constrained_window/BUILD.gn b/components/constrained_window/BUILD.gn
index a62c555..d4653d8b 100644
--- a/components/constrained_window/BUILD.gn
+++ b/components/constrained_window/BUILD.gn
@@ -37,6 +37,7 @@
   if (use_aura) {
     deps += [
       "//ui/aura",
+      "//ui/compositor",
       "//ui/wm",
     ]
   }
diff --git a/components/constrained_window/DEPS b/components/constrained_window/DEPS
index b4fd76f..01e4dfd93 100644
--- a/components/constrained_window/DEPS
+++ b/components/constrained_window/DEPS
@@ -4,6 +4,7 @@
   "+content/public/browser",
   "+ui/aura",
   "+ui/base",
+  "+ui/compositor",
   "+ui/display",
   "+ui/gfx",
   "+ui/views",
diff --git a/components/constrained_window/constrained_window_views.cc b/components/constrained_window/constrained_window_views.cc
index 37de79c..18398d6 100644
--- a/components/constrained_window/constrained_window_views.cc
+++ b/components/constrained_window/constrained_window_views.cc
@@ -24,6 +24,11 @@
 #import "components/constrained_window/native_web_contents_modal_dialog_manager_views_mac.h"
 #endif
 
+#if defined(USE_AURA)
+#include "ui/aura/window.h"
+#include "ui/compositor/dip_util.h"
+#endif
+
 using web_modal::ModalDialogHost;
 using web_modal::ModalDialogHostObserver;
 
@@ -126,6 +131,19 @@
   }
 
   widget->SetBounds(gfx::Rect(position, size));
+
+#if defined(USE_AURA)
+  if (!widget->is_top_level()) {
+    // Toplevel windows are automatiacally snapped, but CHILD windows
+    // may not. If it's not toplevel, snap the widget's layer to pixel
+    // based on the parent toplevel window, which should be snapped.
+    gfx::NativeView window = widget->GetNativeView();
+    views::Widget* toplevel =
+        views::Widget::GetTopLevelWidgetForNativeView(window->parent());
+    ui::SnapLayerToPhysicalPixelBoundary(toplevel->GetLayer(),
+                                         widget->GetLayer());
+  }
+#endif
 }
 
 }  // namespace
diff --git a/components/cronet/android/cronet_library_loader.cc b/components/cronet/android/cronet_library_loader.cc
index b7aa07a..250a6658 100644
--- a/components/cronet/android/cronet_library_loader.cc
+++ b/components/cronet/android/cronet_library_loader.cc
@@ -18,6 +18,7 @@
 #include "base/macros.h"
 #include "base/message_loop/message_loop.h"
 #include "base/metrics/statistics_recorder.h"
+#include "base/task_scheduler/task_scheduler.h"
 #include "components/cronet/android/cronet_bidirectional_stream_adapter.h"
 #include "components/cronet/android/cronet_jni_registration.h"
 #include "components/cronet/android/cronet_upload_data_stream_adapter.h"
@@ -67,6 +68,9 @@
 bool NativeInit() {
   if (!base::android::OnJNIOnLoadInit())
     return false;
+  if (!base::TaskScheduler::GetInstance())
+    base::TaskScheduler::CreateAndStartWithDefaultParams("Cronet");
+
   url::Initialize();
   // Initializes the statistics recorder system. This needs to be done before
   // emitting histograms to prevent memory leaks (crbug.com/707836).
@@ -97,6 +101,9 @@
 }
 
 void CronetOnUnLoad(JavaVM* jvm, void* reserved) {
+  if (base::TaskScheduler::GetInstance())
+    base::TaskScheduler::GetInstance()->Shutdown();
+
   base::android::LibraryLoaderExitHook();
 }
 
diff --git a/components/cronet/url_request_context_config.cc b/components/cronet/url_request_context_config.cc
index 93820d83..0c9daaf 100644
--- a/components/cronet/url_request_context_config.cc
+++ b/components/cronet/url_request_context_config.cc
@@ -22,6 +22,7 @@
 #include "net/cert/multi_threaded_cert_verifier.h"
 #include "net/dns/host_resolver.h"
 #include "net/dns/mapped_host_resolver.h"
+#include "net/http/http_network_session.h"
 #include "net/http/http_server_properties.h"
 #include "net/nqe/network_quality_estimator_params.h"
 #include "net/quic/chromium/quic_utils_chromium.h"
@@ -155,6 +156,7 @@
 
 void URLRequestContextConfig::ParseAndSetExperimentalOptions(
     net::URLRequestContextBuilder* context_builder,
+    net::HttpNetworkSession::Params* session_params,
     net::NetLog* net_log,
     const scoped_refptr<base::SequencedTaskRunner>& file_task_runner) {
   if (experimental_options.empty())
@@ -189,6 +191,7 @@
   effective_experimental_options = dict->CreateDeepCopy();
   StaleHostResolver::StaleOptions stale_dns_options;
   std::string host_resolver_rules_string;
+
   for (base::DictionaryValue::Iterator it(*dict.get()); !it.IsAtEnd();
        it.Advance()) {
     if (it.key() == kQuicFieldTrialName) {
@@ -202,8 +205,8 @@
       std::string quic_connection_options;
       if (quic_args->GetString(kQuicConnectionOptions,
                                &quic_connection_options)) {
-        context_builder->set_quic_connection_options(
-            net::ParseQuicConnectionOptions(quic_connection_options));
+        session_params->quic_connection_options =
+            net::ParseQuicConnectionOptions(quic_connection_options);
       }
 
       // TODO(rtenneti): Delete this option after apps stop using it.
@@ -211,63 +214,63 @@
       bool quic_store_server_configs_in_properties = false;
       if (quic_args->GetBoolean(kQuicStoreServerConfigsInProperties,
                                 &quic_store_server_configs_in_properties)) {
-        context_builder->set_quic_max_server_configs_stored_in_properties(
-            net::kMaxQuicServersToPersist);
+        session_params->quic_max_server_configs_stored_in_properties =
+            net::kMaxQuicServersToPersist;
       }
 
       int quic_max_server_configs_stored_in_properties = 0;
       if (quic_args->GetInteger(
               kQuicMaxServerConfigsStoredInProperties,
               &quic_max_server_configs_stored_in_properties)) {
-        context_builder->set_quic_max_server_configs_stored_in_properties(
-            static_cast<size_t>(quic_max_server_configs_stored_in_properties));
+        session_params->quic_max_server_configs_stored_in_properties =
+            static_cast<size_t>(quic_max_server_configs_stored_in_properties);
       }
 
       int quic_idle_connection_timeout_seconds = 0;
       if (quic_args->GetInteger(kQuicIdleConnectionTimeoutSeconds,
                                 &quic_idle_connection_timeout_seconds)) {
-        context_builder->set_quic_idle_connection_timeout_seconds(
-            quic_idle_connection_timeout_seconds);
+        session_params->quic_idle_connection_timeout_seconds =
+            quic_idle_connection_timeout_seconds;
       }
 
       bool quic_close_sessions_on_ip_change = false;
       if (quic_args->GetBoolean(kQuicCloseSessionsOnIpChange,
                                 &quic_close_sessions_on_ip_change)) {
-        context_builder->set_quic_close_sessions_on_ip_change(
-            quic_close_sessions_on_ip_change);
+        session_params->quic_close_sessions_on_ip_change =
+            quic_close_sessions_on_ip_change;
       }
 
       bool quic_migrate_sessions_on_network_change = false;
       if (quic_args->GetBoolean(kQuicMigrateSessionsOnNetworkChange,
                                 &quic_migrate_sessions_on_network_change)) {
-        context_builder->set_quic_migrate_sessions_on_network_change(
-            quic_migrate_sessions_on_network_change);
+        session_params->quic_migrate_sessions_on_network_change =
+            quic_migrate_sessions_on_network_change;
       }
 
       std::string quic_user_agent_id;
       if (quic_args->GetString(kQuicUserAgentId, &quic_user_agent_id)) {
-        context_builder->set_quic_user_agent_id(quic_user_agent_id);
+        session_params->quic_user_agent_id = quic_user_agent_id;
       }
 
       bool quic_migrate_sessions_early = false;
       if (quic_args->GetBoolean(kQuicMigrateSessionsEarly,
                                 &quic_migrate_sessions_early)) {
-        context_builder->set_quic_migrate_sessions_early(
-            quic_migrate_sessions_early);
+        session_params->quic_migrate_sessions_early =
+            quic_migrate_sessions_early;
       }
 
       bool quic_disable_bidirectional_streams = false;
       if (quic_args->GetBoolean(kQuicDisableBidirectionalStreams,
                                 &quic_disable_bidirectional_streams)) {
-        context_builder->set_quic_disable_bidirectional_streams(
-            quic_disable_bidirectional_streams);
+        session_params->quic_disable_bidirectional_streams =
+            quic_disable_bidirectional_streams;
       }
 
       bool quic_race_cert_verification = false;
       if (quic_args->GetBoolean(kQuicRaceCertVerification,
                                 &quic_race_cert_verification)) {
-        context_builder->set_quic_race_cert_verification(
-            quic_race_cert_verification);
+        session_params->quic_race_cert_verification =
+            quic_race_cert_verification;
       }
 
     } else if (it.key() == kAsyncDnsFieldTrialName) {
@@ -434,12 +437,16 @@
     context_builder->DisableHttpCache();
   }
   context_builder->set_user_agent(user_agent);
-  context_builder->SetSpdyAndQuicEnabled(enable_spdy, enable_quic);
   context_builder->set_sdch_enabled(enable_sdch);
+  net::HttpNetworkSession::Params session_params;
+  session_params.enable_http2 = enable_spdy;
+  session_params.enable_quic = enable_quic;
   if (enable_quic)
-    context_builder->set_quic_user_agent_id(quic_user_agent_id);
+    session_params.quic_user_agent_id = quic_user_agent_id;
 
-  ParseAndSetExperimentalOptions(context_builder, net_log, file_task_runner);
+  ParseAndSetExperimentalOptions(context_builder, &session_params, net_log,
+                                 file_task_runner);
+  context_builder->set_http_network_session_params(session_params);
 
   std::unique_ptr<net::CertVerifier> cert_verifier;
   if (mock_cert_verifier) {
diff --git a/components/cronet/url_request_context_config.h b/components/cronet/url_request_context_config.h
index a7fc179..2c11f360 100644
--- a/components/cronet/url_request_context_config.h
+++ b/components/cronet/url_request_context_config.h
@@ -16,6 +16,7 @@
 #include "base/values.h"
 #include "net/base/hash_value.h"
 #include "net/cert/cert_verifier.h"
+#include "net/http/http_network_session.h"
 #include "net/nqe/effective_connection_type.h"
 
 namespace base {
@@ -186,6 +187,7 @@
   // the URLRequestContextConfig and URLRequestContextBuilder.
   void ParseAndSetExperimentalOptions(
       net::URLRequestContextBuilder* context_builder,
+      net::HttpNetworkSession::Params* session_params,
       net::NetLog* net_log,
       const scoped_refptr<base::SequencedTaskRunner>& file_task_runner);
 
@@ -193,7 +195,8 @@
   // experiments and their corresponding configuration options. The format
   // is a JSON object with the name of the experiment as the key, and the
   // configuration options as the value. An example:
-  //   {"experiment1": {"option1": "option_value1", "option2": "option_value2",
+  //   {"experiment1": {"option1": "option_value1", "option2":
+  //   "option_value2",
   //    ...}, "experiment2: {"option3", "option_value3", ...}, ...}
   const std::string experimental_options;
 
diff --git a/components/dom_distiller/content/renderer/distillability_agent.cc b/components/dom_distiller/content/renderer/distillability_agent.cc
index 4bee7c3e..deea34d 100644
--- a/components/dom_distiller/content/renderer/distillability_agent.cc
+++ b/components/dom_distiller/content/renderer/distillability_agent.cc
@@ -41,7 +41,8 @@
 // The number of updates can be from 0 to 2. See the tests in
 // "distillable_page_utils_browsertest.cc".
 // Most heuristics types only require one update after parsing.
-// Adaboost is the only one doing the second update, which is after loading.
+// Adaboost-based heuristics are the only ones doing the second update,
+// which is after loading.
 bool NeedToUpdate(bool is_loaded) {
   switch (GetDistillerHeuristicsType()) {
     case DistillerHeuristicsType::ALWAYS_TRUE:
@@ -49,6 +50,7 @@
     case DistillerHeuristicsType::OG_ARTICLE:
       return !is_loaded;
     case DistillerHeuristicsType::ADABOOST_MODEL:
+    case DistillerHeuristicsType::ALL_ARTICLES:
       return true;
     case DistillerHeuristicsType::NONE:
     default:
@@ -58,7 +60,8 @@
 
 // Returns whether this update is the last one for the page.
 bool IsLast(bool is_loaded) {
-  if (GetDistillerHeuristicsType() == DistillerHeuristicsType::ADABOOST_MODEL)
+  if (GetDistillerHeuristicsType() == DistillerHeuristicsType::ADABOOST_MODEL ||
+      GetDistillerHeuristicsType() == DistillerHeuristicsType::ALL_ARTICLES)
     return is_loaded;
 
   return true;
@@ -76,7 +79,8 @@
 bool IsDistillablePageAdaboost(WebDocument& doc,
                                const DistillablePageDetector* detector,
                                const DistillablePageDetector* long_page,
-                               bool is_last) {
+                               bool is_last,
+                               bool exclude_mobile) {
   WebDistillabilityFeatures features = doc.DistillabilityFeatures();
   GURL parsed_url(doc.Url());
   if (!parsed_url.is_valid()) {
@@ -145,7 +149,7 @@
   if (blacklisted) {
     return false;
   }
-  if (features.is_mobile_friendly) {
+  if (exclude_mobile && features.is_mobile_friendly) {
     return false;
   }
   return distillable && long_article;
@@ -158,9 +162,13 @@
     case DistillerHeuristicsType::OG_ARTICLE:
       return doc.DistillabilityFeatures().open_graph;
     case DistillerHeuristicsType::ADABOOST_MODEL:
-      return IsDistillablePageAdaboost(doc,
-          DistillablePageDetector::GetNewModel(),
-          DistillablePageDetector::GetLongPageModel(), is_last);
+      return IsDistillablePageAdaboost(
+          doc, DistillablePageDetector::GetNewModel(),
+          DistillablePageDetector::GetLongPageModel(), is_last, true);
+    case DistillerHeuristicsType::ALL_ARTICLES:
+      return IsDistillablePageAdaboost(
+          doc, DistillablePageDetector::GetNewModel(),
+          DistillablePageDetector::GetLongPageModel(), is_last, false);
     case DistillerHeuristicsType::NONE:
     default:
       return false;
diff --git a/components/dom_distiller/core/dom_distiller_switches.cc b/components/dom_distiller/core/dom_distiller_switches.cc
index 70a90f1..0d3976b 100644
--- a/components/dom_distiller/core/dom_distiller_switches.cc
+++ b/components/dom_distiller/core/dom_distiller_switches.cc
@@ -14,6 +14,7 @@
 
 namespace reader_mode_heuristics {
 const char kAdaBoost[] = "adaboost";
+const char kAllArticles[] = "allarticles";
 const char kOGArticle[] = "opengraph";
 const char kAlwaysTrue[] = "alwaystrue";
 const char kNone[] = "none";
diff --git a/components/dom_distiller/core/dom_distiller_switches.h b/components/dom_distiller/core/dom_distiller_switches.h
index 91952d7b..8216477 100644
--- a/components/dom_distiller/core/dom_distiller_switches.h
+++ b/components/dom_distiller/core/dom_distiller_switches.h
@@ -28,6 +28,7 @@
 
 namespace reader_mode_heuristics {
 extern const char kAdaBoost[];
+extern const char kAllArticles[];
 extern const char kOGArticle[];
 extern const char kAlwaysTrue[];
 extern const char kNone[];
diff --git a/components/dom_distiller/core/experiments.cc b/components/dom_distiller/core/experiments.cc
index 900daec..ba011f5 100644
--- a/components/dom_distiller/core/experiments.cc
+++ b/components/dom_distiller/core/experiments.cc
@@ -21,6 +21,9 @@
     if (switch_value == switches::reader_mode_heuristics::kAdaBoost) {
       return DistillerHeuristicsType::ADABOOST_MODEL;
     }
+    if (switch_value == switches::reader_mode_heuristics::kAllArticles) {
+      return DistillerHeuristicsType::ALL_ARTICLES;
+    }
     if (switch_value == switches::reader_mode_heuristics::kOGArticle) {
       return DistillerHeuristicsType::OG_ARTICLE;
     }
@@ -36,6 +39,10 @@
                          base::CompareCase::INSENSITIVE_ASCII)) {
       return DistillerHeuristicsType::ADABOOST_MODEL;
     }
+    if (base::StartsWith(group_name, "AllArticles",
+                         base::CompareCase::INSENSITIVE_ASCII)) {
+      return DistillerHeuristicsType::ALL_ARTICLES;
+    }
     if (base::StartsWith(group_name, "OGArticle",
                          base::CompareCase::INSENSITIVE_ASCII)) {
       return DistillerHeuristicsType::OG_ARTICLE;
diff --git a/components/dom_distiller/core/experiments.h b/components/dom_distiller/core/experiments.h
index 16edfcb..80d02fa8 100644
--- a/components/dom_distiller/core/experiments.h
+++ b/components/dom_distiller/core/experiments.h
@@ -6,14 +6,15 @@
 #define COMPONENTS_DOM_DISTILLER_CORE_EXPERIMENTS_H_
 
 namespace dom_distiller {
-  enum class DistillerHeuristicsType {
-    NONE,
-    OG_ARTICLE,
-    ADABOOST_MODEL,
-    ALWAYS_TRUE,
-  };
+enum class DistillerHeuristicsType {
+  NONE,
+  OG_ARTICLE,
+  ADABOOST_MODEL,
+  ALL_ARTICLES,
+  ALWAYS_TRUE,
+};
 
-  DistillerHeuristicsType GetDistillerHeuristicsType();
+DistillerHeuristicsType GetDistillerHeuristicsType();
 }
 
 #endif  // COMPONENTS_DOM_DISTILLER_CORE_EXPERIMENTS_H_
diff --git a/components/exo/surface_unittest.cc b/components/exo/surface_unittest.cc
index 983c036..d7301cc 100644
--- a/components/exo/surface_unittest.cc
+++ b/components/exo/surface_unittest.cc
@@ -6,7 +6,7 @@
 #include "base/bind.h"
 #include "cc/output/compositor_frame.h"
 #include "cc/quads/texture_draw_quad.h"
-#include "cc/surfaces/surface_manager.h"
+#include "cc/surfaces/frame_sink_manager.h"
 #include "cc/test/begin_frame_args_test.h"
 #include "cc/test/fake_external_begin_frame_source.h"
 #include "components/exo/buffer.h"
@@ -101,8 +101,10 @@
 
 const cc::CompositorFrame& GetFrameFromSurface(Surface* surface) {
   cc::SurfaceId surface_id = surface->GetSurfaceId();
-  cc::SurfaceManager* surface_manager =
-      aura::Env::GetInstance()->context_factory_private()->GetSurfaceManager();
+  cc::SurfaceManager* surface_manager = aura::Env::GetInstance()
+                                            ->context_factory_private()
+                                            ->GetFrameSinkManager()
+                                            ->surface_manager();
   const cc::CompositorFrame& frame =
       surface_manager->GetSurfaceForId(surface_id)->GetActiveFrame();
   return frame;
diff --git a/components/ntp_tiles/webui/resources/ntp_tiles_internals.html b/components/ntp_tiles/webui/resources/ntp_tiles_internals.html
index d97edb82..c407b726 100644
--- a/components/ntp_tiles/webui/resources/ntp_tiles_internals.html
+++ b/components/ntp_tiles/webui/resources/ntp_tiles_internals.html
@@ -116,7 +116,8 @@
           <td class="value" jsdisplay="source == 1">SUGGESTIONS_SERVICE</td>
           <td class="value" jsdisplay="source == 2">POPULAR</td>
           <td class="value" jsdisplay="source == 3">WHITELIST</td>
-          <td class="value" jsdisplay="source &gt; 3">???</td>
+          <td class="value" jsdisplay="source == 4">HOMEPAGE</td>
+          <td class="value" jsdisplay="source &gt; 4">???</td>
         </tr>
         <tr>
           <td class="detail">URL</td>
diff --git a/components/offline_pages/content/background_loader/background_loader_contents.cc b/components/offline_pages/content/background_loader/background_loader_contents.cc
index 199c7d2c..3a08f96 100644
--- a/components/offline_pages/content/background_loader/background_loader_contents.cc
+++ b/components/offline_pages/content/background_loader/background_loader_contents.cc
@@ -116,6 +116,25 @@
   return false;  // No permissions granted.
 }
 
+void BackgroundLoaderContents::AdjustPreviewsStateForNavigation(
+    content::PreviewsState* previews_state) {
+  DCHECK(previews_state);
+
+  // If previews are already disabled, do nothing.
+  if (*previews_state == content::PREVIEWS_OFF ||
+      *previews_state == content::PREVIEWS_NO_TRANSFORM) {
+    return;
+  }
+
+  if (*previews_state == content::PREVIEWS_UNSPECIFIED) {
+    *previews_state = content::PARTIAL_CONTENT_SAFE_PREVIEWS;
+  } else {
+    *previews_state &= content::PARTIAL_CONTENT_SAFE_PREVIEWS;
+    if (*previews_state == 0)
+      *previews_state = content::PREVIEWS_OFF;
+  }
+}
+
 BackgroundLoaderContents::BackgroundLoaderContents()
     : browser_context_(nullptr) {
   web_contents_.reset();
diff --git a/components/offline_pages/content/background_loader/background_loader_contents.h b/components/offline_pages/content/background_loader/background_loader_contents.h
index f4c9acd..ceb0641 100644
--- a/components/offline_pages/content/background_loader/background_loader_contents.h
+++ b/components/offline_pages/content/background_loader/background_loader_contents.h
@@ -76,6 +76,9 @@
                                   const GURL& security_origin,
                                   content::MediaStreamType type) override;
 
+  void AdjustPreviewsStateForNavigation(
+      content::PreviewsState* previews_state) override;
+
  private:
   friend class BackgroundLoaderContentsTest;
   friend class BackgroundLoaderContentsStub;
diff --git a/components/offline_pages/content/background_loader/background_loader_contents_unittest.cc b/components/offline_pages/content/background_loader/background_loader_contents_unittest.cc
index 3cfae2c..ddfe43a 100644
--- a/components/offline_pages/content/background_loader/background_loader_contents_unittest.cc
+++ b/components/offline_pages/content/background_loader/background_loader_contents_unittest.cc
@@ -146,4 +146,34 @@
       content::MediaStreamType::MEDIA_TAB_VIDEO_CAPTURE /* type */));
 }
 
+TEST_F(BackgroundLoaderContentsTest, AdjustPreviewsState) {
+  content::PreviewsState previews_state;
+
+  // If the state starts out as off or disabled, it should stay that way.
+  previews_state = content::PREVIEWS_OFF;
+  contents()->AdjustPreviewsStateForNavigation(&previews_state);
+  EXPECT_EQ(previews_state, content::PREVIEWS_OFF);
+  previews_state = content::PREVIEWS_NO_TRANSFORM;
+  contents()->AdjustPreviewsStateForNavigation(&previews_state);
+  EXPECT_EQ(previews_state, content::PREVIEWS_NO_TRANSFORM);
+
+  // If the state starts out as a state unfriendly to offlining, we should
+  // and out the unfriendly previews.
+  previews_state = content::SERVER_LOFI_ON | content::CLIENT_LOFI_ON;
+  contents()->AdjustPreviewsStateForNavigation(&previews_state);
+  EXPECT_EQ(previews_state, content::SERVER_LOFI_ON);
+
+  // If the state starts out as offlining friendly previews, we should preserve
+  // them.
+  previews_state = content::PARTIAL_CONTENT_SAFE_PREVIEWS;
+  contents()->AdjustPreviewsStateForNavigation(&previews_state);
+  EXPECT_EQ(previews_state, content::PARTIAL_CONTENT_SAFE_PREVIEWS);
+
+  // If there are only offlining unfriendly previews, they should all get turned
+  // off.
+  previews_state = content::CLIENT_LOFI_ON;
+  contents()->AdjustPreviewsStateForNavigation(&previews_state);
+  EXPECT_EQ(previews_state, content::PREVIEWS_OFF);
+}
+
 }  // namespace background_loader
diff --git a/components/omnibox/browser/autocomplete_controller.cc b/components/omnibox/browser/autocomplete_controller.cc
index d251027..ef525db 100644
--- a/components/omnibox/browser/autocomplete_controller.cc
+++ b/components/omnibox/browser/autocomplete_controller.cc
@@ -9,6 +9,7 @@
 #include <string>
 #include <utility>
 
+#include "base/feature_list.h"
 #include "base/format_macros.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
@@ -204,6 +205,7 @@
       stop_timer_duration_(OmniboxFieldTrial::StopTimerFieldTrialDuration()),
       done_(true),
       in_start_(false),
+      search_service_worker_signal_sent_(false),
       template_url_service_(provider_client_->GetTemplateURLService()) {
   provider_types &= ~OmniboxFieldTrial::GetDisabledProviderTypes();
   if (provider_types & AutocompleteProvider::TYPE_BOOKMARK)
@@ -341,6 +343,24 @@
   // need the edit model to update the display.
   UpdateResult(false, true);
 
+  // If the input looks like a query, send a signal predicting that the user is
+  // going to issue a search (either to the default search engine or to a
+  // keyword search engine, as indicated by the destination_url). This allows
+  // any associated service worker to start up early and reduce the latency of a
+  // resulting search. However, to avoid a potentially expensive operation, we
+  // only do this once per session. Additionally, a default match is expected to
+  // be available at this point but we check anyway to guard against an invalid
+  // dereference.
+  if (base::FeatureList::IsEnabled(
+          omnibox::kSpeculativeServiceWorkerStartOnQueryInput) &&
+      (input.type() == metrics::OmniboxInputType::QUERY) &&
+      !search_service_worker_signal_sent_ &&
+      (result_.default_match() != result_.end())) {
+    search_service_worker_signal_sent_ = true;
+    provider_client_->StartServiceWorker(
+        result_.default_match()->destination_url);
+  }
+
   if (!done_) {
     StartExpireTimer();
     StartStopTimer();
@@ -400,6 +420,8 @@
 }
 
 void AutocompleteController::ResetSession() {
+  search_service_worker_signal_sent_ = false;
+
   for (Providers::const_iterator i(providers_.begin()); i != providers_.end();
        ++i)
     (*i)->ResetSession();
diff --git a/components/omnibox/browser/autocomplete_controller.h b/components/omnibox/browser/autocomplete_controller.h
index 75934052..52c13fd 100644
--- a/components/omnibox/browser/autocomplete_controller.h
+++ b/components/omnibox/browser/autocomplete_controller.h
@@ -248,6 +248,11 @@
   // notifications until Start() has been invoked on all providers.
   bool in_start_;
 
+  // True if the signal predicting a likely search has already been sent to the
+  // service worker context during the current input session. False on
+  // controller creation and after |ResetSession| is called.
+  bool search_service_worker_signal_sent_;
+
   TemplateURLService* template_url_service_;
 
   DISALLOW_COPY_AND_ASSIGN(AutocompleteController);
diff --git a/components/omnibox/browser/autocomplete_provider_client.h b/components/omnibox/browser/autocomplete_provider_client.h
index 63e818b..16b7798 100644
--- a/components/omnibox/browser/autocomplete_provider_client.h
+++ b/components/omnibox/browser/autocomplete_provider_client.h
@@ -110,6 +110,12 @@
 
   virtual void PrefetchImage(const GURL& url) = 0;
 
+  // Sends a hint to the service worker context that navigation to
+  // |desination_url| is likely. On platforms where this is supported, the
+  // service worker lookup can be expensive so this method should only be
+  // called once per input session.
+  virtual void StartServiceWorker(const GURL& destination_url) {}
+
   // Called by |controller| when its results have changed and all providers are
   // done processing the autocomplete request. At the //chrome level, this
   // callback results in firing the
diff --git a/components/omnibox/browser/omnibox_edit_model.cc b/components/omnibox/browser/omnibox_edit_model.cc
index 7a8e739..4868f41 100644
--- a/components/omnibox/browser/omnibox_edit_model.cc
+++ b/components/omnibox/browser/omnibox_edit_model.cc
@@ -149,6 +149,7 @@
     if (MaybePrependKeyword(display_text).empty()) {
       base::AutoReset<bool> tmp(&in_revert_, true);
       view_->RevertAll();
+      view_->SelectAll(true);
     } else {
       InternalSetUserText(display_text);
     }
@@ -369,8 +370,13 @@
   keyword_.clear();
   is_keyword_hint_ = false;
   has_temporary_text_ = false;
+  size_t start, end;
+  view_->GetSelectionBounds(&start, &end);
+  // First home the cursor, so view of text is scrolled to left, then correct
+  // it. |SetCaretPos()| doesn't scroll the text, so doing that first wouldn't
+  // accomplish anything.
   view_->SetWindowTextAndCaretPos(permanent_text_, 0, false, true);
-  view_->SelectAll(true);
+  view_->SetCaretPos(std::min(permanent_text_.length(), start));
   client_->OnRevert();
 }
 
@@ -653,6 +659,9 @@
                               SEARCH_ENGINE_MAX);
   }
 
+  // Get the current text before we call RevertAll() which will clear it.
+  base::string16 current_text = view_->GetText();
+
   if (disposition != WindowOpenDisposition::NEW_BACKGROUND_TAB) {
     base::AutoReset<bool> tmp(&in_revert_, true);
     view_->RevertAll();  // Revert the box to its unedited state.
@@ -928,6 +937,7 @@
   // for ease of replacement, and matches other browsers.
   bool user_input_was_in_progress = user_input_in_progress_;
   view_->RevertAll();
+  view_->SelectAll(true);
 
   // If the user was in the midst of editing, don't cancel any underlying page
   // load.  This doesn't match IE or Firefox, but seems more correct.  Note that
diff --git a/components/omnibox/browser/omnibox_field_trial.cc b/components/omnibox/browser/omnibox_field_trial.cc
index 6357182..18b68d8 100644
--- a/components/omnibox/browser/omnibox_field_trial.cc
+++ b/components/omnibox/browser/omnibox_field_trial.cc
@@ -132,6 +132,13 @@
 const base::Feature kUIExperimentVerticalMargin{
     "OmniboxUIExperimentVerticalMargin", base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Feature used to enable speculatively starting a service worker associated
+// with the destination of the default match when the user's input looks like a
+// query.
+const base::Feature kSpeculativeServiceWorkerStartOnQueryInput{
+    "OmniboxSpeculativeServiceWorkerStartOnQueryInput",
+    base::FEATURE_DISABLED_BY_DEFAULT};
+
 }  // namespace omnibox
 
 namespace {
diff --git a/components/omnibox/browser/omnibox_field_trial.h b/components/omnibox/browser/omnibox_field_trial.h
index 43a8c60a..ded0f0c 100644
--- a/components/omnibox/browser/omnibox_field_trial.h
+++ b/components/omnibox/browser/omnibox_field_trial.h
@@ -42,6 +42,7 @@
 extern const base::Feature kUIExperimentNarrowDropdown;
 extern const base::Feature kUIExperimentVerticalLayout;
 extern const base::Feature kUIExperimentVerticalMargin;
+extern const base::Feature kSpeculativeServiceWorkerStartOnQueryInput;
 }
 
 // The set of parameters customizing the HUP scoring.
diff --git a/components/payments/content/android/java/src/org/chromium/components/payments/PaymentManifestDownloader.java b/components/payments/content/android/java/src/org/chromium/components/payments/PaymentManifestDownloader.java
index adcb3d29..95029f4 100644
--- a/components/payments/content/android/java/src/org/chromium/components/payments/PaymentManifestDownloader.java
+++ b/components/payments/content/android/java/src/org/chromium/components/payments/PaymentManifestDownloader.java
@@ -4,6 +4,8 @@
 
 package org.chromium.components.payments;
 
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.VisibleForTesting;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.content_public.browser.WebContents;
@@ -12,7 +14,7 @@
 
 /**
  * See comment in:
- * components/payments/content/android/payment_manifest_downloader.h
+ * components/payments/content/payment_manifest_downloader.h
  */
 @JNINamespace("payments")
 public class PaymentManifestDownloader {
@@ -39,16 +41,32 @@
         void onManifestDownloadFailure();
     }
 
-    private final WebContents mWebContents;
+    private long mNativeObject;
 
     /**
-     * Builds the downloader.
+     * Initializes the native downloader.
      *
-     * @param webContents The web contents to use as the context for the download. If this goes
-     *                    away, the download is cancelled.
+     * @param webContents The web contents to use as the context for the downloads. If this goes
+     *                    away, pending downloads are cancelled.
      */
-    public PaymentManifestDownloader(WebContents webContents) {
-        mWebContents = webContents;
+    public void initialize(WebContents webContents) {
+        ThreadUtils.assertOnUiThread();
+        assert mNativeObject == 0;
+        mNativeObject = nativeInit(webContents);
+    }
+
+    /** @return Whether the native downloader is initialized. */
+    public boolean isInitialized() {
+        ThreadUtils.assertOnUiThread();
+        return mNativeObject != 0;
+    }
+
+    /** Allows HTTP URLs. Should be used for testing only. */
+    @VisibleForTesting
+    public void allowHttpForTest() {
+        ThreadUtils.assertOnUiThread();
+        assert mNativeObject != 0;
+        nativeAllowHttpForTest(mNativeObject);
     }
 
     /**
@@ -58,17 +76,29 @@
      * @param callback   The callback to invoke when finished downloading.
      */
     public void downloadPaymentMethodManifest(URI methodName, ManifestDownloadCallback callback) {
-        nativeDownloadPaymentMethodManifest(mWebContents, methodName, callback);
+        ThreadUtils.assertOnUiThread();
+        assert mNativeObject != 0;
+        nativeDownloadPaymentMethodManifest(mNativeObject, methodName, callback);
     }
 
     /**
      * Downloads the web app manifest file asynchronously.
      *
-     * @param webAppmanifestUri The web app manifest URI with HTTPS scheme.
+     * @param webAppManifestUri The web app manifest URI with HTTPS scheme.
      * @param callback          The callback to invoke when finished downloading.
      */
     public void downloadWebAppManifest(URI webAppManifestUri, ManifestDownloadCallback callback) {
-        nativeDownloadWebAppManifest(mWebContents, webAppManifestUri, callback);
+        ThreadUtils.assertOnUiThread();
+        assert mNativeObject != 0;
+        nativeDownloadWebAppManifest(mNativeObject, webAppManifestUri, callback);
+    }
+
+    /** Destroys the native downloader. */
+    public void destroy() {
+        ThreadUtils.assertOnUiThread();
+        assert mNativeObject != 0;
+        nativeDestroy(mNativeObject);
+        mNativeObject = 0;
     }
 
     @CalledByNative
@@ -76,8 +106,12 @@
         return methodName.toString();
     }
 
-    private static native void nativeDownloadPaymentMethodManifest(
-            WebContents webContents, URI methodName, ManifestDownloadCallback callback);
-    private static native void nativeDownloadWebAppManifest(
-            WebContents webContents, URI webAppManifestUri, ManifestDownloadCallback callback);
+    private static native long nativeInit(WebContents webContents);
+    private native void nativeAllowHttpForTest(long nativePaymentManifestDownloaderAndroid);
+    private native void nativeDownloadPaymentMethodManifest(
+            long nativePaymentManifestDownloaderAndroid, URI methodName,
+            ManifestDownloadCallback callback);
+    private native void nativeDownloadWebAppManifest(long nativePaymentManifestDownloaderAndroid,
+            URI webAppManifestUri, ManifestDownloadCallback callback);
+    private native void nativeDestroy(long nativePaymentManifestDownloaderAndroid);
 }
diff --git a/components/payments/content/android/payment_manifest_downloader_android.cc b/components/payments/content/android/payment_manifest_downloader_android.cc
index b7931b41..d6503d9 100644
--- a/components/payments/content/android/payment_manifest_downloader_android.cc
+++ b/components/payments/content/android/payment_manifest_downloader_android.cc
@@ -2,133 +2,116 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <memory>
-#include <utility>
+#include "components/payments/content/android/payment_manifest_downloader_android.h"
 
 #include "base/android/jni_string.h"
 #include "base/android/scoped_java_ref.h"
-#include "base/logging.h"
 #include "base/memory/ptr_util.h"
-#include "components/payments/content/payment_manifest_downloader.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/browser/web_contents.h"
 #include "jni/PaymentManifestDownloader_jni.h"
 #include "net/url_request/url_request_context_getter.h"
 #include "url/gurl.h"
-#include "url/url_constants.h"
 
 namespace payments {
 namespace {
 
-class SelfDeletingDownloadDelegate
-    : public PaymentManifestDownloader::Delegate {
+class DownloadCallback {
  public:
-  explicit SelfDeletingDownloadDelegate(
-      const base::android::JavaParamRef<jobject>& jcallback)
-      : jcallback_(jcallback), is_downloading_payment_method_manifest_(true) {}
+  DownloadCallback(const base::android::JavaParamRef<jobject>& jcallback)
+      : jcallback_(jcallback) {}
 
-  void set_downloader(std::unique_ptr<PaymentManifestDownloader> downloader) {
-    downloader_ = std::move(downloader);
-  }
+  ~DownloadCallback() {}
 
-  void DownloadPaymentMethodManifest() {
-    is_downloading_payment_method_manifest_ = true;
-    downloader_->DownloadPaymentMethodManifest();
-  }
-
-  void DownloadWebAppManifest() {
-    is_downloading_payment_method_manifest_ = false;
-    downloader_->DownloadWebAppManifest();
-  }
-
-  // PaymentManifestDownloader::Delegate
-  void OnManifestDownloadSuccess(const std::string& content) override {
+  void OnPaymentMethodManifestDownload(const std::string& content) {
     JNIEnv* env = base::android::AttachCurrentThread();
-    if (is_downloading_payment_method_manifest_) {
+
+    if (content.empty()) {
+      Java_ManifestDownloadCallback_onManifestDownloadFailure(env, jcallback_);
+    } else {
       Java_ManifestDownloadCallback_onPaymentMethodManifestDownloadSuccess(
           env, jcallback_,
           base::android::ConvertUTF8ToJavaString(env, content));
+    }
+  }
+
+  void OnWebAppManifestDownload(const std::string& content) {
+    JNIEnv* env = base::android::AttachCurrentThread();
+
+    if (content.empty()) {
+      Java_ManifestDownloadCallback_onManifestDownloadFailure(env, jcallback_);
     } else {
       Java_ManifestDownloadCallback_onWebAppManifestDownloadSuccess(
           env, jcallback_,
           base::android::ConvertUTF8ToJavaString(env, content));
     }
-    delete this;
-  }
-
-  // PaymentManifestDownloader::Delegate
-  void OnManifestDownloadFailure() override {
-    Java_ManifestDownloadCallback_onManifestDownloadFailure(
-        base::android::AttachCurrentThread(), jcallback_);
-    delete this;
   }
 
  private:
-  ~SelfDeletingDownloadDelegate() override {}
-
   base::android::ScopedJavaGlobalRef<jobject> jcallback_;
-  bool is_downloading_payment_method_manifest_;
-  std::unique_ptr<PaymentManifestDownloader> downloader_;
 
-  DISALLOW_COPY_AND_ASSIGN(SelfDeletingDownloadDelegate);
+  DISALLOW_COPY_AND_ASSIGN(DownloadCallback);
 };
 
-SelfDeletingDownloadDelegate* BuildSelfDeletingDownloadDelegate(
-    JNIEnv* env,
-    const base::android::JavaParamRef<jobject>& jweb_contents,
-    const base::android::JavaParamRef<jobject>& juri,
-    const base::android::JavaParamRef<jobject>& jcallback) {
-  SelfDeletingDownloadDelegate* delegate =
-      new SelfDeletingDownloadDelegate(jcallback);
-
-  content::WebContents* web_contents =
-      content::WebContents::FromJavaWebContents(jweb_contents);
-  if (!web_contents) {
-    delegate->OnManifestDownloadFailure();
-    return nullptr;
-  }
-
-  GURL url(base::android::ConvertJavaStringToUTF8(
-      env, Java_PaymentManifestDownloader_getUriString(env, juri)));
-  DCHECK(url.is_valid());
-  DCHECK(url.SchemeIs(url::kHttpsScheme));
-
-  std::unique_ptr<PaymentManifestDownloader> downloader =
-      base::MakeUnique<PaymentManifestDownloader>(
-          content::BrowserContext::GetDefaultStoragePartition(
-              web_contents->GetBrowserContext())
-              ->GetURLRequestContext(),
-          url, delegate);
-  delegate->set_downloader(std::move(downloader));
-
-  return delegate;
-}
-
 }  // namespace
 
-void DownloadPaymentMethodManifest(
+PaymentManifestDownloaderAndroid::PaymentManifestDownloaderAndroid(
+    const scoped_refptr<net::URLRequestContextGetter>& context)
+    : downloader_(context) {}
+
+PaymentManifestDownloaderAndroid::~PaymentManifestDownloaderAndroid() {}
+
+void PaymentManifestDownloaderAndroid::DownloadPaymentMethodManifest(
     JNIEnv* env,
-    const base::android::JavaParamRef<jclass>& jcaller,
-    const base::android::JavaParamRef<jobject>& jweb_contents,
-    const base::android::JavaParamRef<jobject>& jmethod_name,
+    const base::android::JavaParamRef<jobject>& jcaller,
+    const base::android::JavaParamRef<jobject>& juri,
     const base::android::JavaParamRef<jobject>& jcallback) {
-  SelfDeletingDownloadDelegate* delegate = BuildSelfDeletingDownloadDelegate(
-      env, jweb_contents, jmethod_name, jcallback);
-  if (delegate)
-    delegate->DownloadPaymentMethodManifest();
+  downloader_.DownloadPaymentMethodManifest(
+      GURL(base::android::ConvertJavaStringToUTF8(
+          env, Java_PaymentManifestDownloader_getUriString(env, juri))),
+      base::BindOnce(&DownloadCallback::OnPaymentMethodManifestDownload,
+                     base::MakeUnique<DownloadCallback>(jcallback)));
 }
 
-void DownloadWebAppManifest(
+void PaymentManifestDownloaderAndroid::DownloadWebAppManifest(
     JNIEnv* env,
-    const base::android::JavaParamRef<jclass>& jcaller,
-    const base::android::JavaParamRef<jobject>& jweb_contents,
-    const base::android::JavaParamRef<jobject>& jweb_app_manifest_uri,
+    const base::android::JavaParamRef<jobject>& jcaller,
+    const base::android::JavaParamRef<jobject>& juri,
     const base::android::JavaParamRef<jobject>& jcallback) {
-  SelfDeletingDownloadDelegate* delegate = BuildSelfDeletingDownloadDelegate(
-      env, jweb_contents, jweb_app_manifest_uri, jcallback);
-  if (delegate)
-    delegate->DownloadWebAppManifest();
+  downloader_.DownloadWebAppManifest(
+      GURL(base::android::ConvertJavaStringToUTF8(
+          env, Java_PaymentManifestDownloader_getUriString(env, juri))),
+      base::BindOnce(&DownloadCallback::OnWebAppManifestDownload,
+                     base::MakeUnique<DownloadCallback>(jcallback)));
+}
+
+void PaymentManifestDownloaderAndroid::Destroy(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& jcaller) {
+  delete this;
+}
+
+void PaymentManifestDownloaderAndroid::AllowHttpForTest(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& jcaller) {
+  downloader_.AllowHttpForTest();
+}
+
+// Static free function declared and called directly from java.
+// Caller owns the result. Returns 0 on error.
+static jlong Init(JNIEnv* env,
+                  const base::android::JavaParamRef<jclass>& jcaller,
+                  const base::android::JavaParamRef<jobject>& jweb_contents) {
+  content::WebContents* web_contents =
+      content::WebContents::FromJavaWebContents(jweb_contents);
+  if (!web_contents)
+    return 0;
+
+  return reinterpret_cast<jlong>(new PaymentManifestDownloaderAndroid(
+      content::BrowserContext::GetDefaultStoragePartition(
+          web_contents->GetBrowserContext())
+          ->GetURLRequestContext()));
 }
 
 }  // namespace payments
diff --git a/components/payments/content/android/payment_manifest_downloader_android.h b/components/payments/content/android/payment_manifest_downloader_android.h
new file mode 100644
index 0000000..cfbb4cb
--- /dev/null
+++ b/components/payments/content/android/payment_manifest_downloader_android.h
@@ -0,0 +1,56 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_PAYMENTS_CONTENT_ANDROID_PAYMENT_MANIFEST_DOWNLOADER_ANDROID_H_
+#define COMPONENTS_PAYMENTS_CONTENT_ANDROID_PAYMENT_MANIFEST_DOWNLOADER_ANDROID_H_
+
+#include <jni.h>
+
+#include "base/android/jni_android.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "components/payments/content/payment_manifest_downloader.h"
+
+namespace net {
+class URLRequestContextGetter;
+}
+
+namespace payments {
+
+// Android wrapper for the payment manifest downloader.
+class PaymentManifestDownloaderAndroid {
+ public:
+  explicit PaymentManifestDownloaderAndroid(
+      const scoped_refptr<net::URLRequestContextGetter>& context);
+  ~PaymentManifestDownloaderAndroid();
+
+  void DownloadPaymentMethodManifest(
+      JNIEnv* env,
+      const base::android::JavaParamRef<jobject>& jcaller,
+      const base::android::JavaParamRef<jobject>& juri,
+      const base::android::JavaParamRef<jobject>& jcallback);
+
+  void DownloadWebAppManifest(
+      JNIEnv* env,
+      const base::android::JavaParamRef<jobject>& jcaller,
+      const base::android::JavaParamRef<jobject>& juri,
+      const base::android::JavaParamRef<jobject>& jcallback);
+
+  // Deletes this object.
+  void Destroy(JNIEnv* env,
+               const base::android::JavaParamRef<jobject>& jcaller);
+
+  // Allows HTTP URLs. Should be used for testing only.
+  void AllowHttpForTest(JNIEnv* env,
+                        const base::android::JavaParamRef<jobject>& jcaller);
+
+ private:
+  PaymentManifestDownloader downloader_;
+
+  DISALLOW_COPY_AND_ASSIGN(PaymentManifestDownloaderAndroid);
+};
+
+}  // namespace payments
+
+#endif  // COMPONENTS_PAYMENTS_CONTENT_ANDROID_PAYMENT_MANIFEST_DOWNLOADER_ANDROID_H_
diff --git a/components/payments/content/android/payment_manifest_parser_android.cc b/components/payments/content/android/payment_manifest_parser_android.cc
index 2760790..a740ecb 100644
--- a/components/payments/content/android/payment_manifest_parser_android.cc
+++ b/components/payments/content/android/payment_manifest_parser_android.cc
@@ -5,9 +5,11 @@
 #include "components/payments/content/android/payment_manifest_parser_android.h"
 
 #include <stddef.h>
+#include <vector>
 
 #include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
+#include "base/android/scoped_java_ref.h"
 #include "base/bind.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
diff --git a/components/payments/content/android/payment_manifest_parser_android.h b/components/payments/content/android/payment_manifest_parser_android.h
index 5457f81d..65fc725 100644
--- a/components/payments/content/android/payment_manifest_parser_android.h
+++ b/components/payments/content/android/payment_manifest_parser_android.h
@@ -7,11 +7,7 @@
 
 #include <jni.h>
 
-#include <memory>
-#include <vector>
-
 #include "base/android/jni_android.h"
-#include "base/android/scoped_java_ref.h"
 #include "base/macros.h"
 #include "components/payments/content/payment_manifest_parser_host.h"
 
diff --git a/components/payments/content/payment_manifest_downloader.cc b/components/payments/content/payment_manifest_downloader.cc
index 15a8783..63c4e07 100644
--- a/components/payments/content/payment_manifest_downloader.cc
+++ b/components/payments/content/payment_manifest_downloader.cc
@@ -10,6 +10,8 @@
 
 #include "base/bind.h"
 #include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/stl_util.h"
 #include "base/strings/string_split.h"
 #include "components/data_use_measurement/core/data_use_user_data.h"
 #include "components/link_header_util/link_header_util.h"
@@ -24,44 +26,110 @@
 namespace payments {
 namespace {
 
-bool IsValidManifestUrl(const GURL& url) {
-  return url.is_valid() && url.SchemeIs(url::kHttpsScheme);
+GURL ParseResponseHeader(const net::URLFetcher* source) {
+  if (source->GetResponseCode() != net::HTTP_OK &&
+      source->GetResponseCode() != net::HTTP_NO_CONTENT) {
+    return GURL();
+  }
+
+  net::HttpResponseHeaders* headers = source->GetResponseHeaders();
+  if (!headers)
+    return GURL();
+
+  std::string link_header;
+  headers->GetNormalizedHeader("link", &link_header);
+  if (link_header.empty())
+    return GURL();
+
+  std::string payment_method_manifest_url;
+  std::unordered_map<std::string, base::Optional<std::string>> params;
+  for (const auto& value : link_header_util::SplitLinkHeader(link_header)) {
+    if (!link_header_util::ParseLinkHeaderValue(
+            value.first, value.second, &payment_method_manifest_url, &params)) {
+      continue;
+    }
+
+    auto rel = params.find("rel");
+    if (rel == params.end())
+      continue;
+
+    std::vector<std::string> rel_parts =
+        base::SplitString(rel->second.value_or(""), HTTP_LWS,
+                          base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+    if (base::ContainsValue(rel_parts, "payment-method-manifest"))
+      return source->GetOriginalURL().Resolve(payment_method_manifest_url);
+  }
+
+  return GURL();
+}
+
+std::string ParseResponseContent(const net::URLFetcher* source) {
+  std::string content;
+  if (source->GetResponseCode() != net::HTTP_OK)
+    return content;
+
+  bool success = source->GetResponseAsString(&content);
+  DCHECK(success);  // Whether the fetcher was set to store result as string.
+
+  return content;
 }
 
 }  // namespace
 
 PaymentManifestDownloader::PaymentManifestDownloader(
-    const scoped_refptr<net::URLRequestContextGetter>& context,
-    const GURL& url,
-    Delegate* delegate)
-    : context_(context),
-      url_(url),
-      delegate_(delegate),
-      is_downloading_http_link_header_(true) {
-  DCHECK(IsValidManifestUrl(url_));
-}
+    const scoped_refptr<net::URLRequestContextGetter>& context)
+    : context_(context), allow_http_for_test_(false) {}
 
 PaymentManifestDownloader::~PaymentManifestDownloader() {}
 
-void PaymentManifestDownloader::DownloadPaymentMethodManifest() {
-  DCHECK(!fetcher_);
-  is_downloading_http_link_header_ = true;
-  InitiateDownload(url_, net::URLFetcher::HEAD);
+void PaymentManifestDownloader::DownloadPaymentMethodManifest(
+    const GURL& url,
+    DownloadCallback callback) {
+  DCHECK(IsValidManifestUrl(url));
+  InitiateDownload(url, net::URLFetcher::HEAD, std::move(callback));
 }
 
-void PaymentManifestDownloader::DownloadWebAppManifest() {
-  DCHECK(!fetcher_);
-  is_downloading_http_link_header_ = false;
-  InitiateDownload(url_, net::URLFetcher::GET);
+void PaymentManifestDownloader::DownloadWebAppManifest(
+    const GURL& url,
+    DownloadCallback callback) {
+  DCHECK(IsValidManifestUrl(url));
+  InitiateDownload(url, net::URLFetcher::GET, std::move(callback));
+}
+
+void PaymentManifestDownloader::AllowHttpForTest() {
+  allow_http_for_test_ = true;
+}
+
+PaymentManifestDownloader::Download::Download() {}
+
+PaymentManifestDownloader::Download::~Download() {}
+
+void PaymentManifestDownloader::OnURLFetchComplete(
+    const net::URLFetcher* source) {
+  auto download_it = downloads_.find(source);
+  DCHECK(download_it != downloads_.end());
+
+  std::unique_ptr<Download> download = std::move(download_it->second);
+  downloads_.erase(download_it);
+
+  if (download->request_type == net::URLFetcher::HEAD) {
+    GURL url = ParseResponseHeader(source);
+    if (IsValidManifestUrl(url)) {
+      InitiateDownload(url, net::URLFetcher::GET,
+                       std::move(download->callback));
+    } else {
+      std::move(download->callback).Run(std::string());
+    }
+  } else {
+    std::move(download->callback).Run(ParseResponseContent(source));
+  }
 }
 
 void PaymentManifestDownloader::InitiateDownload(
     const GURL& url,
-    net::URLFetcher::RequestType request_type) {
-  if (!IsValidManifestUrl(url)) {
-    delegate_->OnManifestDownloadFailure();
-    return;
-  }
+    net::URLFetcher::RequestType request_type,
+    DownloadCallback callback) {
+  DCHECK(IsValidManifestUrl(url));
 
   net::NetworkTrafficAnnotationTag traffic_annotation =
       net::DefineNetworkTrafficAnnotation("payment_manifest_downloader", R"(
@@ -83,69 +151,31 @@
             "disable all payment apps to stop this feature."
           policy_exception_justification: "Not implemented."
         })");
-  fetcher_ = net::URLFetcher::Create(0 /* id */, url, request_type, this,
-                                     traffic_annotation);
+  std::unique_ptr<net::URLFetcher> fetcher = net::URLFetcher::Create(
+      0 /* id */, url, request_type, this, traffic_annotation);
   data_use_measurement::DataUseUserData::AttachToFetcher(
-      fetcher_.get(), data_use_measurement::DataUseUserData::PAYMENTS);
-  fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
-                         net::LOAD_DO_NOT_SAVE_COOKIES);
-  fetcher_->SetStopOnRedirect(true);
-  fetcher_->SetRequestContext(context_.get());
-  fetcher_->Start();
+      fetcher.get(), data_use_measurement::DataUseUserData::PAYMENTS);
+  fetcher->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
+                        net::LOAD_DO_NOT_SAVE_COOKIES);
+  fetcher->SetStopOnRedirect(true);
+  fetcher->SetRequestContext(context_.get());
+  fetcher->Start();
+
+  auto download = base::MakeUnique<Download>();
+  download->request_type = request_type;
+  download->fetcher = std::move(fetcher);
+  download->callback = std::move(callback);
+
+  const net::URLFetcher* identifier = download->fetcher.get();
+  auto insert_result =
+      downloads_.insert(std::make_pair(identifier, std::move(download)));
+  DCHECK(insert_result.second);  // Whether the insert has succeeded.
 }
 
-void PaymentManifestDownloader::OnURLFetchComplete(
-    const net::URLFetcher* source) {
-  if (source->GetResponseCode() != net::HTTP_OK) {
-    delegate_->OnManifestDownloadFailure();
-    return;
-  }
-
-  if (is_downloading_http_link_header_) {
-    is_downloading_http_link_header_ = false;
-
-    net::HttpResponseHeaders* headers = source->GetResponseHeaders();
-    if (!headers) {
-      delegate_->OnManifestDownloadFailure();
-      return;
-    }
-
-    std::string link_header;
-    headers->GetNormalizedHeader("link", &link_header);
-    if (!link_header.empty()) {
-      std::string payment_method_manifest_url;
-      std::unordered_map<std::string, base::Optional<std::string>> params;
-      for (const auto& value : link_header_util::SplitLinkHeader(link_header)) {
-        if (!link_header_util::ParseLinkHeaderValue(
-                value.first, value.second, &payment_method_manifest_url,
-                &params)) {
-          continue;
-        }
-
-        auto rel = params.find("rel");
-        if (rel == params.end())
-          continue;
-
-        std::vector<std::string> rel_parts =
-            base::SplitString(rel->second.value_or(""), HTTP_LWS,
-                              base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
-        if (std::find(rel_parts.begin(), rel_parts.end(),
-                      "payment-method-manifest") != rel_parts.end()) {
-          InitiateDownload(url_.Resolve(payment_method_manifest_url),
-                           net::URLFetcher::GET);
-          return;
-        }
-      }
-    }
-  } else {
-    std::string content;
-    if (source->GetResponseAsString(&content) && !content.empty()) {
-      delegate_->OnManifestDownloadSuccess(content);
-      return;
-    }
-  }
-
-  delegate_->OnManifestDownloadFailure();
+bool PaymentManifestDownloader::IsValidManifestUrl(const GURL& url) {
+  return url.is_valid() &&
+         (url.SchemeIs(url::kHttpsScheme) ||
+          (url.SchemeIs(url::kHttpScheme) && allow_http_for_test_));
 }
 
 }  // namespace payments
diff --git a/components/payments/content/payment_manifest_downloader.h b/components/payments/content/payment_manifest_downloader.h
index ac1a95b..6ef80d3b 100644
--- a/components/payments/content/payment_manifest_downloader.h
+++ b/components/payments/content/payment_manifest_downloader.h
@@ -5,9 +5,11 @@
 #ifndef COMPONENTS_PAYMENTS_CONTENT_PAYMENT_MANIFEST_DOWNLOADER_H_
 #define COMPONENTS_PAYMENTS_CONTENT_PAYMENT_MANIFEST_DOWNLOADER_H_
 
+#include <map>
 #include <memory>
 #include <string>
 
+#include "base/callback_forward.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "net/url_request/url_fetcher.h"
@@ -28,34 +30,23 @@
 // HTTP response codes are 200.
 class PaymentManifestDownloader : public net::URLFetcherDelegate {
  public:
-  // The interface for receiving the result of downloading a manifest.
-  class Delegate {
-   public:
-    // Called when a manifest has been successfully downloaded.
-    virtual void OnManifestDownloadSuccess(const std::string& content) = 0;
+  // Called on completed download of a manifest. Download failure results in
+  // empty contents. Failure to download the manifest can happen because of the
+  // following reasons:
+  //  - HTTP response code is not 200. (204 is also allowed for HEAD request.)
+  //  - HTTP GET on the manifest URL returns empty content.
+  //
+  // In the case of a payment method manifest download, can also be called
+  // when:
+  //  - HTTP response headers are absent.
+  //  - HTTP response headers do not contain Link headers.
+  //  - Link header does not contain rel="payment-method-manifest".
+  //  - Link header does not contain a valid URL.
+  using DownloadCallback = base::OnceCallback<void(const std::string&)>;
 
-    // Called when failed to download the manifest for any reason:
-    //  - HTTP response code is not 200.
-    //  - HTTP GET on the manifest URL returns empty content.
-    //
-    // In the case of a payment method manifest download, can also be called
-    // when:
-    //  - HTTP response headers are absent.
-    //  - HTTP response headers do not contain Link headers.
-    //  - Link header does not contain rel="payment-method-manifest".
-    //  - Link header does not contain a valid URL.
-    virtual void OnManifestDownloadFailure() = 0;
-
-   protected:
-    virtual ~Delegate() {}
-  };
-
-  // |delegate| should not be null and must outlive this object. |url| should be
-  // a valid URL with HTTPS scheme.
-  PaymentManifestDownloader(
-      const scoped_refptr<net::URLRequestContextGetter>& context,
-      const GURL& url,
-      Delegate* delegate);
+  // |delegate| should not be null and must outlive this object.
+  explicit PaymentManifestDownloader(
+      const scoped_refptr<net::URLRequestContextGetter>& context);
 
   ~PaymentManifestDownloader() override;
 
@@ -76,28 +67,48 @@
   //    The absolute location must use HTTPS scheme.
   //
   // 2) GET request for the payment method manifest file.
-  void DownloadPaymentMethodManifest();
+  //
+  // |url| should be a valid URL with HTTPS scheme.
+  void DownloadPaymentMethodManifest(const GURL& url,
+                                     DownloadCallback callback);
 
   // Download a web app manifest via a single HTTP request:
   //
   // 1) GET request for the payment method name.
-  void DownloadWebAppManifest();
+  //
+  // |url| should be a valid URL with HTTPS scheme.
+  void DownloadWebAppManifest(const GURL& url, DownloadCallback callback);
+
+  // Allows HTTP URLs. Should be used only for testing.
+  void AllowHttpForTest();
 
  private:
-  void InitiateDownload(const GURL& url,
-                        net::URLFetcher::RequestType request_type);
+  // Information about an ongoing download request.
+  struct Download {
+    Download();
+    ~Download();
+
+    net::URLFetcher::RequestType request_type;
+    std::unique_ptr<net::URLFetcher> fetcher;
+    DownloadCallback callback;
+  };
 
   // net::URLFetcherDelegate
   void OnURLFetchComplete(const net::URLFetcher* source) override;
 
+  void InitiateDownload(const GURL& url,
+                        net::URLFetcher::RequestType request_type,
+                        DownloadCallback callback);
+  bool IsValidManifestUrl(const GURL& url);
+
   scoped_refptr<net::URLRequestContextGetter> context_;
-  const GURL url_;
+  bool allow_http_for_test_;
 
-  // Non-owned. Never null. Outlives this object.
-  Delegate* delegate_;
-
-  bool is_downloading_http_link_header_;
-  std::unique_ptr<net::URLFetcher> fetcher_;
+  // Downloads are identified by net::URLFetcher pointers, because that's the
+  // only unique piece of information that OnURLFetchComplete() receives. Can't
+  // rely on the URL of the download, because of possible collision between HEAD
+  // and GET requests.
+  std::map<const net::URLFetcher*, std::unique_ptr<Download>> downloads_;
 
   DISALLOW_COPY_AND_ASSIGN(PaymentManifestDownloader);
 };
diff --git a/components/payments/content/payment_manifest_downloader_unittest.cc b/components/payments/content/payment_manifest_downloader_unittest.cc
index bc7d26b..a1266601 100644
--- a/components/payments/content/payment_manifest_downloader_unittest.cc
+++ b/components/payments/content/payment_manifest_downloader_unittest.cc
@@ -15,22 +15,21 @@
 namespace payments {
 namespace {
 
-class PaymentMethodManifestDownloaderTest
-    : public testing::Test,
-      public PaymentManifestDownloader::Delegate {
+class PaymentMethodManifestDownloaderTest : public testing::Test {
  public:
   PaymentMethodManifestDownloaderTest()
       : context_(new net::TestURLRequestContextGetter(
             base::ThreadTaskRunnerHandle::Get())),
-        downloader_(context_, GURL("https://bobpay.com"), this) {
-    downloader_.DownloadPaymentMethodManifest();
+        downloader_(context_) {
+    downloader_.DownloadPaymentMethodManifest(
+        GURL("https://bobpay.com"),
+        base::BindOnce(&PaymentMethodManifestDownloaderTest::OnManifestDownload,
+                       base::Unretained(this)));
   }
 
   ~PaymentMethodManifestDownloaderTest() override {}
 
-  // PaymentManifestDownloader::Delegate
-  MOCK_METHOD1(OnManifestDownloadSuccess, void(const std::string& content));
-  MOCK_METHOD0(OnManifestDownloadFailure, void());
+  MOCK_METHOD1(OnManifestDownload, void(const std::string& content));
 
   net::TestURLFetcher* fetcher() { return factory_.GetFetcherByID(0); }
 
@@ -46,7 +45,7 @@
 TEST_F(PaymentMethodManifestDownloaderTest, HttpHeadResponse404IsFailure) {
   fetcher()->set_response_code(404);
 
-  EXPECT_CALL(*this, OnManifestDownloadFailure());
+  EXPECT_CALL(*this, OnManifestDownload(std::string()));
 
   fetcher()->delegate()->OnURLFetchComplete(fetcher());
 }
@@ -54,7 +53,7 @@
 TEST_F(PaymentMethodManifestDownloaderTest, NoHttpHeadersIsFailure) {
   fetcher()->set_response_code(200);
 
-  EXPECT_CALL(*this, OnManifestDownloadFailure());
+  EXPECT_CALL(*this, OnManifestDownload(std::string()));
 
   fetcher()->delegate()->OnURLFetchComplete(fetcher());
 }
@@ -65,7 +64,7 @@
   fetcher()->set_response_headers(headers);
   fetcher()->set_response_code(200);
 
-  EXPECT_CALL(*this, OnManifestDownloadFailure());
+  EXPECT_CALL(*this, OnManifestDownload(std::string()));
 
   fetcher()->delegate()->OnURLFetchComplete(fetcher());
 }
@@ -77,7 +76,7 @@
   fetcher()->set_response_headers(headers);
   fetcher()->set_response_code(200);
 
-  EXPECT_CALL(*this, OnManifestDownloadFailure());
+  EXPECT_CALL(*this, OnManifestDownload(std::string()));
 
   fetcher()->delegate()->OnURLFetchComplete(fetcher());
 }
@@ -89,7 +88,7 @@
   fetcher()->set_response_headers(headers);
   fetcher()->set_response_code(200);
 
-  EXPECT_CALL(*this, OnManifestDownloadFailure());
+  EXPECT_CALL(*this, OnManifestDownload(std::string()));
 
   fetcher()->delegate()->OnURLFetchComplete(fetcher());
 }
@@ -101,7 +100,7 @@
   fetcher()->set_response_headers(headers);
   fetcher()->set_response_code(200);
 
-  EXPECT_CALL(*this, OnManifestDownloadFailure());
+  EXPECT_CALL(*this, OnManifestDownload(std::string()));
 
   fetcher()->delegate()->OnURLFetchComplete(fetcher());
 }
@@ -114,7 +113,7 @@
   fetcher()->set_response_headers(headers);
   fetcher()->set_response_code(200);
 
-  EXPECT_CALL(*this, OnManifestDownloadFailure());
+  EXPECT_CALL(*this, OnManifestDownload(std::string()));
 
   fetcher()->delegate()->OnURLFetchComplete(fetcher());
 }
@@ -128,7 +127,7 @@
   fetcher()->delegate()->OnURLFetchComplete(fetcher());
   fetcher()->set_response_code(404);
 
-  EXPECT_CALL(*this, OnManifestDownloadFailure());
+  EXPECT_CALL(*this, OnManifestDownload(std::string()));
 
   fetcher()->delegate()->OnURLFetchComplete(fetcher());
 }
@@ -142,7 +141,7 @@
   fetcher()->delegate()->OnURLFetchComplete(fetcher());
   fetcher()->set_response_code(200);
 
-  EXPECT_CALL(*this, OnManifestDownloadFailure());
+  EXPECT_CALL(*this, OnManifestDownload(std::string()));
 
   fetcher()->delegate()->OnURLFetchComplete(fetcher());
 }
@@ -157,7 +156,24 @@
   fetcher()->SetResponseString("manifest content");
   fetcher()->set_response_code(200);
 
-  EXPECT_CALL(*this, OnManifestDownloadSuccess("manifest content"));
+  EXPECT_CALL(*this, OnManifestDownload("manifest content"));
+
+  fetcher()->delegate()->OnURLFetchComplete(fetcher());
+}
+
+TEST_F(PaymentMethodManifestDownloaderTest, HeaderResponseCode204IsSuccess) {
+  scoped_refptr<net::HttpResponseHeaders> headers(
+      new net::HttpResponseHeaders(std::string()));
+  headers->AddHeader("Link: <manifest.json>; rel=payment-method-manifest");
+  fetcher()->set_response_headers(headers);
+  // HTTP code 204 means "no content", which is not a problem for an HTTP HEAD
+  // request.
+  fetcher()->set_response_code(204);
+  fetcher()->delegate()->OnURLFetchComplete(fetcher());
+  fetcher()->SetResponseString("manifest content");
+  fetcher()->set_response_code(200);
+
+  EXPECT_CALL(*this, OnManifestDownload("manifest content"));
 
   fetcher()->delegate()->OnURLFetchComplete(fetcher());
 }
@@ -197,27 +213,26 @@
   fetcher()->set_response_headers(headers);
   fetcher()->set_response_code(200);
 
-  EXPECT_CALL(*this, OnManifestDownloadFailure());
+  EXPECT_CALL(*this, OnManifestDownload(std::string()));
 
   fetcher()->delegate()->OnURLFetchComplete(fetcher());
 }
 
-class WebAppManifestDownloaderTest
-    : public testing::Test,
-      public PaymentManifestDownloader::Delegate {
+class WebAppManifestDownloaderTest : public testing::Test {
  public:
   WebAppManifestDownloaderTest()
       : context_(new net::TestURLRequestContextGetter(
             base::ThreadTaskRunnerHandle::Get())),
-        downloader_(context_, GURL("https://bobpay.com"), this) {
-    downloader_.DownloadWebAppManifest();
+        downloader_(context_) {
+    downloader_.DownloadWebAppManifest(
+        GURL("https://bobpay.com"),
+        base::BindOnce(&WebAppManifestDownloaderTest::OnManifestDownload,
+                       base::Unretained(this)));
   }
 
   ~WebAppManifestDownloaderTest() override {}
 
-  // PaymentManifestDownloader::Delegate
-  MOCK_METHOD1(OnManifestDownloadSuccess, void(const std::string& content));
-  MOCK_METHOD0(OnManifestDownloadFailure, void());
+  MOCK_METHOD1(OnManifestDownload, void(const std::string& content));
 
   net::TestURLFetcher* fetcher() { return factory_.GetFetcherByID(0); }
 
@@ -233,7 +248,7 @@
 TEST_F(WebAppManifestDownloaderTest, HttpGetResponse404IsFailure) {
   fetcher()->set_response_code(404);
 
-  EXPECT_CALL(*this, OnManifestDownloadFailure());
+  EXPECT_CALL(*this, OnManifestDownload(std::string()));
 
   fetcher()->delegate()->OnURLFetchComplete(fetcher());
 }
@@ -241,7 +256,7 @@
 TEST_F(WebAppManifestDownloaderTest, EmptyHttpGetResponseIsFailure) {
   fetcher()->set_response_code(200);
 
-  EXPECT_CALL(*this, OnManifestDownloadFailure());
+  EXPECT_CALL(*this, OnManifestDownload(std::string()));
 
   fetcher()->delegate()->OnURLFetchComplete(fetcher());
 }
@@ -250,7 +265,7 @@
   fetcher()->SetResponseString("manifest content");
   fetcher()->set_response_code(200);
 
-  EXPECT_CALL(*this, OnManifestDownloadSuccess("manifest content"));
+  EXPECT_CALL(*this, OnManifestDownload("manifest content"));
 
   fetcher()->delegate()->OnURLFetchComplete(fetcher());
 }
diff --git a/components/payments/content/payment_request_spec.cc b/components/payments/content/payment_request_spec.cc
index fce3e29..bc6a5a9 100644
--- a/components/payments/content/payment_request_spec.cc
+++ b/components/payments/content/payment_request_spec.cc
@@ -8,6 +8,7 @@
 
 #include "base/logging.h"
 #include "base/strings/utf_string_conversions.h"
+#include "components/payments/core/payment_instrument.h"
 #include "components/payments/core/payment_method_data.h"
 #include "components/payments/core/payment_request_data_util.h"
 #include "components/strings/grit/components_strings.h"
@@ -57,6 +58,68 @@
   return autofill::CreditCard::CARD_TYPE_UNKNOWN;
 }
 
+PaymentMethodData CreatePaymentMethodData(
+    const mojom::PaymentMethodDataPtr& method_data_entry) {
+  PaymentMethodData method_data;
+  method_data.supported_methods = method_data_entry->supported_methods;
+
+  // Transfer the supported basic card networks (visa, amex) and types
+  // (credit, debit).
+  for (const mojom::BasicCardNetwork& network :
+       method_data_entry->supported_networks) {
+    method_data.supported_networks.push_back(GetBasicCardNetworkName(network));
+  }
+  for (const mojom::BasicCardType& type : method_data_entry->supported_types) {
+    autofill::CreditCard::CardType card_type = GetBasicCardType(type);
+    method_data.supported_types.insert(card_type);
+  }
+  return method_data;
+}
+
+// Validates the |method_data| and fills |supported_card_networks_|,
+// |supported_card_networks_set_| and |basic_card_specified_networks_|.
+void PopulateValidatedMethodData(
+    const std::vector<PaymentMethodData>& method_data_vector,
+    std::vector<std::string>* supported_card_networks,
+    std::set<std::string>* basic_card_specified_networks,
+    std::set<std::string>* supported_card_networks_set,
+    std::set<autofill::CreditCard::CardType>* supported_card_types_set,
+    std::map<std::string, std::set<std::string>>* stringified_method_data) {
+  data_util::ParseBasicCardSupportedNetworks(method_data_vector,
+                                             supported_card_networks,
+                                             basic_card_specified_networks);
+  supported_card_networks_set->insert(supported_card_networks->begin(),
+                                      supported_card_networks->end());
+
+  data_util::ParseSupportedCardTypes(method_data_vector,
+                                     supported_card_types_set);
+}
+
+void PopulateValidatedMethodData(
+    const std::vector<mojom::PaymentMethodDataPtr>& method_data_mojom,
+    std::vector<std::string>* supported_card_networks,
+    std::set<std::string>* basic_card_specified_networks,
+    std::set<std::string>* supported_card_networks_set,
+    std::set<autofill::CreditCard::CardType>* supported_card_types_set,
+    std::map<std::string, std::set<std::string>>* stringified_method_data) {
+  std::vector<PaymentMethodData> method_data_vector;
+  method_data_vector.reserve(method_data_mojom.size());
+  for (const mojom::PaymentMethodDataPtr& method_data_entry :
+       method_data_mojom) {
+    for (const std::string& method : method_data_entry->supported_methods) {
+      (*stringified_method_data)[method].insert(
+          method_data_entry->stringified_data);
+    }
+
+    method_data_vector.push_back(CreatePaymentMethodData(method_data_entry));
+  }
+
+  PopulateValidatedMethodData(
+      method_data_vector, supported_card_networks,
+      basic_card_specified_networks, supported_card_networks_set,
+      supported_card_types_set, stringified_method_data);
+}
+
 }  // namespace
 
 const char kBasicCardMethodName[] = "basic-card";
@@ -74,7 +137,10 @@
   if (observer)
     AddObserver(observer);
   UpdateSelectedShippingOption(/*after_update=*/false);
-  PopulateValidatedMethodData(method_data);
+  PopulateValidatedMethodData(
+      method_data, &supported_card_networks_, &basic_card_specified_networks_,
+      &supported_card_networks_set_, &supported_card_types_set_,
+      &stringified_method_data_);
 }
 PaymentRequestSpec::~PaymentRequestSpec() {}
 
@@ -161,44 +227,60 @@
                      });
 }
 
-void PaymentRequestSpec::PopulateValidatedMethodData(
-    const std::vector<mojom::PaymentMethodDataPtr>& method_data_mojom) {
-  std::vector<PaymentMethodData> method_data_vector;
-  method_data_vector.reserve(method_data_mojom.size());
-  for (const mojom::PaymentMethodDataPtr& method_data_entry :
-       method_data_mojom) {
-    for (const std::string& method : method_data_entry->supported_methods) {
-      stringified_method_data_[method].insert(
-          method_data_entry->stringified_data);
-    }
+const mojom::PaymentItemPtr& PaymentRequestSpec::GetTotal(
+    PaymentInstrument* selected_instrument) const {
+  const mojom::PaymentDetailsModifierPtr* modifier =
+      GetApplicableModifier(selected_instrument);
+  return modifier ? (*modifier)->total : details().total;
+}
 
-    PaymentMethodData method_data;
-    method_data.supported_methods = method_data_entry->supported_methods;
-
-    // Transfer the supported basic card networks (visa, amex) and types
-    // (credit, debit).
-    for (const mojom::BasicCardNetwork& network :
-         method_data_entry->supported_networks) {
-      method_data.supported_networks.push_back(
-          GetBasicCardNetworkName(network));
-    }
-    for (const mojom::BasicCardType& type :
-         method_data_entry->supported_types) {
-      autofill::CreditCard::CardType card_type = GetBasicCardType(type);
-      method_data.supported_types.insert(card_type);
-    }
-
-    method_data_vector.push_back(std::move(method_data));
+std::vector<const mojom::PaymentItemPtr*> PaymentRequestSpec::GetDisplayItems(
+    PaymentInstrument* selected_instrument) const {
+  std::vector<const mojom::PaymentItemPtr*> display_items;
+  const mojom::PaymentDetailsModifierPtr* modifier =
+      GetApplicableModifier(selected_instrument);
+  for (const auto& item : details().display_items) {
+    display_items.push_back(&item);
   }
 
-  data_util::ParseBasicCardSupportedNetworks(method_data_vector,
-                                             &supported_card_networks_,
-                                             &basic_card_specified_networks_);
-  supported_card_networks_set_.insert(supported_card_networks_.begin(),
-                                      supported_card_networks_.end());
+  if (modifier) {
+    for (const auto& additional_item : (*modifier)->additional_display_items) {
+      display_items.push_back(&additional_item);
+    }
+  }
+  return display_items;
+}
 
-  data_util::ParseSupportedCardTypes(method_data_vector,
-                                     &supported_card_types_set_);
+const std::vector<mojom::PaymentShippingOptionPtr>&
+PaymentRequestSpec::GetShippingOptions() const {
+  return details().shipping_options;
+}
+
+const mojom::PaymentDetailsModifierPtr*
+PaymentRequestSpec::GetApplicableModifier(
+    PaymentInstrument* selected_instrument) const {
+  if (!selected_instrument)
+    return nullptr;
+
+  for (const auto& modifier : details().modifiers) {
+    std::vector<std::string> supported_networks;
+    std::set<autofill::CreditCard::CardType> supported_types;
+    // The following 3 are unused but required by PopulateValidatedMethodData.
+    std::set<std::string> basic_card_specified_networks;
+    std::set<std::string> supported_card_networks_set;
+    std::map<std::string, std::set<std::string>> stringified_method_data;
+    PopulateValidatedMethodData(
+        {CreatePaymentMethodData(modifier->method_data)}, &supported_networks,
+        &basic_card_specified_networks, &supported_card_networks_set,
+        &supported_types, &stringified_method_data);
+
+    if (selected_instrument->IsValidForModifier(
+            modifier->method_data->supported_methods, supported_types,
+            supported_networks)) {
+      return &modifier;
+    }
+  }
+  return nullptr;
 }
 
 void PaymentRequestSpec::UpdateSelectedShippingOption(bool after_update) {
diff --git a/components/payments/content/payment_request_spec.h b/components/payments/content/payment_request_spec.h
index 9170efaf..9930459 100644
--- a/components/payments/content/payment_request_spec.h
+++ b/components/payments/content/payment_request_spec.h
@@ -20,6 +20,8 @@
 
 namespace payments {
 
+class PaymentInstrument;
+
 // Identifier for the basic card payment method in the PaymentMethodData.
 extern const char kBasicCardMethodName[];
 
@@ -112,19 +114,30 @@
     return selected_shipping_option_error_;
   }
 
-  const mojom::PaymentDetails& details() const { return *details_.get(); }
-
   void StartWaitingForUpdateWith(UpdateReason reason);
-
   bool IsMixedCurrency() const;
 
   UpdateReason current_update_reason() const { return current_update_reason_; }
 
+  // Returns the total object of this payment request, taking into account the
+  // applicable modifier for |selected_instrument| if any.
+  const mojom::PaymentItemPtr& GetTotal(
+      PaymentInstrument* selected_instrument) const;
+  // Returns the display items for this payment request, taking into account the
+  // applicable modifier for |selected_instrument| if any.
+  std::vector<const mojom::PaymentItemPtr*> GetDisplayItems(
+      PaymentInstrument* selected_instrument) const;
+
+  const std::vector<mojom::PaymentShippingOptionPtr>& GetShippingOptions()
+      const;
+
  private:
-  // Validates the |method_data| and fills |supported_card_networks_|,
-  // |supported_card_networks_set_| and |basic_card_specified_networks_|.
-  void PopulateValidatedMethodData(
-      const std::vector<mojom::PaymentMethodDataPtr>& method_data);
+  // Returns the first applicable modifier in the Payment Request for the
+  // |selected_instrument|.
+  const mojom::PaymentDetailsModifierPtr* GetApplicableModifier(
+      PaymentInstrument* selected_instrument) const;
+
+  const mojom::PaymentDetails& details() const { return *details_.get(); }
 
   // Updates the |selected_shipping_option| based on the data passed to this
   // payment request by the website. This will set selected_shipping_option_ to
diff --git a/components/payments/content/payment_request_state.cc b/components/payments/content/payment_request_state.cc
index 1fefb16..f62ed9b 100644
--- a/components/payments/content/payment_request_state.cc
+++ b/components/payments/content/payment_request_state.cc
@@ -298,6 +298,9 @@
   journey_logger_->SetNumberOfSuggestionsShown(
       JourneyLogger::Section::SECTION_CREDIT_CARDS,
       available_instruments().size());
+
+  if (!available_instruments().empty())
+    journey_logger_->SetUserHadInitialFormOfPayment();
 }
 
 void PaymentRequestState::SetDefaultProfileSelections() {
diff --git a/components/payments/core/autofill_payment_instrument.cc b/components/payments/core/autofill_payment_instrument.cc
index c18e195..2b096c3 100644
--- a/components/payments/core/autofill_payment_instrument.cc
+++ b/components/payments/core/autofill_payment_instrument.cc
@@ -4,6 +4,7 @@
 
 #include "components/payments/core/autofill_payment_instrument.h"
 
+#include <algorithm>
 #include <memory>
 
 #include "base/json/json_writer.h"
@@ -122,6 +123,44 @@
       autofill::AutofillType(autofill::CREDIT_CARD_NAME_FULL), app_locale_);
 }
 
+bool AutofillPaymentInstrument::IsValidForModifier(
+    const std::vector<std::string>& method,
+    const std::set<autofill::CreditCard::CardType>& supported_types,
+    const std::vector<std::string>& supported_networks) const {
+  // This instrument only matches basic-card.
+  if (std::find(method.begin(), method.end(), "basic-card") == method.end())
+    return false;
+
+  // If supported_types is not specified and this instrument matches the method,
+  // the modifier is applicable. If supported_types is populated, it must
+  // contain this card's type to be applicable. The same is true for
+  // supported_networks.
+  bool is_supported_type =
+      supported_types.empty() ||
+      std::find(supported_types.begin(), supported_types.end(),
+                credit_card_.card_type()) != supported_types.end();
+
+  // supported_types may contain CARD_TYPE_UNKNOWN because of the parsing
+  // function but the modifiers shouldn't be applied since the website can't be
+  // sure that the instrument is an applicable card.
+  if (is_supported_type &&
+      credit_card_.card_type() ==
+          autofill::CreditCard::CardType::CARD_TYPE_UNKNOWN)
+    return false;
+
+  bool is_supported_network = supported_networks.empty();
+  if (!is_supported_network) {
+    std::string basic_card_network =
+        autofill::data_util::GetPaymentRequestData(credit_card_.network())
+            .basic_card_issuer_network;
+    is_supported_network =
+        std::find(supported_networks.begin(), supported_networks.end(),
+                  basic_card_network) != supported_networks.end();
+  }
+
+  return is_supported_type && is_supported_network;
+}
+
 void AutofillPaymentInstrument::OnFullCardRequestSucceeded(
     const autofill::CreditCard& card,
     const base::string16& cvc) {
diff --git a/components/payments/core/autofill_payment_instrument.h b/components/payments/core/autofill_payment_instrument.h
index c3b2944b..7ec5071 100644
--- a/components/payments/core/autofill_payment_instrument.h
+++ b/components/payments/core/autofill_payment_instrument.h
@@ -48,6 +48,10 @@
   void RecordUse() override;
   base::string16 GetLabel() const override;
   base::string16 GetSublabel() const override;
+  bool IsValidForModifier(
+      const std::vector<std::string>& method,
+      const std::set<autofill::CreditCard::CardType>& supported_types,
+      const std::vector<std::string>& supported_networks) const override;
 
   // autofill::payments::FullCardRequest::ResultDelegate:
   void OnFullCardRequestSucceeded(const autofill::CreditCard& card,
diff --git a/components/payments/core/journey_logger.cc b/components/payments/core/journey_logger.cc
index ccf337b3..ae2d4b6 100644
--- a/components/payments/core/journey_logger.cc
+++ b/components/payments/core/journey_logger.cc
@@ -106,6 +106,29 @@
   was_show_called_ = true;
 }
 
+void JourneyLogger::SetEventOccurred(Event event) {
+  events_ |= event;
+}
+
+void JourneyLogger::SetSelectedPaymentMethod(
+    SelectedPaymentMethod payment_method) {
+  payment_method_ = payment_method;
+}
+
+void JourneyLogger::SetRequestedInformation(bool requested_shipping,
+                                            bool requested_email,
+                                            bool requested_phone,
+                                            bool requested_name) {
+  // This method should only be called once per Payment Request.
+  DCHECK(requested_information_ == REQUESTED_INFORMATION_MAX);
+
+  requested_information_ =
+      (requested_shipping ? REQUESTED_INFORMATION_SHIPPING : 0) |
+      (requested_email ? REQUESTED_INFORMATION_EMAIL : 0) |
+      (requested_phone ? REQUESTED_INFORMATION_PHONE : 0) |
+      (requested_name ? REQUESTED_INFORMATION_NAME : 0);
+}
+
 void JourneyLogger::SetCompleted() {
   UMA_HISTOGRAM_BOOLEAN("PaymentRequest.CheckoutFunnel.Completed", true);
 
@@ -135,27 +158,8 @@
   UMA_HISTOGRAM_BOOLEAN("PaymentRequest.CheckoutFunnel.Initiated", true);
 }
 
-void JourneyLogger::SetEventOccurred(Event event) {
-  events_ |= event;
-}
-
-void JourneyLogger::SetSelectedPaymentMethod(
-    SelectedPaymentMethod payment_method) {
-  payment_method_ = payment_method;
-}
-
-void JourneyLogger::SetRequestedInformation(bool requested_shipping,
-                                            bool requested_email,
-                                            bool requested_phone,
-                                            bool requested_name) {
-  // This method should only be called once per Payment Request.
-  DCHECK(requested_information_ == REQUESTED_INFORMATION_MAX);
-
-  requested_information_ =
-      (requested_shipping ? REQUESTED_INFORMATION_SHIPPING : 0) |
-      (requested_email ? REQUESTED_INFORMATION_EMAIL : 0) |
-      (requested_phone ? REQUESTED_INFORMATION_PHONE : 0) |
-      (requested_name ? REQUESTED_INFORMATION_NAME : 0);
+void JourneyLogger::SetUserHadInitialFormOfPayment() {
+  user_had_initial_form_of_payment_ = true;
 }
 
 void JourneyLogger::RecordJourneyStatsHistograms(
@@ -249,6 +253,20 @@
         "EffectOnCompletion",
         completion_status, COMPLETION_STATUS_MAX);
   }
+
+  // Recond the metric about completion status based on whether the user
+  // initally had a form of payment on file.
+  if (user_had_initial_form_of_payment_) {
+    base::UmaHistogramEnumeration(
+        "PaymentRequest.UserHadInitialFormOfPayment."
+        "EffectOnCompletion",
+        completion_status, COMPLETION_STATUS_MAX);
+  } else {
+    base::UmaHistogramEnumeration(
+        "PaymentRequest.UserDidNotHaveInitialFormOfPayment."
+        "EffectOnCompletion",
+        completion_status, COMPLETION_STATUS_MAX);
+  }
 }
 
 void JourneyLogger::RecordCanMakePaymentStats(
diff --git a/components/payments/core/journey_logger.h b/components/payments/core/journey_logger.h
index 4fba129..143903fa 100644
--- a/components/payments/core/journey_logger.h
+++ b/components/payments/core/journey_logger.h
@@ -186,6 +186,10 @@
   // reason.
   void SetNotShown(NotShownReason reason);
 
+  // Records the fact that the user had a Payment Method on file at the start of
+  // the Payment Request.
+  void SetUserHadInitialFormOfPayment();
+
  private:
   static const int NUMBER_OF_SECTIONS = 3;
 
@@ -254,6 +258,7 @@
   bool was_can_make_payments_used_ = false;
   bool could_make_payment_ = false;
   bool was_show_called_ = false;
+  bool user_had_initial_form_of_payment_ = false;
   bool is_incognito_;
 
   // Accumulates the many events that have happened during the Payment Request.
diff --git a/components/payments/core/journey_logger_unittest.cc b/components/payments/core/journey_logger_unittest.cc
index 2fd288e..394960f 100644
--- a/components/payments/core/journey_logger_unittest.cc
+++ b/components/payments/core/journey_logger_unittest.cc
@@ -693,6 +693,85 @@
               testing::ContainerEq(base::HistogramTester::CountsMap()));
 }
 
+// Tests that the UserHadInitialFormOfPayment metric is correctly logged.
+TEST(JourneyLoggerTest,
+     RecordJourneyStatsHistograms_UserHadInitialFormOfPayment) {
+  base::HistogramTester histogram_tester;
+  JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
+                       /*ukm_recorder=*/nullptr);
+
+  // The merchant only requests payment information.
+  logger.SetRequestedInformation(
+      /*requested_shipping=*/false, /*requested_email=*/false,
+      /*requested_phone=*/false, /*requested_name=*/false);
+
+  // Simulate that the user had an inital form of payment.
+  logger.SetUserHadInitialFormOfPayment();
+
+  // Simulate that the Payment Request was shown to the user.
+  logger.SetShowCalled();
+
+  // Simulate that the the checkout is aborted.
+  logger.SetAborted(JourneyLogger::ABORT_REASON_OTHER);
+
+  histogram_tester.ExpectBucketCount(
+      "PaymentRequest.UserHadInitialFormOfPayment.EffectOnCompletion",
+      JourneyLogger::COMPLETION_STATUS_OTHER_ABORTED, 1);
+  histogram_tester.ExpectTotalCount(
+      "PaymentRequest.UserDidNotHaveInitialFormOfPayment.EffectOnCompletion",
+      0);
+}
+
+// Tests that the UserDidNotHaveInitialFormOfPayment metric is correctly logged.
+TEST(JourneyLoggerTest,
+     RecordJourneyStatsHistograms_UserDidNotHaveInitialFormOfPayment) {
+  base::HistogramTester histogram_tester;
+  JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
+                       /*ukm_recorder=*/nullptr);
+
+  // The merchant only requests payment information.
+  logger.SetRequestedInformation(
+      /*requested_shipping=*/false, /*requested_email=*/false,
+      /*requested_phone=*/false, /*requested_name=*/false);
+
+  // Simulate that the Payment Request was shown to the user.
+  logger.SetShowCalled();
+
+  // Simulate that the the checkout is aborted.
+  logger.SetAborted(JourneyLogger::ABORT_REASON_OTHER);
+
+  histogram_tester.ExpectBucketCount(
+      "PaymentRequest.UserDidNotHaveInitialFormOfPayment.EffectOnCompletion",
+      JourneyLogger::COMPLETION_STATUS_OTHER_ABORTED, 1);
+  histogram_tester.ExpectTotalCount(
+      "PaymentRequest.UserHadInitialFormOfPayment.EffectOnCompletion", 0);
+}
+
+// Tests that the InitialFormOfPayment metrics are only logged if the Payment
+// Request is shown.
+TEST(JourneyLoggerTest,
+     RecordJourneyStatsHistograms_InitialFormOfPayment_NotShown) {
+  base::HistogramTester histogram_tester;
+  JourneyLogger logger_with_fop(/*is_incognito=*/false, /*url=*/GURL(""),
+                                /*ukm_recorder=*/nullptr);
+  JourneyLogger logger_without_fop(/*is_incognito=*/false, /*url=*/GURL(""),
+                                   /*ukm_recorder=*/nullptr);
+
+  // Set that the user had an initial form of payment.
+  logger_with_fop.SetUserHadInitialFormOfPayment();
+
+  // Simulate that the the checkouts are aborted.
+  logger_with_fop.SetAborted(JourneyLogger::ABORT_REASON_OTHER);
+  logger_without_fop.SetAborted(JourneyLogger::ABORT_REASON_ABORTED_BY_USER);
+
+  // There should be no logs for the two metrics.
+  histogram_tester.ExpectTotalCount(
+      "PaymentRequest.UserDidNotHaveInitialFormOfPayment.EffectOnCompletion",
+      0);
+  histogram_tester.ExpectTotalCount(
+      "PaymentRequest.UserHadInitialFormOfPayment.EffectOnCompletion", 0);
+}
+
 // Tests that the metrics are logged correctly for two simultaneous Payment
 // Requests.
 TEST(JourneyLoggerTest, RecordJourneyStatsHistograms_TwoPaymentRequests) {
diff --git a/components/payments/core/payment_instrument.h b/components/payments/core/payment_instrument.h
index f189c02..ecdc99e 100644
--- a/components/payments/core/payment_instrument.h
+++ b/components/payments/core/payment_instrument.h
@@ -7,9 +7,11 @@
 
 #include <set>
 #include <string>
+#include <vector>
 
 #include "base/macros.h"
 #include "base/strings/string16.h"
+#include "components/autofill/core/browser/credit_card.h"
 
 namespace payments {
 
@@ -55,6 +57,13 @@
   virtual base::string16 GetLabel() const = 0;
   virtual base::string16 GetSublabel() const = 0;
 
+  // Returns true if this payment instrument can be used to fulfill a request
+  // specifying |method| as a supported method of payment, false otherwise.
+  virtual bool IsValidForModifier(
+      const std::vector<std::string>& method,
+      const std::set<autofill::CreditCard::CardType>& supported_types,
+      const std::vector<std::string>& supported_networks) const = 0;
+
   const std::string& method_name() const { return method_name_; }
   int icon_resource_id() const { return icon_resource_id_; }
   Type type() { return type_; }
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 03305bf..99f9c91f 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -5394,7 +5394,7 @@
       'id': 127,
       'caption': '''Enable metrics reporting''',
       'tags': ['admin-sharing'],
-      'desc': '''Controls whether usage metrics are reported back to Google. If set to true, <ph name="PRODUCT_OS_NAME">$2<ex>Google Chrome OS</ex></ph> will report usage metrics. If not configured or set to false, metrics reporting will be disabled.''',
+      'desc': '''Controls whether usage metrics and diagnostic data, including crash reports, are reported back to Google. If set to true, <ph name="PRODUCT_OS_NAME">$2<ex>Google Chrome OS</ex></ph> will report usage metrics and diagnostic data. If not configured or set to false, metrics and diagnostic data reporting will be disabled.''',
       'arc_support': 'This policy also controls Android usage and diagnostic data collection.',
     },
     {
diff --git a/components/safe_browsing/csd.proto b/components/safe_browsing/csd.proto
index e33f34c7..b561039 100644
--- a/components/safe_browsing/csd.proto
+++ b/components/safe_browsing/csd.proto
@@ -483,7 +483,8 @@
   // Fields 19-21 are reserved for server-side use and are never sent by the
   // client.
 
-  // A binary contained in an archive (e.g., a .zip archive).
+  // A binary or archive contained in an archive (e.g., a .exe in a .zip
+  // archive, or a .zip inside a .zip).
   message ArchivedBinary {
     optional string file_basename = 1;
     optional DownloadType download_type = 2;
diff --git a/components/search_provider_logos/logo_cache.cc b/components/search_provider_logos/logo_cache.cc
index 70fbe7b..3c4b272 100644
--- a/components/search_provider_logos/logo_cache.cc
+++ b/components/search_provider_logos/logo_cache.cc
@@ -57,16 +57,16 @@
     : cache_directory_(cache_directory),
       metadata_is_valid_(false) {
   // The LogoCache can be constructed on any thread, as long as it's used
-  // on a single thread after construction.
-  thread_checker_.DetachFromThread();
+  // on a single sequence after construction.
+  DETACH_FROM_SEQUENCE(sequence_checker_);
 }
 
 LogoCache::~LogoCache() {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 }
 
 void LogoCache::UpdateCachedLogoMetadata(const LogoMetadata& metadata) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(metadata_);
   DCHECK_EQ(metadata_->fingerprint, metadata.fingerprint);
 
@@ -75,24 +75,24 @@
 }
 
 const LogoMetadata* LogoCache::GetCachedLogoMetadata() {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   ReadMetadataIfNeeded();
   return metadata_.get();
 }
 
 void LogoCache::SetCachedLogo(const EncodedLogo* logo) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   std::unique_ptr<LogoMetadata> metadata;
   if (logo) {
-    metadata.reset(new LogoMetadata(logo->metadata));
+    metadata = base::MakeUnique<LogoMetadata>(logo->metadata);
     logo_num_bytes_ = static_cast<int>(logo->encoded_image->size());
   }
   UpdateMetadata(std::move(metadata));
-  WriteLogo(logo ? logo->encoded_image : NULL);
+  WriteLogo(logo ? logo->encoded_image : nullptr);
 }
 
 std::unique_ptr<EncodedLogo> LogoCache::GetCachedLogo() {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   ReadMetadataIfNeeded();
   if (!metadata_)
diff --git a/components/search_provider_logos/logo_cache.h b/components/search_provider_logos/logo_cache.h
index e4a19af..da421c4 100644
--- a/components/search_provider_logos/logo_cache.h
+++ b/components/search_provider_logos/logo_cache.h
@@ -43,14 +43,14 @@
   // Updates the metadata for the cached logo.
   virtual void UpdateCachedLogoMetadata(const LogoMetadata& metadata);
 
-  // Returns metadata for the cached logo, or NULL if logo is cached.
+  // Returns metadata for the cached logo, or null if logo is cached.
   virtual const LogoMetadata* GetCachedLogoMetadata();
 
-  // Sets the cached logo and metadata. |logo| may be NULL, in which case the
+  // Sets the cached logo and metadata. |logo| may be null, in which case the
   // cached logo and metadata will be cleared.
   virtual void SetCachedLogo(const EncodedLogo* logo);
 
-  // Returns the cached logo, or NULL if no logo is cached or the cached logo is
+  // Returns the cached logo, or null if no logo is cached or the cached logo is
   // corrupt.
   virtual std::unique_ptr<EncodedLogo> GetCachedLogo();
 
@@ -62,7 +62,7 @@
   FRIEND_TEST_ALL_PREFIXES(LogoCacheTest, RetrieveCorruptMetadata);
   FRIEND_TEST_ALL_PREFIXES(LogoCacheTest, RetrieveCorruptLogo);
 
-  // Converts string |str| to a LogoMetadata object and returns it. Returns NULL
+  // Converts string |str| to a LogoMetadata object and returns it. Returns null
   // if |str| cannot be converted.
   static std::unique_ptr<LogoMetadata> LogoMetadataFromString(
       const std::string& str,
@@ -84,7 +84,7 @@
 
   // If the cached logo's metadata isn't available in memory (i.e.
   // |metadata_is_valid_| is false), reads it from disk and stores it in
-  // |metadata_|. If no logo is cached, |metadata_| will be updated to NULL.
+  // |metadata_|. If no logo is cached, |metadata_| will be updated to null.
   void ReadMetadataIfNeeded();
 
   // Writes the metadata for the cached logo to disk.
@@ -104,9 +104,9 @@
   // The directory in which the cached logo and metadata will be saved.
   base::FilePath cache_directory_;
 
-  // The metadata describing the cached logo, or NULL if no logo is cached. This
+  // The metadata describing the cached logo, or null if no logo is cached. This
   // value is meaningful iff |metadata_is_valid_| is true; otherwise, the
-  // metadata must be read from file and |metadata_| will be NULL.
+  // metadata must be read from file and |metadata_| will be null.
   // Note: Once read from file, metadata will be stored in memory indefinitely.
   std::unique_ptr<LogoMetadata> metadata_;
   bool metadata_is_valid_;
@@ -116,8 +116,8 @@
   // is complete and corresponds to the current metadata file.
   int logo_num_bytes_;
 
-  // Ensure LogoCache is only used on a single thread.
-  base::ThreadChecker thread_checker_;
+  // Ensure LogoCache is only used sequentially.
+  SEQUENCE_CHECKER(sequence_checker_);
 
   DISALLOW_COPY_AND_ASSIGN(LogoCache);
 };
diff --git a/components/search_provider_logos/logo_cache_unittest.cc b/components/search_provider_logos/logo_cache_unittest.cc
index 0de7e38..2e0ca57 100644
--- a/components/search_provider_logos/logo_cache_unittest.cc
+++ b/components/search_provider_logos/logo_cache_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/callback.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
+#include "base/memory/ptr_util.h"
 #include "base/run_loop.h"
 #include "base/time/time.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -55,17 +56,17 @@
   return encoded_image_str;
 }
 
-EncodedLogo GetExampleLogo() {
-  EncodedLogo logo;
-  logo.encoded_image = CreateExampleImage(837);
-  logo.metadata = GetExampleMetadata();
+std::unique_ptr<EncodedLogo> GetExampleLogo() {
+  auto logo = base::MakeUnique<EncodedLogo>();
+  logo->encoded_image = CreateExampleImage(837);
+  logo->metadata = GetExampleMetadata();
   return logo;
 }
 
-EncodedLogo GetExampleLogo2() {
-  EncodedLogo logo;
-  logo.encoded_image = CreateExampleImage(345);
-  logo.metadata = GetExampleMetadata2();
+std::unique_ptr<EncodedLogo> GetExampleLogo2() {
+  auto logo = base::MakeUnique<EncodedLogo>();
+  logo->encoded_image = CreateExampleImage(345);
+  logo->metadata = GetExampleMetadata2();
   return logo;
 }
 
@@ -106,27 +107,27 @@
   }
 
   void InitCache() {
-    cache_.reset(new LogoCache(
-        cache_parent_dir_.GetPath().Append(FILE_PATH_LITERAL("cache"))));
+    cache_ = base::MakeUnique<LogoCache>(
+        cache_parent_dir_.GetPath().Append(FILE_PATH_LITERAL("cache")));
   }
 
   void ExpectMetadata(const LogoMetadata* expected_metadata) {
     const LogoMetadata* retrieved_metadata = cache_->GetCachedLogoMetadata();
     if (expected_metadata) {
-      ASSERT_TRUE(retrieved_metadata != NULL);
+      ASSERT_TRUE(retrieved_metadata);
       ExpectMetadataEqual(*expected_metadata, *retrieved_metadata);
     } else {
-      ASSERT_TRUE(retrieved_metadata == NULL);
+      ASSERT_FALSE(retrieved_metadata);
     }
   }
 
   void ExpectLogo(const EncodedLogo* expected_logo) {
     std::unique_ptr<EncodedLogo> retrieved_logo(cache_->GetCachedLogo());
     if (expected_logo) {
-      ASSERT_TRUE(retrieved_logo.get() != NULL);
+      ASSERT_TRUE(retrieved_logo.get());
       ExpectLogosEqual(*expected_logo, *retrieved_logo);
     } else {
-      ASSERT_TRUE(retrieved_logo.get() == NULL);
+      ASSERT_FALSE(retrieved_logo.get());
     }
   }
 
@@ -157,7 +158,7 @@
   int logo_num_bytes = 33;
   std::unique_ptr<LogoMetadata> metadata =
       LogoCache::LogoMetadataFromString("", &logo_num_bytes);
-  ASSERT_TRUE(metadata.get() == NULL);
+  ASSERT_FALSE(metadata);
 
   LogoMetadata example_metadata = GetExampleMetadata2();
   std::string corrupt_str;
@@ -165,17 +166,17 @@
       example_metadata, logo_num_bytes, &corrupt_str);
   corrupt_str.append("@");
   metadata = LogoCache::LogoMetadataFromString(corrupt_str, &logo_num_bytes);
-  ASSERT_TRUE(metadata.get() == NULL);
+  ASSERT_FALSE(metadata);
 }
 
 TEST_F(LogoCacheTest, StoreAndRetrieveMetadata) {
   // Expect no metadata at first.
-  ExpectMetadata(NULL);
+  ExpectMetadata(nullptr);
 
   // Set initial metadata.
-  EncodedLogo logo = GetExampleLogo();
-  LogoMetadata& metadata = logo.metadata;
-  cache_->SetCachedLogo(&logo);
+  std::unique_ptr<EncodedLogo> logo = GetExampleLogo();
+  LogoMetadata& metadata = logo->metadata;
+  cache_->SetCachedLogo(logo.get());
   ExpectMetadata(&metadata);
 
   // Update metadata.
@@ -194,42 +195,42 @@
 
 TEST_F(LogoCacheTest, StoreAndRetrieveLogo) {
   // Expect no metadata at first.
-  ExpectLogo(NULL);
+  ExpectLogo(nullptr);
 
   // Set initial logo.
-  EncodedLogo logo = GetExampleLogo();
-  cache_->SetCachedLogo(&logo);
-  ExpectLogo(&logo);
+  std::unique_ptr<EncodedLogo> logo = GetExampleLogo();
+  cache_->SetCachedLogo(logo.get());
+  ExpectLogo(logo.get());
 
-  // Update logo to NULL.
-  cache_->SetCachedLogo(NULL);
-  ExpectLogo(NULL);
+  // Update logo to null.
+  cache_->SetCachedLogo(nullptr);
+  ExpectLogo(nullptr);
 
   // Read logo back from disk.
   SimulateRestart();
-  ExpectLogo(NULL);
+  ExpectLogo(nullptr);
 
   // Update logo.
   logo = GetExampleLogo2();
-  cache_->SetCachedLogo(&logo);
-  ExpectLogo(&logo);
+  cache_->SetCachedLogo(logo.get());
+  ExpectLogo(logo.get());
 
   // Read logo back from disk.
   SimulateRestart();
-  ExpectLogo(&logo);
+  ExpectLogo(logo.get());
 }
 
 TEST_F(LogoCacheTest, RetrieveCorruptMetadata) {
   // Set initial logo.
-  EncodedLogo logo = GetExampleLogo2();
-  cache_->SetCachedLogo(&logo);
-  ExpectLogo(&logo);
+  std::unique_ptr<EncodedLogo> logo = GetExampleLogo2();
+  cache_->SetCachedLogo(logo.get());
+  ExpectLogo(logo.get());
 
-  // Corrupt metadata and expect NULL for both logo and metadata.
+  // Corrupt metadata and expect null for both logo and metadata.
   SimulateRestart();
   ShortenFile(cache_->GetMetadataPath());
-  ExpectMetadata(NULL);
-  ExpectLogo(NULL);
+  ExpectMetadata(nullptr);
+  ExpectLogo(nullptr);
 
   // Ensure corrupt cache files are deleted.
   EXPECT_FALSE(base::PathExists(cache_->GetMetadataPath()));
@@ -238,16 +239,17 @@
 
 TEST_F(LogoCacheTest, RetrieveCorruptLogo) {
   // Set initial logo.
-  EncodedLogo logo = GetExampleLogo();
-  cache_->SetCachedLogo(&logo);
-  ExpectLogo(&logo);
+  std::unique_ptr<EncodedLogo> logo = GetExampleLogo();
+  cache_->SetCachedLogo(logo.get());
+  ExpectLogo(logo.get());
 
-  // Corrupt logo and expect NULL.
+  // Corrupt logo and expect nullptr.
   SimulateRestart();
   ShortenFile(cache_->GetLogoPath());
-  ExpectLogo(NULL);
-  // Once the logo is noticed to be NULL, the metadata should also be cleared.
-  ExpectMetadata(NULL);
+  ExpectLogo(nullptr);
+
+  // Once the logo is noticed to be null, the metadata should also be cleared.
+  ExpectMetadata(nullptr);
 
   // Ensure corrupt cache files are deleted.
   EXPECT_FALSE(base::PathExists(cache_->GetMetadataPath()));
diff --git a/components/search_provider_logos/logo_tracker.cc b/components/search_provider_logos/logo_tracker.cc
index d4d0bcf..021e52e 100644
--- a/components/search_provider_logos/logo_tracker.cc
+++ b/components/search_provider_logos/logo_tracker.cc
@@ -7,10 +7,12 @@
 #include <algorithm>
 #include <utility>
 
+#include "base/bind_helpers.h"
 #include "base/command_line.h"
 #include "base/message_loop/message_loop.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/task_runner_util.h"
+#include "base/task_scheduler/post_task.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/default_clock.h"
 #include "components/data_use_measurement/core/data_use_user_data.h"
@@ -62,33 +64,26 @@
   return logo_cache->GetCachedLogo();
 }
 
-void DeleteLogoCacheOnFileThread(LogoCache* logo_cache) {
-  delete logo_cache;
-}
-
 }  // namespace
 
 LogoTracker::LogoTracker(
     base::FilePath cached_logo_directory,
-    scoped_refptr<base::SequencedTaskRunner> file_task_runner,
-    scoped_refptr<base::TaskRunner> background_task_runner,
     scoped_refptr<net::URLRequestContextGetter> request_context_getter,
     std::unique_ptr<LogoDelegate> delegate)
     : is_idle_(true),
       is_cached_logo_valid_(false),
       logo_delegate_(std::move(delegate)),
-      logo_cache_(new LogoCache(cached_logo_directory)),
-      clock_(new base::DefaultClock()),
-      file_task_runner_(file_task_runner),
-      background_task_runner_(background_task_runner),
+      cache_task_runner_(base::CreateSequencedTaskRunnerWithTraits(
+          {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
+           base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})),
+      logo_cache_(new LogoCache(cached_logo_directory),
+                  base::OnTaskRunnerDeleter(cache_task_runner_)),
+      clock_(base::MakeUnique<base::DefaultClock>()),
       request_context_getter_(request_context_getter),
       weak_ptr_factory_(this) {}
 
 LogoTracker::~LogoTracker() {
   ReturnToIdle(kDownloadOutcomeNotTracked);
-  file_task_runner_->PostTask(
-      FROM_HERE, base::Bind(&DeleteLogoCacheOnFileThread, logo_cache_));
-  logo_cache_ = NULL;
 }
 
 void LogoTracker::SetServerAPI(
@@ -112,11 +107,9 @@
   if (is_idle_) {
     is_idle_ = false;
     base::PostTaskAndReplyWithResult(
-        file_task_runner_.get(),
-        FROM_HERE,
+        cache_task_runner_.get(), FROM_HERE,
         base::Bind(&GetLogoFromCacheOnFileThread,
-                   logo_cache_,
-                   logo_url_,
+                   base::Unretained(logo_cache_.get()), logo_url_,
                    clock_->Now()),
         base::Bind(&LogoTracker::OnCachedLogoRead,
                    weak_ptr_factory_.GetWeakPtr()));
@@ -131,9 +124,9 @@
 
 void LogoTracker::SetLogoCacheForTests(std::unique_ptr<LogoCache> cache) {
   DCHECK(cache);
-  file_task_runner_->PostTask(
-        FROM_HERE, base::Bind(&DeleteLogoCacheOnFileThread, logo_cache_));
-  logo_cache_ = cache.release();
+  // Call reset() and release() to keep the deleter of the |logo_cache_| member
+  // and run it on the old value.
+  logo_cache_.reset(cache.release());
 }
 
 void LogoTracker::SetClockForTests(std::unique_ptr<base::Clock> clock) {
@@ -191,18 +184,16 @@
 }
 
 void LogoTracker::SetCachedLogo(std::unique_ptr<EncodedLogo> logo) {
-  file_task_runner_->PostTask(
+  cache_task_runner_->PostTask(
       FROM_HERE,
-      base::Bind(&LogoCache::SetCachedLogo,
-                 base::Unretained(logo_cache_),
+      base::Bind(&LogoCache::SetCachedLogo, base::Unretained(logo_cache_.get()),
                  base::Owned(logo.release())));
 }
 
 void LogoTracker::SetCachedMetadata(const LogoMetadata& metadata) {
-  file_task_runner_->PostTask(FROM_HERE,
-                              base::Bind(&LogoCache::UpdateCachedLogoMetadata,
-                                         base::Unretained(logo_cache_),
-                                         metadata));
+  cache_task_runner_->PostTask(
+      FROM_HERE, base::Bind(&LogoCache::UpdateCachedLogoMetadata,
+                            base::Unretained(logo_cache_.get()), metadata));
 }
 
 void LogoTracker::FetchLogo() {
@@ -362,8 +353,10 @@
   bool from_http_cache = source->WasCached();
 
   bool* parsing_failed = new bool(false);
-  base::PostTaskAndReplyWithResult(
-      background_task_runner_.get(), FROM_HERE,
+  base::PostTaskWithTraitsAndReplyWithResult(
+      FROM_HERE,
+      {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
+       base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
       base::Bind(parse_logo_response_func_, base::Passed(&response),
                  response_time, parsing_failed),
       base::Bind(&LogoTracker::OnFreshLogoParsed,
diff --git a/components/search_provider_logos/logo_tracker.h b/components/search_provider_logos/logo_tracker.h
index af8a10b..1362f75 100644
--- a/components/search_provider_logos/logo_tracker.h
+++ b/components/search_provider_logos/logo_tracker.h
@@ -93,9 +93,6 @@
   // |cached_logo_directory| is the directory in which the cached logo and its
   // metadata should be saved.
   //
-  // |file_task_runner| is the SequencedTaskRunner that should be used to run
-  // file system operations.
-  //
   // |background_task_runner| is the TaskRunner that should be used to for
   // CPU-intensive background operations.
   //
@@ -103,8 +100,6 @@
   // the logo.
   explicit LogoTracker(
       base::FilePath cached_logo_directory,
-      scoped_refptr<base::SequencedTaskRunner> file_task_runner,
-      scoped_refptr<base::TaskRunner> background_task_runner,
       scoped_refptr<net::URLRequestContextGetter> request_context_getter,
       std::unique_ptr<LogoDelegate> delegate);
 
@@ -234,18 +229,16 @@
 
   std::unique_ptr<LogoDelegate> logo_delegate_;
 
-  // The cache used to persist the logo on disk. Used only on the file thread.
-  LogoCache* logo_cache_;
+  // The SequencedTaskRunner on which the cache lives.
+  scoped_refptr<base::SequencedTaskRunner> cache_task_runner_;
+
+  // The cache used to persist the logo on disk. Used only on a background
+  // SequencedTaskRunner.
+  std::unique_ptr<LogoCache, base::OnTaskRunnerDeleter> logo_cache_;
 
   // Clock used to determine current time. Can be overridden in tests.
   std::unique_ptr<base::Clock> clock_;
 
-  // The SequencedTaskRunner on which file system operations will be run.
-  scoped_refptr<base::SequencedTaskRunner> file_task_runner_;
-
-  // The TaskRunner on which the server's response will be parsed.
-  scoped_refptr<base::TaskRunner> background_task_runner_;
-
   // The URLRequestContextGetter used for network requests.
   scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
 
diff --git a/components/search_provider_logos/logo_tracker_unittest.cc b/components/search_provider_logos/logo_tracker_unittest.cc
index acd3c7b..346ee57 100644
--- a/components/search_provider_logos/logo_tracker_unittest.cc
+++ b/components/search_provider_logos/logo_tracker_unittest.cc
@@ -21,6 +21,7 @@
 #include "base/run_loop.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/stringprintf.h"
+#include "base/test/scoped_task_environment.h"
 #include "base/test/simple_test_clock.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
@@ -188,7 +189,7 @@
   Logo actual_logo;
   if (actual_encoded_logo)
     actual_logo = DecodeLogo(*actual_encoded_logo);
-  ExpectLogosEqual(expected_logo, actual_encoded_logo ? &actual_logo : NULL);
+  ExpectLogosEqual(expected_logo, actual_encoded_logo ? &actual_logo : nullptr);
 }
 
 ACTION_P(ExpectLogosEqualAction, expected_logo) {
@@ -234,18 +235,18 @@
     logo_->metadata = metadata;
   }
 
-  virtual const LogoMetadata* GetCachedLogoMetadataInternal() {
+  const LogoMetadata* GetCachedLogoMetadataInternal() {
     return metadata_.get();
   }
 
-  virtual void SetCachedLogoInternal(const EncodedLogo* logo) {
-    logo_.reset(logo ? new EncodedLogo(*logo) : NULL);
-    metadata_.reset(logo ? new LogoMetadata(logo->metadata) : NULL);
+  void SetCachedLogoInternal(const EncodedLogo* logo) {
+    logo_ = logo ? base::MakeUnique<EncodedLogo>(*logo) : nullptr;
+    metadata_ = logo ? base::MakeUnique<LogoMetadata>(logo->metadata) : nullptr;
   }
 
   std::unique_ptr<EncodedLogo> GetCachedLogo() override {
     OnGetCachedLogo();
-    return base::WrapUnique(logo_ ? new EncodedLogo(*logo_) : NULL);
+    return logo_ ? base::MakeUnique<EncodedLogo>(*logo_) : nullptr;
   }
 
  private:
@@ -274,7 +275,7 @@
   void ExpectFreshLogo(const Logo* expected_fresh_logo) {
     Mock::VerifyAndClearExpectations(this);
     EXPECT_CALL(*this, OnLogoAvailable(_, true)).Times(0);
-    EXPECT_CALL(*this, OnLogoAvailable(NULL, true));
+    EXPECT_CALL(*this, OnLogoAvailable(nullptr, true));
     EXPECT_CALL(*this, OnLogoAvailable(_, false))
         .WillOnce(ExpectLogosEqualAction(expected_fresh_logo));
     EXPECT_CALL(*this, OnObserverRemoved()).Times(1);
@@ -314,32 +315,29 @@
 class LogoTrackerTest : public ::testing::Test {
  protected:
   LogoTrackerTest()
-      : message_loop_(new base::MessageLoop()),
-        logo_url_("https://google.com/doodleoftheday?size=hp"),
+      : logo_url_("https://google.com/doodleoftheday?size=hp"),
         test_clock_(new base::SimpleTestClock()),
         logo_cache_(new NiceMock<MockLogoCache>()),
-        fake_url_fetcher_factory_(NULL) {
+        fake_url_fetcher_factory_(nullptr) {
     test_clock_->SetNow(base::Time::FromJsTime(INT64_C(1388686828000)));
     logo_tracker_ =
-        new LogoTracker(base::FilePath(), base::ThreadTaskRunnerHandle::Get(),
-                        base::ThreadTaskRunnerHandle::Get(),
-                        new net::TestURLRequestContextGetter(
-                            base::ThreadTaskRunnerHandle::Get()),
-                        std::unique_ptr<LogoDelegate>(new TestLogoDelegate()));
+        base::MakeUnique<LogoTracker>(base::FilePath(),
+                                      new net::TestURLRequestContextGetter(
+                                          base::ThreadTaskRunnerHandle::Get()),
+                                      base::MakeUnique<TestLogoDelegate>());
     logo_tracker_->SetServerAPI(
         logo_url_, base::Bind(&GoogleParseLogoResponse),
         base::Bind(&GoogleAppendQueryparamsToLogoURL, false));
-    logo_tracker_->SetClockForTests(std::unique_ptr<base::Clock>(test_clock_));
-    logo_tracker_->SetLogoCacheForTests(
-        std::unique_ptr<LogoCache>(logo_cache_));
+    logo_tracker_->SetClockForTests(base::WrapUnique(test_clock_));
+    logo_tracker_->SetLogoCacheForTests(base::WrapUnique(logo_cache_));
   }
 
-  virtual void TearDown() {
-    // logo_tracker_ owns logo_cache_, which gets destructed on the file thread
-    // after logo_tracker_'s destruction. Ensure that logo_cache_ is actually
-    // destructed before the test ends to make gmock happy.
-    delete logo_tracker_;
-    base::RunLoop().RunUntilIdle();
+  void TearDown() override {
+    // |logo_tracker_| owns |logo_cache_|, which gets destroyed on a background
+    // sequence after |logo_tracker_|s destruction. Ensure that |logo_cache_| is
+    // actually destroyed before the test ends to make gmock happy.
+    logo_tracker_.reset();
+    task_environment_.RunUntilIdle();
   }
 
   // Returns the response that the server would send for the given logo.
@@ -360,16 +358,16 @@
           net::URLRequestStatus::SUCCESS,
       net::HttpStatusCode response_code = net::HTTP_OK);
 
-  // Calls logo_tracker_->GetLogo() with listener_ and waits for the
+  // Calls logo_tracker_->GetLogo() with |observer_| and waits for the
   // asynchronous response(s).
   void GetLogo();
 
-  std::unique_ptr<base::MessageLoop> message_loop_;
+  base::test::ScopedTaskEnvironment task_environment_;
   GURL logo_url_;
   base::SimpleTestClock* test_clock_;
   NiceMock<MockLogoCache>* logo_cache_;
   net::FakeURLFetcherFactory fake_url_fetcher_factory_;
-  LogoTracker* logo_tracker_;
+  std::unique_ptr<LogoTracker> logo_tracker_;
   NiceMock<MockLogoObserver> observer_;
 };
 
@@ -401,7 +399,7 @@
 
 void LogoTrackerTest::GetLogo() {
   logo_tracker_->GetLogo(&observer_);
-  base::RunLoop().RunUntilIdle();
+  task_environment_.RunUntilIdle();
 }
 
 // Tests -----------------------------------------------------------------------
@@ -441,18 +439,18 @@
 TEST_F(LogoTrackerTest, EmptyCacheAndFailedDownload) {
   EXPECT_CALL(*logo_cache_, UpdateCachedLogoMetadata(_)).Times(0);
   EXPECT_CALL(*logo_cache_, SetCachedLogo(_)).Times(0);
-  EXPECT_CALL(*logo_cache_, SetCachedLogo(NULL)).Times(AnyNumber());
+  EXPECT_CALL(*logo_cache_, SetCachedLogo(nullptr)).Times(AnyNumber());
 
   SetServerResponse("server is borked");
-  observer_.ExpectCachedLogo(NULL);
+  observer_.ExpectCachedLogo(nullptr);
   GetLogo();
 
   SetServerResponse("", net::URLRequestStatus::FAILED, net::HTTP_OK);
-  observer_.ExpectCachedLogo(NULL);
+  observer_.ExpectCachedLogo(nullptr);
   GetLogo();
 
   SetServerResponse("", net::URLRequestStatus::SUCCESS, net::HTTP_BAD_GATEWAY);
-  observer_.ExpectCachedLogo(NULL);
+  observer_.ExpectCachedLogo(nullptr);
   GetLogo();
 }
 
@@ -506,7 +504,7 @@
   observer_.ExpectCachedLogo(&cached_logo);
   GetLogo();
 
-  EXPECT_TRUE(logo_cache_->GetCachedLogoMetadata() != NULL);
+  ASSERT_TRUE(logo_cache_->GetCachedLogoMetadata());
   EXPECT_EQ(fresh_logo.metadata.expiration_time,
             logo_cache_->GetCachedLogoMetadata()->expiration_time);
 
@@ -571,10 +569,10 @@
   SetServerResponseWhenFingerprint(cached_logo.metadata.fingerprint,
                                    ")]}' {\"update\":{}}");
 
-  logo_cache_->ExpectSetCachedLogo(NULL);
+  logo_cache_->ExpectSetCachedLogo(nullptr);
   EXPECT_CALL(*logo_cache_, UpdateCachedLogoMetadata(_)).Times(0);
   EXPECT_CALL(*logo_cache_, OnGetCachedLogo()).Times(AtMost(1));
-  observer_.ExpectCachedAndFreshLogos(&cached_logo, NULL);
+  observer_.ExpectCachedAndFreshLogos(&cached_logo, nullptr);
 
   GetLogo();
 }
@@ -587,9 +585,9 @@
 
   EXPECT_CALL(*logo_cache_, UpdateCachedLogoMetadata(_)).Times(0);
   EXPECT_CALL(*logo_cache_, SetCachedLogo(_)).Times(0);
-  EXPECT_CALL(*logo_cache_, SetCachedLogo(NULL)).Times(AnyNumber());
+  EXPECT_CALL(*logo_cache_, SetCachedLogo(nullptr)).Times(AnyNumber());
   EXPECT_CALL(*logo_cache_, OnGetCachedLogo()).Times(AtMost(1));
-  observer_.ExpectCachedLogo(NULL);
+  observer_.ExpectCachedLogo(nullptr);
   GetLogo();
 }
 
@@ -602,7 +600,7 @@
 
   const LogoMetadata* cached_metadata =
       logo_cache_->GetCachedLogoMetadata();
-  EXPECT_TRUE(cached_metadata != NULL);
+  ASSERT_TRUE(cached_metadata);
   EXPECT_FALSE(cached_metadata->can_show_after_expiration);
   EXPECT_EQ(test_clock_->Now() + time_to_live,
             cached_metadata->expiration_time);
@@ -616,7 +614,7 @@
 
   const LogoMetadata* cached_metadata =
       logo_cache_->GetCachedLogoMetadata();
-  EXPECT_TRUE(cached_metadata != NULL);
+  ASSERT_TRUE(cached_metadata);
   EXPECT_TRUE(cached_metadata->can_show_after_expiration);
   EXPECT_EQ(test_clock_->Now() + base::TimeDelta::FromDays(30),
             cached_metadata->expiration_time);
@@ -665,9 +663,9 @@
 
   EXPECT_CALL(*logo_cache_, UpdateCachedLogoMetadata(_)).Times(0);
   EXPECT_CALL(*logo_cache_, SetCachedLogo(_)).Times(0);
-  EXPECT_CALL(*logo_cache_, SetCachedLogo(NULL)).Times(AnyNumber());
+  EXPECT_CALL(*logo_cache_, SetCachedLogo(nullptr)).Times(AnyNumber());
   EXPECT_CALL(*logo_cache_, OnGetCachedLogo()).Times(AtMost(1));
-  observer_.ExpectCachedLogo(NULL);
+  observer_.ExpectCachedLogo(nullptr);
   GetLogo();
 }
 
@@ -681,9 +679,9 @@
 
   EXPECT_CALL(*logo_cache_, UpdateCachedLogoMetadata(_)).Times(0);
   EXPECT_CALL(*logo_cache_, SetCachedLogo(_)).Times(0);
-  EXPECT_CALL(*logo_cache_, SetCachedLogo(NULL)).Times(AnyNumber());
+  EXPECT_CALL(*logo_cache_, SetCachedLogo(nullptr)).Times(AnyNumber());
   EXPECT_CALL(*logo_cache_, OnGetCachedLogo()).Times(AtMost(1));
-  observer_.ExpectCachedLogo(NULL);
+  observer_.ExpectCachedLogo(nullptr);
   GetLogo();
 }
 
@@ -715,16 +713,16 @@
   const int kNumListeners = 10;
   std::vector<std::unique_ptr<MockLogoObserver>> listeners;
   for (int i = 0; i < kNumListeners; ++i) {
-    MockLogoObserver* listener = new MockLogoObserver();
+    auto listener = base::MakeUnique<MockLogoObserver>();
     listener->ExpectCachedAndFreshLogos(&cached_logo, &fresh_logo);
-    listeners.push_back(base::WrapUnique(listener));
+    listeners.push_back(std::move(listener));
   }
-  EnqueueObservers(logo_tracker_, listeners, 0);
+  EnqueueObservers(logo_tracker_.get(), listeners, 0);
 
   EXPECT_CALL(*logo_cache_, SetCachedLogo(_)).Times(AtMost(3));
   EXPECT_CALL(*logo_cache_, OnGetCachedLogo()).Times(AtMost(3));
 
-  base::RunLoop().RunUntilIdle();
+  task_environment_.RunUntilIdle();
 }
 
 TEST_F(LogoTrackerTest, DeleteObserversWhenLogoURLChanged) {
@@ -743,7 +741,7 @@
   listener2.ExpectFreshLogo(&logo);
   logo_tracker_->GetLogo(&listener2);
 
-  base::RunLoop().RunUntilIdle();
+  task_environment_.RunUntilIdle();
 }
 
 }  // namespace
diff --git a/components/signin/core/browser/fake_signin_manager.h b/components/signin/core/browser/fake_signin_manager.h
index e0168754..b3943e0 100644
--- a/components/signin/core/browser/fake_signin_manager.h
+++ b/components/signin/core/browser/fake_signin_manager.h
@@ -13,9 +13,7 @@
 #include "components/signin/core/browser/signin_manager.h"
 #include "components/signin/core/browser/signin_metrics.h"
 
-// SigninManager to use for testing. Tests should use the type
-// SigninManagerForTesting to ensure that the right type for their platform is
-// used.
+// SigninManager to use for testing.
 
 class FakeSigninManagerBase : public SigninManagerBase {
  public:
@@ -44,7 +42,7 @@
 
   void set_password(const std::string& password) { password_ = password; }
 
-  void SignIn(const std::string& account_id,
+  void SignIn(const std::string& gaia_id,
               const std::string& username,
               const std::string& password);
 
diff --git a/components/sync/test/fake_server/fake_server.cc b/components/sync/test/fake_server/fake_server.cc
index 8eb3dcc..9f490a1 100644
--- a/components/sync/test/fake_server/fake_server.cc
+++ b/components/sync/test/fake_server/fake_server.cc
@@ -401,13 +401,20 @@
   return id;
 }
 
+void FakeServer::OverrideResponseType(
+    ResponseTypeProvider response_type_override) {
+  response_type_override_ = std::move(response_type_override);
+}
+
 void FakeServer::BuildEntryResponseForSuccessfulCommit(
     const std::string& entity_id,
     sync_pb::CommitResponse_EntryResponse* entry_response) {
   EntityMap::const_iterator iter = entities_.find(entity_id);
   CHECK(iter != entities_.end());
   const FakeServerEntity& entity = *iter->second;
-  entry_response->set_response_type(sync_pb::CommitResponse::SUCCESS);
+  entry_response->set_response_type(response_type_override_
+                                        ? response_type_override_.Run(entity)
+                                        : sync_pb::CommitResponse::SUCCESS);
   entry_response->set_id_string(entity.id());
 
   if (entity.IsDeleted()) {
diff --git a/components/sync/test/fake_server/fake_server.h b/components/sync/test/fake_server/fake_server.h
index b37a00c..9cd8ed5 100644
--- a/components/sync/test/fake_server/fake_server.h
+++ b/components/sync/test/fake_server/fake_server.h
@@ -146,6 +146,18 @@
   // Returns the current FakeServer as a WeakPtr.
   base::WeakPtr<FakeServer> AsWeakPtr();
 
+  using ResponseTypeProvider =
+      base::RepeatingCallback<sync_pb::CommitResponse::ResponseType(
+          const FakeServerEntity& entity)>;
+
+  // Use this callback to generate response types for entities. They will still
+  // be "committed" and stored as normal, this only affects the response type
+  // the client sees. This allows tests to still inspect what the client has
+  // done, although not as useful of a mechanism for multi client tests. Care
+  // should be taken when failing responses, as the client will go into
+  // exponential backoff, which can cause tests to be slow or time out.
+  void OverrideResponseType(ResponseTypeProvider response_type_override);
+
  private:
   using EntityMap = std::map<std::string, std::unique_ptr<FakeServerEntity>>;
 
@@ -255,6 +267,8 @@
   sync_pb::ClientToServerMessage last_commit_message_;
   sync_pb::ClientToServerMessage last_getupdates_message_;
 
+  ResponseTypeProvider response_type_override_;
+
   // Used to verify that FakeServer is only used from one thread.
   base::ThreadChecker thread_checker_;
 
diff --git a/components/update_client/background_downloader_win.cc b/components/update_client/background_downloader_win.cc
index f82dc1c..fd391de 100644
--- a/components/update_client/background_downloader_win.cc
+++ b/components/update_client/background_downloader_win.cc
@@ -24,6 +24,7 @@
 #include "base/macros.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/sequenced_task_runner.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/win/scoped_co_mem.h"
 #include "components/update_client/utils.h"
@@ -320,52 +321,29 @@
   return jobs->empty() ? S_FALSE : S_OK;
 }
 
-// Compares the job creation time and returns true if the job creation time
-// is older than |num_days|.
-class JobCreationOlderThanDays {
- public:
-  explicit JobCreationOlderThanDays(int num_days) : num_days_(num_days) {}
-  bool operator()(const ComPtr<IBackgroundCopyJob>& job) const;
-
- private:
-  int num_days_;
-};
-
-bool JobCreationOlderThanDays::operator()(
-    const ComPtr<IBackgroundCopyJob>& job) const {
+bool JobCreationOlderThanDaysPredicate(ComPtr<IBackgroundCopyJob> job,
+                                       int num_days) {
   BG_JOB_TIMES times = {};
   HRESULT hr = job->GetTimes(&times);
   if (FAILED(hr))
     return false;
 
-  const base::TimeDelta time_delta(base::TimeDelta::FromDays(num_days_));
+  const base::TimeDelta time_delta(base::TimeDelta::FromDays(num_days));
   const base::Time creation_time(base::Time::FromFileTime(times.CreationTime));
 
   return creation_time + time_delta < base::Time::Now();
 }
 
-// Compares the url of a file in a job and returns true if the remote name
-// of any file in a job matches the argument.
-class JobFileUrlEqual {
- public:
-  explicit JobFileUrlEqual(const base::string16& remote_name)
-      : remote_name_(remote_name) {}
-  bool operator()(const ComPtr<IBackgroundCopyJob>& job) const;
-
- private:
-  base::string16 remote_name_;
-};
-
-bool JobFileUrlEqual::operator()(const ComPtr<IBackgroundCopyJob>& job) const {
+bool JobFileUrlEqualPredicate(ComPtr<IBackgroundCopyJob> job, const GURL& url) {
   std::vector<ComPtr<IBackgroundCopyFile>> files;
   HRESULT hr = GetFilesInJob(job, &files);
   if (FAILED(hr))
     return false;
 
   for (size_t i = 0; i != files.size(); ++i) {
-    ScopedCoMem<base::char16> name;
-    if (SUCCEEDED(files[i]->GetRemoteName(&name)) &&
-        remote_name_.compare(name) == 0)
+    ScopedCoMem<base::char16> remote_name;
+    if (SUCCEEDED(files[i]->GetRemoteName(&remote_name)) &&
+        url == GURL(base::StringPiece16(remote_name)))
       return true;
   }
 
@@ -426,8 +404,11 @@
   last_sweep = current_time;
 
   std::vector<ComPtr<IBackgroundCopyJob>> jobs;
-  HRESULT hr = FindBitsJobIf(JobCreationOlderThanDays(kPurgeStaleJobsAfterDays),
-                             bits_manager, &jobs);
+  HRESULT hr = FindBitsJobIf(
+      std::bind2nd(std::ptr_fun(JobCreationOlderThanDaysPredicate),
+                   kPurgeStaleJobsAfterDays),
+      bits_manager, &jobs);
+
   if (FAILED(hr))
     return hr;
 
@@ -766,8 +747,9 @@
 HRESULT BackgroundDownloader::CreateOrOpenJob(const GURL& url,
                                               ComPtr<IBackgroundCopyJob>* job) {
   std::vector<ComPtr<IBackgroundCopyJob>> jobs;
-  HRESULT hr = FindBitsJobIf(JobFileUrlEqual(base::SysUTF8ToWide(url.spec())),
-                             bits_manager_, &jobs);
+  HRESULT hr =
+      FindBitsJobIf(std::bind2nd(std::ptr_fun(JobFileUrlEqualPredicate), url),
+                    bits_manager_, &jobs);
   if (SUCCEEDED(hr) && !jobs.empty()) {
     *job = jobs.front();
     return hr;
diff --git a/components/viz/DEPS b/components/viz/DEPS
deleted file mode 100644
index f8654e9ef..0000000
--- a/components/viz/DEPS
+++ /dev/null
@@ -1,4 +0,0 @@
-include_rules = [
-  "+gpu",
-  "+mojo/public",
-]
diff --git a/components/viz/client/DEPS b/components/viz/client/DEPS
index 86ba24d6..3450aad8 100644
--- a/components/viz/client/DEPS
+++ b/components/viz/client/DEPS
@@ -1,4 +1,9 @@
 include_rules = [
   "+cc",
+  "-cc/blink",
+  "-cc/test",
+
+  "+mojo/public/cpp/bindings",
+  "+mojo/public/cpp/system",
   "+ui/gfx/geometry",
 ]
diff --git a/components/viz/common/DEPS b/components/viz/common/DEPS
index 6df009f..2455aca 100644
--- a/components/viz/common/DEPS
+++ b/components/viz/common/DEPS
@@ -1,16 +1,22 @@
-include_rules = [
-  "+ui/gfx/geometry",
-  "+services/ui/gpu/interfaces",
-  "+third_party/skia",
-]
-
 specific_include_rules = {
+  # DEPS for GLHelper and friends which are in the root common/ directory.
+  ".*\.(cc|h)": [
+    "+gpu/GLES2",
+    "+gpu/command_buffer/client",
+    "+gpu/command_buffer/common",
+    "+gpu/ipc/common",
+    "+ui/gfx/geometry",
+    "+services/ui/gpu/interfaces",
+    "+third_party/skia",
+  ],
   ".*_unittest\.cc": [
+    "+gpu/ipc/gl_in_process_context.h",
     "+media/base",
     "+ui/gfx",
     "+ui/gl",
   ],
   ".*_benchmark\.cc": [
+    "+gpu/ipc/gl_in_process_context.h",
     "+ui/gfx",
   ],
 }
diff --git a/components/viz/host/DEPS b/components/viz/host/DEPS
index c4eca43..b4d33612 100644
--- a/components/viz/host/DEPS
+++ b/components/viz/host/DEPS
@@ -1,6 +1,12 @@
 include_rules = [
   "+cc/ipc",
   "+cc/surfaces",
+  "+gpu/command_buffer/client",
+  "+gpu/command_buffer/common",
+  "+gpu/ipc/client",
+  "+gpu/ipc/common",
+  "+gpu/ipc/host",
+  "+mojo/public/cpp/bindings",
   "+services/ui/gpu/interfaces",
 ]
 
diff --git a/components/viz/service/display_embedder/DEPS b/components/viz/service/display_embedder/DEPS
index dba221e..f97559d 100644
--- a/components/viz/service/display_embedder/DEPS
+++ b/components/viz/service/display_embedder/DEPS
@@ -11,6 +11,7 @@
   "+gpu/command_buffer/service",
   "+gpu/ipc/client",
   "+gpu/ipc/common",
+  "+gpu/ipc/in_process_command_buffer.h",
   "+gpu/ipc/service",
   "+mojo/public/cpp/bindings",
   "+mojo/public/cpp/system",
diff --git a/components/viz/service/frame_sinks/DEPS b/components/viz/service/frame_sinks/DEPS
index de07c6b..8e8b659 100644
--- a/components/viz/service/frame_sinks/DEPS
+++ b/components/viz/service/frame_sinks/DEPS
@@ -4,4 +4,6 @@
   "+cc/surfaces",
   "+cc/scheduler",
   "+components/viz/display_embedder",
+  "+gpu/ipc/common",
+  "+mojo/public/cpp/bindings",
 ]
diff --git a/components/viz/service/frame_sinks/frame_sink_manager_impl.cc b/components/viz/service/frame_sinks/frame_sink_manager_impl.cc
index 121c13e..0f82baf 100644
--- a/components/viz/service/frame_sinks/frame_sink_manager_impl.cc
+++ b/components/viz/service/frame_sinks/frame_sink_manager_impl.cc
@@ -24,12 +24,12 @@
                    : cc::SurfaceManager::LifetimeType::SEQUENCES),
       display_provider_(display_provider),
       binding_(this) {
-  manager_.AddObserver(this);
+  manager_.surface_manager()->AddObserver(this);
 }
 
 FrameSinkManagerImpl::~FrameSinkManagerImpl() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  manager_.RemoveObserver(this);
+  manager_.surface_manager()->RemoveObserver(this);
 }
 
 void FrameSinkManagerImpl::BindPtrAndSetClient(
diff --git a/components/viz/service/frame_sinks/frame_sink_manager_impl.h b/components/viz/service/frame_sinks/frame_sink_manager_impl.h
index bf6102c..597b22c 100644
--- a/components/viz/service/frame_sinks/frame_sink_manager_impl.h
+++ b/components/viz/service/frame_sinks/frame_sink_manager_impl.h
@@ -14,7 +14,7 @@
 #include "base/threading/thread_checker.h"
 #include "cc/ipc/frame_sink_manager.mojom.h"
 #include "cc/surfaces/frame_sink_id.h"
-#include "cc/surfaces/surface_manager.h"
+#include "cc/surfaces/frame_sink_manager.h"
 #include "cc/surfaces/surface_observer.h"
 #include "components/viz/service/frame_sinks/gpu_compositor_frame_sink_delegate.h"
 #include "components/viz/service/viz_service_export.h"
@@ -42,7 +42,7 @@
                        DisplayProvider* display_provider);
   ~FrameSinkManagerImpl() override;
 
-  cc::SurfaceManager* surface_manager() { return &manager_; }
+  cc::FrameSinkManager* frame_sink_manager() { return &manager_; }
 
   // Binds |this| as a FrameSinkManager for a given |request|. This may
   // only be called once.
@@ -93,10 +93,10 @@
   void OnClientConnectionLost(const cc::FrameSinkId& frame_sink_id) override;
   void OnPrivateConnectionLost(const cc::FrameSinkId& frame_sink_id) override;
 
-  // SurfaceManager should be the first object constructed and the last object
+  // FrameSinkManager should be the first object constructed and the last object
   // destroyed in order to ensure that all other objects that depend on it have
   // access to a valid pointer for the entirety of their lifetimes.
-  cc::SurfaceManager manager_;
+  cc::FrameSinkManager manager_;
 
   // Provides a cc::Display for CreateRootCompositorFrameSink().
   DisplayProvider* const display_provider_;
diff --git a/components/viz/service/frame_sinks/gpu_compositor_frame_sink.cc b/components/viz/service/frame_sinks/gpu_compositor_frame_sink.cc
index 427cc86..6a75a191 100644
--- a/components/viz/service/frame_sinks/gpu_compositor_frame_sink.cc
+++ b/components/viz/service/frame_sinks/gpu_compositor_frame_sink.cc
@@ -10,7 +10,7 @@
 
 GpuCompositorFrameSink::GpuCompositorFrameSink(
     GpuCompositorFrameSinkDelegate* delegate,
-    cc::SurfaceManager* surface_manager,
+    cc::FrameSinkManager* frame_sink_manager,
     const cc::FrameSinkId& frame_sink_id,
     cc::mojom::CompositorFrameSinkRequest request,
     cc::mojom::CompositorFrameSinkPrivateRequest
@@ -19,7 +19,7 @@
     : delegate_(delegate),
       support_(cc::CompositorFrameSinkSupport::Create(
           this,
-          surface_manager,
+          frame_sink_manager,
           frame_sink_id,
           false /* is_root */,
           true /* handles_frame_sink_id_invalidation */,
diff --git a/components/viz/service/frame_sinks/gpu_compositor_frame_sink.h b/components/viz/service/frame_sinks/gpu_compositor_frame_sink.h
index b711cec..24a6ad2 100644
--- a/components/viz/service/frame_sinks/gpu_compositor_frame_sink.h
+++ b/components/viz/service/frame_sinks/gpu_compositor_frame_sink.h
@@ -28,7 +28,7 @@
  public:
   GpuCompositorFrameSink(
       GpuCompositorFrameSinkDelegate* delegate,
-      cc::SurfaceManager* surface_manager,
+      cc::FrameSinkManager* frame_sink_manager,
       const cc::FrameSinkId& frame_sink_id,
       cc::mojom::CompositorFrameSinkRequest request,
       cc::mojom::CompositorFrameSinkPrivateRequest private_request,
diff --git a/components/viz/service/frame_sinks/gpu_root_compositor_frame_sink.cc b/components/viz/service/frame_sinks/gpu_root_compositor_frame_sink.cc
index bb04a8f..26ad063 100644
--- a/components/viz/service/frame_sinks/gpu_root_compositor_frame_sink.cc
+++ b/components/viz/service/frame_sinks/gpu_root_compositor_frame_sink.cc
@@ -8,12 +8,13 @@
 
 #include "cc/surfaces/compositor_frame_sink_support.h"
 #include "cc/surfaces/display.h"
+#include "cc/surfaces/frame_sink_manager.h"
 
 namespace viz {
 
 GpuRootCompositorFrameSink::GpuRootCompositorFrameSink(
     GpuCompositorFrameSinkDelegate* delegate,
-    cc::SurfaceManager* surface_manager,
+    cc::FrameSinkManager* frame_sink_manager,
     const cc::FrameSinkId& frame_sink_id,
     std::unique_ptr<cc::Display> display,
     std::unique_ptr<cc::BeginFrameSource> begin_frame_source,
@@ -25,7 +26,7 @@
     : delegate_(delegate),
       support_(cc::CompositorFrameSinkSupport::Create(
           this,
-          surface_manager,
+          frame_sink_manager,
           frame_sink_id,
           true /* is_root */,
           true /* handles_frame_sink_id_invalidation */,
@@ -45,13 +46,13 @@
   compositor_frame_sink_private_binding_.set_connection_error_handler(
       base::Bind(&GpuRootCompositorFrameSink::OnPrivateConnectionLost,
                  base::Unretained(this)));
-  surface_manager->RegisterBeginFrameSource(display_begin_frame_source_.get(),
-                                            frame_sink_id);
-  display_->Initialize(this, surface_manager);
+  frame_sink_manager->RegisterBeginFrameSource(
+      display_begin_frame_source_.get(), frame_sink_id);
+  display_->Initialize(this, frame_sink_manager->surface_manager());
 }
 
 GpuRootCompositorFrameSink::~GpuRootCompositorFrameSink() {
-  support_->surface_manager()->UnregisterBeginFrameSource(
+  support_->frame_sink_manager()->UnregisterBeginFrameSource(
       display_begin_frame_source_.get());
 }
 
diff --git a/components/viz/service/frame_sinks/gpu_root_compositor_frame_sink.h b/components/viz/service/frame_sinks/gpu_root_compositor_frame_sink.h
index aee3e1ea..6d28a2f 100644
--- a/components/viz/service/frame_sinks/gpu_root_compositor_frame_sink.h
+++ b/components/viz/service/frame_sinks/gpu_root_compositor_frame_sink.h
@@ -21,7 +21,7 @@
 class BeginFrameSource;
 class CompositorFrameSinkSupport;
 class Display;
-class SurfaceManager;
+class FrameSinkManager;
 }
 
 namespace viz {
@@ -37,7 +37,7 @@
  public:
   GpuRootCompositorFrameSink(
       GpuCompositorFrameSinkDelegate* delegate,
-      cc::SurfaceManager* surface_manager,
+      cc::FrameSinkManager* frame_sink_manager,
       const cc::FrameSinkId& frame_sink_id,
       std::unique_ptr<cc::Display> display,
       std::unique_ptr<cc::BeginFrameSource> begin_frame_source,
diff --git a/content/browser/accessibility/browser_accessibility.cc b/content/browser/accessibility/browser_accessibility.cc
index 06f8106..99d26e5 100644
--- a/content/browser/accessibility/browser_accessibility.cc
+++ b/content/browser/accessibility/browser_accessibility.cc
@@ -128,6 +128,11 @@
   return false;
 }
 
+bool BrowserAccessibility::IsDocument() const {
+  return GetRole() == ui::AX_ROLE_ROOT_WEB_AREA ||
+         GetRole() == ui::AX_ROLE_WEB_AREA;
+}
+
 bool BrowserAccessibility::IsTextOnlyObject() const {
   return GetRole() == ui::AX_ROLE_STATIC_TEXT ||
          GetRole() == ui::AX_ROLE_LINE_BREAK ||
@@ -958,6 +963,14 @@
   return html_tag == "textarea";
 }
 
+// In general we should use IsEditField() instead if we want to check for
+// something that has a caret and the user can edit.
+// TODO(aleventhal) this name is confusing because it returns true for combobox,
+// and we should take a look at why a combobox is considered a text control. The
+// ARIA spec says a combobox would contain (but not be) a text field. It looks
+// like a mistake may have been made in that comboboxes have been considered a
+// kind of textbox. Find an appropriate name for this function, and consider
+// returning false for comboboxes it doesn't break existing websites.
 bool BrowserAccessibility::IsSimpleTextControl() const {
   // Time fields, color wells and spinner buttons might also use text fields as
   // constituent parts, but they are not considered text fields as a whole.
@@ -972,6 +985,11 @@
   }
 }
 
+bool BrowserAccessibility::IsEditField() const {
+  return GetRole() == ui::AX_ROLE_TEXT_FIELD ||
+         GetRole() == ui::AX_ROLE_SEARCH_BOX;
+}
+
 // Indicates if this object is at the root of a rich edit text control.
 bool BrowserAccessibility::IsRichTextControl() const {
   return HasState(ui::AX_STATE_RICHLY_EDITABLE) &&
diff --git a/content/browser/accessibility/browser_accessibility.h b/content/browser/accessibility/browser_accessibility.h
index b7a098d..6716a2e 100644
--- a/content/browser/accessibility/browser_accessibility.h
+++ b/content/browser/accessibility/browser_accessibility.h
@@ -99,6 +99,10 @@
   // Return true if this object is equal to or a descendant of |ancestor|.
   bool IsDescendantOf(const BrowserAccessibility* ancestor) const;
 
+  bool IsDocument() const;
+
+  bool IsEditField() const;
+
   // Returns true if this object is used only for representing text.
   bool IsTextOnlyObject() const;
 
diff --git a/content/browser/accessibility/browser_accessibility_com_win.cc b/content/browser/accessibility/browser_accessibility_com_win.cc
index 809a6ce4..57ff9025 100644
--- a/content/browser/accessibility/browser_accessibility_com_win.cc
+++ b/content/browser/accessibility/browser_accessibility_com_win.cc
@@ -2851,8 +2851,7 @@
   *num_children = owner()->PlatformChildCount();
   *unique_id = -owner()->unique_id();
 
-  if (owner()->GetRole() == ui::AX_ROLE_ROOT_WEB_AREA ||
-      owner()->GetRole() == ui::AX_ROLE_WEB_AREA) {
+  if (owner()->IsDocument()) {
     *node_type = NODETYPE_DOCUMENT;
   } else if (owner()->IsTextOnlyObject()) {
     *node_type = NODETYPE_TEXT;
@@ -3696,8 +3695,7 @@
     win_attributes_->ia2_attributes.push_back(L"valuetext:" + value);
   } else {
     // On Windows, the value of a document should be its url.
-    if (owner()->GetRole() == ui::AX_ROLE_ROOT_WEB_AREA ||
-        owner()->GetRole() == ui::AX_ROLE_WEB_AREA) {
+    if (owner()->IsDocument()) {
       value = base::UTF8ToUTF16(Manager()->GetTreeData().url);
     }
     // If this doesn't have a value and is linked then set its value to the url
@@ -4944,9 +4942,22 @@
   if (owner()->HasState(ui::AX_STATE_HORIZONTAL))
     ia2_state |= IA2_STATE_HORIZONTAL;
 
-  if (owner()->HasState(ui::AX_STATE_EDITABLE))
+  const bool is_editable = owner()->HasState(ui::AX_STATE_EDITABLE);
+  if (is_editable)
     ia2_state |= IA2_STATE_EDITABLE;
 
+  if (owner()->IsRichTextControl() || owner()->IsEditField()) {
+    // Support multi/single line states if root editable or appropriate role.
+    // We support the edit box roles even if the area is not actually editable,
+    // because it is technically feasible for JS to implement the edit box
+    // by controlling selection.
+    if (owner()->HasState(ui::AX_STATE_MULTILINE)) {
+      ia2_state |= IA2_STATE_MULTI_LINE;
+    } else {
+      ia2_state |= IA2_STATE_SINGLE_LINE;
+    }
+  }
+
   if (!owner()->GetStringAttribute(ui::AX_ATTR_AUTO_COMPLETE).empty())
     ia2_state |= IA2_STATE_SUPPORTS_AUTOCOMPLETION;
 
@@ -5319,11 +5330,6 @@
     case ui::AX_ROLE_TEXT_FIELD:
     case ui::AX_ROLE_SEARCH_BOX:
       ia_role = ROLE_SYSTEM_TEXT;
-      if (owner()->HasState(ui::AX_STATE_MULTILINE)) {
-        ia2_state |= IA2_STATE_MULTI_LINE;
-      } else {
-        ia2_state |= IA2_STATE_SINGLE_LINE;
-      }
       ia2_state |= IA2_STATE_SELECTABLE_TEXT;
       break;
     case ui::AX_ROLE_ABBR:
diff --git a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
index 34484feb..da028936 100644
--- a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -198,7 +198,7 @@
   AddFilter(filters, "haspopup");
   AddFilter(filters, "horizontal");
   AddFilter(filters, "invisible");
-  // TODO(aleventhal) multiline
+  AddFilter(filters, "multiline");
   AddFilter(filters, "multiselectable");
   AddFilter(filters, "protected");
   // TODO(aleventhal) Add readonly support back after control mode refactor
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index 6e9ddad..be8eddc 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -1215,7 +1215,7 @@
 
 #if !defined(OS_ANDROID)
   host_frame_sink_manager_.reset();
-  frame_sink_manager_.reset();
+  frame_sink_manager_impl_.reset();
 #endif
 
   // The device monitors are using |system_monitor_| as dependency, so delete
@@ -1361,8 +1361,8 @@
 }
 
 #if !defined(OS_ANDROID)
-cc::SurfaceManager* BrowserMainLoop::GetSurfaceManager() const {
-  return frame_sink_manager_->surface_manager();
+cc::FrameSinkManager* BrowserMainLoop::GetFrameSinkManager() const {
+  return frame_sink_manager_impl_->frame_sink_manager();
 }
 #endif
 
@@ -1452,7 +1452,7 @@
   }
 #if !defined(OS_ANDROID)
   if (!service_manager::ServiceManagerIsRemote()) {
-    frame_sink_manager_ =
+    frame_sink_manager_impl_ =
         base::MakeUnique<viz::FrameSinkManagerImpl>(false, nullptr);
 
     host_frame_sink_manager_ = base::MakeUnique<viz::HostFrameSinkManager>();
@@ -1460,7 +1460,7 @@
     // TODO(danakj): Don't make a FrameSinkManagerImpl when display is in the
     // Gpu process, instead get the mojo pointer from the Gpu process.
     surface_utils::ConnectWithInProcessFrameSinkManager(
-        host_frame_sink_manager_.get(), frame_sink_manager_.get());
+        host_frame_sink_manager_.get(), frame_sink_manager_impl_.get());
   }
 #endif
 
diff --git a/content/browser/browser_main_loop.h b/content/browser/browser_main_loop.h
index 7b72970..2a076b2 100644
--- a/content/browser/browser_main_loop.h
+++ b/content/browser/browser_main_loop.h
@@ -36,7 +36,7 @@
 }  // namespace base
 
 namespace cc {
-class SurfaceManager;
+class FrameSinkManager;
 }
 
 namespace discardable_memory {
@@ -189,7 +189,7 @@
 
   // TODO(crbug.com/657959): This will be removed once there are no users, as
   // SurfaceManager is being moved out of process.
-  cc::SurfaceManager* GetSurfaceManager() const;
+  cc::FrameSinkManager* GetFrameSinkManager() const;
 #endif
 
   void StopStartupTracingTimer();
@@ -362,7 +362,7 @@
   // access to |in_process_frame_sink_manager_| should happen via
   // |host_frame_sink_manager_| instead which uses Mojo. See
   // http://crbug.com/657959.
-  std::unique_ptr<viz::FrameSinkManagerImpl> frame_sink_manager_;
+  std::unique_ptr<viz::FrameSinkManagerImpl> frame_sink_manager_impl_;
 #endif
 
   // DO NOT add members here. Add them to the right categories above.
diff --git a/content/browser/browser_plugin/browser_plugin_guest.cc b/content/browser/browser_plugin/browser_plugin_guest.cc
index 4889010..30cc0ed 100644
--- a/content/browser/browser_plugin/browser_plugin_guest.cc
+++ b/content/browser/browser_plugin/browser_plugin_guest.cc
@@ -15,9 +15,9 @@
 #include "base/pickle.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
+#include "cc/surfaces/frame_sink_manager.h"
 #include "cc/surfaces/surface.h"
 #include "cc/surfaces/surface_info.h"
-#include "cc/surfaces/surface_manager.h"
 #include "content/browser/browser_plugin/browser_plugin_embedder.h"
 #include "content/browser/browser_thread_impl.h"
 #include "content/browser/child_process_security_policy_impl.h"
@@ -440,14 +440,14 @@
 void BrowserPluginGuest::OnSatisfySequence(
     int instance_id,
     const cc::SurfaceSequence& sequence) {
-  GetSurfaceManager()->SatisfySequence(sequence);
+  GetFrameSinkManager()->surface_manager()->SatisfySequence(sequence);
 }
 
 void BrowserPluginGuest::OnRequireSequence(
     int instance_id,
     const cc::SurfaceId& id,
     const cc::SurfaceSequence& sequence) {
-  GetSurfaceManager()->RequireSequence(id, sequence);
+  GetFrameSinkManager()->surface_manager()->RequireSequence(id, sequence);
 }
 
 void BrowserPluginGuest::ResendEventToEmbedder(
diff --git a/content/browser/child_process_launcher_helper_posix.cc b/content/browser/child_process_launcher_helper_posix.cc
index 9b2f6e0fc..38b818d 100644
--- a/content/browser/child_process_launcher_helper_posix.cc
+++ b/content/browser/child_process_launcher_helper_posix.cc
@@ -115,7 +115,8 @@
       base::PlatformFile file =
           OpenFileIfNecessary(key_path_iter.second, &region);
       if (file == base::kInvalidPlatformFile) {
-        DLOG(ERROR) << "Ignoring invalid file " << key_path_iter.second.value();
+        DLOG(WARNING) << "Ignoring invalid file "
+                      << key_path_iter.second.value();
         continue;
       }
       file_switch_value_builder.AddEntry(key_path_iter.first, key);
diff --git a/content/browser/compositor/gpu_process_transport_factory.cc b/content/browser/compositor/gpu_process_transport_factory.cc
index 59c85d93..19b47d9 100644
--- a/content/browser/compositor/gpu_process_transport_factory.cc
+++ b/content/browser/compositor/gpu_process_transport_factory.cc
@@ -29,7 +29,7 @@
 #include "cc/surfaces/direct_layer_tree_frame_sink.h"
 #include "cc/surfaces/display.h"
 #include "cc/surfaces/display_scheduler.h"
-#include "cc/surfaces/surface_manager.h"
+#include "cc/surfaces/frame_sink_manager.h"
 #include "components/viz/common/gl_helper.h"
 #include "components/viz/host/host_frame_sink_manager.h"
 #include "components/viz/service/display_embedder/compositor_overlay_candidate_validator.h"
@@ -591,10 +591,10 @@
       compositor->widget());
 #endif
   if (data->synthetic_begin_frame_source) {
-    GetSurfaceManager()->UnregisterBeginFrameSource(
+    GetFrameSinkManager()->UnregisterBeginFrameSource(
         data->synthetic_begin_frame_source.get());
   } else if (data->gpu_vsync_begin_frame_source) {
-    GetSurfaceManager()->UnregisterBeginFrameSource(
+    GetFrameSinkManager()->UnregisterBeginFrameSource(
         data->gpu_vsync_begin_frame_source.get());
   }
 
@@ -609,8 +609,8 @@
       std::move(display_output_surface), std::move(scheduler),
       base::MakeUnique<cc::TextureMailboxDeleter>(
           compositor->task_runner().get()));
-  GetSurfaceManager()->RegisterBeginFrameSource(begin_frame_source,
-                                                compositor->frame_sink_id());
+  GetFrameSinkManager()->RegisterBeginFrameSource(begin_frame_source,
+                                                  compositor->frame_sink_id());
   // Note that we are careful not to destroy prior BeginFrameSource objects
   // until we have reset |data->display|.
   data->synthetic_begin_frame_source = std::move(synthetic_begin_frame_source);
@@ -622,12 +622,12 @@
   auto layer_tree_frame_sink =
       vulkan_context_provider
           ? base::MakeUnique<cc::DirectLayerTreeFrameSink>(
-                compositor->frame_sink_id(), GetSurfaceManager(),
+                compositor->frame_sink_id(), GetFrameSinkManager(),
                 data->display.get(),
                 static_cast<scoped_refptr<cc::VulkanContextProvider>>(
                     vulkan_context_provider))
           : base::MakeUnique<cc::DirectLayerTreeFrameSink>(
-                compositor->frame_sink_id(), GetSurfaceManager(),
+                compositor->frame_sink_id(), GetFrameSinkManager(),
                 data->display.get(), context_provider,
                 shared_worker_context_provider_, GetGpuMemoryBufferManager(),
                 viz::ServerSharedBitmapManager::current());
@@ -671,10 +671,10 @@
     gpu::GpuSurfaceTracker::Get()->RemoveSurface(data->surface_handle);
 #endif
   if (data->synthetic_begin_frame_source) {
-    GetSurfaceManager()->UnregisterBeginFrameSource(
+    GetFrameSinkManager()->UnregisterBeginFrameSource(
         data->synthetic_begin_frame_source.get());
   } else if (data->gpu_vsync_begin_frame_source) {
-    GetSurfaceManager()->UnregisterBeginFrameSource(
+    GetFrameSinkManager()->UnregisterBeginFrameSource(
         data->gpu_vsync_begin_frame_source.get());
   }
   per_compositor_data_.erase(it);
@@ -727,10 +727,6 @@
   return frame_sink_id_allocator_.NextFrameSinkId();
 }
 
-cc::SurfaceManager* GpuProcessTransportFactory::GetSurfaceManager() {
-  return BrowserMainLoop::GetInstance()->GetSurfaceManager();
-}
-
 viz::HostFrameSinkManager*
 GpuProcessTransportFactory::GetHostFrameSinkManager() {
   return BrowserMainLoop::GetInstance()->host_frame_sink_manager();
@@ -831,6 +827,10 @@
   observer_list_.RemoveObserver(observer);
 }
 
+cc::FrameSinkManager* GpuProcessTransportFactory::GetFrameSinkManager() {
+  return BrowserMainLoop::GetInstance()->GetFrameSinkManager();
+}
+
 viz::GLHelper* GpuProcessTransportFactory::GetGLHelper() {
   if (!gl_helper_ && !per_compositor_data_.empty()) {
     scoped_refptr<cc::ContextProvider> provider =
diff --git a/content/browser/compositor/gpu_process_transport_factory.h b/content/browser/compositor/gpu_process_transport_factory.h
index d9c0b77..7c17eb8d 100644
--- a/content/browser/compositor/gpu_process_transport_factory.h
+++ b/content/browser/compositor/gpu_process_transport_factory.h
@@ -63,7 +63,6 @@
   void RemoveReflector(ui::Reflector* reflector) override;
   void RemoveCompositor(ui::Compositor* compositor) override;
   cc::FrameSinkId AllocateFrameSinkId() override;
-  cc::SurfaceManager* GetSurfaceManager() override;
   viz::HostFrameSinkManager* GetHostFrameSinkManager() override;
   void SetDisplayVisible(ui::Compositor* compositor, bool visible) override;
   void ResizeDisplay(ui::Compositor* compositor,
@@ -81,6 +80,7 @@
   // ImageTransportFactory implementation.
   ui::ContextFactory* GetContextFactory() override;
   ui::ContextFactoryPrivate* GetContextFactoryPrivate() override;
+  cc::FrameSinkManager* GetFrameSinkManager() override;
   viz::GLHelper* GetGLHelper() override;
   void SetGpuChannelEstablishFactory(
       gpu::GpuChannelEstablishFactory* factory) override;
diff --git a/content/browser/compositor/surface_utils.cc b/content/browser/compositor/surface_utils.cc
index 04184659..185d48d 100644
--- a/content/browser/compositor/surface_utils.cc
+++ b/content/browser/compositor/surface_utils.cc
@@ -164,14 +164,14 @@
 #endif
 }
 
-cc::SurfaceManager* GetSurfaceManager() {
+cc::FrameSinkManager* GetFrameSinkManager() {
 #if defined(OS_ANDROID)
-  return CompositorImpl::GetSurfaceManager();
+  return CompositorImpl::GetFrameSinkManager();
 #else
   ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
   if (factory == NULL)
     return nullptr;
-  return factory->GetContextFactoryPrivate()->GetSurfaceManager();
+  return factory->GetContextFactoryPrivate()->GetFrameSinkManager();
 #endif
 }
 
diff --git a/content/browser/compositor/surface_utils.h b/content/browser/compositor/surface_utils.h
index cf6a677..3a4c680a2 100644
--- a/content/browser/compositor/surface_utils.h
+++ b/content/browser/compositor/surface_utils.h
@@ -15,7 +15,7 @@
 
 namespace cc {
 class CopyOutputResult;
-class SurfaceManager;
+class FrameSinkManager;
 }  // namespace cc
 
 namespace viz {
@@ -27,7 +27,7 @@
 
 CONTENT_EXPORT cc::FrameSinkId AllocateFrameSinkId();
 
-CONTENT_EXPORT cc::SurfaceManager* GetSurfaceManager();
+CONTENT_EXPORT cc::FrameSinkManager* GetFrameSinkManager();
 
 CONTENT_EXPORT viz::HostFrameSinkManager* GetHostFrameSinkManager();
 
diff --git a/content/browser/compositor/test/no_transport_image_transport_factory.cc b/content/browser/compositor/test/no_transport_image_transport_factory.cc
index 8581cbe0..03f857eb 100644
--- a/content/browser/compositor/test/no_transport_image_transport_factory.cc
+++ b/content/browser/compositor/test/no_transport_image_transport_factory.cc
@@ -20,7 +20,7 @@
 NoTransportImageTransportFactory::NoTransportImageTransportFactory()
     : frame_sink_manager_(false /* use surface references */, nullptr),
       context_factory_(&host_frame_sink_manager_,
-                       frame_sink_manager_.surface_manager()) {
+                       frame_sink_manager_.frame_sink_manager()) {
   surface_utils::ConnectWithInProcessFrameSinkManager(&host_frame_sink_manager_,
                                                       &frame_sink_manager_);
 
diff --git a/content/browser/devtools/devtools_url_request_interceptor.h b/content/browser/devtools/devtools_url_request_interceptor.h
index cfd139a..e209149 100644
--- a/content/browser/devtools/devtools_url_request_interceptor.h
+++ b/content/browser/devtools/devtools_url_request_interceptor.h
@@ -27,7 +27,7 @@
 
 // An interceptor that creates DevToolsURLInterceptorRequestJobs for requests
 // from pages where interception has been enabled via
-// Network.enableRequestInterception.
+// Network.setRequestInterceptionEnabled.
 class DevToolsURLRequestInterceptor : public net::URLRequestInterceptor {
  public:
   explicit DevToolsURLRequestInterceptor(BrowserContext* browser_context);
diff --git a/content/browser/devtools/protocol/network_handler.cc b/content/browser/devtools/protocol/network_handler.cc
index e5a831c..0891103 100644
--- a/content/browser/devtools/protocol/network_handler.cc
+++ b/content/browser/devtools/protocol/network_handler.cc
@@ -508,7 +508,7 @@
 Response NetworkHandler::Disable() {
   enabled_ = false;
   user_agent_ = std::string();
-  EnableRequestInterception(false);
+  SetRequestInterceptionEnabled(false);
   return Response::FallThrough();
 }
 
@@ -826,7 +826,7 @@
   return enabled_ ? user_agent_ : std::string();
 }
 
-DispatchResponse NetworkHandler::EnableRequestInterception(bool enabled) {
+DispatchResponse NetworkHandler::SetRequestInterceptionEnabled(bool enabled) {
   if (interception_enabled_ == enabled)
     return Response::OK();  // Nothing to do.
 
diff --git a/content/browser/devtools/protocol/network_handler.h b/content/browser/devtools/protocol/network_handler.h
index 2f450db2..f0d64c93 100644
--- a/content/browser/devtools/protocol/network_handler.h
+++ b/content/browser/devtools/protocol/network_handler.h
@@ -70,7 +70,7 @@
   Response SetUserAgentOverride(const std::string& user_agent) override;
   Response CanEmulateNetworkConditions(bool* result) override;
 
-  DispatchResponse EnableRequestInterception(bool enabled) override;
+  DispatchResponse SetRequestInterceptionEnabled(bool enabled) override;
   void ContinueInterceptedRequest(
       const std::string& request_id,
       Maybe<std::string> error_reason,
diff --git a/content/browser/devtools/protocol_config.json b/content/browser/devtools/protocol_config.json
index 5d58560..45e37cd 100644
--- a/content/browser/devtools/protocol_config.json
+++ b/content/browser/devtools/protocol_config.json
@@ -37,7 +37,7 @@
             },
             {
                 "domain": "Network",
-                "include": ["enable", "disable", "clearBrowserCache", "clearBrowserCookies", "getCookies", "getAllCookies", "deleteCookie", "setCookie", "setUserAgentOverride", "canEmulateNetworkConditions", "enableRequestInterception", "continueInterceptedRequest"],
+                "include": ["enable", "disable", "clearBrowserCache", "clearBrowserCookies", "getCookies", "getAllCookies", "deleteCookie", "setCookie", "setUserAgentOverride", "canEmulateNetworkConditions", "setRequestInterceptionEnabled", "continueInterceptedRequest"],
                 "include_types": ["CookieSameSite", "Cookie", "Response", "Headers", "Request", "ResourceTiming", "SecurityDetails", "SignedCertificateTimestamp", "Initiator", "ResourcePriority", "RequestWillBeSentNotification", "ResponseReceivedNotification", "LoadingFinishedNotification", "LoadingFailedNotification", "RequestWillBeSentNotification", 
                                   "ErrorReason", "RequestInterceptedNotification", "AuthChallenge", "AuthChallengeResponse"],
                 "include_events": ["requestWillBeSent", "responseReceived", "loadingFinished", "loadingFailed", "requestIntercepted"],
diff --git a/content/browser/frame_host/cross_process_frame_connector.cc b/content/browser/frame_host/cross_process_frame_connector.cc
index dc42db7..fe36990 100644
--- a/content/browser/frame_host/cross_process_frame_connector.cc
+++ b/content/browser/frame_host/cross_process_frame_connector.cc
@@ -4,9 +4,9 @@
 
 #include "content/browser/frame_host/cross_process_frame_connector.h"
 
+#include "cc/surfaces/frame_sink_manager.h"
 #include "cc/surfaces/surface.h"
 #include "cc/surfaces/surface_hittest.h"
-#include "cc/surfaces/surface_manager.h"
 #include "content/browser/compositor/surface_utils.h"
 #include "content/browser/frame_host/frame_tree.h"
 #include "content/browser/frame_host/frame_tree_node.h"
@@ -97,13 +97,13 @@
 
 void CrossProcessFrameConnector::OnSatisfySequence(
     const cc::SurfaceSequence& sequence) {
-  GetSurfaceManager()->SatisfySequence(sequence);
+  GetFrameSinkManager()->surface_manager()->SatisfySequence(sequence);
 }
 
 void CrossProcessFrameConnector::OnRequireSequence(
     const cc::SurfaceId& id,
     const cc::SurfaceSequence& sequence) {
-  GetSurfaceManager()->RequireSequence(id, sequence);
+  GetFrameSinkManager()->surface_manager()->RequireSequence(id, sequence);
 }
 
 gfx::Rect CrossProcessFrameConnector::ChildFrameRect() {
@@ -139,7 +139,7 @@
   // is necessary.
   *transformed_point =
       gfx::ConvertPointToPixel(view_->current_surface_scale_factor(), point);
-  cc::SurfaceHittest hittest(nullptr, GetSurfaceManager());
+  cc::SurfaceHittest hittest(nullptr, GetFrameSinkManager()->surface_manager());
   if (!hittest.TransformPointToTargetSurface(original_surface, local_surface_id,
                                              transformed_point))
     return false;
diff --git a/content/browser/frame_host/interstitial_page_impl.cc b/content/browser/frame_host/interstitial_page_impl.cc
index 96c4267..b25a6afd 100644
--- a/content/browser/frame_host/interstitial_page_impl.cc
+++ b/content/browser/frame_host/interstitial_page_impl.cc
@@ -783,12 +783,14 @@
 
 void InterstitialPageImpl::CreateNewWidget(int32_t render_process_id,
                                            int32_t route_id,
+                                           mojom::WidgetPtr widget,
                                            blink::WebPopupType popup_type) {
   NOTREACHED() << "InterstitialPage does not support showing drop-downs.";
 }
 
 void InterstitialPageImpl::CreateNewFullscreenWidget(int32_t render_process_id,
-                                                     int32_t route_id) {
+                                                     int32_t route_id,
+                                                     mojom::WidgetPtr widget) {
   NOTREACHED()
       << "InterstitialPage does not support showing full screen popups.";
 }
diff --git a/content/browser/frame_host/interstitial_page_impl.h b/content/browser/frame_host/interstitial_page_impl.h
index cbab206..2550b95b 100644
--- a/content/browser/frame_host/interstitial_page_impl.h
+++ b/content/browser/frame_host/interstitial_page_impl.h
@@ -147,9 +147,11 @@
       BrowserContext* browser_context) const override;
   void CreateNewWidget(int32_t render_process_id,
                        int32_t route_id,
+                       mojom::WidgetPtr widget,
                        blink::WebPopupType popup_type) override;
   void CreateNewFullscreenWidget(int32_t render_process_id,
-                                 int32_t route_id) override;
+                                 int32_t route_id,
+                                 mojom::WidgetPtr widget) override;
   void ShowCreatedWidget(int process_id,
                          int route_id,
                          const gfx::Rect& initial_rect) override;
diff --git a/content/browser/frame_host/navigator_delegate.cc b/content/browser/frame_host/navigator_delegate.cc
index 6b067c50..786a58a 100644
--- a/content/browser/frame_host/navigator_delegate.cc
+++ b/content/browser/frame_host/navigator_delegate.cc
@@ -30,4 +30,7 @@
   return nullptr;
 }
 
+void NavigatorDelegate::AdjustPreviewsStateForNavigation(
+    PreviewsState* previews_state) {}
+
 }  // namespace content
diff --git a/content/browser/frame_host/navigator_delegate.h b/content/browser/frame_host/navigator_delegate.h
index 93a3e437..f632319f 100644
--- a/content/browser/frame_host/navigator_delegate.h
+++ b/content/browser/frame_host/navigator_delegate.h
@@ -11,6 +11,7 @@
 #include "content/public/browser/navigation_throttle.h"
 #include "content/public/browser/navigation_ui_data.h"
 #include "content/public/browser/reload_type.h"
+#include "content/public/common/previews_state.h"
 #include "ui/base/page_transition_types.h"
 #include "ui/base/window_open_disposition.h"
 
@@ -131,6 +132,13 @@
   // Whether the delegate is displaying an interstitial page over the current
   // page.
   virtual bool ShowingInterstitialPage() const = 0;
+
+  // Gives the delegate a chance to adjust the previews state during navigation.
+  // When called, previews_state will be pointing to a valid set of previews, or
+  // an enum value disabling previews.  The call will change the value of
+  // previews_state in place, and must change it to either a value disabling
+  // previews, or a subset of the previews passed in.
+  virtual void AdjustPreviewsStateForNavigation(PreviewsState* previews_state);
 };
 
 }  // namspace content
diff --git a/content/browser/frame_host/navigator_impl.cc b/content/browser/frame_host/navigator_impl.cc
index dd1dc184..297e8f0 100644
--- a/content/browser/frame_host/navigator_impl.cc
+++ b/content/browser/frame_host/navigator_impl.cc
@@ -369,6 +369,10 @@
     previews_state = PREVIEWS_NO_TRANSFORM;
   }
 
+  // Give the delegate an opportunity to adjust the previews state.
+  if (delegate_)
+    delegate_->AdjustPreviewsStateForNavigation(&previews_state);
+
   // PlzNavigate: the RenderFrameHosts are no longer asked to navigate.
   if (IsBrowserSideNavigationEnabled()) {
     navigation_data_.reset(new NavigationMetricsData(navigation_start, dest_url,
diff --git a/content/browser/frame_host/render_widget_host_view_child_frame.cc b/content/browser/frame_host/render_widget_host_view_child_frame.cc
index f3d24295..83d5c86a 100644
--- a/content/browser/frame_host/render_widget_host_view_child_frame.cc
+++ b/content/browser/frame_host/render_widget_host_view_child_frame.cc
@@ -16,8 +16,10 @@
 #include "cc/output/copy_output_request.h"
 #include "cc/output/copy_output_result.h"
 #include "cc/surfaces/compositor_frame_sink_support.h"
+#include "cc/surfaces/frame_sink_manager.h"
 #include "cc/surfaces/surface.h"
 #include "cc/surfaces/surface_manager.h"
+#include "components/viz/host/host_frame_sink_manager.h"
 #include "content/browser/accessibility/browser_accessibility_manager.h"
 #include "content/browser/browser_plugin/browser_plugin_guest.h"
 #include "content/browser/compositor/surface_utils.h"
@@ -64,7 +66,7 @@
       background_color_(SK_ColorWHITE),
       weak_factory_(this) {
   if (!service_manager::ServiceManagerIsRemote()) {
-    GetSurfaceManager()->RegisterFrameSinkId(frame_sink_id_);
+    GetFrameSinkManager()->RegisterFrameSinkId(frame_sink_id_);
     CreateCompositorFrameSinkSupport();
   }
 }
@@ -72,8 +74,8 @@
 RenderWidgetHostViewChildFrame::~RenderWidgetHostViewChildFrame() {
   if (!service_manager::ServiceManagerIsRemote()) {
     ResetCompositorFrameSinkSupport();
-    if (GetSurfaceManager())
-      GetSurfaceManager()->InvalidateFrameSinkId(frame_sink_id_);
+    if (GetFrameSinkManager())
+      GetFrameSinkManager()->InvalidateFrameSinkId(frame_sink_id_);
   }
 }
 
@@ -106,8 +108,8 @@
   if (frame_connector_) {
     if (parent_frame_sink_id_.is_valid() &&
         !service_manager::ServiceManagerIsRemote()) {
-      GetSurfaceManager()->UnregisterFrameSinkHierarchy(parent_frame_sink_id_,
-                                                        frame_sink_id_);
+      GetFrameSinkManager()->UnregisterFrameSinkHierarchy(parent_frame_sink_id_,
+                                                          frame_sink_id_);
     }
     parent_frame_sink_id_ = cc::FrameSinkId();
     local_surface_id_ = cc::LocalSurfaceId();
@@ -124,8 +126,8 @@
       parent_frame_sink_id_ = parent_view->GetFrameSinkId();
       DCHECK(parent_frame_sink_id_.is_valid());
       if (!service_manager::ServiceManagerIsRemote()) {
-        GetSurfaceManager()->RegisterFrameSinkHierarchy(parent_frame_sink_id_,
-                                                        frame_sink_id_);
+        GetFrameSinkManager()->RegisterFrameSinkHierarchy(parent_frame_sink_id_,
+                                                          frame_sink_id_);
       }
     }
 
@@ -477,7 +479,7 @@
     return;
   cc::SurfaceSequence sequence =
       cc::SurfaceSequence(frame_sink_id_, next_surface_sequence_++);
-  cc::SurfaceManager* manager = GetSurfaceManager();
+  cc::SurfaceManager* manager = GetFrameSinkManager()->surface_manager();
   cc::SurfaceId surface_id(frame_sink_id_, local_surface_id_);
   // The renderer process will satisfy this dependency when it creates a
   // SurfaceLayer.
@@ -832,11 +834,11 @@
   constexpr bool handles_frame_sink_id_invalidation = false;
   constexpr bool needs_sync_points = true;
   support_ = cc::CompositorFrameSinkSupport::Create(
-      this, GetSurfaceManager(), frame_sink_id_, is_root,
+      this, GetFrameSinkManager(), frame_sink_id_, is_root,
       handles_frame_sink_id_invalidation, needs_sync_points);
   if (parent_frame_sink_id_.is_valid()) {
-    GetSurfaceManager()->RegisterFrameSinkHierarchy(parent_frame_sink_id_,
-                                                    frame_sink_id_);
+    GetFrameSinkManager()->RegisterFrameSinkHierarchy(parent_frame_sink_id_,
+                                                      frame_sink_id_);
   }
   if (host_->needs_begin_frames())
     support_->SetNeedsBeginFrame(true);
@@ -846,8 +848,8 @@
   if (!support_)
     return;
   if (parent_frame_sink_id_.is_valid()) {
-    GetSurfaceManager()->UnregisterFrameSinkHierarchy(parent_frame_sink_id_,
-                                                      frame_sink_id_);
+    GetFrameSinkManager()->UnregisterFrameSinkHierarchy(parent_frame_sink_id_,
+                                                        frame_sink_id_);
   }
   support_.reset();
 }
diff --git a/content/browser/frame_host/render_widget_host_view_child_frame_unittest.cc b/content/browser/frame_host/render_widget_host_view_child_frame_unittest.cc
index f85b047..3c53aef 100644
--- a/content/browser/frame_host/render_widget_host_view_child_frame_unittest.cc
+++ b/content/browser/frame_host/render_widget_host_view_child_frame_unittest.cc
@@ -205,8 +205,9 @@
   if (id.is_valid()) {
 #if !defined(OS_ANDROID)
     ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
-    cc::SurfaceManager* manager =
-        factory->GetContextFactoryPrivate()->GetSurfaceManager();
+    cc::SurfaceManager* manager = factory->GetContextFactoryPrivate()
+                                      ->GetFrameSinkManager()
+                                      ->surface_manager();
     cc::Surface* surface = manager->GetSurfaceForId(id);
     EXPECT_TRUE(surface);
     // There should be a SurfaceSequence created by the RWHVChildFrame.
diff --git a/content/browser/frame_host/render_widget_host_view_guest_unittest.cc b/content/browser/frame_host/render_widget_host_view_guest_unittest.cc
index 2e2a4cd0..4f21dc2 100644
--- a/content/browser/frame_host/render_widget_host_view_guest_unittest.cc
+++ b/content/browser/frame_host/render_widget_host_view_guest_unittest.cc
@@ -265,7 +265,8 @@
 #if !defined(OS_ANDROID)
   cc::SurfaceManager* manager = ImageTransportFactory::GetInstance()
                                     ->GetContextFactoryPrivate()
-                                    ->GetSurfaceManager();
+                                    ->GetFrameSinkManager()
+                                    ->surface_manager();
   cc::Surface* surface = manager->GetSurfaceForId(id);
   EXPECT_TRUE(surface);
   // There should be a SurfaceSequence created by the RWHVGuest.
diff --git a/content/browser/loader/mojo_async_resource_handler_unittest.cc b/content/browser/loader/mojo_async_resource_handler_unittest.cc
index 1fe6c2b..c05fe38 100644
--- a/content/browser/loader/mojo_async_resource_handler_unittest.cc
+++ b/content/browser/loader/mojo_async_resource_handler_unittest.cc
@@ -180,9 +180,9 @@
     ADD_FAILURE() << "RequestComplete should not be called.";
   }
 
-  PreviewsState GetPreviewsState(
-      const net::URLRequest& url_request,
-      content::ResourceContext* resource_context) override {
+  PreviewsState GetPreviewsState(const net::URLRequest& url_request,
+                                 content::ResourceContext* resource_context,
+                                 PreviewsState previews_to_allow) override {
     ADD_FAILURE() << "GetPreviewsState should not be called.";
     return PREVIEWS_UNSPECIFIED;
   }
diff --git a/content/browser/loader/resource_dispatcher_host_browsertest.cc b/content/browser/loader/resource_dispatcher_host_browsertest.cc
index e328092..97044ed1 100644
--- a/content/browser/loader/resource_dispatcher_host_browsertest.cc
+++ b/content/browser/loader/resource_dispatcher_host_browsertest.cc
@@ -647,7 +647,8 @@
 
   PreviewsState GetPreviewsState(
       const net::URLRequest& request,
-      content::ResourceContext* resource_context) override {
+      content::ResourceContext* resource_context,
+      content::PreviewsState previews_to_allow) override {
     DCHECK_CURRENTLY_ON(BrowserThread::IO);
     EXPECT_FALSE(should_get_previews_state_called_);
     should_get_previews_state_called_ = true;
diff --git a/content/browser/loader/resource_dispatcher_host_impl.cc b/content/browser/loader/resource_dispatcher_host_impl.cc
index f6d1b57..4080c66 100644
--- a/content/browser/loader/resource_dispatcher_host_impl.cc
+++ b/content/browser/loader/resource_dispatcher_host_impl.cc
@@ -132,7 +132,8 @@
 namespace {
 
 constexpr net::NetworkTrafficAnnotationTag kTrafficAnnotation =
-    net::DefineNetworkTrafficAnnotation("resource_dispather_host", R"(
+    net::DefineNetworkTrafficAnnotation("resource_dispather_host",
+                                        R"(
         semantics {
           sender: "Resource Dispatcher Host"
           description:
@@ -247,14 +248,26 @@
 
 // Returns the PreviewsState after requesting it from the delegate. The
 // PreviewsState is a bitmask of potentially several Previews optimizations.
-PreviewsState GetPreviewsState(PreviewsState previews_state,
+// If previews_to_allow is set to anything other than PREVIEWS_UNSPECIFIED,
+// it is either the values passed in for a sub-frame to use, or if this is
+// the main frame, it is a limitation on which previews to allow.
+PreviewsState GetPreviewsState(PreviewsState previews_to_allow,
                                ResourceDispatcherHostDelegate* delegate,
                                const net::URLRequest& request,
                                ResourceContext* resource_context,
                                bool is_main_frame) {
-  // previews_state is set to PREVIEWS_OFF when reloading with Lo-Fi disabled.
-  if (previews_state == PREVIEWS_UNSPECIFIED && delegate && is_main_frame)
-    return delegate->GetPreviewsState(request, resource_context);
+  // If previews have already been turned off, or we are inheriting values on a
+  // sub-frame, don't check any further.
+  if (previews_to_allow & PREVIEWS_OFF ||
+      previews_to_allow & PREVIEWS_NO_TRANSFORM || !is_main_frame ||
+      !delegate) {
+    return previews_to_allow;
+  }
+
+  // Get the mask of previews we could apply to the current navigation.
+  PreviewsState previews_state =
+      delegate->GetPreviewsState(request, resource_context, previews_to_allow);
+
   return previews_state;
 }
 
@@ -1385,6 +1398,14 @@
 
   new_request->SetLoadFlags(load_flags);
 
+  // Update the previews state, but only if this is not using PlzNavigate.
+  PreviewsState previews_state = request_data.previews_state;
+  if (!IsBrowserSideNavigationEnabled()) {
+    previews_state = GetPreviewsState(
+        request_data.previews_state, delegate_, *new_request, resource_context,
+        request_data.resource_type == RESOURCE_TYPE_MAIN_FRAME);
+  }
+
   // Make extra info and read footer (contains request ID).
   ResourceRequestInfoImpl* extra_info = new ResourceRequestInfoImpl(
       requester_info, route_id,
@@ -1399,12 +1420,10 @@
       request_data.enable_load_timing, request_data.enable_upload_progress,
       do_not_prompt_for_login, request_data.referrer_policy,
       request_data.visibility_state, resource_context, report_raw_headers,
-      !is_sync_load,
-      GetPreviewsState(request_data.previews_state, delegate_, *new_request,
-                       resource_context,
-                       request_data.resource_type == RESOURCE_TYPE_MAIN_FRAME),
-      request_data.request_body, request_data.initiated_in_secure_context);
+      !is_sync_load, previews_state, request_data.request_body,
+      request_data.initiated_in_secure_context);
   extra_info->SetBlobHandles(std::move(blob_handles));
+
   // Request takes ownership.
   extra_info->AssociateWithRequest(new_request.get());
 
@@ -2123,6 +2142,10 @@
         BrowserThread::GetTaskRunnerForThread(BrowserThread::FILE).get()));
   }
 
+  PreviewsState previews_state =
+      GetPreviewsState(info.common_params.previews_state, delegate_,
+                       *new_request, resource_context, info.is_main_frame);
+
   // Make extra info and read footer (contains request ID).
   //
   // TODO(davidben): Associate the request with the FrameTreeNode and/or tab so
@@ -2151,9 +2174,7 @@
       info.common_params.referrer.policy, info.page_visibility_state,
       resource_context, info.report_raw_headers,
       true,  // is_async
-      GetPreviewsState(info.common_params.previews_state, delegate_,
-                       *new_request, resource_context, info.is_main_frame),
-      info.common_params.post_data,
+      previews_state, info.common_params.post_data,
       // TODO(mek): Currently initiated_in_secure_context is only used for
       // subresource requests, so it doesn't matter what value it gets here.
       // If in the future this changes this should be updated to somehow get a
diff --git a/content/browser/media/android/media_resource_getter_impl.cc b/content/browser/media/android/media_resource_getter_impl.cc
index f103e435..05ff22c 100644
--- a/content/browser/media/android/media_resource_getter_impl.cc
+++ b/content/browser/media/android/media_resource_getter_impl.cc
@@ -11,7 +11,6 @@
 #include "base/macros.h"
 #include "base/path_service.h"
 #include "base/task_scheduler/post_task.h"
-#include "content/browser/blob_storage/chrome_blob_storage_context.h"
 #include "content/browser/child_process_security_policy_impl.h"
 #include "content/browser/fileapi/browser_file_system_helper.h"
 #include "content/browser/resource_context_impl.h"
@@ -29,10 +28,6 @@
 #include "net/http/http_transaction_factory.h"
 #include "net/url_request/url_request_context.h"
 #include "net/url_request/url_request_context_getter.h"
-#include "storage/browser/blob/blob_data_handle.h"
-#include "storage/browser/blob/blob_data_item.h"
-#include "storage/browser/blob/blob_data_snapshot.h"
-#include "storage/browser/blob/blob_storage_context.h"
 #include "url/gurl.h"
 
 using base::android::ConvertUTF8ToJavaString;
@@ -42,49 +37,19 @@
 namespace content {
 
 static void ReturnResultOnUIThread(
-    const base::Callback<void(const std::string&)>& callback,
+    base::OnceCallback<void(const std::string&)> callback,
     const std::string& result) {
-  BrowserThread::PostTask(
-      BrowserThread::UI, FROM_HERE, base::Bind(callback, result));
-}
-
-static void RequestPlatformPathFromBlobURL(
-    const GURL& url,
-    ResourceContext* resource_context,
-    const base::Callback<void(const std::string&)>& callback) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  ChromeBlobStorageContext* blob_storage_context =
-      GetChromeBlobStorageContextForResourceContext(resource_context);
-
-  std::unique_ptr<storage::BlobDataHandle> handle =
-      blob_storage_context->context()->GetBlobDataFromPublicURL(url);
-  if (!handle) {
-    // There are plenty of cases where handle can be empty. The most trivial is
-    // when JS has aready revoked the given blob URL via URL.revokeObjectURL
-    ReturnResultOnUIThread(callback, std::string());
-    return;
-  }
-  std::unique_ptr<storage::BlobDataSnapshot> data = handle->CreateSnapshot();
-  if (!data) {
-    ReturnResultOnUIThread(callback, std::string());
-    NOTREACHED();
-    return;
-  }
-  const std::vector<scoped_refptr<storage::BlobDataItem>>& items =
-      data->items();
-
-  // TODO(qinmin): handle the case when the blob data is not a single file.
-  DLOG_IF(WARNING, items.size() != 1u)
-      << "More than one blob item is present: " << items.size();
-  ReturnResultOnUIThread(callback, items[0]->path().value());
+  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+                          base::BindOnce(std::move(callback), result));
 }
 
 static void RequestPlaformPathFromFileSystemURL(
     const GURL& url,
     int render_process_id,
     scoped_refptr<storage::FileSystemContext> file_system_context,
-    const base::Callback<void(const std::string&)>& callback) {
-  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+    media::MediaResourceGetter::GetPlatformPathCB callback) {
+  DCHECK(file_system_context->default_file_task_runner()
+             ->RunsTasksInCurrentSequence());
   base::FilePath platform_path;
   SyncGetPlatformPath(file_system_context.get(),
                       render_process_id,
@@ -93,31 +58,34 @@
   base::FilePath data_storage_path;
   PathService::Get(base::DIR_ANDROID_APP_DATA, &data_storage_path);
   if (data_storage_path.IsParent(platform_path))
-    ReturnResultOnUIThread(callback, platform_path.value());
+    ReturnResultOnUIThread(std::move(callback), platform_path.value());
   else
-    ReturnResultOnUIThread(callback, std::string());
+    ReturnResultOnUIThread(std::move(callback), std::string());
 }
 
 // Posts a task to the UI thread to run the callback function.
 static void PostMediaMetadataCallbackTask(
-    const media::MediaResourceGetter::ExtractMediaMetadataCB& callback,
-    JNIEnv* env, ScopedJavaLocalRef<jobject>& j_metadata) {
+    media::MediaResourceGetter::ExtractMediaMetadataCB callback,
+    JNIEnv* env,
+    ScopedJavaLocalRef<jobject>& j_metadata) {
   BrowserThread::PostTask(
       BrowserThread::UI, FROM_HERE,
-      base::Bind(callback, base::TimeDelta::FromMilliseconds(
-                               Java_MediaMetadata_getDurationInMilliseconds(
-                                   env, j_metadata)),
-                 Java_MediaMetadata_getWidth(env, j_metadata),
-                 Java_MediaMetadata_getHeight(env, j_metadata),
-                 Java_MediaMetadata_isSuccess(env, j_metadata)));
+      base::BindOnce(
+          std::move(callback),
+          base::TimeDelta::FromMilliseconds(
+              Java_MediaMetadata_getDurationInMilliseconds(env, j_metadata)),
+          Java_MediaMetadata_getWidth(env, j_metadata),
+          Java_MediaMetadata_getHeight(env, j_metadata),
+          Java_MediaMetadata_isSuccess(env, j_metadata)));
 }
 
 // Gets the metadata from a media URL. When finished, a task is posted to the UI
 // thread to run the callback function.
 static void GetMediaMetadata(
-    const std::string& url, const std::string& cookies,
+    const std::string& url,
+    const std::string& cookies,
     const std::string& user_agent,
-    const media::MediaResourceGetter::ExtractMediaMetadataCB& callback) {
+    media::MediaResourceGetter::ExtractMediaMetadataCB callback) {
   JNIEnv* env = base::android::AttachCurrentThread();
 
   ScopedJavaLocalRef<jstring> j_url_string = ConvertUTF8ToJavaString(env, url);
@@ -127,8 +95,7 @@
   ScopedJavaLocalRef<jobject> j_metadata =
       Java_MediaResourceGetter_extractMediaMetadata(env, j_url_string,
                                                     j_cookies, j_user_agent);
-
-  PostMediaMetadataCallbackTask(callback, env, j_metadata);
+  PostMediaMetadataCallbackTask(std::move(callback), env, j_metadata);
 }
 
 // Gets the metadata from a file descriptor. When finished, a task is posted to
@@ -137,14 +104,14 @@
     const int fd,
     const int64_t offset,
     const int64_t size,
-    const media::MediaResourceGetter::ExtractMediaMetadataCB& callback) {
+    media::MediaResourceGetter::ExtractMediaMetadataCB callback) {
   JNIEnv* env = base::android::AttachCurrentThread();
 
   ScopedJavaLocalRef<jobject> j_metadata =
       Java_MediaResourceGetter_extractMediaMetadataFromFd(
           env, fd, offset, size);
 
-  PostMediaMetadataCallbackTask(callback, env, j_metadata);
+  PostMediaMetadataCallbackTask(std::move(callback), env, j_metadata);
 }
 
 // The task object that retrieves media resources on the IO thread.
@@ -161,18 +128,21 @@
   net::AuthCredentials RequestAuthCredentials(const GURL& url) const;
 
   // Called by MediaResourceGetterImpl to start getting cookies for a URL.
-  void RequestCookies(
-      const GURL& url, const GURL& first_party_for_cookies,
-      const media::MediaResourceGetter::GetCookieCB& callback);
+  void RequestCookies(const GURL& url,
+                      const GURL& first_party_for_cookies,
+                      media::MediaResourceGetter::GetCookieCB callback);
+
+  // Returns the task runner that all methods should be called.
+  scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner() const;
 
  private:
   friend class base::RefCountedThreadSafe<MediaResourceGetterTask>;
   virtual ~MediaResourceGetterTask();
 
-  void CheckPolicyForCookies(
-      const GURL& url, const GURL& first_party_for_cookies,
-      const media::MediaResourceGetter::GetCookieCB& callback,
-      const net::CookieList& cookie_list);
+  void CheckPolicyForCookies(const GURL& url,
+                             const GURL& first_party_for_cookies,
+                             media::MediaResourceGetter::GetCookieCB callback,
+                             const net::CookieList& cookie_list);
 
   // Context getter used to get the CookieStore and auth cache.
   net::URLRequestContextGetter* context_getter_;
@@ -202,7 +172,7 @@
 
 net::AuthCredentials MediaResourceGetterTask::RequestAuthCredentials(
     const GURL& url) const {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
   net::HttpTransactionFactory* factory =
       context_getter_->GetURLRequestContext()->http_transaction_factory();
   if (!factory)
@@ -224,33 +194,39 @@
 }
 
 void MediaResourceGetterTask::RequestCookies(
-    const GURL& url, const GURL& first_party_for_cookies,
-    const media::MediaResourceGetter::GetCookieCB& callback) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+    const GURL& url,
+    const GURL& first_party_for_cookies,
+    media::MediaResourceGetter::GetCookieCB callback) {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
   ChildProcessSecurityPolicyImpl* policy =
       ChildProcessSecurityPolicyImpl::GetInstance();
   if (!policy->CanAccessDataForOrigin(render_process_id_, url)) {
-    callback.Run(std::string());
+    std::move(callback).Run(std::string());
     return;
   }
 
   net::CookieStore* cookie_store =
       context_getter_->GetURLRequestContext()->cookie_store();
   if (!cookie_store) {
-    callback.Run(std::string());
+    std::move(callback).Run(std::string());
     return;
   }
 
   cookie_store->GetAllCookiesForURLAsync(
-      url, base::Bind(&MediaResourceGetterTask::CheckPolicyForCookies, this,
-                      url, first_party_for_cookies, callback));
+      url, base::BindOnce(&MediaResourceGetterTask::CheckPolicyForCookies, this,
+                          url, first_party_for_cookies, std::move(callback)));
+}
+
+scoped_refptr<base::SingleThreadTaskRunner>
+MediaResourceGetterTask::GetTaskRunner() const {
+  return context_getter_->GetNetworkTaskRunner();
 }
 
 void MediaResourceGetterTask::CheckPolicyForCookies(
-    const GURL& url, const GURL& first_party_for_cookies,
-    const media::MediaResourceGetter::GetCookieCB& callback,
+    const GURL& url,
+    const GURL& first_party_for_cookies,
+    media::MediaResourceGetter::GetCookieCB callback,
     const net::CookieList& cookie_list) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   if (GetContentClient()->browser()->AllowGetCookie(
       url, first_party_for_cookies, cookie_list,
       resource_context_, render_process_id_, render_frame_id_)) {
@@ -258,9 +234,9 @@
         context_getter_->GetURLRequestContext()->cookie_store();
     net::CookieOptions options;
     options.set_include_httponly();
-    cookie_store->GetCookiesWithOptionsAsync(url, options, callback);
+    cookie_store->GetCookiesWithOptionsAsync(url, options, std::move(callback));
   } else {
-    callback.Run(std::string());
+    std::move(callback).Run(std::string());
   }
 }
 
@@ -279,101 +255,95 @@
 MediaResourceGetterImpl::~MediaResourceGetterImpl() {}
 
 void MediaResourceGetterImpl::GetAuthCredentials(
-    const GURL& url, const GetAuthCredentialsCB& callback) {
+    const GURL& url,
+    GetAuthCredentialsCB callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   scoped_refptr<MediaResourceGetterTask> task = new MediaResourceGetterTask(
       browser_context_, 0, 0);
 
-  BrowserThread::PostTaskAndReplyWithResult(
-      BrowserThread::IO,
-      FROM_HERE,
-      base::Bind(&MediaResourceGetterTask::RequestAuthCredentials, task, url),
-      base::Bind(&MediaResourceGetterImpl::GetAuthCredentialsCallback,
-                 weak_factory_.GetWeakPtr(), callback));
+  PostTaskAndReplyWithResult(
+      task->GetTaskRunner().get(), FROM_HERE,
+      base::BindOnce(&MediaResourceGetterTask::RequestAuthCredentials, task,
+                     url),
+      base::BindOnce(&MediaResourceGetterImpl::GetAuthCredentialsCallback,
+                     weak_factory_.GetWeakPtr(), std::move(callback)));
 }
 
-void MediaResourceGetterImpl::GetCookies(
-    const GURL& url, const GURL& first_party_for_cookies,
-    const GetCookieCB& callback) {
+void MediaResourceGetterImpl::GetCookies(const GURL& url,
+                                         const GURL& first_party_for_cookies,
+                                         GetCookieCB callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   scoped_refptr<MediaResourceGetterTask> task = new MediaResourceGetterTask(
       browser_context_, render_process_id_, render_frame_id_);
 
-  GetCookieCB cb = base::Bind(&MediaResourceGetterImpl::GetCookiesCallback,
-                              weak_factory_.GetWeakPtr(),
-                              callback);
-  BrowserThread::PostTask(
-      BrowserThread::IO,
+  GetCookieCB cb =
+      base::BindOnce(&MediaResourceGetterImpl::GetCookiesCallback,
+                     weak_factory_.GetWeakPtr(), std::move(callback));
+  task->GetTaskRunner()->PostTask(
       FROM_HERE,
-      base::Bind(&MediaResourceGetterTask::RequestCookies,
-                 task, url, first_party_for_cookies,
-                 base::Bind(&ReturnResultOnUIThread, cb)));
+      base::BindOnce(&MediaResourceGetterTask::RequestCookies, task, url,
+                     first_party_for_cookies,
+                     base::BindOnce(&ReturnResultOnUIThread, std::move(cb))));
 }
 
 void MediaResourceGetterImpl::GetAuthCredentialsCallback(
-    const GetAuthCredentialsCB& callback,
+    GetAuthCredentialsCB callback,
     const net::AuthCredentials& credentials) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  callback.Run(credentials.username(), credentials.password());
+  std::move(callback).Run(credentials.username(), credentials.password());
 }
 
-void MediaResourceGetterImpl::GetCookiesCallback(
-    const GetCookieCB& callback, const std::string& cookies) {
+void MediaResourceGetterImpl::GetCookiesCallback(GetCookieCB callback,
+                                                 const std::string& cookies) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  callback.Run(cookies);
+  std::move(callback).Run(cookies);
 }
 
 void MediaResourceGetterImpl::GetPlatformPathFromURL(
-    const GURL& url, const GetPlatformPathCB& callback) {
+    const GURL& url,
+    GetPlatformPathCB callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DCHECK(url.SchemeIsFileSystem() || url.SchemeIs(url::kBlobScheme));
+  DCHECK(url.SchemeIsFileSystem());
 
   GetPlatformPathCB cb =
-      base::Bind(&MediaResourceGetterImpl::GetPlatformPathCallback,
-                 weak_factory_.GetWeakPtr(),
-                 callback);
-
-  if (url.SchemeIs(url::kBlobScheme)) {
-    BrowserThread::PostTask(
-        BrowserThread::IO,
-        FROM_HERE,
-        base::Bind(&RequestPlatformPathFromBlobURL, url,
-                   browser_context_->GetResourceContext(), cb));
-    return;
-  }
+      base::BindOnce(&MediaResourceGetterImpl::GetPlatformPathCallback,
+                     weak_factory_.GetWeakPtr(), std::move(callback));
 
   scoped_refptr<storage::FileSystemContext> context(file_system_context_);
-  BrowserThread::PostTask(
-      BrowserThread::FILE,
-      FROM_HERE,
-      base::Bind(&RequestPlaformPathFromFileSystemURL, url, render_process_id_,
-                 context, cb));
+  context->default_file_task_runner()->PostTask(
+      FROM_HERE, base::BindOnce(&RequestPlaformPathFromFileSystemURL, url,
+                                render_process_id_, context, std::move(cb)));
 }
 
 void MediaResourceGetterImpl::GetPlatformPathCallback(
-    const GetPlatformPathCB& callback, const std::string& platform_path) {
+    GetPlatformPathCB callback,
+    const std::string& platform_path) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  callback.Run(platform_path);
+  std::move(callback).Run(platform_path);
 }
 
 void MediaResourceGetterImpl::ExtractMediaMetadata(
-    const std::string& url, const std::string& cookies,
-    const std::string& user_agent, const ExtractMediaMetadataCB& callback) {
+    const std::string& url,
+    const std::string& cookies,
+    const std::string& user_agent,
+    ExtractMediaMetadataCB callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  base::PostTaskWithTraits(
-      FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
-      base::BindOnce(&GetMediaMetadata, url, cookies, user_agent, callback));
+  base::PostTaskWithTraits(FROM_HERE,
+                           {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
+                           base::BindOnce(&GetMediaMetadata, url, cookies,
+                                          user_agent, std::move(callback)));
 }
 
 void MediaResourceGetterImpl::ExtractMediaMetadata(
     const int fd,
     const int64_t offset,
     const int64_t size,
-    const ExtractMediaMetadataCB& callback) {
+    ExtractMediaMetadataCB callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  base::PostTaskWithTraits(
-      FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
-      base::BindOnce(&GetMediaMetadataFromFd, fd, offset, size, callback));
+  base::PostTaskWithTraits(FROM_HERE,
+                           {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
+                           base::BindOnce(&GetMediaMetadataFromFd, fd, offset,
+                                          size, std::move(callback)));
 }
 
 }  // namespace content
diff --git a/content/browser/media/android/media_resource_getter_impl.h b/content/browser/media/android/media_resource_getter_impl.h
index 268e9095..29d2c7e 100644
--- a/content/browser/media/android/media_resource_getter_impl.h
+++ b/content/browser/media/android/media_resource_getter_impl.h
@@ -43,34 +43,32 @@
   // media::MediaResourceGetter implementation.
   // Must be called on the UI thread.
   void GetAuthCredentials(const GURL& url,
-                          const GetAuthCredentialsCB& callback) override;
+                          GetAuthCredentialsCB callback) override;
   void GetCookies(const GURL& url,
                   const GURL& first_party_for_cookies,
-                  const GetCookieCB& callback) override;
+                  GetCookieCB callback) override;
   void GetPlatformPathFromURL(const GURL& url,
-                              const GetPlatformPathCB& callback) override;
+                              GetPlatformPathCB callback) override;
   void ExtractMediaMetadata(const std::string& url,
                             const std::string& cookies,
                             const std::string& user_agent,
-                            const ExtractMediaMetadataCB& callback) override;
+                            ExtractMediaMetadataCB callback) override;
   void ExtractMediaMetadata(const int fd,
                             const int64_t offset,
                             const int64_t size,
-                            const ExtractMediaMetadataCB& callback) override;
+                            ExtractMediaMetadataCB callback) override;
 
  private:
   // Called when GetAuthCredentials() finishes.
-  void GetAuthCredentialsCallback(
-      const GetAuthCredentialsCB& callback,
-      const net::AuthCredentials& credentials);
+  void GetAuthCredentialsCallback(GetAuthCredentialsCB callback,
+                                  const net::AuthCredentials& credentials);
 
   // Called when GetCookies() finishes.
-  void GetCookiesCallback(
-      const GetCookieCB& callback, const std::string& cookies);
+  void GetCookiesCallback(GetCookieCB callback, const std::string& cookies);
 
   // Called when GetPlatformPathFromFileSystemURL() finishes.
-  void GetPlatformPathCallback(
-      const GetPlatformPathCB& callback, const std::string& platform_path);
+  void GetPlatformPathCallback(GetPlatformPathCB callback,
+                               const std::string& platform_path);
 
   // BrowserContext to retrieve URLRequestContext and ResourceContext.
   BrowserContext* browser_context_;
diff --git a/content/browser/renderer_host/compositor_impl_android.cc b/content/browser/renderer_host/compositor_impl_android.cc
index 6105608..9f9fd012 100644
--- a/content/browser/renderer_host/compositor_impl_android.cc
+++ b/content/browser/renderer_host/compositor_impl_android.cc
@@ -103,10 +103,10 @@
   CompositorDependencies() : frame_sink_id_allocator(kDefaultClientId) {
     // TODO(danakj): Don't make a FrameSinkManagerImpl when display is in the
     // Gpu process, instead get the mojo pointer from the Gpu process.
-    frame_sink_manager =
+    frame_sink_manager_impl =
         base::MakeUnique<viz::FrameSinkManagerImpl>(false, nullptr);
     surface_utils::ConnectWithInProcessFrameSinkManager(
-        &host_frame_sink_manager, frame_sink_manager.get());
+        &host_frame_sink_manager, frame_sink_manager_impl.get());
   }
 
   SingleThreadTaskGraphRunner task_graph_runner;
@@ -117,7 +117,7 @@
   // access to |in_process_frame_sink_manager_| should happen via
   // |host_frame_sink_manager_| instead which uses Mojo. See
   // http://crbug.com/657959.
-  std::unique_ptr<viz::FrameSinkManagerImpl> frame_sink_manager;
+  std::unique_ptr<viz::FrameSinkManagerImpl> frame_sink_manager_impl;
 
 #if BUILDFLAG(ENABLE_VULKAN)
   scoped_refptr<cc::VulkanContextProvider> vulkan_context_provider;
@@ -419,8 +419,9 @@
 }
 
 // static
-cc::SurfaceManager* CompositorImpl::GetSurfaceManager() {
-  return g_compositor_dependencies.Get().frame_sink_manager->surface_manager();
+cc::FrameSinkManager* CompositorImpl::GetFrameSinkManager() {
+  return g_compositor_dependencies.Get()
+      .frame_sink_manager_impl->frame_sink_manager();
 }
 
 // static
@@ -452,7 +453,7 @@
       num_successive_context_creation_failures_(0),
       layer_tree_frame_sink_request_pending_(false),
       weak_factory_(this) {
-  GetSurfaceManager()->RegisterFrameSinkId(frame_sink_id_);
+  GetFrameSinkManager()->RegisterFrameSinkId(frame_sink_id_);
   DCHECK(client);
   DCHECK(root_window);
   DCHECK(root_window->GetLayer() == nullptr);
@@ -470,7 +471,7 @@
   root_window_->SetLayer(nullptr);
   // Clean-up any surface references.
   SetSurface(NULL);
-  GetSurfaceManager()->InvalidateFrameSinkId(frame_sink_id_);
+  GetFrameSinkManager()->InvalidateFrameSinkId(frame_sink_id_);
 }
 
 bool CompositorImpl::IsForSubframe() {
@@ -580,7 +581,7 @@
     has_layer_tree_frame_sink_ = false;
     pending_frames_ = 0;
     if (display_) {
-      GetSurfaceManager()->UnregisterBeginFrameSource(
+      GetFrameSinkManager()->UnregisterBeginFrameSource(
           root_window_->GetBeginFrameSource());
     }
     display_.reset();
@@ -794,7 +795,7 @@
     // TODO(danakj): Populate gpu_capabilities_ for VulkanContextProvider.
   }
 
-  cc::SurfaceManager* manager = GetSurfaceManager();
+  cc::FrameSinkManager* manager = GetFrameSinkManager();
   auto* task_runner = base::ThreadTaskRunnerHandle::Get().get();
   std::unique_ptr<cc::DisplayScheduler> scheduler(new cc::DisplayScheduler(
       root_window_->GetBeginFrameSource(), task_runner,
@@ -828,7 +829,7 @@
           ->GetDisplayNearestWindow(root_window_)
           .color_space();
   display_->SetColorSpace(display_color_space, display_color_space);
-  GetSurfaceManager()->RegisterBeginFrameSource(
+  GetFrameSinkManager()->RegisterBeginFrameSource(
       root_window_->GetBeginFrameSource(), frame_sink_id_);
   host_->SetLayerTreeFrameSink(std::move(layer_tree_frame_sink));
 }
@@ -898,8 +899,8 @@
 
 void CompositorImpl::AddChildFrameSink(const cc::FrameSinkId& frame_sink_id) {
   if (has_layer_tree_frame_sink_) {
-    GetSurfaceManager()->RegisterFrameSinkHierarchy(frame_sink_id_,
-                                                    frame_sink_id);
+    GetFrameSinkManager()->RegisterFrameSinkHierarchy(frame_sink_id_,
+                                                      frame_sink_id);
   } else {
     pending_child_frame_sink_ids_.insert(frame_sink_id);
   }
@@ -912,8 +913,8 @@
     pending_child_frame_sink_ids_.erase(it);
     return;
   }
-  GetSurfaceManager()->UnregisterFrameSinkHierarchy(frame_sink_id_,
-                                                    frame_sink_id);
+  GetFrameSinkManager()->UnregisterFrameSinkHierarchy(frame_sink_id_,
+                                                      frame_sink_id);
 }
 
 bool CompositorImpl::HavePendingReadbacks() {
diff --git a/content/browser/renderer_host/compositor_impl_android.h b/content/browser/renderer_host/compositor_impl_android.h
index b7db712..3279a62 100644
--- a/content/browser/renderer_host/compositor_impl_android.h
+++ b/content/browser/renderer_host/compositor_impl_android.h
@@ -35,10 +35,10 @@
 class AnimationHost;
 class Display;
 class FrameSinkId;
+class FrameSinkManager;
 class Layer;
 class LayerTreeHost;
 class OutputSurface;
-class SurfaceManager;
 class VulkanContextProvider;
 }
 
@@ -64,7 +64,7 @@
 
   static bool IsInitialized();
 
-  static cc::SurfaceManager* GetSurfaceManager();
+  static cc::FrameSinkManager* GetFrameSinkManager();
   static viz::HostFrameSinkManager* GetHostFrameSinkManager();
   static cc::FrameSinkId AllocateFrameSinkId();
 
diff --git a/content/browser/renderer_host/delegated_frame_host.cc b/content/browser/renderer_host/delegated_frame_host.cc
index 383015a..95e7c87 100644
--- a/content/browser/renderer_host/delegated_frame_host.cc
+++ b/content/browser/renderer_host/delegated_frame_host.cc
@@ -19,9 +19,9 @@
 #include "cc/resources/single_release_callback.h"
 #include "cc/resources/texture_mailbox.h"
 #include "cc/surfaces/compositor_frame_sink_support.h"
+#include "cc/surfaces/frame_sink_manager.h"
 #include "cc/surfaces/surface.h"
 #include "cc/surfaces/surface_hittest.h"
-#include "cc/surfaces/surface_manager.h"
 #include "components/viz/common/gl_helper.h"
 #include "content/browser/compositor/surface_utils.h"
 #include "content/browser/gpu/compositor_util.h"
@@ -53,8 +53,9 @@
       frame_evictor_(new viz::FrameEvictor(this)) {
   ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
   factory->GetContextFactory()->AddObserver(this);
-  factory->GetContextFactoryPrivate()->GetSurfaceManager()->RegisterFrameSinkId(
-      frame_sink_id_);
+  factory->GetContextFactoryPrivate()
+      ->GetFrameSinkManager()
+      ->RegisterFrameSinkId(frame_sink_id_);
   CreateCompositorFrameSinkSupport();
 }
 
@@ -176,7 +177,8 @@
   cc::SurfaceId surface_id(frame_sink_id_, local_surface_id_);
   if (!surface_id.is_valid())
     return surface_id;
-  cc::SurfaceHittest hittest(delegate, GetSurfaceManager());
+  cc::SurfaceHittest hittest(delegate,
+                             GetFrameSinkManager()->surface_manager());
   gfx::Transform target_transform;
   cc::SurfaceId target_local_surface_id =
       hittest.GetTargetSurfaceAtPoint(surface_id, point, &target_transform);
@@ -197,7 +199,7 @@
   if (original_surface == surface_id)
     return true;
 
-  cc::SurfaceHittest hittest(nullptr, GetSurfaceManager());
+  cc::SurfaceHittest hittest(nullptr, GetFrameSinkManager()->surface_manager());
   return hittest.TransformPointToTargetSurface(original_surface, surface_id,
                                                transformed_point);
 }
@@ -448,8 +450,8 @@
     EvictDelegatedFrame();
   } else {
     ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
-    cc::SurfaceManager* manager =
-        factory->GetContextFactoryPrivate()->GetSurfaceManager();
+    cc::FrameSinkManager* manager =
+        factory->GetContextFactoryPrivate()->GetFrameSinkManager();
 
     frame.metadata.latency_info.insert(frame.metadata.latency_info.end(),
                                        skipped_latency_info_list_.begin(),
@@ -466,7 +468,7 @@
       cc::SurfaceInfo surface_info(surface_id, frame_device_scale_factor,
                                    frame_size);
       client_->DelegatedFrameHostGetLayer()->SetShowPrimarySurface(
-          surface_info, manager->reference_factory());
+          surface_info, manager->surface_manager()->reference_factory());
       client_->DelegatedFrameHostGetLayer()->SetFallbackSurface(surface_info);
       current_surface_size_ = frame_size;
       current_scale_factor_ = frame_device_scale_factor;
@@ -776,7 +778,7 @@
   ResetCompositorFrameSinkSupport();
 
   factory->GetContextFactoryPrivate()
-      ->GetSurfaceManager()
+      ->GetFrameSinkManager()
       ->InvalidateFrameSinkId(frame_sink_id_);
 
   DCHECK(!vsync_manager_.get());
@@ -842,7 +844,7 @@
   constexpr bool needs_sync_points = true;
   ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
   support_ = cc::CompositorFrameSinkSupport::Create(
-      this, factory->GetContextFactoryPrivate()->GetSurfaceManager(),
+      this, factory->GetContextFactoryPrivate()->GetFrameSinkManager(),
       frame_sink_id_, is_root, handles_frame_sink_id_invalidation,
       needs_sync_points);
   if (compositor_)
diff --git a/content/browser/renderer_host/input/input_device_change_observer.cc b/content/browser/renderer_host/input/input_device_change_observer.cc
index e0b66d9..704b40c 100644
--- a/content/browser/renderer_host/input/input_device_change_observer.cc
+++ b/content/browser/renderer_host/input/input_device_change_observer.cc
@@ -4,12 +4,15 @@
 
 #include "content/browser/renderer_host/input/input_device_change_observer.h"
 #include "base/trace_event/trace_event.h"
+#include "build/build_config.h"
 #include "content/public/common/web_preferences.h"
 
 #if defined(OS_WIN)
 #include "ui/events/devices/input_device_observer_win.h"
 #elif defined(OS_LINUX)
 #include "ui/events/devices/input_device_manager.h"
+#elif defined(OS_ANDROID)
+#include "ui/events/devices/input_device_observer_android.h"
 #endif
 
 namespace content {
@@ -20,6 +23,8 @@
   ui::InputDeviceObserverWin::GetInstance()->AddObserver(this);
 #elif defined(OS_LINUX)
   ui::InputDeviceManager::GetInstance()->AddObserver(this);
+#elif defined(OS_ANDROID)
+  ui::InputDeviceObserverAndroid::GetInstance()->AddObserver(this);
 #endif
 }
 
@@ -28,6 +33,8 @@
   ui::InputDeviceObserverWin::GetInstance()->RemoveObserver(this);
 #elif defined(OS_LINUX)
   ui::InputDeviceManager::GetInstance()->RemoveObserver(this);
+#elif defined(OS_ANDROID)
+  ui::InputDeviceObserverAndroid::GetInstance()->RemoveObserver(this);
 #endif
   render_view_host_ = nullptr;
 }
diff --git a/content/browser/renderer_host/input/interaction_mq_dynamic_browsertest.cc b/content/browser/renderer_host/input/interaction_mq_dynamic_browsertest.cc
index 013f8c4..6125b87 100644
--- a/content/browser/renderer_host/input/interaction_mq_dynamic_browsertest.cc
+++ b/content/browser/renderer_host/input/interaction_mq_dynamic_browsertest.cc
@@ -4,6 +4,7 @@
 
 #include "base/macros.h"
 #include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
 #include "content/browser/renderer_host/render_view_host_impl.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test_utils.h"
@@ -25,7 +26,7 @@
 
 }  //  namespace
 
-#if defined(OS_WIN) || defined(OS_LINUX)
+#if defined(OS_WIN) || defined(OS_LINUX) || defined(OS_ANDROID)
 IN_PROC_BROWSER_TEST_F(InteractionMediaQueriesDynamicTest,
                        PointerMediaQueriesDynamic) {
   GURL test_url = GetTestUrl("", "interaction-mq-dynamic.html");
diff --git a/content/browser/renderer_host/input/legacy_ipc_widget_input_handler.cc b/content/browser/renderer_host/input/legacy_ipc_widget_input_handler.cc
index fa69e4ad..12f039c 100644
--- a/content/browser/renderer_host/input/legacy_ipc_widget_input_handler.cc
+++ b/content/browser/renderer_host/input/legacy_ipc_widget_input_handler.cc
@@ -98,7 +98,7 @@
 }
 
 void LegacyIPCWidgetInputHandler::DispatchEvent(
-    content::mojom::EventPtr event,
+    std::unique_ptr<content::InputEvent> event,
     DispatchEventCallback callback) {
   // We only expect these events to be called with the mojo enabled input
   // channel. The LegacyInputRouterImpl will handle sending the events
@@ -107,7 +107,7 @@
 }
 
 void LegacyIPCWidgetInputHandler::DispatchNonBlockingEvent(
-    content::mojom::EventPtr) {
+    std::unique_ptr<content::InputEvent> event) {
   // We only expect these events to be called with the mojo enabled input
   // channel. The LegacyInputRouterImpl will handle sending the events
   // directly.
diff --git a/content/browser/renderer_host/input/legacy_ipc_widget_input_handler.h b/content/browser/renderer_host/input/legacy_ipc_widget_input_handler.h
index af0ee2d..9bc8f0c6 100644
--- a/content/browser/renderer_host/input/legacy_ipc_widget_input_handler.h
+++ b/content/browser/renderer_host/input/legacy_ipc_widget_input_handler.h
@@ -41,9 +41,10 @@
   void RequestTextInputStateUpdate() override;
   void RequestCompositionUpdates(bool immediate_request,
                                  bool monitor_request) override;
-  void DispatchEvent(content::mojom::EventPtr event,
+  void DispatchEvent(std::unique_ptr<content::InputEvent> event,
                      DispatchEventCallback callback) override;
-  void DispatchNonBlockingEvent(content::mojom::EventPtr) override;
+  void DispatchNonBlockingEvent(
+      std::unique_ptr<content::InputEvent> event) override;
 
  private:
   void SendInput(std::unique_ptr<IPC::Message> message);
diff --git a/content/browser/renderer_host/offscreen_canvas_surface_impl.cc b/content/browser/renderer_host/offscreen_canvas_surface_impl.cc
index 745e0b1..7cb16fd1 100644
--- a/content/browser/renderer_host/offscreen_canvas_surface_impl.cc
+++ b/content/browser/renderer_host/offscreen_canvas_surface_impl.cc
@@ -8,7 +8,7 @@
 #include <utility>
 
 #include "base/memory/ptr_util.h"
-#include "cc/surfaces/surface_manager.h"
+#include "cc/surfaces/frame_sink_manager.h"
 #include "components/viz/host/host_frame_sink_manager.h"
 #include "content/browser/compositor/surface_utils.h"
 
@@ -70,11 +70,12 @@
 
 void OffscreenCanvasSurfaceImpl::Require(const cc::SurfaceId& surface_id,
                                          const cc::SurfaceSequence& sequence) {
-  GetSurfaceManager()->RequireSequence(surface_id, sequence);
+  GetFrameSinkManager()->surface_manager()->RequireSequence(surface_id,
+                                                            sequence);
 }
 
 void OffscreenCanvasSurfaceImpl::Satisfy(const cc::SurfaceSequence& sequence) {
-  GetSurfaceManager()->SatisfySequence(sequence);
+  GetFrameSinkManager()->surface_manager()->SatisfySequence(sequence);
 }
 
 void OffscreenCanvasSurfaceImpl::OnSurfaceConnectionClosed() {
diff --git a/content/browser/renderer_host/render_message_filter.cc b/content/browser/renderer_host/render_message_filter.cc
index 5f4a396a..43b7512a120 100644
--- a/content/browser/renderer_host/render_message_filter.cc
+++ b/content/browser/renderer_host/render_message_filter.cc
@@ -201,17 +201,21 @@
 
 void RenderMessageFilter::CreateNewWidget(int32_t opener_id,
                                           blink::WebPopupType popup_type,
+                                          mojom::WidgetPtr widget,
                                           CreateNewWidgetCallback callback) {
   int route_id = MSG_ROUTING_NONE;
-  render_widget_helper_->CreateNewWidget(opener_id, popup_type, &route_id);
+  render_widget_helper_->CreateNewWidget(opener_id, popup_type,
+                                         std::move(widget), &route_id);
   std::move(callback).Run(route_id);
 }
 
 void RenderMessageFilter::CreateFullscreenWidget(
     int opener_id,
+    mojom::WidgetPtr widget,
     CreateFullscreenWidgetCallback callback) {
   int route_id = 0;
-  render_widget_helper_->CreateNewFullscreenWidget(opener_id, &route_id);
+  render_widget_helper_->CreateNewFullscreenWidget(opener_id, std::move(widget),
+                                                   &route_id);
   std::move(callback).Run(route_id);
 }
 
diff --git a/content/browser/renderer_host/render_message_filter.h b/content/browser/renderer_host/render_message_filter.h
index 5cdea21..68b59b1 100644
--- a/content/browser/renderer_host/render_message_filter.h
+++ b/content/browser/renderer_host/render_message_filter.h
@@ -113,8 +113,10 @@
   void GenerateRoutingID(GenerateRoutingIDCallback routing_id) override;
   void CreateNewWidget(int32_t opener_id,
                        blink::WebPopupType popup_type,
+                       mojom::WidgetPtr widget,
                        CreateNewWidgetCallback callback) override;
   void CreateFullscreenWidget(int opener_id,
+                              mojom::WidgetPtr widget,
                               CreateFullscreenWidgetCallback callback) override;
   void GetSharedBitmapAllocationNotifier(
       cc::mojom::SharedBitmapAllocationNotifierAssociatedRequest request)
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index a0791373..caea5b7 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -2898,6 +2898,10 @@
 
 void RenderProcessHostImpl::SetEchoCanceller3(bool enable) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  // TODO(hlundin) Implement a test to verify that the setting works both with
+  // aec_dump_consumers already registered, and with those registered in the
+  // future. crbug.com/740104
+  override_aec3_ = enable;
 
   // Piggybacking on AEC dumps.
   // TODO(hlundin): Change name for aec_dump_consumers_;
@@ -3730,6 +3734,9 @@
         WebRTCInternals::GetInstance()->GetAudioDebugRecordingsFilePath());
     EnableAecDumpForId(file_with_extensions, id);
   }
+  if (override_aec3_) {
+    Send(new AudioProcessingMsg_EnableAec3(id, *override_aec3_));
+  }
 }
 
 void RenderProcessHostImpl::UnregisterAecDumpConsumerOnUIThread(int id) {
diff --git a/content/browser/renderer_host/render_process_host_impl.h b/content/browser/renderer_host/render_process_host_impl.h
index 94a0d1f7..9889ce7 100644
--- a/content/browser/renderer_host/render_process_host_impl.h
+++ b/content/browser/renderer_host/render_process_host_impl.h
@@ -650,6 +650,7 @@
 
   // Must be accessed on UI thread.
   std::vector<int> aec_dump_consumers_;
+  base::Optional<bool> override_aec3_;
 
   WebRtcStopRtpDumpCallback stop_rtp_dump_callback_;
 
diff --git a/content/browser/renderer_host/render_view_host_delegate.h b/content/browser/renderer_host/render_view_host_delegate.h
index 1490ac4..1740108 100644
--- a/content/browser/renderer_host/render_view_host_delegate.h
+++ b/content/browser/renderer_host/render_view_host_delegate.h
@@ -14,6 +14,7 @@
 #include "base/strings/string16.h"
 #include "content/browser/dom_storage/session_storage_namespace_impl.h"
 #include "content/common/content_export.h"
+#include "content/common/render_message_filter.mojom.h"
 #include "net/base/load_states.h"
 #include "third_party/WebKit/public/web/WebPopupType.h"
 
@@ -130,11 +131,13 @@
   // is (select, autofill...).
   virtual void CreateNewWidget(int32_t render_process_id,
                                int32_t route_id,
+                               mojom::WidgetPtr widget,
                                blink::WebPopupType popup_type) {}
 
   // Creates a full screen RenderWidget. Similar to above.
   virtual void CreateNewFullscreenWidget(int32_t render_process_id,
-                                         int32_t route_id) {}
+                                         int32_t route_id,
+                                         mojom::WidgetPtr widget) {}
 
   // Show the newly created widget with the specified bounds.
   // The widget is identified by the route_id passed to CreateNewWidget.
diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc
index 006dab2..1b14ec9 100644
--- a/content/browser/renderer_host/render_view_host_impl.cc
+++ b/content/browser/renderer_host/render_view_host_impl.cc
@@ -756,12 +756,16 @@
 }
 
 void RenderViewHostImpl::CreateNewWidget(int32_t route_id,
+                                         mojom::WidgetPtr widget,
                                          blink::WebPopupType popup_type) {
-  delegate_->CreateNewWidget(GetProcess()->GetID(), route_id, popup_type);
+  delegate_->CreateNewWidget(GetProcess()->GetID(), route_id, std::move(widget),
+                             popup_type);
 }
 
-void RenderViewHostImpl::CreateNewFullscreenWidget(int32_t route_id) {
-  delegate_->CreateNewFullscreenWidget(GetProcess()->GetID(), route_id);
+void RenderViewHostImpl::CreateNewFullscreenWidget(int32_t route_id,
+                                                   mojom::WidgetPtr widget) {
+  delegate_->CreateNewFullscreenWidget(GetProcess()->GetID(), route_id,
+                                       std::move(widget));
 }
 
 void RenderViewHostImpl::OnShowWidget(int route_id,
diff --git a/content/browser/renderer_host/render_view_host_impl.h b/content/browser/renderer_host/render_view_host_impl.h
index c36ebdd..7e0cc57 100644
--- a/content/browser/renderer_host/render_view_host_impl.h
+++ b/content/browser/renderer_host/render_view_host_impl.h
@@ -24,6 +24,7 @@
 #include "content/browser/renderer_host/render_widget_host_impl.h"
 #include "content/browser/renderer_host/render_widget_host_owner_delegate.h"
 #include "content/browser/site_instance_impl.h"
+#include "content/common/render_message_filter.mojom.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/render_process_host_observer.h"
 #include "content/public/browser/render_view_host.h"
@@ -177,10 +178,12 @@
 
   // Creates a new RenderWidget with the given route id.  |popup_type| indicates
   // if this widget is a popup and what kind of popup it is (select, autofill).
-  void CreateNewWidget(int32_t route_id, blink::WebPopupType popup_type);
+  void CreateNewWidget(int32_t route_id,
+                       mojom::WidgetPtr widget,
+                       blink::WebPopupType popup_type);
 
   // Creates a full screen RenderWidget.
-  void CreateNewFullscreenWidget(int32_t route_id);
+  void CreateNewFullscreenWidget(int32_t route_id, mojom::WidgetPtr widget);
 
   // Send RenderViewReady to observers once the process is launched, but not
   // re-entrantly.
diff --git a/content/browser/renderer_host/render_view_host_unittest.cc b/content/browser/renderer_host/render_view_host_unittest.cc
index b2f5bd5..28d28aa 100644
--- a/content/browser/renderer_host/render_view_host_unittest.cc
+++ b/content/browser/renderer_host/render_view_host_unittest.cc
@@ -31,6 +31,15 @@
 
 namespace content {
 
+class WidgetImpl : public mojom::Widget {
+ public:
+  explicit WidgetImpl(mojo::InterfaceRequest<mojom::Widget> request)
+      : binding_(this, std::move(request)) {}
+
+ private:
+  mojo::Binding<mojom::Widget> binding_;
+};
+
 class RenderViewHostTestBrowserClient : public TestContentBrowserClient {
  public:
   RenderViewHostTestBrowserClient() {}
@@ -79,7 +88,11 @@
 // Create a full screen popup RenderWidgetHost and View.
 TEST_F(RenderViewHostTest, CreateFullscreenWidget) {
   int32_t routing_id = process()->GetNextRoutingID();
-  test_rvh()->CreateNewFullscreenWidget(routing_id);
+
+  mojom::WidgetPtr widget;
+  std::unique_ptr<WidgetImpl> widget_impl =
+      base::MakeUnique<WidgetImpl>(mojo::MakeRequest(&widget));
+  test_rvh()->CreateNewFullscreenWidget(routing_id, std::move(widget));
 }
 
 // Ensure we do not grant bindings to a process shared with unprivileged views.
diff --git a/content/browser/renderer_host/render_widget_helper.cc b/content/browser/renderer_host/render_widget_helper.cc
index 22c246a..9c1be29 100644
--- a/content/browser/renderer_host/render_widget_helper.cc
+++ b/content/browser/renderer_host/render_widget_helper.cc
@@ -87,37 +87,48 @@
 
 void RenderWidgetHelper::CreateNewWidget(int opener_id,
                                          blink::WebPopupType popup_type,
+                                         mojom::WidgetPtr widget,
                                          int* route_id) {
   *route_id = GetNextRoutingID();
-  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
-                          base::Bind(&RenderWidgetHelper::OnCreateWidgetOnUI,
-                                     this, opener_id, *route_id, popup_type));
+
+  BrowserThread::PostTask(
+      BrowserThread::UI, FROM_HERE,
+      base::BindOnce(&RenderWidgetHelper::OnCreateWidgetOnUI, this, opener_id,
+                     *route_id, widget.PassInterface(), popup_type));
 }
 
 void RenderWidgetHelper::CreateNewFullscreenWidget(int opener_id,
+                                                   mojom::WidgetPtr widget,
                                                    int* route_id) {
   *route_id = GetNextRoutingID();
   BrowserThread::PostTask(
       BrowserThread::UI, FROM_HERE,
-      base::Bind(&RenderWidgetHelper::OnCreateFullscreenWidgetOnUI, this,
-                 opener_id, *route_id));
+      base::BindOnce(&RenderWidgetHelper::OnCreateFullscreenWidgetOnUI, this,
+                     opener_id, *route_id, widget.PassInterface()));
 }
 
 void RenderWidgetHelper::OnCreateWidgetOnUI(int32_t opener_id,
                                             int32_t route_id,
+                                            mojom::WidgetPtrInfo widget_info,
                                             blink::WebPopupType popup_type) {
+  mojom::WidgetPtr widget;
+  widget.Bind(std::move(widget_info));
   RenderViewHostImpl* host = RenderViewHostImpl::FromID(
       render_process_id_, opener_id);
   if (host)
-    host->CreateNewWidget(route_id, popup_type);
+    host->CreateNewWidget(route_id, std::move(widget), popup_type);
 }
 
-void RenderWidgetHelper::OnCreateFullscreenWidgetOnUI(int32_t opener_id,
-                                                      int32_t route_id) {
+void RenderWidgetHelper::OnCreateFullscreenWidgetOnUI(
+    int32_t opener_id,
+    int32_t route_id,
+    mojom::WidgetPtrInfo widget_info) {
+  mojom::WidgetPtr widget;
+  widget.Bind(std::move(widget_info));
   RenderViewHostImpl* host = RenderViewHostImpl::FromID(
       render_process_id_, opener_id);
   if (host)
-    host->CreateNewFullscreenWidget(route_id);
+    host->CreateNewFullscreenWidget(route_id, std::move(widget));
 }
 
 }  // namespace content
diff --git a/content/browser/renderer_host/render_widget_helper.h b/content/browser/renderer_host/render_widget_helper.h
index ffd67fe..a6e185c1 100644
--- a/content/browser/renderer_host/render_widget_helper.h
+++ b/content/browser/renderer_host/render_widget_helper.h
@@ -58,8 +58,11 @@
   // IO THREAD ONLY -----------------------------------------------------------
   void CreateNewWidget(int opener_id,
                        blink::WebPopupType popup_type,
+                       mojom::WidgetPtr,
                        int* route_id);
-  void CreateNewFullscreenWidget(int opener_id, int* route_id);
+  void CreateNewFullscreenWidget(int opener_id,
+                                 mojom::WidgetPtr,
+                                 int* route_id);
 
  private:
   friend class base::RefCountedThreadSafe<RenderWidgetHelper>;
@@ -71,10 +74,13 @@
   // Called on the UI thread to finish creating a widget.
   void OnCreateWidgetOnUI(int32_t opener_id,
                           int32_t route_id,
+                          mojom::WidgetPtrInfo widget,
                           blink::WebPopupType popup_type);
 
   // Called on the UI thread to create a fullscreen widget.
-  void OnCreateFullscreenWidgetOnUI(int32_t opener_id, int32_t route_id);
+  void OnCreateFullscreenWidgetOnUI(int32_t opener_id,
+                                    int32_t route_id,
+                                    mojom::WidgetPtrInfo widget);
 
   // Called on the IO thread to resume a paused navigation in the network
   // stack without transferring it to a new renderer process.
diff --git a/content/browser/renderer_host/render_widget_host_view_android.cc b/content/browser/renderer_host/render_widget_host_view_android.cc
index d4b5e98..845d0f8d 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.cc
+++ b/content/browser/renderer_host/render_widget_host_view_android.cc
@@ -30,9 +30,9 @@
 #include "cc/output/copy_output_result.h"
 #include "cc/output/latency_info_swap_promise.h"
 #include "cc/resources/single_release_callback.h"
+#include "cc/surfaces/frame_sink_manager.h"
 #include "cc/surfaces/surface.h"
 #include "cc/surfaces/surface_hittest.h"
-#include "cc/surfaces/surface_manager.h"
 #include "cc/trees/layer_tree_host.h"
 #include "components/viz/common/gl_helper.h"
 #include "content/browser/accessibility/browser_accessibility_manager_android.h"
@@ -480,7 +480,7 @@
     cc::FrameSinkId frame_sink_id =
         host_->AllocateFrameSinkId(false /* is_guest_view_hack */);
     delegated_frame_host_.reset(new ui::DelegatedFrameHostAndroid(
-        &view_, CompositorImpl::GetSurfaceManager(), this, frame_sink_id));
+        &view_, CompositorImpl::GetFrameSinkManager(), this, frame_sink_id));
 
     // Let the page-level input event router know about our frame sink ID
     // for surface-based hit testing.
@@ -842,7 +842,8 @@
 
   cc::SurfaceId surface_id = delegated_frame_host_->SurfaceId();
   if (surface_id.is_valid()) {
-    cc::SurfaceHittest hittest(delegate, GetSurfaceManager());
+    cc::SurfaceHittest hittest(delegate,
+                               GetFrameSinkManager()->surface_manager());
     gfx::Transform target_transform;
     surface_id = hittest.GetTargetSurfaceAtPoint(surface_id, point_in_pixels,
                                                  &target_transform);
@@ -905,7 +906,7 @@
     return true;
 
   *transformed_point = point_in_pixels;
-  cc::SurfaceHittest hittest(nullptr, GetSurfaceManager());
+  cc::SurfaceHittest hittest(nullptr, GetFrameSinkManager()->surface_manager());
   if (!hittest.TransformPointToTargetSurface(original_surface, surface_id,
                                              transformed_point))
     return false;
diff --git a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
index 2e142be..489533b 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
@@ -27,9 +27,9 @@
 #include "cc/output/compositor_frame.h"
 #include "cc/output/compositor_frame_metadata.h"
 #include "cc/output/copy_output_request.h"
+#include "cc/surfaces/frame_sink_manager.h"
 #include "cc/surfaces/local_surface_id_allocator.h"
 #include "cc/surfaces/surface.h"
-#include "cc/surfaces/surface_manager.h"
 #include "cc/test/begin_frame_args_test.h"
 #include "cc/test/fake_external_begin_frame_source.h"
 #include "cc/test/fake_surface_observer.h"
@@ -2447,8 +2447,9 @@
 TEST_F(RenderWidgetHostViewAuraTest, TwoOutputSurfaces) {
   cc::FakeSurfaceObserver manager_observer;
   ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
-  cc::SurfaceManager* manager =
-      factory->GetContextFactoryPrivate()->GetSurfaceManager();
+  cc::SurfaceManager* manager = factory->GetContextFactoryPrivate()
+                                    ->GetFrameSinkManager()
+                                    ->surface_manager();
   manager->AddObserver(&manager_observer);
 
   gfx::Size view_size(100, 100);
@@ -2600,8 +2601,9 @@
   cc::SurfaceId id = view_->GetDelegatedFrameHost()->SurfaceIdForTesting();
   if (id.is_valid()) {
     ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
-    cc::SurfaceManager* manager =
-        factory->GetContextFactoryPrivate()->GetSurfaceManager();
+    cc::SurfaceManager* manager = factory->GetContextFactoryPrivate()
+                                      ->GetFrameSinkManager()
+                                      ->surface_manager();
     cc::Surface* surface = manager->GetSurfaceForId(id);
     EXPECT_TRUE(surface);
 
@@ -3446,8 +3448,9 @@
 
   cc::FakeSurfaceObserver observer;
   ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
-  cc::SurfaceManager* surface_manager =
-      factory->GetContextFactoryPrivate()->GetSurfaceManager();
+  cc::SurfaceManager* surface_manager = factory->GetContextFactoryPrivate()
+                                            ->GetFrameSinkManager()
+                                            ->surface_manager();
   surface_manager->AddObserver(&observer);
 
   view_->SetNeedsBeginFrames(true);
diff --git a/content/browser/security_exploit_browsertest.cc b/content/browser/security_exploit_browsertest.cc
index 28b9a02..35c121a 100644
--- a/content/browser/security_exploit_browsertest.cc
+++ b/content/browser/security_exploit_browsertest.cc
@@ -173,6 +173,14 @@
 
 }  // namespace
 
+class WidgetImpl : public mojom::Widget {
+ public:
+  explicit WidgetImpl(mojo::InterfaceRequest<mojom::Widget> request)
+      : binding_(this, std::move(request)) {}
+
+ private:
+  mojo::Binding<mojom::Widget> binding_;
+};
 
 // The goal of these tests will be to "simulate" exploited renderer processes,
 // which can send arbitrary IPC messages and confuse browser process internal
@@ -294,11 +302,16 @@
       PrepareToDuplicateHosts(shell(), &duplicate_routing_id);
   EXPECT_NE(MSG_ROUTING_NONE, duplicate_routing_id);
 
+  mojom::WidgetPtr widget;
+  std::unique_ptr<WidgetImpl> widget_impl =
+      base::MakeUnique<WidgetImpl>(mojo::MakeRequest(&widget));
+
   // Since this test executes on the UI thread and hopping threads might cause
   // different timing in the test, let's simulate a CreateNewWidget call coming
   // from the IO thread.  Use the existing window routing id to cause a
   // deliberate collision.
-  pending_rvh->CreateNewWidget(duplicate_routing_id, blink::kWebPopupTypePage);
+  pending_rvh->CreateNewWidget(duplicate_routing_id, std::move(widget),
+                               blink::kWebPopupTypePage);
 
   // If the above operation doesn't crash, the test has succeeded!
 }
diff --git a/content/browser/web_contents/web_contents_android.cc b/content/browser/web_contents/web_contents_android.cc
index 84ea2af..52df13b 100644
--- a/content/browser/web_contents/web_contents_android.cc
+++ b/content/browser/web_contents/web_contents_android.cc
@@ -317,12 +317,6 @@
   return rwhva->GetCachedBackgroundColor();
 }
 
-ScopedJavaLocalRef<jstring> WebContentsAndroid::GetURL(
-    JNIEnv* env,
-    const JavaParamRef<jobject>& obj) const {
-  return ConvertUTF8ToJavaString(env, web_contents_->GetURL().spec());
-}
-
 ScopedJavaLocalRef<jstring> WebContentsAndroid::GetLastCommittedURL(
     JNIEnv* env,
     const JavaParamRef<jobject>&) const {
diff --git a/content/browser/web_contents/web_contents_android.h b/content/browser/web_contents/web_contents_android.h
index 6d26a839..f18a3b8 100644
--- a/content/browser/web_contents/web_contents_android.h
+++ b/content/browser/web_contents/web_contents_android.h
@@ -74,9 +74,6 @@
                          const base::android::JavaParamRef<jobject>& obj);
   jint GetBackgroundColor(JNIEnv* env,
                           const base::android::JavaParamRef<jobject>& obj);
-  base::android::ScopedJavaLocalRef<jstring> GetURL(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>&) const;
   base::android::ScopedJavaLocalRef<jstring> GetLastCommittedURL(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>&) const;
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 9738d39b..cb077e5bd 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -2357,18 +2357,23 @@
 
 void WebContentsImpl::CreateNewWidget(int32_t render_process_id,
                                       int32_t route_id,
+                                      mojom::WidgetPtr widget,
                                       blink::WebPopupType popup_type) {
-  CreateNewWidget(render_process_id, route_id, false, popup_type);
+  CreateNewWidget(render_process_id, route_id, false, std::move(widget),
+                  popup_type);
 }
 
 void WebContentsImpl::CreateNewFullscreenWidget(int32_t render_process_id,
-                                                int32_t route_id) {
-  CreateNewWidget(render_process_id, route_id, true, blink::kWebPopupTypeNone);
+                                                int32_t route_id,
+                                                mojom::WidgetPtr widget) {
+  CreateNewWidget(render_process_id, route_id, true, std::move(widget),
+                  blink::kWebPopupTypeNone);
 }
 
 void WebContentsImpl::CreateNewWidget(int32_t render_process_id,
                                       int32_t route_id,
                                       bool is_fullscreen,
+                                      mojom::WidgetPtr widget,
                                       blink::WebPopupType popup_type) {
   RenderProcessHost* process = RenderProcessHost::FromID(render_process_id);
   // A message to create a new widget can only come from an active process for
@@ -3157,6 +3162,12 @@
   return interstitial_page_ != nullptr;
 }
 
+void WebContentsImpl::AdjustPreviewsStateForNavigation(
+    PreviewsState* previews_state) {
+  if (delegate_)
+    delegate_->AdjustPreviewsStateForNavigation(previews_state);
+}
+
 InterstitialPage* WebContentsImpl::GetInterstitialPage() const {
   return interstitial_page_;
 }
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index 785da1a..d897b52 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -403,6 +403,7 @@
   void RestoreFocus() override;
   void FocusThroughTabTraversal(bool reverse) override;
   bool ShowingInterstitialPage() const override;
+  void AdjustPreviewsStateForNavigation(PreviewsState* previews_state) override;
   InterstitialPage* GetInterstitialPage() const override;
   bool IsSavable() override;
   void OnSavePage() override;
@@ -599,9 +600,11 @@
   void UpdatePreferredSize(const gfx::Size& pref_size) override;
   void CreateNewWidget(int32_t render_process_id,
                        int32_t route_id,
+                       mojom::WidgetPtr widget,
                        blink::WebPopupType popup_type) override;
   void CreateNewFullscreenWidget(int32_t render_process_id,
-                                 int32_t route_id) override;
+                                 int32_t route_id,
+                                 mojom::WidgetPtr widget) override;
   void ShowCreatedWidget(int process_id,
                          int route_id,
                          const gfx::Rect& initial_rect) override;
@@ -1191,6 +1194,7 @@
   void CreateNewWidget(int32_t render_process_id,
                        int32_t route_id,
                        bool is_fullscreen,
+                       mojom::WidgetPtr widget,
                        blink::WebPopupType popup_type);
 
   // Helper for ShowCreatedWidget/ShowCreatedFullscreenWidget.
diff --git a/content/browser/webrtc/webrtc_media_recorder_browsertest.cc b/content/browser/webrtc/webrtc_media_recorder_browsertest.cc
index 2a51201..3d815a27 100644
--- a/content/browser/webrtc/webrtc_media_recorder_browsertest.cc
+++ b/content/browser/webrtc/webrtc_media_recorder_browsertest.cc
@@ -73,13 +73,7 @@
   MakeTypicalCall("testStartStopAndRecorderState();", kMediaRecorderHtmlFile);
 }
 
-// Flaky on Linux Tsan (crbug.com/736268)
-#if defined(THREAD_SANITIZER)
-#define MAYBE_StartAndDataAvailable DISABLED_StartAndDataAvailable
-#else
-#define MAYBE_StartAndDataAvailable StartAndDataAvailable
-#endif
-IN_PROC_BROWSER_TEST_P(WebRtcMediaRecorderTest, MAYBE_StartAndDataAvailable) {
+IN_PROC_BROWSER_TEST_P(WebRtcMediaRecorderTest, StartAndDataAvailable) {
   MaybeForceDisableEncodeAccelerator(GetParam().disable_accelerator);
   MakeTypicalCall(base::StringPrintf("testStartAndDataAvailable(\"%s\");",
                                      GetParam().mime_type.c_str()),
@@ -150,41 +144,18 @@
                   kMediaRecorderHtmlFile);
 }
 
-// Flaky on Linux Tsan (crbug.com/736268)
-#if defined(THREAD_SANITIZER)
-#define MAYBE_IllegalStopThrowsDOMError DISABLED_IllegalStopThrowsDOMError
-#else
-#define MAYBE_IllegalStopThrowsDOMError IllegalStopThrowsDOMError
-#endif
-IN_PROC_BROWSER_TEST_F(WebRtcMediaRecorderTest,
-                       MAYBE_IllegalStopThrowsDOMError) {
+IN_PROC_BROWSER_TEST_F(WebRtcMediaRecorderTest, IllegalStopThrowsDOMError) {
   MakeTypicalCall("testIllegalStopThrowsDOMError();", kMediaRecorderHtmlFile);
 }
 
-// Flaky on Linux Tsan (crbug.com/736268)
-#if defined(THREAD_SANITIZER)
-#define MAYBE_IllegalStartWhileRecordingThrowsDOMError \
-  DISABLED_IllegalStartWhileRecordingThrowsDOMError
-#else
-#define MAYBE_IllegalStartWhileRecordingThrowsDOMError \
-  IllegalStartWhileRecordingThrowsDOMError
-#endif
 IN_PROC_BROWSER_TEST_F(WebRtcMediaRecorderTest,
-                       MAYBE_IllegalStartWhileRecordingThrowsDOMError) {
+                       IllegalStartWhileRecordingThrowsDOMError) {
   MakeTypicalCall("testIllegalStartInRecordingStateThrowsDOMError();",
                   kMediaRecorderHtmlFile);
 }
 
-// Flaky on Linux Tsan (crbug.com/736268)
-#if defined(THREAD_SANITIZER)
-#define MAYBE_IllegalStartWhilePausedThrowsDOMError \
-  DISABLED_IllegalStartWhilePausedThrowsDOMError
-#else
-#define MAYBE_IllegalStartWhilePausedThrowsDOMError \
-  IllegalStartWhilePausedThrowsDOMError
-#endif
 IN_PROC_BROWSER_TEST_F(WebRtcMediaRecorderTest,
-                       MAYBE_IllegalStartWhilePausedThrowsDOMError) {
+                       IllegalStartWhilePausedThrowsDOMError) {
   MakeTypicalCall("testIllegalStartInPausedStateThrowsDOMError();",
                   kMediaRecorderHtmlFile);
 }
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index f871bb4..0cc4552 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -176,6 +176,8 @@
     "input/input_event_dispatch_type.h",
     "input/input_event_stream_validator.cc",
     "input/input_event_stream_validator.h",
+    "input/input_event_struct_traits.cc",
+    "input/input_event_struct_traits.h",
     "input/input_param_traits.cc",
     "input/input_param_traits.h",
     "input/synthetic_gesture_packet.cc",
@@ -283,8 +285,6 @@
     "sandbox_init_mac.cc",
     "sandbox_init_mac.h",
     "sandbox_init_win.cc",
-    "sandbox_linux/android/sandbox_bpf_base_policy_android.cc",
-    "sandbox_linux/android/sandbox_bpf_base_policy_android.h",
     "sandbox_linux/bpf_cros_arm_gpu_policy_linux.cc",
     "sandbox_linux/bpf_cros_arm_gpu_policy_linux.h",
     "sandbox_linux/bpf_gpu_policy_linux.cc",
@@ -540,12 +540,6 @@
         "sandbox_linux/sandbox_bpf_base_policy_linux.h",
       ]
     }
-    if (is_android) {
-      sources -= [
-        "sandbox_linux/android/sandbox_bpf_base_policy_android.cc",
-        "sandbox_linux/android/sandbox_bpf_base_policy_android.h",
-      ]
-    }
   }
 
   if (is_mac) {
@@ -626,6 +620,7 @@
     "service_worker/service_worker_types.mojom",
     "storage_partition_service.mojom",
     "video_capture.mojom",
+    "widget.mojom",
     "worker_url_loader_factory_provider.mojom",
   ]
 
diff --git a/content/common/input/OWNERS b/content/common/input/OWNERS
index d74c0a7..ec220b4 100644
--- a/content/common/input/OWNERS
+++ b/content/common/input/OWNERS
@@ -6,6 +6,8 @@
 per-file *.mojom=file://ipc/SECURITY_OWNERS
 per-file *_param_traits*.*=set noparent
 per-file *_param_traits*.*=file://ipc/SECURITY_OWNERS
+per-file *_struct_traits*.*=set noparent
+per-file *_struct_traits*.*=file://ipc/SECURITY_OWNERS
 
 # TEAM: input-dev@chromium.org
 # COMPONENT: Blink>Input
diff --git a/content/common/input/input_event_struct_traits.cc b/content/common/input/input_event_struct_traits.cc
new file mode 100644
index 0000000..a02c660
--- /dev/null
+++ b/content/common/input/input_event_struct_traits.cc
@@ -0,0 +1,543 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/common/input/input_event_struct_traits.h"
+
+#include "base/i18n/char_iterator.h"
+#include "content/common/input_messages.h"
+#include "third_party/WebKit/public/platform/WebKeyboardEvent.h"
+#include "ui/latency/mojo/latency_info_struct_traits.h"
+
+namespace mojo {
+namespace {
+
+void CopyString(blink::WebUChar* dst, const base::string16& text) {
+  base::i18n::UTF16CharIterator iter(&text);
+  size_t pos = 0;
+  while (!iter.end() && pos < blink::WebKeyboardEvent::kTextLengthCap - 1) {
+    dst[pos++] = iter.get();
+    iter.Advance();
+  }
+  dst[pos] = '\0';
+}
+
+content::mojom::PointerDataPtr PointerDataFromPointerProperties(
+    const blink::WebPointerProperties& pointer,
+    content::mojom::MouseDataPtr mouse_data) {
+  return content::mojom::PointerData::New(
+      pointer.id, pointer.force, pointer.tilt_x, pointer.tilt_y,
+      pointer.tangential_pressure, pointer.twist, pointer.button,
+      pointer.pointer_type, pointer.movement_x, pointer.movement_y,
+      pointer.PositionInWidget(), pointer.PositionInScreen(),
+      std::move(mouse_data));
+}
+
+void PointerPropertiesFromPointerData(
+    const content::mojom::PointerDataPtr& pointer_data,
+    blink::WebPointerProperties* pointer_properties) {
+  pointer_properties->id = pointer_data->pointer_id;
+  pointer_properties->force = pointer_data->force;
+  pointer_properties->tilt_x = pointer_data->tilt_x;
+  pointer_properties->tilt_y = pointer_data->tilt_y;
+  pointer_properties->tangential_pressure = pointer_data->tangential_pressure;
+  pointer_properties->twist = pointer_data->twist;
+  pointer_properties->button = pointer_data->button;
+  pointer_properties->pointer_type = pointer_data->pointer_type;
+  pointer_properties->movement_x = pointer_data->movement_x;
+  pointer_properties->movement_y = pointer_data->movement_y;
+}
+
+void TouchPointPropertiesFromPointerData(
+    const content::mojom::TouchPointPtr& mojo_touch_point,
+    blink::WebTouchPoint* touch_point) {
+  PointerPropertiesFromPointerData(mojo_touch_point->pointer_data, touch_point);
+  touch_point->state = mojo_touch_point->state;
+  touch_point->radius_x = mojo_touch_point->radius_x;
+  touch_point->radius_y = mojo_touch_point->radius_y;
+  touch_point->rotation_angle = mojo_touch_point->rotation_angle;
+  touch_point->SetPositionInWidget(
+      mojo_touch_point->pointer_data->widget_position.x(),
+      mojo_touch_point->pointer_data->widget_position.y());
+  touch_point->SetPositionInScreen(
+      mojo_touch_point->pointer_data->screen_position.x(),
+      mojo_touch_point->pointer_data->screen_position.y());
+}
+
+// TODO(dtapuska): Remove once SetPositionInXXX moves to WebPointerProperties.
+void MouseEventPropertiesFromPointerData(
+    const content::mojom::PointerDataPtr& pointer_data,
+    blink::WebMouseEvent* mouse_event) {
+  PointerPropertiesFromPointerData(pointer_data, mouse_event);
+  mouse_event->SetPositionInWidget(pointer_data->widget_position.x(),
+                                   pointer_data->widget_position.y());
+  mouse_event->SetPositionInScreen(pointer_data->screen_position.x(),
+                                   pointer_data->screen_position.y());
+}
+
+}  // namespace
+
+bool StructTraits<content::mojom::EventDataView, InputEventUniquePtr>::Read(
+    content::mojom::EventDataView event,
+    InputEventUniquePtr* out) {
+  DCHECK(!out->get());
+
+  out->reset(new content::InputEvent());
+
+  blink::WebInputEvent::Type type;
+  if (!event.ReadType(&type))
+    return false;
+
+  if (blink::WebInputEvent::IsKeyboardEventType(type)) {
+    content::mojom::KeyDataPtr key_data;
+    if (!event.ReadKeyData<content::mojom::KeyDataPtr>(&key_data))
+      return false;
+
+    (*out)->web_event.reset(new blink::WebKeyboardEvent(
+        type, event.modifiers(), event.timestamp_seconds()));
+
+    blink::WebKeyboardEvent* key_event =
+        static_cast<blink::WebKeyboardEvent*>((*out)->web_event.get());
+    key_event->windows_key_code = key_data->windows_key_code;
+    key_event->native_key_code = key_data->native_key_code;
+    key_event->dom_code = key_data->dom_code;
+    key_event->dom_key = key_data->dom_key;
+    key_event->is_system_key = key_data->is_system_key;
+    key_event->is_browser_shortcut = key_data->is_browser_shortcut;
+    CopyString(key_event->text, key_data->text);
+    CopyString(key_event->unmodified_text, key_data->unmodified_text);
+  } else if (blink::WebInputEvent::IsGestureEventType(type)) {
+    content::mojom::GestureDataPtr gesture_data;
+    if (!event.ReadGestureData<content::mojom::GestureDataPtr>(&gesture_data))
+      return false;
+    (*out)->web_event.reset(new blink::WebGestureEvent(
+        type, event.modifiers(), event.timestamp_seconds()));
+
+    blink::WebGestureEvent* gesture_event =
+        static_cast<blink::WebGestureEvent*>((*out)->web_event.get());
+    gesture_event->x = gesture_data->widget_position.x();
+    gesture_event->y = gesture_data->widget_position.y();
+    gesture_event->global_x = gesture_data->screen_position.x();
+    gesture_event->global_y = gesture_data->screen_position.y();
+    gesture_event->source_device = gesture_data->source_device;
+    gesture_event->unique_touch_event_id = gesture_data->unique_touch_event_id;
+    gesture_event->resending_plugin_id = gesture_data->resending_plugin_id;
+
+    if (gesture_data->contact_size) {
+      switch (type) {
+        default:
+          break;
+        case blink::WebInputEvent::Type::kGestureTapDown:
+        case blink::WebInputEvent::Type::kGestureTapUnconfirmed:
+        case blink::WebInputEvent::Type::kGestureDoubleTap:
+          gesture_event->data.tap_down.width =
+              gesture_data->contact_size->width();
+          gesture_event->data.tap_down.height =
+              gesture_data->contact_size->height();
+          break;
+        case blink::WebInputEvent::Type::kGestureShowPress:
+          gesture_event->data.show_press.width =
+              gesture_data->contact_size->width();
+          gesture_event->data.show_press.height =
+              gesture_data->contact_size->height();
+          break;
+        case blink::WebInputEvent::Type::kGestureTap:
+          gesture_event->data.tap.width = gesture_data->contact_size->width();
+          gesture_event->data.tap.height = gesture_data->contact_size->height();
+          break;
+        case blink::WebInputEvent::Type::kGestureLongPress:
+          gesture_event->data.long_press.width =
+              gesture_data->contact_size->width();
+          gesture_event->data.long_press.height =
+              gesture_data->contact_size->height();
+          break;
+
+        case blink::WebInputEvent::Type::kGestureTwoFingerTap:
+          gesture_event->data.two_finger_tap.first_finger_width =
+              gesture_data->contact_size->width();
+          gesture_event->data.two_finger_tap.first_finger_height =
+              gesture_data->contact_size->height();
+          break;
+        case blink::WebInputEvent::Type::kGestureScrollBegin:
+          gesture_event->data.scroll_begin.delta_x_hint =
+              gesture_data->scroll_data->delta_x;
+          gesture_event->data.scroll_begin.delta_y_hint =
+              gesture_data->scroll_data->delta_y;
+          gesture_event->data.scroll_begin.delta_hint_units =
+              gesture_data->scroll_data->delta_units;
+          gesture_event->data.scroll_begin.target_viewport =
+              gesture_data->scroll_data->target_viewport;
+          gesture_event->data.scroll_begin.inertial_phase =
+              gesture_data->scroll_data->inertial_phase;
+          gesture_event->data.scroll_begin.synthetic =
+              gesture_data->scroll_data->synthetic;
+          gesture_event->data.scroll_begin.pointer_count =
+              gesture_data->scroll_data->pointer_count;
+          break;
+        case blink::WebInputEvent::Type::kGestureScrollEnd:
+          gesture_event->data.scroll_end.delta_units =
+              gesture_data->scroll_data->delta_units;
+          gesture_event->data.scroll_end.inertial_phase =
+              gesture_data->scroll_data->inertial_phase;
+          gesture_event->data.scroll_end.synthetic =
+              gesture_data->scroll_data->synthetic;
+          break;
+        case blink::WebInputEvent::Type::kGestureScrollUpdate:
+          gesture_event->data.scroll_update.delta_x =
+              gesture_data->scroll_data->delta_x;
+          gesture_event->data.scroll_update.delta_y =
+              gesture_data->scroll_data->delta_y;
+          gesture_event->data.scroll_update.delta_units =
+              gesture_data->scroll_data->delta_units;
+          gesture_event->data.scroll_update.inertial_phase =
+              gesture_data->scroll_data->inertial_phase;
+          if (gesture_data->scroll_data->update_details) {
+            gesture_event->data.scroll_update.velocity_x =
+                gesture_data->scroll_data->update_details->velocity_x;
+            gesture_event->data.scroll_update.velocity_y =
+                gesture_data->scroll_data->update_details->velocity_y;
+            gesture_event->data.scroll_update
+                .previous_update_in_sequence_prevented =
+                gesture_data->scroll_data->update_details
+                    ->previous_update_in_sequence_prevented;
+            gesture_event->data.scroll_update.prevent_propagation =
+                gesture_data->scroll_data->update_details->prevent_propagation;
+          }
+          break;
+      }
+    }
+
+    if (gesture_data->scroll_data) {
+      switch (type) {
+        default:
+          break;
+        case blink::WebInputEvent::Type::kGestureScrollBegin:
+          gesture_event->data.scroll_begin.delta_x_hint =
+              gesture_data->scroll_data->delta_x;
+          gesture_event->data.scroll_begin.delta_y_hint =
+              gesture_data->scroll_data->delta_y;
+          gesture_event->data.scroll_begin.delta_hint_units =
+              gesture_data->scroll_data->delta_units;
+          gesture_event->data.scroll_begin.target_viewport =
+              gesture_data->scroll_data->target_viewport;
+          gesture_event->data.scroll_begin.inertial_phase =
+              gesture_data->scroll_data->inertial_phase;
+          gesture_event->data.scroll_begin.synthetic =
+              gesture_data->scroll_data->synthetic;
+          gesture_event->data.scroll_begin.pointer_count =
+              gesture_data->scroll_data->pointer_count;
+          break;
+        case blink::WebInputEvent::Type::kGestureScrollEnd:
+          gesture_event->data.scroll_end.delta_units =
+              gesture_data->scroll_data->delta_units;
+          gesture_event->data.scroll_end.inertial_phase =
+              gesture_data->scroll_data->inertial_phase;
+          gesture_event->data.scroll_end.synthetic =
+              gesture_data->scroll_data->synthetic;
+          break;
+        case blink::WebInputEvent::Type::kGestureScrollUpdate:
+          gesture_event->data.scroll_update.delta_x =
+              gesture_data->scroll_data->delta_x;
+          gesture_event->data.scroll_update.delta_y =
+              gesture_data->scroll_data->delta_y;
+          gesture_event->data.scroll_update.delta_units =
+              gesture_data->scroll_data->delta_units;
+          gesture_event->data.scroll_update.inertial_phase =
+              gesture_data->scroll_data->inertial_phase;
+          if (gesture_data->scroll_data->update_details) {
+            gesture_event->data.scroll_update.velocity_x =
+                gesture_data->scroll_data->update_details->velocity_x;
+            gesture_event->data.scroll_update.velocity_y =
+                gesture_data->scroll_data->update_details->velocity_y;
+            gesture_event->data.scroll_update
+                .previous_update_in_sequence_prevented =
+                gesture_data->scroll_data->update_details
+                    ->previous_update_in_sequence_prevented;
+            gesture_event->data.scroll_update.prevent_propagation =
+                gesture_data->scroll_data->update_details->prevent_propagation;
+          }
+          break;
+      }
+    }
+
+    if (gesture_data->fling_data) {
+      switch (type) {
+        default:
+          break;
+        case blink::WebInputEvent::Type::kGestureFlingStart:
+          gesture_event->data.fling_start.velocity_x =
+              gesture_data->fling_data->velocity_x;
+          gesture_event->data.fling_start.velocity_y =
+              gesture_data->fling_data->velocity_y;
+          gesture_event->data.fling_start.target_viewport =
+              gesture_data->fling_data->target_viewport;
+          break;
+        case blink::WebInputEvent::Type::kGestureFlingCancel:
+          gesture_event->data.fling_cancel.target_viewport =
+              gesture_data->fling_data->target_viewport;
+          gesture_event->data.fling_cancel.prevent_boosting =
+              gesture_data->fling_data->prevent_boosting;
+          break;
+      }
+    }
+
+    if (gesture_data->pinch_data &&
+        type == blink::WebInputEvent::Type::kGesturePinchUpdate) {
+      gesture_event->data.pinch_update.zoom_disabled =
+          gesture_data->pinch_data->zoom_disabled;
+      gesture_event->data.pinch_update.scale = gesture_data->pinch_data->scale;
+    }
+  } else if (blink::WebInputEvent::IsTouchEventType(type)) {
+    content::mojom::TouchDataPtr touch_data;
+    if (!event.ReadTouchData<content::mojom::TouchDataPtr>(&touch_data))
+      return false;
+
+    (*out)->web_event.reset(new blink::WebTouchEvent(
+        type, event.modifiers(), event.timestamp_seconds()));
+
+    blink::WebTouchEvent* touch_event =
+        static_cast<blink::WebTouchEvent*>((*out)->web_event.get());
+    std::vector<content::mojom::TouchPointPtr> touches;
+    unsigned i;
+    for (i = 0; i < touch_data->touches.size() &&
+                i < blink::WebTouchEvent::kTouchesLengthCap;
+         ++i) {
+      blink::WebTouchPoint& touch_point = touch_event->touches[i];
+      TouchPointPropertiesFromPointerData(touch_data->touches[i], &touch_point);
+    }
+
+    touch_event->touches_length = i;
+    touch_event->dispatch_type = touch_data->cancelable;
+    touch_event->moved_beyond_slop_region =
+        touch_data->moved_beyond_slop_region;
+    touch_event->touch_start_or_first_touch_move =
+        touch_data->touch_start_or_first_move;
+    touch_event->unique_touch_event_id = touch_data->unique_touch_event_id;
+  } else if (blink::WebInputEvent::IsMouseEventType(type) ||
+             type == blink::WebInputEvent::Type::kMouseWheel) {
+    content::mojom::PointerDataPtr pointer_data;
+    if (!event.ReadPointerData<content::mojom::PointerDataPtr>(&pointer_data))
+      return false;
+
+    if (blink::WebInputEvent::IsMouseEventType(type)) {
+      (*out)->web_event.reset(new blink::WebMouseEvent(
+          type, event.modifiers(), event.timestamp_seconds()));
+    } else {
+      (*out)->web_event.reset(new blink::WebMouseWheelEvent(
+          type, event.modifiers(), event.timestamp_seconds()));
+    }
+
+    blink::WebMouseEvent* mouse_event =
+        static_cast<blink::WebMouseEvent*>((*out)->web_event.get());
+
+    MouseEventPropertiesFromPointerData(pointer_data, mouse_event);
+    if (pointer_data->mouse_data) {
+      mouse_event->click_count = pointer_data->mouse_data->click_count;
+
+      if (type == blink::WebInputEvent::Type::kMouseWheel &&
+          pointer_data->mouse_data->wheel_data) {
+        blink::WebMouseWheelEvent* wheel_event =
+            static_cast<blink::WebMouseWheelEvent*>(mouse_event);
+        content::mojom::WheelDataPtr& wheel_data =
+            pointer_data->mouse_data->wheel_data;
+        wheel_event->delta_x = wheel_data->delta_x;
+        wheel_event->delta_y = wheel_data->delta_y;
+        wheel_event->wheel_ticks_x = wheel_data->wheel_ticks_x;
+        wheel_event->wheel_ticks_y = wheel_data->wheel_ticks_y;
+        wheel_event->acceleration_ratio_x = wheel_data->acceleration_ratio_x;
+        wheel_event->acceleration_ratio_y = wheel_data->acceleration_ratio_y;
+        wheel_event->resending_plugin_id = wheel_data->resending_plugin_id;
+        wheel_event->phase =
+            static_cast<blink::WebMouseWheelEvent::Phase>(wheel_data->phase);
+        wheel_event->momentum_phase =
+            static_cast<blink::WebMouseWheelEvent::Phase>(
+                wheel_data->momentum_phase);
+        wheel_event->scroll_by_page = wheel_data->scroll_by_page;
+        wheel_event->has_precise_scrolling_deltas =
+            wheel_data->has_precise_scrolling_deltas;
+        wheel_event->dispatch_type = wheel_data->cancelable;
+      }
+    }
+
+  } else {
+    return false;
+  }
+
+  return event.ReadLatency(&((*out)->latency_info));
+}
+
+void* StructTraits<content::mojom::EventDataView,
+                   InputEventUniquePtr>::SetUpContext(const InputEventUniquePtr&
+                                                          event) {
+  InputEventSerializationContext* context =
+      new InputEventSerializationContext();
+
+  if (!event->web_event)
+    return context;
+
+  if (blink::WebInputEvent::IsKeyboardEventType(event->web_event->GetType())) {
+    const blink::WebKeyboardEvent* key_event =
+        static_cast<const blink::WebKeyboardEvent*>(event->web_event.get());
+    context->key_data = content::mojom::KeyData::New(
+        key_event->dom_key, key_event->dom_code, key_event->windows_key_code,
+        key_event->native_key_code, key_event->is_system_key,
+        key_event->is_browser_shortcut, key_event->text,
+        key_event->unmodified_text);
+    return context;
+  }
+  if (blink::WebInputEvent::IsGestureEventType(event->web_event->GetType())) {
+    const blink::WebGestureEvent* gesture_event =
+        static_cast<const blink::WebGestureEvent*>(event->web_event.get());
+
+    context->gesture_data = content::mojom::GestureData::New();
+    content::mojom::GestureDataPtr& gesture_data = context->gesture_data;
+    gesture_data->screen_position = gesture_event->PositionInScreen();
+    gesture_data->widget_position = gesture_event->PositionInWidget();
+    gesture_data->source_device = gesture_event->source_device;
+    gesture_data->unique_touch_event_id = gesture_event->unique_touch_event_id;
+    gesture_data->resending_plugin_id = gesture_event->resending_plugin_id;
+
+    switch (gesture_event->GetType()) {
+      default:
+        break;
+      case blink::WebInputEvent::Type::kGestureTapDown:
+        gesture_data->contact_size =
+            gfx::Size(gesture_event->data.tap_down.width,
+                      gesture_event->data.tap_down.height);
+        break;
+      case blink::WebInputEvent::Type::kGestureShowPress:
+        gesture_data->contact_size =
+            gfx::Size(gesture_event->data.show_press.width,
+                      gesture_event->data.show_press.height);
+        break;
+      case blink::WebInputEvent::Type::kGestureTap:
+      case blink::WebInputEvent::Type::kGestureTapUnconfirmed:
+      case blink::WebInputEvent::Type::kGestureDoubleTap:
+        gesture_data->contact_size = gfx::Size(gesture_event->data.tap.width,
+                                               gesture_event->data.tap.height);
+        gesture_data->tap_data =
+            content::mojom::TapData::New(gesture_event->data.tap.tap_count);
+        break;
+      case blink::WebInputEvent::Type::kGestureLongPress:
+        gesture_data->contact_size =
+            gfx::Size(gesture_event->data.long_press.width,
+                      gesture_event->data.long_press.height);
+        break;
+
+      case blink::WebInputEvent::Type::kGestureTwoFingerTap:
+        gesture_data->contact_size =
+            gfx::Size(gesture_event->data.two_finger_tap.first_finger_width,
+                      gesture_event->data.two_finger_tap.first_finger_height);
+        break;
+      case blink::WebInputEvent::Type::kGestureScrollBegin:
+        gesture_data->scroll_data = content::mojom::ScrollData::New(
+            gesture_event->data.scroll_begin.delta_x_hint,
+            gesture_event->data.scroll_begin.delta_y_hint,
+            gesture_event->data.scroll_begin.delta_hint_units,
+            gesture_event->data.scroll_begin.target_viewport,
+            gesture_event->data.scroll_begin.inertial_phase,
+            gesture_event->data.scroll_begin.synthetic,
+            gesture_event->data.scroll_begin.pointer_count, nullptr);
+        break;
+      case blink::WebInputEvent::Type::kGestureScrollEnd:
+        gesture_data->scroll_data = content::mojom::ScrollData::New(
+            0, 0, gesture_event->data.scroll_end.delta_units, false,
+            gesture_event->data.scroll_end.inertial_phase,
+            gesture_event->data.scroll_end.synthetic, 0, nullptr);
+        break;
+      case blink::WebInputEvent::Type::kGestureScrollUpdate:
+        gesture_data->scroll_data = content::mojom::ScrollData::New(
+            gesture_event->data.scroll_update.delta_x,
+            gesture_event->data.scroll_update.delta_y,
+            gesture_event->data.scroll_update.delta_units, false,
+            gesture_event->data.scroll_update.inertial_phase, false, 0,
+            content::mojom::ScrollUpdate::New(
+                gesture_event->data.scroll_update.velocity_x,
+                gesture_event->data.scroll_update.velocity_y,
+                gesture_event->data.scroll_update
+                    .previous_update_in_sequence_prevented,
+                gesture_event->data.scroll_update.prevent_propagation));
+        break;
+      case blink::WebInputEvent::Type::kGestureFlingStart:
+        gesture_data->fling_data = content::mojom::FlingData::New(
+            gesture_event->data.fling_start.velocity_x,
+            gesture_event->data.fling_start.velocity_y,
+            gesture_event->data.fling_start.target_viewport, false);
+        break;
+      case blink::WebInputEvent::Type::kGestureFlingCancel:
+        gesture_data->fling_data = content::mojom::FlingData::New(
+            0, 0, gesture_event->data.fling_cancel.target_viewport,
+            gesture_event->data.fling_cancel.prevent_boosting);
+        break;
+      case blink::WebInputEvent::Type::kGesturePinchUpdate:
+        gesture_data->pinch_data = content::mojom::PinchData::New(
+            gesture_event->data.pinch_update.zoom_disabled,
+            gesture_event->data.pinch_update.scale);
+        break;
+    }
+    return context;
+  }
+  if (blink::WebInputEvent::IsTouchEventType(event->web_event->GetType())) {
+    const blink::WebTouchEvent* touch_event =
+        static_cast<const blink::WebTouchEvent*>(event->web_event.get());
+
+    context->touch_data = content::mojom::TouchData::New(
+        touch_event->dispatch_type, touch_event->moved_beyond_slop_region,
+        touch_event->touch_start_or_first_touch_move,
+        touch_event->unique_touch_event_id,
+        std::vector<content::mojom::TouchPointPtr>());
+
+    for (unsigned i = 0; i < touch_event->touches_length; ++i) {
+      content::mojom::PointerDataPtr pointer_data =
+          PointerDataFromPointerProperties(touch_event->touches[i], nullptr);
+      context->touch_data->touches.emplace_back(content::mojom::TouchPoint::New(
+          touch_event->touches[i].state, touch_event->touches[i].radius_x,
+          touch_event->touches[i].radius_y,
+          touch_event->touches[i].rotation_angle, std::move(pointer_data)));
+    }
+
+    return context;
+  }
+
+  bool is_wheel_event =
+      event->web_event->GetType() == blink::WebInputEvent::Type::kMouseWheel;
+  if (blink::WebInputEvent::IsMouseEventType(event->web_event->GetType()) ||
+      is_wheel_event) {
+    const blink::WebMouseEvent* mouse_event =
+        static_cast<const blink::WebMouseEvent*>(event->web_event.get());
+
+    content::mojom::WheelDataPtr wheel_data;
+    if (is_wheel_event) {
+      const blink::WebMouseWheelEvent* wheel_event =
+          static_cast<const blink::WebMouseWheelEvent*>(mouse_event);
+      wheel_data = content::mojom::WheelData::New(
+          wheel_event->delta_x, wheel_event->delta_y,
+          wheel_event->wheel_ticks_x, wheel_event->wheel_ticks_y,
+          wheel_event->acceleration_ratio_x, wheel_event->acceleration_ratio_y,
+          wheel_event->resending_plugin_id, wheel_event->phase,
+          wheel_event->momentum_phase, wheel_event->scroll_by_page,
+          wheel_event->has_precise_scrolling_deltas,
+          wheel_event->dispatch_type);
+    }
+
+    context->pointer_data = PointerDataFromPointerProperties(
+        *mouse_event, content::mojom::MouseData::New(mouse_event->click_count,
+                                                     std::move(wheel_data)));
+    return context;
+  }
+
+  return context;
+}
+
+void StructTraits<content::mojom::EventDataView, InputEventUniquePtr>::
+    TearDownContext(const InputEventUniquePtr& event, void* context) {
+  delete static_cast<InputEventSerializationContext*>(context);
+}
+
+StructTraits<content::mojom::EventDataView, InputEventUniquePtr>::
+    InputEventSerializationContext::InputEventSerializationContext() {}
+
+StructTraits<content::mojom::EventDataView, InputEventUniquePtr>::
+    InputEventSerializationContext::~InputEventSerializationContext() {}
+
+}  // namespace mojo
diff --git a/content/common/input/input_event_struct_traits.h b/content/common/input/input_event_struct_traits.h
new file mode 100644
index 0000000..9237448
--- /dev/null
+++ b/content/common/input/input_event_struct_traits.h
@@ -0,0 +1,78 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_COMMON_INPUT_INPUT_EVENT_STRUCT_TRAITS_H_
+#define CONTENT_COMMON_INPUT_INPUT_EVENT_STRUCT_TRAITS_H_
+
+#include "content/common/input/input_handler.mojom.h"
+
+namespace content {
+class InputEvent;
+}
+
+namespace mojo {
+
+using InputEventUniquePtr = std::unique_ptr<content::InputEvent>;
+
+template <>
+struct StructTraits<content::mojom::EventDataView, InputEventUniquePtr> {
+  static blink::WebInputEvent::Type type(const InputEventUniquePtr& event) {
+    return event->web_event->GetType();
+  }
+
+  static int32_t modifiers(const InputEventUniquePtr& event) {
+    return event->web_event->GetModifiers();
+  }
+
+  static double timestamp_seconds(const InputEventUniquePtr& event) {
+    return event->web_event->TimeStampSeconds();
+  }
+
+  static const ui::LatencyInfo& latency(const InputEventUniquePtr& event) {
+    return event->latency_info;
+  }
+
+  static const content::mojom::KeyDataPtr& key_data(
+      const InputEventUniquePtr& event,
+      void* context) {
+    return static_cast<InputEventSerializationContext*>(context)->key_data;
+  }
+
+  static const content::mojom::PointerDataPtr& pointer_data(
+      const InputEventUniquePtr& event,
+      void* context) {
+    return static_cast<InputEventSerializationContext*>(context)->pointer_data;
+  }
+
+  static const content::mojom::GestureDataPtr& gesture_data(
+      const InputEventUniquePtr& event,
+      void* context) {
+    return static_cast<InputEventSerializationContext*>(context)->gesture_data;
+  }
+
+  static const content::mojom::TouchDataPtr& touch_data(
+      const InputEventUniquePtr& event,
+      void* context) {
+    return static_cast<InputEventSerializationContext*>(context)->touch_data;
+  }
+
+  static bool Read(content::mojom::EventDataView r, InputEventUniquePtr* out);
+  static void* SetUpContext(const InputEventUniquePtr& handle);
+  static void TearDownContext(const InputEventUniquePtr& handle, void* context);
+
+ private:
+  struct InputEventSerializationContext {
+    content::mojom::KeyDataPtr key_data;
+    content::mojom::GestureDataPtr gesture_data;
+    content::mojom::PointerDataPtr pointer_data;
+    content::mojom::TouchDataPtr touch_data;
+
+    InputEventSerializationContext();
+    ~InputEventSerializationContext();
+  };
+};
+
+}  // namespace mojo
+
+#endif  // CONTENT_COMMON_INPUT_INPUT_EVENT_STRUCT_TRAITS_H_
diff --git a/content/common/native_types.typemap b/content/common/native_types.typemap
index 9a14d13..94240ef 100644
--- a/content/common/native_types.typemap
+++ b/content/common/native_types.typemap
@@ -29,6 +29,7 @@
 traits_headers = [
   "//content/common/frame_messages.h",
   "//content/common/input_messages.h",
+  "//content/common/input/input_event_struct_traits.h",
   "//content/common/view_messages.h",
   "//content/public/common/common_param_traits.h",
 ]
@@ -58,6 +59,7 @@
   "content.mojom.Cancelability=blink::WebInputEvent::DispatchType",
   "content.mojom.EffectiveConnectionType=net::EffectiveConnectionType",
   "content.mojom.EditCommand=content::EditCommand",
+  "content.mojom.Event=std::unique_ptr<content::InputEvent>[move_only]",
   "content.mojom.EventType=blink::WebInputEvent::Type",
   "content.mojom.FrameOwnerProperties=content::FrameOwnerProperties",
   "content.mojom.FrameReplicationState=content::FrameReplicationState",
diff --git a/content/common/render_message_filter.mojom b/content/common/render_message_filter.mojom
index 5a7da2f4e..720032d 100644
--- a/content/common/render_message_filter.mojom
+++ b/content/common/render_message_filter.mojom
@@ -5,7 +5,9 @@
 module content.mojom;
 
 import "cc/ipc/shared_bitmap_allocation_notifier.mojom";
+import "content/common/input/input_handler.mojom";
 import "content/common/native_types.mojom";
+import "content/common/widget.mojom";
 
 interface RenderMessageFilter {
   // Synchronously generates a new routing ID for the caller.
@@ -13,11 +15,11 @@
 
   // Similar to CreateWindow, except used for sub-widgets, like <select>
   // dropdowns.
-  [Sync] CreateNewWidget(int32 opener_id, content.mojom.WebPopupType popup_type)
+  [Sync] CreateNewWidget(int32 opener_id, content.mojom.WebPopupType popup_type, Widget widget)
       => (int32 route_id);
 
   // Similar to CreateWidget except the widget is a full screen window.
-  [Sync] CreateFullscreenWidget(int32 opener_id)
+  [Sync] CreateFullscreenWidget(int32 opener_id, Widget widget)
       => (int32 route_id);
 
   GetSharedBitmapAllocationNotifier(
diff --git a/content/common/sandbox_linux/android/sandbox_bpf_base_policy_android.h b/content/common/sandbox_linux/android/sandbox_bpf_base_policy_android.h
deleted file mode 100644
index 79a5d44..0000000
--- a/content/common/sandbox_linux/android/sandbox_bpf_base_policy_android.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_COMMON_SANDBOX_LINUX_ANDROID_SANDBOX_BPF_BASE_POLICY_ANDROID_H_
-#define CONTENT_COMMON_SANDBOX_LINUX_ANDROID_SANDBOX_BPF_BASE_POLICY_ANDROID_H_
-
-#include <sys/types.h>
-
-#include "base/macros.h"
-#include "content/common/sandbox_linux/sandbox_bpf_base_policy_linux.h"
-
-namespace content {
-
-// This class builds on top of the generic Linux baseline policy to reduce
-// Linux kernel attack surface. It augments the list of allowed syscalls to
-// allow ones required by the Android runtime.
-class SandboxBPFBasePolicyAndroid : public SandboxBPFBasePolicy {
- public:
-  SandboxBPFBasePolicyAndroid();
-  ~SandboxBPFBasePolicyAndroid() override;
-
-  // sandbox::SandboxBPFPolicy:
-  sandbox::bpf_dsl::ResultExpr EvaluateSyscall(
-      int system_call_number) const override;
-
- private:
-  const pid_t pid_;
-
-  DISALLOW_COPY_AND_ASSIGN(SandboxBPFBasePolicyAndroid);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_COMMON_SANDBOX_LINUX_ANDROID_SANDBOX_BPF_BASE_POLICY_ANDROID_H_
diff --git a/content/common/widget.mojom b/content/common/widget.mojom
new file mode 100644
index 0000000..a6013b4e
--- /dev/null
+++ b/content/common/widget.mojom
@@ -0,0 +1,13 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module content.mojom;
+
+import "content/common/input/input_handler.mojom";
+
+// Interface representing the Widget.
+interface Widget {
+  // TODO(dtapuska): Implementation pending. crbug.com/722928.
+  // GetWidgetInputHandler(WidgetInputHandler& request);
+};
diff --git a/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java b/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java
index f7b899e9..5a7ca4c 100644
--- a/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java
@@ -368,12 +368,6 @@
     }
 
     @Override
-    public String getUrl() {
-        if (isDestroyed()) return null;
-        return nativeGetURL(mNativeWebContentsAndroid);
-    }
-
-    @Override
     public String getLastCommittedUrl() {
         return nativeGetLastCommittedURL(mNativeWebContentsAndroid);
     }
@@ -664,7 +658,6 @@
     private native void nativeSelectWordAroundCaret(long nativeWebContentsAndroid);
     private native void nativeAdjustSelectionByCharacterOffset(
             long nativeWebContentsAndroid, int startAdjust, int endAdjust);
-    private native String nativeGetURL(long nativeWebContentsAndroid);
     private native String nativeGetLastCommittedURL(long nativeWebContentsAndroid);
     private native boolean nativeIsIncognito(long nativeWebContentsAndroid);
     private native void nativeResumeLoadingCreatedWebContents(long nativeWebContentsAndroid);
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/WebContents.java b/content/public/android/java/src/org/chromium/content_public/browser/WebContents.java
index 4a0bcc23..f172b22 100644
--- a/content/public/android/java/src/org/chromium/content_public/browser/WebContents.java
+++ b/content/public/android/java/src/org/chromium/content_public/browser/WebContents.java
@@ -238,13 +238,6 @@
     public void adjustSelectionByCharacterOffset(int startAdjust, int endAdjust);
 
     /**
-     * Get the URL of the current page.
-     *
-     * @return The URL of the current page.
-     */
-    String getUrl();
-
-    /**
      * Gets the last committed URL. It represents the current page that is
      * displayed in this WebContents. It represents the current security context.
      *
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java b/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java
index 3d35612..df20d008 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java
@@ -630,7 +630,7 @@
 
     private void reloadPage() throws Throwable {
         // Reload the page, then focus will be lost and keyboard should be hidden.
-        mRule.fullyLoadUrl(mRule.getContentViewCore().getWebContents().getUrl());
+        mRule.fullyLoadUrl(mRule.getContentViewCore().getWebContents().getLastCommittedUrl());
     }
 
     @Test
diff --git a/content/public/app/mojo/content_browser_manifest.json b/content/public/app/mojo/content_browser_manifest.json
index 989f45a..820f611 100644
--- a/content/public/app/mojo/content_browser_manifest.json
+++ b/content/public/app/mojo/content_browser_manifest.json
@@ -19,7 +19,6 @@
           "blink::mojom::BackgroundFetchService",
           "blink::mojom::BackgroundSyncService",
           "blink::mojom::BroadcastChannelProvider",
-          "blink::mojom::BudgetService",
           "blink::mojom::Hyphenation",
           "blink::mojom::MimeRegistry",
           "blink::mojom::NotificationService",
diff --git a/content/public/app/mojo/content_renderer_manifest.json b/content/public/app/mojo/content_renderer_manifest.json
index 27b37ef..52a453ba 100644
--- a/content/public/app/mojo/content_renderer_manifest.json
+++ b/content/public/app/mojo/content_renderer_manifest.json
@@ -44,6 +44,7 @@
           "blink::mojom::ManifestManager",
           "content::mojom::ImageDownloader",
           "content::mojom::FrameInputHandler",
+          "content::mojom::Widget",
           "mojom::MediaDevicesListener"
         ]
       },
diff --git a/content/public/browser/resource_dispatcher_host_delegate.cc b/content/public/browser/resource_dispatcher_host_delegate.cc
index 8b1a59c..94b7755 100644
--- a/content/public/browser/resource_dispatcher_host_delegate.cc
+++ b/content/public/browser/resource_dispatcher_host_delegate.cc
@@ -88,7 +88,8 @@
 
 PreviewsState ResourceDispatcherHostDelegate::GetPreviewsState(
     const net::URLRequest& url_request,
-    content::ResourceContext* resource_context) {
+    content::ResourceContext* resource_context,
+    PreviewsState previews_to_allow) {
   return PREVIEWS_UNSPECIFIED;
 }
 
diff --git a/content/public/browser/resource_dispatcher_host_delegate.h b/content/public/browser/resource_dispatcher_host_delegate.h
index c3c96991..8a5a133 100644
--- a/content/public/browser/resource_dispatcher_host_delegate.h
+++ b/content/public/browser/resource_dispatcher_host_delegate.h
@@ -126,10 +126,13 @@
   // Asks the embedder for the PreviewsState which says which previews should
   // be enabled for the given request. The PreviewsState is a bitmask of
   // potentially several Previews optimizations. It is only called for requests
-  // with an unspecified Previews state.
+  // with an unspecified Previews state.  If previews_to_allow is set to
+  // anything other than PREVIEWS_UNSPECIFIED, it is taken as a limit on
+  // available preview states.
   virtual PreviewsState GetPreviewsState(
       const net::URLRequest& url_request,
-      content::ResourceContext* resource_context);
+      content::ResourceContext* resource_context,
+      PreviewsState previews_to_allow);
 
   // Asks the embedder for NavigationData related to this request. It is only
   // called for navigation requests.
diff --git a/content/public/browser/web_contents_delegate.h b/content/public/browser/web_contents_delegate.h
index 05d3fe14..5aa1a16a 100644
--- a/content/public/browser/web_contents_delegate.h
+++ b/content/public/browser/web_contents_delegate.h
@@ -18,6 +18,7 @@
 #include "content/public/browser/invalidate_type.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/media_stream_request.h"
+#include "content/public/common/previews_state.h"
 #include "content/public/common/window_container_type.mojom.h"
 #include "third_party/WebKit/public/platform/WebDisplayMode.h"
 #include "third_party/WebKit/public/platform/WebDragOperation.h"
@@ -566,6 +567,10 @@
                                                  const url::Origin& origin,
                                                  const GURL& resource_url);
 
+  // Give WebContentsDelegates the opportunity to adjust the previews state.
+  virtual void AdjustPreviewsStateForNavigation(PreviewsState* previews_state) {
+  }
+
  protected:
   virtual ~WebContentsDelegate();
 
diff --git a/content/public/common/previews_state.h b/content/public/common/previews_state.h
index 4559261..77bf8fc 100644
--- a/content/public/common/previews_state.h
+++ b/content/public/common/previews_state.h
@@ -40,6 +40,10 @@
   PREVIEWS_STATE_LAST = PREVIEWS_OFF
 };
 
+// Combination of all previews that are guaranteed not to provide partial
+// content.
+const PreviewsState PARTIAL_CONTENT_SAFE_PREVIEWS = SERVER_LOFI_ON;
+
 // Ensure that content::PreviewsState and blink::WebURLRequest::PreviewsState
 // are kept in sync.
 STATIC_ASSERT_PREVIEWS_ENUM(PREVIEWS_UNSPECIFIED,
diff --git a/content/public/test/android/javatests/src/org/chromium/content/browser/test/util/HistoryUtils.java b/content/public/test/android/javatests/src/org/chromium/content/browser/test/util/HistoryUtils.java
index 8047718..71d42cfe 100644
--- a/content/public/test/android/javatests/src/org/chromium/content/browser/test/util/HistoryUtils.java
+++ b/content/public/test/android/javatests/src/org/chromium/content/browser/test/util/HistoryUtils.java
@@ -100,11 +100,11 @@
     }
 
     /**
-     * Calls {@link NavigationController#getHostname()} on UI Thread to get the current URL.
+     * Calls {@link WebContents#getLastCommittedUrl()} on UI Thread to get the current URL.
      *
      * @param instrumentation an Instrumentation instance.
      * @param contentViewCore a ContentViewCore instance.
-     * @return the URL of the current page
+     * @return the last committed URL of the provided ContentViewCore.
      * @throws Throwable
      */
     public static String getUrlOnUiThread(Instrumentation instrumentation,
@@ -113,7 +113,7 @@
                 instrumentation, new Callable<String>() {
                     @Override
                     public String call() throws Exception {
-                        return webContents.getUrl();
+                        return webContents.getLastCommittedUrl();
                     }
                 });
     }
diff --git a/content/public/test/browser_test_utils.cc b/content/public/test/browser_test_utils.cc
index ec91284..b88b61ff 100644
--- a/content/public/test/browser_test_utils.cc
+++ b/content/public/test/browser_test_utils.cc
@@ -26,6 +26,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/values.h"
 #include "build/build_config.h"
+#include "cc/surfaces/frame_sink_manager.h"
 #include "cc/surfaces/surface.h"
 #include "cc/surfaces/surface_manager.h"
 #include "content/browser/accessibility/browser_accessibility.h"
@@ -1319,7 +1320,7 @@
 SurfaceHitTestReadyNotifier::SurfaceHitTestReadyNotifier(
     RenderWidgetHostViewBase* target_view)
     : target_view_(target_view) {
-  surface_manager_ = GetSurfaceManager();
+  surface_manager_ = GetFrameSinkManager()->surface_manager();
 }
 
 void SurfaceHitTestReadyNotifier::WaitForSurfaceReady(
diff --git a/content/public/test/mock_render_thread.cc b/content/public/test/mock_render_thread.cc
index 432d781c..886a353 100644
--- a/content/public/test/mock_render_thread.cc
+++ b/content/public/test/mock_render_thread.cc
@@ -40,6 +40,7 @@
 
   void CreateNewWidget(int32_t opener_id,
                        blink::WebPopupType popup_type,
+                       mojom::WidgetPtr widget,
                        CreateNewWidgetCallback callback) override {
     // See comment in CreateNewWindow().
     NOTREACHED();
@@ -47,6 +48,7 @@
 
   bool CreateNewWidget(int32_t opener_id,
                        blink::WebPopupType popup_type,
+                       mojom::WidgetPtr widget,
                        int32_t* route_id) override {
     thread_->OnCreateWidget(opener_id, popup_type, route_id);
     return true;
@@ -54,6 +56,7 @@
 
   void CreateFullscreenWidget(
       int opener_id,
+      mojom::WidgetPtr widget,
       CreateFullscreenWidgetCallback callback) override {
     NOTREACHED();
   }
diff --git a/content/public/test/navigation_simulator.cc b/content/public/test/navigation_simulator.cc
index 6867290..b15489d 100644
--- a/content/public/test/navigation_simulator.cc
+++ b/content/public/test/navigation_simulator.cc
@@ -16,6 +16,7 @@
 #include "content/public/common/resource_request_body.h"
 #include "content/test/test_navigation_url_loader.h"
 #include "content/test/test_render_frame_host.h"
+#include "net/base/host_port_pair.h"
 #include "net/base/load_flags.h"
 #include "net/url_request/redirect_info.h"
 
@@ -98,6 +99,7 @@
       render_frame_host_(render_frame_host),
       handle_(nullptr),
       navigation_url_(original_url),
+      socket_address_("2001:db8::1", 80),
       weak_factory_(this) {
   if (render_frame_host->GetParent()) {
     if (!render_frame_host->frame_tree_node()->has_committed_real_load())
@@ -318,8 +320,7 @@
   params.contents_mime_type = "text/html";
   params.method = "GET";
   params.http_status_code = 200;
-  params.socket_address.set_host("2001:db8::1");
-  params.socket_address.set_port(80);
+  params.socket_address = socket_address_;
   params.history_list_was_cleared = false;
   params.original_request_url = navigation_url_;
   params.was_within_same_document = false;
@@ -450,8 +451,7 @@
   params.contents_mime_type = "text/html";
   params.method = "GET";
   params.http_status_code = 200;
-  params.socket_address.set_host("2001:db8::1");
-  params.socket_address.set_port(80);
+  params.socket_address = socket_address_;
   params.history_list_was_cleared = false;
   params.original_request_url = navigation_url_;
   params.was_within_same_document = true;
@@ -484,6 +484,13 @@
   referrer_ = referrer;
 }
 
+void NavigationSimulator::SetSocketAddress(
+    const net::HostPortPair& socket_address) {
+  CHECK_LE(state_, STARTED) << "The socket address cannot be set after the "
+                               "navigation has committed or failed";
+  socket_address_ = socket_address;
+}
+
 NavigationThrottle::ThrottleCheckResult
 NavigationSimulator::GetLastThrottleCheckResult() {
   return last_throttle_check_result_.value();
diff --git a/content/public/test/navigation_simulator.h b/content/public/test/navigation_simulator.h
index 86e15c1..da5ea4a6 100644
--- a/content/public/test/navigation_simulator.h
+++ b/content/public/test/navigation_simulator.h
@@ -14,6 +14,7 @@
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/common/referrer.h"
 #include "content/public/test/navigation_simulator.h"
+#include "net/base/host_port_pair.h"
 #include "ui/base/page_transition_types.h"
 
 class GURL;
@@ -129,9 +130,7 @@
   // --------------------------------------------------------------------------
 
   // The following functions are used to specify the parameters of the
-  // navigation. Changes should be  made before calling |Start|, unless they are
-  // meant to apply to a redirect. In that case, they should be made before
-  // calling |Redirect|.
+  // navigation.
 
   // The following parameters are constant during the navigation and may only be
   // specified before calling |Start|.
@@ -143,6 +142,12 @@
   // |Redirect|.
   virtual void SetReferrer(const Referrer& referrer);
 
+  // The following parameters can change at any point until the page fails or
+  // commits. They should be specified before calling |Fail| or |Commit|.
+  virtual void SetSocketAddress(const net::HostPortPair& socket_address);
+
+  // --------------------------------------------------------------------------
+
   // Gets the last throttle check result computed by the navigation throttles.
   // It is an error to call this before Start() is called.
   virtual NavigationThrottle::ThrottleCheckResult GetLastThrottleCheckResult();
@@ -201,6 +206,7 @@
   NavigationHandleImpl* handle_;
 
   GURL navigation_url_;
+  net::HostPortPair socket_address_;
   Referrer referrer_;
   ui::PageTransition transition_ = ui::PAGE_TRANSITION_LINK;
 
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index 580e320..ca2c49f 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -770,7 +770,6 @@
       "//third_party/webrtc/api:video_frame_api",
       "//third_party/webrtc/api/audio_codecs:builtin_audio_decoder_factory",
       "//third_party/webrtc/api/audio_codecs:builtin_audio_encoder_factory",
-      "//third_party/webrtc/common_video:common_video",
       "//third_party/webrtc/media:rtc_media",
       "//third_party/webrtc/media:rtc_media_base",
       "//third_party/webrtc/modules/audio_device",
diff --git a/content/renderer/android/synchronous_layer_tree_frame_sink.cc b/content/renderer/android/synchronous_layer_tree_frame_sink.cc
index c007c2d..ad3f85ab 100644
--- a/content/renderer/android/synchronous_layer_tree_frame_sink.cc
+++ b/content/renderer/android/synchronous_layer_tree_frame_sink.cc
@@ -25,8 +25,8 @@
 #include "cc/quads/surface_draw_quad.h"
 #include "cc/surfaces/compositor_frame_sink_support.h"
 #include "cc/surfaces/display.h"
+#include "cc/surfaces/frame_sink_manager.h"
 #include "cc/surfaces/local_surface_id_allocator.h"
-#include "cc/surfaces/surface_manager.h"
 #include "content/common/android/sync_compositor_messages.h"
 #include "content/common/view_messages.h"
 #include "content/renderer/android/synchronous_compositor_filter.h"
@@ -126,8 +126,8 @@
       sender_(RenderThreadImpl::current()->sync_compositor_message_filter()),
       memory_policy_(0u),
       frame_swap_message_queue_(frame_swap_message_queue),
-      surface_manager_(new cc::SurfaceManager),
-      local_surface_id_allocator_(new cc::LocalSurfaceIdAllocator()),
+      frame_sink_manager_(new cc::FrameSinkManager),
+      local_surface_id_allocator_(new cc::LocalSurfaceIdAllocator),
       begin_frame_source_(std::move(begin_frame_source)) {
   DCHECK(registry_);
   DCHECK(sender_);
@@ -177,10 +177,10 @@
   constexpr bool handles_frame_sink_id_invalidation = true;
   constexpr bool needs_sync_points = true;
   root_support_ = cc::CompositorFrameSinkSupport::Create(
-      this, surface_manager_.get(), kRootFrameSinkId, root_support_is_root,
+      this, frame_sink_manager_.get(), kRootFrameSinkId, root_support_is_root,
       handles_frame_sink_id_invalidation, needs_sync_points);
   child_support_ = cc::CompositorFrameSinkSupport::Create(
-      this, surface_manager_.get(), kChildFrameSinkId, child_support_is_root,
+      this, frame_sink_manager_.get(), kChildFrameSinkId, child_support_is_root,
       handles_frame_sink_id_invalidation, needs_sync_points);
 
   cc::RendererSettings software_renderer_settings;
@@ -200,7 +200,8 @@
       shared_bitmap_manager_, nullptr /* gpu_memory_buffer_manager */,
       software_renderer_settings, kRootFrameSinkId, std::move(output_surface),
       nullptr /* scheduler */, nullptr /* texture_mailbox_deleter */));
-  display_->Initialize(&display_client_, surface_manager_.get());
+  display_->Initialize(&display_client_,
+                       frame_sink_manager_->surface_manager());
   display_->SetVisible(true);
   return true;
 }
@@ -217,7 +218,7 @@
   software_output_surface_ = nullptr;
   display_ = nullptr;
   local_surface_id_allocator_ = nullptr;
-  surface_manager_ = nullptr;
+  frame_sink_manager_ = nullptr;
   cc::LayerTreeFrameSink::DetachFromClient();
   CancelFallbackTick();
 }
diff --git a/content/renderer/android/synchronous_layer_tree_frame_sink.h b/content/renderer/android/synchronous_layer_tree_frame_sink.h
index 19e9831..9b66c5b 100644
--- a/content/renderer/android/synchronous_layer_tree_frame_sink.h
+++ b/content/renderer/android/synchronous_layer_tree_frame_sink.h
@@ -30,8 +30,8 @@
 class CompositorFrameSinkSupport;
 class ContextProvider;
 class Display;
+class FrameSinkManager;
 class LocalSurfaceIdAllocator;
-class SurfaceManager;
 }  // namespace cc
 
 namespace IPC {
@@ -153,19 +153,19 @@
 
   // TODO(danakj): These don't to be stored in unique_ptrs when OutputSurface
   // is owned/destroyed on the compositor thread.
-  std::unique_ptr<cc::SurfaceManager> surface_manager_;
+  std::unique_ptr<cc::FrameSinkManager> frame_sink_manager_;
   std::unique_ptr<cc::LocalSurfaceIdAllocator> local_surface_id_allocator_;
   cc::LocalSurfaceId child_local_surface_id_;
   cc::LocalSurfaceId root_local_surface_id_;
   gfx::Size child_size_;
   gfx::Size display_size_;
   float device_scale_factor_ = 0;
-  // Uses surface_manager_.
+  // Uses frame_sink_manager_.
   std::unique_ptr<cc::CompositorFrameSinkSupport> root_support_;
-  // Uses surface_manager_.
+  // Uses frame_sink_manager_.
   std::unique_ptr<cc::CompositorFrameSinkSupport> child_support_;
   StubDisplayClient display_client_;
-  // Uses surface_manager_.
+  // Uses frame_sink_manager_.
   std::unique_ptr<cc::Display> display_;
   // Owned by |display_|.
   SoftwareOutputSurface* software_output_surface_ = nullptr;
diff --git a/content/renderer/media/gpu/rtc_video_encoder.cc b/content/renderer/media/gpu/rtc_video_encoder.cc
index 8c671e5..2185d7e 100644
--- a/content/renderer/media/gpu/rtc_video_encoder.cc
+++ b/content/renderer/media/gpu/rtc_video_encoder.cc
@@ -47,16 +47,25 @@
   DISALLOW_IMPLICIT_CONSTRUCTORS(RTCTimestamps);
 };
 
-webrtc::VideoCodecType ProfileToWebRtcVideoCodecType(
-    media::VideoCodecProfile profile) {
-  if (profile >= media::VP8PROFILE_MIN && profile <= media::VP8PROFILE_MAX) {
-    return webrtc::kVideoCodecVP8;
-  } else if (profile >= media::H264PROFILE_MIN &&
-             profile <= media::H264PROFILE_MAX) {
-    return webrtc::kVideoCodecH264;
+// Translate from webrtc::VideoCodecType and webrtc::VideoCodec to
+// media::VideoCodecProfile.
+media::VideoCodecProfile WebRTCVideoCodecToVideoCodecProfile(
+    webrtc::VideoCodecType type,
+    const webrtc::VideoCodec* codec_settings) {
+  DCHECK_EQ(type, codec_settings->codecType);
+  switch (type) {
+    case webrtc::kVideoCodecVP8:
+      return media::VP8PROFILE_ANY;
+    case webrtc::kVideoCodecVP9:
+      return media::VP9PROFILE_MIN;
+    case webrtc::kVideoCodecH264:
+      // TODO(magjed): WebRTC is only using Baseline profile for now. Update
+      // once http://crbug/webrtc/6337 is fixed.
+      return media::H264PROFILE_BASELINE;
+    default:
+      NOTREACHED() << "Unrecognized video codec type";
+      return media::VIDEO_CODEC_PROFILE_UNKNOWN;
   }
-  NOTREACHED() << "Invalid profile " << GetProfileName(profile);
-  return webrtc::kVideoCodecUnknown;
 }
 
 // Populates struct webrtc::RTPFragmentationHeader for H264 codec.
@@ -783,12 +792,12 @@
 }
 
 RTCVideoEncoder::RTCVideoEncoder(
-    media::VideoCodecProfile profile,
+    webrtc::VideoCodecType type,
     media::GpuVideoAcceleratorFactories* gpu_factories)
-    : profile_(profile),
+    : video_codec_type_(type),
       gpu_factories_(gpu_factories),
       gpu_task_runner_(gpu_factories->GetTaskRunner()) {
-  DVLOG(1) << "RTCVideoEncoder(): profile=" << GetProfileName(profile);
+  DVLOG(1) << "RTCVideoEncoder(): codec type=" << type;
 }
 
 RTCVideoEncoder::~RTCVideoEncoder() {
@@ -809,7 +818,9 @@
     Release();
   }
 
-  impl_ = new Impl(gpu_factories_, ProfileToWebRtcVideoCodecType(profile_));
+  impl_ = new Impl(gpu_factories_, video_codec_type_);
+  const media::VideoCodecProfile profile = WebRTCVideoCodecToVideoCodecProfile(
+      impl_->video_codec_type(), codec_settings);
 
   base::WaitableEvent initialization_waiter(
       base::WaitableEvent::ResetPolicy::MANUAL,
@@ -817,14 +828,17 @@
   int32_t initialization_retval = WEBRTC_VIDEO_CODEC_UNINITIALIZED;
   gpu_task_runner_->PostTask(
       FROM_HERE,
-      base::Bind(&RTCVideoEncoder::Impl::CreateAndInitializeVEA, impl_,
+      base::Bind(&RTCVideoEncoder::Impl::CreateAndInitializeVEA,
+                 impl_,
                  gfx::Size(codec_settings->width, codec_settings->height),
-                 codec_settings->startBitrate, profile_, &initialization_waiter,
+                 codec_settings->startBitrate,
+                 profile,
+                 &initialization_waiter,
                  &initialization_retval));
 
   // webrtc::VideoEncoder expects this call to be synchronous.
   initialization_waiter.Wait();
-  RecordInitEncodeUMA(initialization_retval, profile_);
+  RecordInitEncodeUMA(initialization_retval, profile);
   return initialization_retval;
 }
 
diff --git a/content/renderer/media/gpu/rtc_video_encoder.h b/content/renderer/media/gpu/rtc_video_encoder.h
index 67d59b3..bef4f21c 100644
--- a/content/renderer/media/gpu/rtc_video_encoder.h
+++ b/content/renderer/media/gpu/rtc_video_encoder.h
@@ -41,7 +41,7 @@
 class CONTENT_EXPORT RTCVideoEncoder
     : NON_EXPORTED_BASE(public webrtc::VideoEncoder) {
  public:
-  RTCVideoEncoder(media::VideoCodecProfile profile,
+  RTCVideoEncoder(webrtc::VideoCodecType type,
                   media::GpuVideoAcceleratorFactories* gpu_factories);
   ~RTCVideoEncoder() override;
 
@@ -68,7 +68,8 @@
   void RecordInitEncodeUMA(int32_t init_retval,
                            media::VideoCodecProfile profile);
 
-  const media::VideoCodecProfile profile_;
+  // The video codec type, as reported to WebRTC.
+  const webrtc::VideoCodecType video_codec_type_;
 
   // Factory for creating VEAs, shared memory buffers, etc.
   media::GpuVideoAcceleratorFactories* gpu_factories_;
diff --git a/content/renderer/media/gpu/rtc_video_encoder_factory.cc b/content/renderer/media/gpu/rtc_video_encoder_factory.cc
index bbece0d..f3189de 100644
--- a/content/renderer/media/gpu/rtc_video_encoder_factory.cc
+++ b/content/renderer/media/gpu/rtc_video_encoder_factory.cc
@@ -11,23 +11,23 @@
 #include "content/renderer/media/gpu/rtc_video_encoder.h"
 #include "media/gpu/ipc/client/gpu_video_encode_accelerator_host.h"
 #include "media/renderers/gpu_video_accelerator_factories.h"
-#include "third_party/webrtc/common_video/h264/profile_level_id.h"
+#include "media/video/video_encode_accelerator.h"
 
 namespace content {
 
 namespace {
 
 // Translate from media::VideoEncodeAccelerator::SupportedProfile to
-// cricket::WebRtcVideoEncoderFactory::VideoCodec, or return nothing if the
-// profile isn't supported.
-base::Optional<cricket::VideoCodec> VEAToWebRTCCodec(
+// one or more instances of cricket::WebRtcVideoEncoderFactory::VideoCodec
+void VEAToWebRTCCodecs(
+    std::vector<cricket::VideoCodec>* codecs,
     const media::VideoEncodeAccelerator::SupportedProfile& profile) {
   DCHECK_EQ(profile.max_framerate_denominator, 1U);
 
   if (profile.profile >= media::VP8PROFILE_MIN &&
       profile.profile <= media::VP8PROFILE_MAX) {
     if (base::FeatureList::IsEnabled(features::kWebRtcHWVP8Encoding)) {
-      return base::Optional<cricket::VideoCodec>(cricket::VideoCodec("VP8"));
+      codecs->push_back(cricket::VideoCodec("VP8"));
     }
   } else if (profile.profile >= media::H264PROFILE_MIN &&
              profile.profile <= media::H264PROFILE_MAX) {
@@ -41,41 +41,10 @@
 #endif  // BUILDFLAG(RTC_USE_H264) && !defined(MEDIA_DISABLE_FFMPEG)
     if (webrtc_h264_sw_enabled ||
         base::FeatureList::IsEnabled(features::kWebRtcHWH264Encoding)) {
-      webrtc::H264::Profile h264_profile;
-      switch (profile.profile) {
-        case media::H264PROFILE_BASELINE:
-          h264_profile = webrtc::H264::kProfileBaseline;
-          break;
-        case media::H264PROFILE_MAIN:
-          h264_profile = webrtc::H264::kProfileMain;
-          break;
-        case media::H264PROFILE_HIGH:
-          h264_profile = webrtc::H264::kProfileHigh;
-          break;
-        default:
-          // Unsupported H264 profile in WebRTC.
-          return base::Optional<cricket::VideoCodec>();
-      }
-
-      const int width = profile.max_resolution.width();
-      const int height = profile.max_resolution.height();
-      const int fps = profile.max_framerate_numerator;
-      DCHECK_EQ(1u, profile.max_framerate_denominator);
-
-      const rtc::Optional<webrtc::H264::Level> h264_level =
-          webrtc::H264::SupportedLevel(width * height, fps);
-      const webrtc::H264::ProfileLevelId profile_level_id(
-          h264_profile, h264_level.value_or(webrtc::H264::kLevel1));
-
-      cricket::VideoCodec codec("H264");
-      codec.SetParam(cricket::kH264FmtpProfileLevelId,
-                     *webrtc::H264::ProfileLevelIdToString(profile_level_id));
-      codec.SetParam(cricket::kH264FmtpLevelAsymmetryAllowed, "1");
-      codec.SetParam(cricket::kH264FmtpPacketizationMode, "1");
-      return base::Optional<cricket::VideoCodec>(codec);
+      // TODO(magjed): Propagate H264 profile information.
+      codecs->push_back(cricket::VideoCodec("H264"));
     }
   }
-  return base::Optional<cricket::VideoCodec>();
 }
 
 }  // anonymous namespace
@@ -85,36 +54,20 @@
     : gpu_factories_(gpu_factories) {
   const media::VideoEncodeAccelerator::SupportedProfiles& profiles =
       gpu_factories_->GetVideoEncodeAcceleratorSupportedProfiles();
-  for (const auto& profile : profiles) {
-    base::Optional<cricket::VideoCodec> codec = VEAToWebRTCCodec(profile);
-    if (codec) {
-      supported_codecs_.push_back(std::move(*codec));
-      profiles_.push_back(profile.profile);
-    }
-  }
-  // There should be a 1:1 mapping between media::VideoCodecProfile and
-  // cricket::VideoCodec.
-  CHECK_EQ(profiles_.size(), supported_codecs_.size());
+  for (const auto& profile : profiles)
+    VEAToWebRTCCodecs(&supported_codecs_, profile);
 }
 
 RTCVideoEncoderFactory::~RTCVideoEncoderFactory() {}
 
 webrtc::VideoEncoder* RTCVideoEncoderFactory::CreateVideoEncoder(
     const cricket::VideoCodec& codec) {
-  for (size_t i = 0; i < supported_codecs_.size(); ++i) {
-    if (!cricket::CodecNamesEq(codec.name, supported_codecs_[i].name))
-      continue;
-    // Check H264 profile.
-    using webrtc::H264::ParseSdpProfileLevelId;
-    if (cricket::CodecNamesEq(codec.name.c_str(), cricket::kH264CodecName) &&
-        ParseSdpProfileLevelId(codec.params)->profile !=
-            ParseSdpProfileLevelId(supported_codecs_[i].params)->profile) {
-      continue;
+  for (const cricket::VideoCodec& supported_codec : supported_codecs_) {
+    if (cricket::CodecNamesEq(codec.name, supported_codec.name)) {
+      webrtc::VideoCodecType type = webrtc::PayloadNameToCodecType(codec.name)
+                                        .value_or(webrtc::kVideoCodecUnknown);
+      return new RTCVideoEncoder(type, gpu_factories_);
     }
-    // There should be a 1:1 mapping between media::VideoCodecProfile and
-    // cricket::VideoCodec.
-    CHECK_EQ(profiles_.size(), supported_codecs_.size());
-    return new RTCVideoEncoder(profiles_[i], gpu_factories_);
   }
   return nullptr;
 }
diff --git a/content/renderer/media/gpu/rtc_video_encoder_factory.h b/content/renderer/media/gpu/rtc_video_encoder_factory.h
index c5bcde0..307f8f92 100644
--- a/content/renderer/media/gpu/rtc_video_encoder_factory.h
+++ b/content/renderer/media/gpu/rtc_video_encoder_factory.h
@@ -39,9 +39,6 @@
   media::GpuVideoAcceleratorFactories* gpu_factories_;
 
   // List of supported cricket::WebRtcVideoEncoderFactory::VideoCodec.
-  // |profiles_| and |supported_codecs_| have the same length and the profile
-  // for |supported_codecs_[i]| is |profiles_[i]|.
-  std::vector<media::VideoCodecProfile> profiles_;
   std::vector<cricket::VideoCodec> supported_codecs_;
 
   DISALLOW_COPY_AND_ASSIGN(RTCVideoEncoderFactory);
diff --git a/content/renderer/media/gpu/rtc_video_encoder_unittest.cc b/content/renderer/media/gpu/rtc_video_encoder_unittest.cc
index 889b024..3fd7a91b 100644
--- a/content/renderer/media/gpu/rtc_video_encoder_unittest.cc
+++ b/content/renderer/media/gpu/rtc_video_encoder_unittest.cc
@@ -113,19 +113,7 @@
 
   void CreateEncoder(webrtc::VideoCodecType codec_type) {
     DVLOG(3) << __func__;
-    media::VideoCodecProfile media_profile;
-    switch (codec_type) {
-      case webrtc::kVideoCodecVP8:
-        media_profile = media::VP8PROFILE_ANY;
-        break;
-      case webrtc::kVideoCodecH264:
-        media_profile = media::H264PROFILE_BASELINE;
-        break;
-      default:
-        ADD_FAILURE() << "Unexpected codec type: " << codec_type;
-        media_profile = media::VIDEO_CODEC_PROFILE_UNKNOWN;
-    }
-    rtc_encoder_ = base::MakeUnique<RTCVideoEncoder>(media_profile,
+    rtc_encoder_ = base::MakeUnique<RTCVideoEncoder>(codec_type,
                                                      mock_gpu_factories_.get());
   }
 
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 8bd2dbd..9255fbd 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -1429,10 +1429,15 @@
   if (render_view()->webview())
     active_url = render_view()->GetURLForGraphicsContext3D();
 
+  mojom::WidgetPtr widget_channel;
+  mojom::WidgetRequest widget_channel_request =
+      mojo::MakeRequest(&widget_channel);
+
   // Synchronous IPC to obtain a routing id for the fullscreen widget.
   int32_t fullscreen_widget_routing_id = MSG_ROUTING_NONE;
   if (!RenderThreadImpl::current_render_message_filter()
            ->CreateFullscreenWidget(render_view()->routing_id(),
+                                    std::move(widget_channel),
                                     &fullscreen_widget_routing_id)) {
     return nullptr;
   }
@@ -1443,7 +1448,7 @@
   RenderWidgetFullscreenPepper* widget = RenderWidgetFullscreenPepper::Create(
       fullscreen_widget_routing_id, show_callback,
       GetRenderWidget()->compositor_deps(), plugin, active_url,
-      GetRenderWidget()->screen_info());
+      GetRenderWidget()->screen_info(), std::move(widget_channel_request));
   // TODO(nick): The show() handshake seems like unnecessary complexity here,
   // since there's no real delay between CreateFullscreenWidget and
   // ShowCreatedFullscreenWidget. Would it be simpler to have the
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index 13bf677..7c43924 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -340,7 +340,8 @@
                            const ScreenInfo& screen_info,
                            bool swapped_out,
                            bool hidden,
-                           bool never_visible)
+                           bool never_visible,
+                           mojom::WidgetRequest widget_request)
     : routing_id_(widget_routing_id),
       compositor_deps_(compositor_deps),
       webwidget_internal_(nullptr),
@@ -381,6 +382,7 @@
       time_to_first_active_paint_recorded_(true),
       was_shown_time_(base::TimeTicks::Now()),
       current_content_source_id_(0),
+      widget_binding_(this, std::move(widget_request)),
       weak_ptr_factory_(this) {
   DCHECK_NE(routing_id_, MSG_ROUTING_NONE);
   if (!swapped_out)
@@ -431,16 +433,21 @@
     CompositorDependencies* compositor_deps,
     blink::WebPopupType popup_type,
     const ScreenInfo& screen_info) {
+  mojom::WidgetPtr widget_channel;
+  mojom::WidgetRequest widget_channel_request =
+      mojo::MakeRequest(&widget_channel);
+
   // Do a synchronous IPC to obtain a routing ID.
   int32_t routing_id = MSG_ROUTING_NONE;
   if (!RenderThreadImpl::current_render_message_filter()->CreateNewWidget(
-          opener->GetRoutingID(), popup_type, &routing_id)) {
+          opener->GetRoutingID(), popup_type, std::move(widget_channel),
+          &routing_id)) {
     return nullptr;
   }
 
   scoped_refptr<RenderWidget> widget(
       new RenderWidget(routing_id, compositor_deps, popup_type, screen_info,
-                       false, false, false));
+                       false, false, false, std::move(widget_channel_request)));
   ShowCallback opener_callback =
       base::Bind(&RenderViewImpl::ShowCreatedPopupWidget, opener->GetWeakPtr());
   widget->Init(opener_callback, RenderWidget::CreateWebWidget(widget.get()));
diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h
index 8f1d752..c9c67df4 100644
--- a/content/renderer/render_widget.h
+++ b/content/renderer/render_widget.h
@@ -31,6 +31,7 @@
 #include "content/common/edit_command.h"
 #include "content/common/features.h"
 #include "content/common/input/synthetic_gesture_params.h"
+#include "content/common/widget.mojom.h"
 #include "content/public/common/drop_data.h"
 #include "content/public/common/screen_info.h"
 #include "content/renderer/devtools/render_widget_screen_metrics_emulator_delegate.h"
@@ -44,6 +45,7 @@
 #include "ipc/ipc_listener.h"
 #include "ipc/ipc_message.h"
 #include "ipc/ipc_sender.h"
+#include "mojo/public/cpp/bindings/binding.h"
 #include "third_party/WebKit/public/platform/WebDisplayMode.h"
 #include "third_party/WebKit/public/platform/WebInputEvent.h"
 #include "third_party/WebKit/public/platform/WebRect.h"
@@ -128,6 +130,7 @@
     : public IPC::Listener,
       public IPC::Sender,
       NON_EXPORTED_BASE(virtual public blink::WebWidgetClient),
+      public mojom::Widget,
       public RenderWidgetCompositorDelegate,
       public RenderWidgetInputHandlerDelegate,
       public RenderWidgetScreenMetricsEmulatorDelegate,
@@ -446,7 +449,8 @@
                const ScreenInfo& screen_info,
                bool swapped_out,
                bool hidden,
-               bool never_visible);
+               bool never_visible,
+               mojom::WidgetRequest widget_request = nullptr);
 
   ~RenderWidget() override;
 
@@ -898,6 +902,8 @@
 
   scoped_refptr<MainThreadEventQueue> input_event_queue_;
 
+  mojo::Binding<mojom::Widget> widget_binding_;
+
   base::WeakPtrFactory<RenderWidget> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(RenderWidget);
diff --git a/content/renderer/render_widget_fullscreen_pepper.cc b/content/renderer/render_widget_fullscreen_pepper.cc
index 3b646ee3..a40db586f 100644
--- a/content/renderer/render_widget_fullscreen_pepper.cc
+++ b/content/renderer/render_widget_fullscreen_pepper.cc
@@ -253,12 +253,14 @@
     CompositorDependencies* compositor_deps,
     PepperPluginInstanceImpl* plugin,
     const GURL& active_url,
-    const ScreenInfo& screen_info) {
+    const ScreenInfo& screen_info,
+    mojom::WidgetRequest widget_request) {
   DCHECK_NE(MSG_ROUTING_NONE, routing_id);
   DCHECK(!show_callback.is_null());
   scoped_refptr<RenderWidgetFullscreenPepper> widget(
       new RenderWidgetFullscreenPepper(routing_id, compositor_deps, plugin,
-                                       active_url, screen_info));
+                                       active_url, screen_info,
+                                       std::move(widget_request)));
   widget->Init(show_callback, new PepperWidget(widget.get()));
   widget->AddRef();
   return widget.get();
@@ -269,14 +271,16 @@
     CompositorDependencies* compositor_deps,
     PepperPluginInstanceImpl* plugin,
     const GURL& active_url,
-    const ScreenInfo& screen_info)
+    const ScreenInfo& screen_info,
+    mojom::WidgetRequest widget_request)
     : RenderWidget(routing_id,
                    compositor_deps,
                    blink::kWebPopupTypeNone,
                    screen_info,
                    false,
                    false,
-                   false),
+                   false,
+                   std::move(widget_request)),
       active_url_(active_url),
       plugin_(plugin),
       layer_(NULL),
diff --git a/content/renderer/render_widget_fullscreen_pepper.h b/content/renderer/render_widget_fullscreen_pepper.h
index 5aa2140..b5aef5c 100644
--- a/content/renderer/render_widget_fullscreen_pepper.h
+++ b/content/renderer/render_widget_fullscreen_pepper.h
@@ -36,7 +36,8 @@
       CompositorDependencies* compositor_deps,
       PepperPluginInstanceImpl* plugin,
       const GURL& active_url,
-      const ScreenInfo& screen_info);
+      const ScreenInfo& screen_info,
+      mojom::WidgetRequest widget_request);
 
   // pepper::FullscreenContainer API.
   void Invalidate() override;
@@ -61,7 +62,8 @@
                                CompositorDependencies* compositor_deps,
                                PepperPluginInstanceImpl* plugin,
                                const GURL& active_url,
-                               const ScreenInfo& screen_info);
+                               const ScreenInfo& screen_info,
+                               mojom::WidgetRequest widget_request);
   ~RenderWidgetFullscreenPepper() override;
 
   // RenderWidget API.
diff --git a/content/renderer/render_widget_unittest.cc b/content/renderer/render_widget_unittest.cc
index c4401340..09e8486 100644
--- a/content/renderer/render_widget_unittest.cc
+++ b/content/renderer/render_widget_unittest.cc
@@ -9,6 +9,7 @@
 
 #include "base/macros.h"
 #include "base/test/histogram_tester.h"
+#include "base/test/scoped_task_environment.h"
 #include "content/common/input/synthetic_web_input_event_builders.h"
 #include "content/common/input_messages.h"
 #include "content/common/resize_params.h"
@@ -154,6 +155,7 @@
     widget_->Release();
     DCHECK(widget_->HasOneRef());
   }
+
   ~RenderWidgetUnittest() override {}
 
   InteractiveRenderWidget* widget() const { return widget_.get(); }
@@ -162,6 +164,9 @@
     return histogram_tester_;
   }
 
+ protected:
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+
  private:
   MockRenderProcess render_process_;
   MockRenderThread render_thread_;
@@ -462,6 +467,9 @@
   PopupRenderWidget* widget() const { return widget_.get(); }
   FakeCompositorDependencies compositor_deps_;
 
+ protected:
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+
  private:
   MockRenderProcess render_process_;
   MockRenderThread render_thread_;
diff --git a/content/renderer/renderer_main_platform_delegate_android.cc b/content/renderer/renderer_main_platform_delegate_android.cc
index fd6dde5..ffcb0a7d 100644
--- a/content/renderer/renderer_main_platform_delegate_android.cc
+++ b/content/renderer/renderer_main_platform_delegate_android.cc
@@ -14,8 +14,8 @@
 #include "sandbox/sandbox_features.h"
 
 #if BUILDFLAG(USE_SECCOMP_BPF)
-#include "content/common/sandbox_linux/android/sandbox_bpf_base_policy_android.h"
 #include "content/renderer/seccomp_sandbox_status_android.h"
+#include "sandbox/linux/seccomp-bpf-helpers/baseline_policy_android.h"
 #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
 #endif
 
@@ -115,7 +115,7 @@
         << "Seccomp sandbox";
   }
 
-  sandbox::SandboxBPF sandbox(new SandboxBPFBasePolicyAndroid());
+  sandbox::SandboxBPF sandbox(new sandbox::BaselinePolicyAndroid());
   CHECK(
       sandbox.StartSandbox(sandbox::SandboxBPF::SeccompLevel::MULTI_THREADED));
 
diff --git a/content/shell/android/java/src/org/chromium/content_shell/Shell.java b/content/shell/android/java/src/org/chromium/content_shell/Shell.java
index fb232075..63077b4 100644
--- a/content/shell/android/java/src/org/chromium/content_shell/Shell.java
+++ b/content/shell/android/java/src/org/chromium/content_shell/Shell.java
@@ -170,7 +170,7 @@
                 mPrevButton.setVisibility(hasFocus ? GONE : VISIBLE);
                 mStopReloadButton.setVisibility(hasFocus ? GONE : VISIBLE);
                 if (!hasFocus) {
-                    mUrlTextView.setText(mWebContents.getUrl());
+                    mUrlTextView.setText(mWebContents.getVisibleUrl());
                 }
             }
         });
@@ -195,7 +195,7 @@
     public void loadUrl(String url) {
         if (url == null) return;
 
-        if (TextUtils.equals(url, mWebContents.getUrl())) {
+        if (TextUtils.equals(url, mWebContents.getLastCommittedUrl())) {
             mNavigationController.reload(true);
         } else {
             mNavigationController.loadUrl(new LoadUrlParams(sanitizeUrl(url)));
@@ -301,8 +301,8 @@
         mWebContents = mContentViewCore.getWebContents();
         mNavigationController = mWebContents.getNavigationController();
         if (getParent() != null) mContentViewCore.onShow();
-        if (mWebContents.getUrl() != null) {
-            mUrlTextView.setText(mWebContents.getUrl());
+        if (mWebContents.getVisibleUrl() != null) {
+            mUrlTextView.setText(mWebContents.getVisibleUrl());
         }
         ((FrameLayout) findViewById(R.id.contentview_holder)).addView(cv,
                 new FrameLayout.LayoutParams(
diff --git a/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellShellManagementTest.java b/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellShellManagementTest.java
index 37419f5..a278eafc 100644
--- a/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellShellManagementTest.java
+++ b/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellShellManagementTest.java
@@ -40,14 +40,20 @@
         final ContentShellActivity activity =
                 mActivityTestRule.launchContentShellWithUrl(TEST_PAGE_1);
         Assert.assertEquals(TEST_PAGE_1,
-                activity.getActiveShell().getContentViewCore().getWebContents().getUrl());
+                activity.getActiveShell()
+                        .getContentViewCore()
+                        .getWebContents()
+                        .getVisibleUrl());
 
         Shell previousActiveShell = activity.getActiveShell();
         Assert.assertFalse(previousActiveShell.isDestroyed());
 
         mActivityTestRule.loadNewShell(TEST_PAGE_2);
         Assert.assertEquals(TEST_PAGE_2,
-                activity.getActiveShell().getContentViewCore().getWebContents().getUrl());
+                activity.getActiveShell()
+                        .getContentViewCore()
+                        .getWebContents()
+                        .getVisibleUrl());
 
         Assert.assertNotSame(previousActiveShell, activity.getActiveShell());
         Assert.assertTrue(previousActiveShell.isDestroyed());
diff --git a/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellTestCommon.java b/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellTestCommon.java
index bda9840..ac347249 100644
--- a/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellTestCommon.java
+++ b/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellTestCommon.java
@@ -85,7 +85,8 @@
         ContentShellActivity activity = launchContentShellWithUrl(isolatedTestFileUrl);
         Assert.assertNotNull(mCallback.getActivityForTestCommon());
         waitForActiveShellToBeDoneLoading();
-        Assert.assertEquals(isolatedTestFileUrl, getContentViewCore().getWebContents().getUrl());
+        Assert.assertEquals(
+                isolatedTestFileUrl, getContentViewCore().getWebContents().getLastCommittedUrl());
         return activity;
     }
 
@@ -107,7 +108,8 @@
                     updateFailureReason("Shell is still loading.");
                     return false;
                 }
-                if (TextUtils.isEmpty(shell.getContentViewCore().getWebContents().getUrl())) {
+                if (TextUtils.isEmpty(
+                            shell.getContentViewCore().getWebContents().getLastCommittedUrl())) {
                     updateFailureReason("Shell's URL is empty or null.");
                     return false;
                 }
diff --git a/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellUrlTest.java b/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellUrlTest.java
index 59ef4cd..ea35aa9 100644
--- a/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellUrlTest.java
+++ b/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellUrlTest.java
@@ -35,7 +35,10 @@
         Assert.assertNotNull(activity);
 
         // Make sure that the URL is set as expected.
-        Assert.assertEquals(
-                URL, activity.getActiveShell().getContentViewCore().getWebContents().getUrl());
+        Assert.assertEquals(URL,
+                activity.getActiveShell()
+                        .getContentViewCore()
+                        .getWebContents()
+                        .getVisibleUrl());
     }
 }
diff --git a/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ContentShellActivity.java b/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ContentShellActivity.java
index d7a2eb5..6d20e412 100644
--- a/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ContentShellActivity.java
+++ b/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ContentShellActivity.java
@@ -146,7 +146,8 @@
         super.onSaveInstanceState(outState);
         ContentViewCore contentViewCore = getActiveContentViewCore();
         if (contentViewCore != null) {
-            outState.putString(ACTIVE_SHELL_URL_KEY, contentViewCore.getWebContents().getUrl());
+            outState.putString(
+                    ACTIVE_SHELL_URL_KEY, contentViewCore.getWebContents().getLastCommittedUrl());
         }
 
         mWindowAndroid.saveInstanceState(outState);
diff --git a/content/test/data/accessibility/aria/aria-multiline-expected-blink.txt b/content/test/data/accessibility/aria/aria-multiline-expected-blink.txt
new file mode 100644
index 0000000..37f1547
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-multiline-expected-blink.txt
@@ -0,0 +1,3 @@
+rootWebArea
+++textField
+++textField multiline
diff --git a/content/test/data/accessibility/aria/aria-multiline-expected-win.txt b/content/test/data/accessibility/aria/aria-multiline-expected-win.txt
index a34a7163..8fac0ab 100644
--- a/content/test/data/accessibility/aria/aria-multiline-expected-win.txt
+++ b/content/test/data/accessibility/aria/aria-multiline-expected-win.txt
@@ -1,3 +1,3 @@
 ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE
 ++ROLE_SYSTEM_TEXT READONLY IA2_STATE_SINGLE_LINE xml-roles:textbox
-++ROLE_SYSTEM_TEXT READONLY IA2_STATE_MULTI_LINE xml-roles:textbox
+++ROLE_SYSTEM_TEXT READONLY IA2_STATE_MULTI_LINE xml-roles:textbox
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-multiline.html b/content/test/data/accessibility/aria/aria-multiline.html
index e8186a9..3eb9657 100644
--- a/content/test/data/accessibility/aria/aria-multiline.html
+++ b/content/test/data/accessibility/aria/aria-multiline.html
@@ -3,6 +3,7 @@
 @WIN-ALLOW:IA2_STATE_SINGLE_LINE
 @WIN-ALLOW:IA2_STATE_MULTI_LINE
 @WIN-ALLOW:xml-roles*
+@BLINK-ALLOW:multiline
 -->
 <!DOCTYPE html>
 <html>
diff --git a/content/test/data/accessibility/aria/aria-posinset-expected-blink.txt b/content/test/data/accessibility/aria/aria-posinset-expected-blink.txt
index d54814e..b96496d 100644
--- a/content/test/data/accessibility/aria/aria-posinset-expected-blink.txt
+++ b/content/test/data/accessibility/aria/aria-posinset-expected-blink.txt
@@ -6,20 +6,20 @@
 ++++listBoxOption selectable name='Item 1' setSize=2 posInSet=1
 ++++listBoxOption selectable name='Item 2' setSize=2 posInSet=2
 ++form
-++++radioButton setSize=4 posInSet=3 checkedState=3
+++++radioButton setSize=4 posInSet=3 checkedState=1
 ++++staticText name='1'
 ++++++inlineTextBox name='1'
 ++++lineBreak name='<newline>'
 ++++++inlineTextBox name='<newline>'
-++++radioButton setSize=4 posInSet=4 checkedState=3
+++++radioButton setSize=4 posInSet=4 checkedState=1
 ++++staticText name='2'
 ++++++inlineTextBox name='2'
-++radioButton setSize=2 posInSet=1 checkedState=3
+++radioButton setSize=2 posInSet=1 checkedState=1
 ++staticText name='Apple'
 ++++inlineTextBox name='Apple'
 ++lineBreak name='<newline>'
 ++++inlineTextBox name='<newline>'
-++radioButton setSize=2 posInSet=2 checkedState=3
+++radioButton setSize=2 posInSet=2 checkedState=1
 ++staticText name='Banana'
 ++++inlineTextBox name='Banana'
 ++group name='Cake'
@@ -38,6 +38,6 @@
 ++++++++++inlineTextBox name='red'
 ++++++lineBreak name='<newline>'
 ++++++++inlineTextBox name='<newline>'
-++++++radioButton name='blue' setSize=1 posInSet=1 checkedState=3
+++++++radioButton name='blue' setSize=1 posInSet=1 checkedState=1
 ++staticText name='Done'
 ++++inlineTextBox name='Done'
diff --git a/content/test/data/accessibility/aria/aria-posinset-expected-mac.txt b/content/test/data/accessibility/aria/aria-posinset-expected-mac.txt
index 34903d46..0027c71 100644
--- a/content/test/data/accessibility/aria/aria-posinset-expected-mac.txt
+++ b/content/test/data/accessibility/aria/aria-posinset-expected-mac.txt
@@ -6,15 +6,15 @@
 ++++AXStaticText AXRoleDescription='text' AXValue='Item 1' AXARIASetSize='2' AXARIAPosInSet='1'
 ++++AXStaticText AXRoleDescription='text' AXValue='Item 2' AXARIASetSize='2' AXARIAPosInSet='2'
 ++AXGroup AXRoleDescription='form'
-++++AXRadioButton AXRoleDescription='radio button' AXValue='2' AXARIASetSize='4' AXARIAPosInSet='3'
+++++AXRadioButton AXRoleDescription='radio button' AXValue='0' AXARIASetSize='4' AXARIAPosInSet='3'
 ++++AXStaticText AXRoleDescription='text' AXValue='1'
 ++++AXUnknown AXRoleDescription='unknown' AXTitle='<newline>'
-++++AXRadioButton AXRoleDescription='radio button' AXValue='2' AXARIASetSize='4' AXARIAPosInSet='4'
+++++AXRadioButton AXRoleDescription='radio button' AXValue='0' AXARIASetSize='4' AXARIAPosInSet='4'
 ++++AXStaticText AXRoleDescription='text' AXValue='2'
-++AXRadioButton AXRoleDescription='radio button' AXValue='2' AXARIASetSize='2' AXARIAPosInSet='1'
+++AXRadioButton AXRoleDescription='radio button' AXValue='0' AXARIASetSize='2' AXARIAPosInSet='1'
 ++AXStaticText AXRoleDescription='text' AXValue='Apple'
 ++AXUnknown AXRoleDescription='unknown' AXTitle='<newline>'
-++AXRadioButton AXRoleDescription='radio button' AXValue='2' AXARIASetSize='2' AXARIAPosInSet='2'
+++AXRadioButton AXRoleDescription='radio button' AXValue='0' AXARIASetSize='2' AXARIAPosInSet='2'
 ++AXStaticText AXRoleDescription='text' AXValue='Banana'
 ++AXGroup AXRoleDescription='group' AXTitle='Cake'
 ++++AXGroup AXRoleDescription='group'
@@ -28,5 +28,5 @@
 ++++++AXGroup AXRoleDescription='group'
 ++++++++AXStaticText AXRoleDescription='text' AXValue='red'
 ++++++AXUnknown AXRoleDescription='unknown' AXTitle='<newline>'
-++++++AXRadioButton AXRoleDescription='radio button' AXTitle='blue' AXValue='2' AXARIASetSize='1' AXARIAPosInSet='1'
+++++++AXRadioButton AXRoleDescription='radio button' AXTitle='blue' AXValue='0' AXARIASetSize='1' AXARIAPosInSet='1'
 ++AXStaticText AXRoleDescription='text' AXValue='Done'
diff --git a/content/test/data/accessibility/aria/aria-posinset-expected-win.txt b/content/test/data/accessibility/aria/aria-posinset-expected-win.txt
index dc89ba5..7d6d3e0 100644
--- a/content/test/data/accessibility/aria/aria-posinset-expected-win.txt
+++ b/content/test/data/accessibility/aria/aria-posinset-expected-win.txt
@@ -6,15 +6,15 @@
 ++++ROLE_SYSTEM_LISTITEM name='Item 1' FOCUSABLE setsize:2 posinset:1
 ++++ROLE_SYSTEM_LISTITEM name='Item 2' FOCUSABLE setsize:2 posinset:2
 ++IA2_ROLE_FORM
-++++ROLE_SYSTEM_RADIOBUTTON MIXED FOCUSABLE IA2_STATE_CHECKABLE setsize:4 posinset:3 checkable:true
+++++ROLE_SYSTEM_RADIOBUTTON FOCUSABLE IA2_STATE_CHECKABLE setsize:4 posinset:3 checkable:true
 ++++ROLE_SYSTEM_STATICTEXT name='1'
 ++++ROLE_SYSTEM_WHITESPACE name='<newline>'
-++++ROLE_SYSTEM_RADIOBUTTON MIXED FOCUSABLE IA2_STATE_CHECKABLE setsize:4 posinset:4 checkable:true
+++++ROLE_SYSTEM_RADIOBUTTON FOCUSABLE IA2_STATE_CHECKABLE setsize:4 posinset:4 checkable:true
 ++++ROLE_SYSTEM_STATICTEXT name='2'
-++ROLE_SYSTEM_RADIOBUTTON MIXED FOCUSABLE IA2_STATE_CHECKABLE setsize:2 posinset:1 checkable:true
+++ROLE_SYSTEM_RADIOBUTTON FOCUSABLE IA2_STATE_CHECKABLE setsize:2 posinset:1 checkable:true
 ++ROLE_SYSTEM_STATICTEXT name='Apple'
 ++ROLE_SYSTEM_WHITESPACE name='<newline>'
-++ROLE_SYSTEM_RADIOBUTTON MIXED FOCUSABLE IA2_STATE_CHECKABLE setsize:2 posinset:2 checkable:true
+++ROLE_SYSTEM_RADIOBUTTON FOCUSABLE IA2_STATE_CHECKABLE setsize:2 posinset:2 checkable:true
 ++ROLE_SYSTEM_STATICTEXT name='Banana'
 ++ROLE_SYSTEM_GROUPING name='Cake'
 ++++IA2_ROLE_LABEL
@@ -28,5 +28,5 @@
 ++++++IA2_ROLE_LABEL
 ++++++++ROLE_SYSTEM_STATICTEXT name='red'
 ++++++ROLE_SYSTEM_WHITESPACE name='<newline>'
-++++++ROLE_SYSTEM_RADIOBUTTON name='blue' MIXED FOCUSABLE IA2_STATE_CHECKABLE setsize:1 posinset:1 checkable:true
+++++++ROLE_SYSTEM_RADIOBUTTON name='blue' FOCUSABLE IA2_STATE_CHECKABLE setsize:1 posinset:1 checkable:true
 ++ROLE_SYSTEM_STATICTEXT name='Done'
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-textbox-expected-blink.txt b/content/test/data/accessibility/aria/aria-textbox-expected-blink.txt
index ea8d527..e50c5949 100644
--- a/content/test/data/accessibility/aria/aria-textbox-expected-blink.txt
+++ b/content/test/data/accessibility/aria/aria-textbox-expected-blink.txt
@@ -2,6 +2,6 @@
 ++textField
 ++++staticText name='TextBox1'
 ++++++inlineTextBox name='TextBox1'
-++textField
+++textField multiline
 ++++staticText name='TextBox2'
-++++++inlineTextBox name='TextBox2'
\ No newline at end of file
+++++++inlineTextBox name='TextBox2'
diff --git a/content/test/data/accessibility/aria/aria-textbox-expected-win.txt b/content/test/data/accessibility/aria/aria-textbox-expected-win.txt
index a7238bc..c38d268 100644
--- a/content/test/data/accessibility/aria/aria-textbox-expected-win.txt
+++ b/content/test/data/accessibility/aria/aria-textbox-expected-win.txt
@@ -2,4 +2,4 @@
 ++ROLE_SYSTEM_TEXT value='TextBox1' READONLY IA2_STATE_SINGLE_LINE xml-roles:textbox ia2_hypertext='TextBox1' n_selections=0
 ++++ROLE_SYSTEM_STATICTEXT name='TextBox1' ia2_hypertext='TextBox1' n_selections=0
 ++ROLE_SYSTEM_TEXT value='TextBox2' READONLY IA2_STATE_MULTI_LINE xml-roles:textbox ia2_hypertext='TextBox2' n_selections=0
-++++ROLE_SYSTEM_STATICTEXT name='TextBox2' ia2_hypertext='TextBox2' n_selections=0
+++++ROLE_SYSTEM_STATICTEXT name='TextBox2' ia2_hypertext='TextBox2' n_selections=0
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-textbox-with-rich-text-expected-win.txt b/content/test/data/accessibility/aria/aria-textbox-with-rich-text-expected-win.txt
index e47278b5..0f5ac10 100644
--- a/content/test/data/accessibility/aria/aria-textbox-with-rich-text-expected-win.txt
+++ b/content/test/data/accessibility/aria/aria-textbox-with-rich-text-expected-win.txt
@@ -1,5 +1,5 @@
 ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE ia2_hypertext='<obj0><obj1>' caret_offset=0 n_selections=0
-++ROLE_SYSTEM_TEXT value='TextBox1<newline><newline>' FOCUSABLE IA2_STATE_MULTI_LINE xml-roles:textbox ia2_hypertext='<obj0>' caret_offset=0 n_selections=0
+++ROLE_SYSTEM_TEXT value='TextBox1<newline><newline>' FOCUSABLE IA2_STATE_SINGLE_LINE xml-roles:textbox ia2_hypertext='<obj0>' caret_offset=0 n_selections=0
 ++++IA2_ROLE_HEADING name='TextBox1' xml-roles:heading ia2_hypertext='TextBox1' caret_offset=0 n_selections=0
 ++++++ROLE_SYSTEM_STATICTEXT name='TextBox1' ia2_hypertext='TextBox1' caret_offset=0 n_selections=0
 ++ROLE_SYSTEM_TEXT value='TextBox2<newline><newline>Some text.' FOCUSABLE IA2_STATE_MULTI_LINE xml-roles:textbox ia2_hypertext='<obj0><obj1>' n_selections=0
diff --git a/content/test/data/accessibility/aria/aria-textbox-with-selection-expected-android.txt b/content/test/data/accessibility/aria/aria-textbox-with-selection-expected-android.txt
index f314ee1..61d5a59 100644
--- a/content/test/data/accessibility/aria/aria-textbox-with-selection-expected-android.txt
+++ b/content/test/data/accessibility/aria/aria-textbox-with-selection-expected-android.txt
@@ -1,3 +1,3 @@
 android.webkit.WebView focusable focused scrollable
 ++android.widget.EditText clickable editable_text has_non_empty_value name='Single line.' text_change_added_count=12
-++android.widget.EditText clickable editable_text has_non_empty_value multiline name='Multiple<newline>lines.' text_change_added_count=15
\ No newline at end of file
+++android.widget.EditText clickable editable_text has_non_empty_value multiline name='Multiple<newline>lines.' text_change_added_count=15
diff --git a/content/test/data/accessibility/aria/aria-textbox-with-selection-expected-blink.txt b/content/test/data/accessibility/aria/aria-textbox-with-selection-expected-blink.txt
index 5eccce2..332bf686 100644
--- a/content/test/data/accessibility/aria/aria-textbox-with-selection-expected-blink.txt
+++ b/content/test/data/accessibility/aria/aria-textbox-with-selection-expected-blink.txt
@@ -2,7 +2,7 @@
 ++textField
 ++++staticText name='Single line.'
 ++++++inlineTextBox name='Single line.'
-++textField
+++textField multiline
 ++++staticText name='Multiple'
 ++++++inlineTextBox name='Multiple'
 ++++lineBreak name='<newline>'
diff --git a/content/test/data/accessibility/aria/aria-textbox-with-selection.html b/content/test/data/accessibility/aria/aria-textbox-with-selection.html
index 8333775..ebe738c 100644
--- a/content/test/data/accessibility/aria/aria-textbox-with-selection.html
+++ b/content/test/data/accessibility/aria/aria-textbox-with-selection.html
@@ -13,6 +13,7 @@
 <html>
 <body>
   <div id="text1" role="textbox">Single line.</div>
+  <!-- Will not expose multiline state because it's not actually editable. -->
   <div id="text2" role="textbox" aria-multiline="true">Multiple<br>lines.</div>
 
   <script>
diff --git a/content/test/data/accessibility/aria/aria-textbox.html b/content/test/data/accessibility/aria/aria-textbox.html
index 93435e6..8a0cb02a 100644
--- a/content/test/data/accessibility/aria/aria-textbox.html
+++ b/content/test/data/accessibility/aria/aria-textbox.html
@@ -16,6 +16,8 @@
 <body>
   <!-- There should be no selection on the document because the textboxes are
       not content editable. -->
+  <!-- There should be no single/multi-line state on the textboxes because
+      they are not content editable -->
   <div role="textbox">TextBox1</div>
   <div role="textbox" aria-multiline="true">TextBox2</div>
 </body>
diff --git a/content/test/data/accessibility/css/color-expected-blink.txt b/content/test/data/accessibility/css/color-expected-blink.txt
index 11c08de6e..2d89989 100644
--- a/content/test/data/accessibility/css/color-expected-blink.txt
+++ b/content/test/data/accessibility/css/color-expected-blink.txt
@@ -2,7 +2,7 @@
 ++paragraph backgroundColor=-16776961 color=-65536
 ++++staticText name='Red on blue.' backgroundColor=-16776961 color=-65536
 ++++++inlineTextBox name='Red on blue.' color=-16777216
-++genericContainer backgroundColor=-1 color=-16777216
+++genericContainer multiline backgroundColor=-1 color=-16777216
 ++++staticText name='Default.' backgroundColor=-1 color=-16777216
 ++++++inlineTextBox name='Default.' color=-16777216
 ++++staticText name='Blue background.' backgroundColor=-16776961 color=-16777216
diff --git a/content/test/data/accessibility/css/color-expected-win.txt b/content/test/data/accessibility/css/color-expected-win.txt
index 45a1594..31090193 100644
--- a/content/test/data/accessibility/css/color-expected-win.txt
+++ b/content/test/data/accessibility/css/color-expected-win.txt
@@ -1,7 +1,7 @@
 ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE offset:0 background-color:rgb(0\,0\,255) color:rgb(255\,0\,0) offset:1 background-color:rgb(255\,255\,255) color:rgb(0\,0\,0) ia2_hypertext='<obj0><obj1>'
 ++IA2_ROLE_PARAGRAPH offset:0 background-color:rgb(0\,0\,255) color:rgb(255\,0\,0) ia2_hypertext='Red on blue.'
 ++++ROLE_SYSTEM_STATICTEXT name='Red on blue.' offset:0 background-color:rgb(0\,0\,255) color:rgb(255\,0\,0) ia2_hypertext='Red on blue.'
-++IA2_ROLE_SECTION FOCUSABLE offset:0 background-color:rgb(255\,255\,255) color:rgb(0\,0\,0) offset:8 background-color:rgb(0\,0\,255) color:rgb(0\,0\,0) offset:24 background-color:rgb(255\,255\,255) color:rgb(0\,255\,0) ia2_hypertext='Default.Blue background.Green text.'
+++IA2_ROLE_SECTION FOCUSABLE IA2_STATE_MULTI_LINE offset:0 background-color:rgb(255\,255\,255) color:rgb(0\,0\,0) offset:8 background-color:rgb(0\,0\,255) color:rgb(0\,0\,0) offset:24 background-color:rgb(255\,255\,255) color:rgb(0\,255\,0) ia2_hypertext='Default.Blue background.Green text.'
 ++++ROLE_SYSTEM_STATICTEXT name='Default.' offset:0 background-color:rgb(255\,255\,255) color:rgb(0\,0\,0) ia2_hypertext='Default.'
 ++++ROLE_SYSTEM_STATICTEXT name='Blue background.' offset:0 background-color:rgb(0\,0\,255) color:rgb(0\,0\,0) ia2_hypertext='Blue background.'
-++++ROLE_SYSTEM_STATICTEXT name='Green text.' offset:0 background-color:rgb(255\,255\,255) color:rgb(0\,255\,0) ia2_hypertext='Green text.'
+++++ROLE_SYSTEM_STATICTEXT name='Green text.' offset:0 background-color:rgb(255\,255\,255) color:rgb(0\,255\,0) ia2_hypertext='Green text.'
\ No newline at end of file
diff --git a/content/test/data/accessibility/css/font-style-expected-blink.txt b/content/test/data/accessibility/css/font-style-expected-blink.txt
index 4866fe8e..846a3be5 100644
--- a/content/test/data/accessibility/css/font-style-expected-blink.txt
+++ b/content/test/data/accessibility/css/font-style-expected-blink.txt
@@ -14,7 +14,7 @@
 ++++++inlineTextBox name='over'
 ++++staticText name='dog' textStyle=7
 ++++++inlineTextBox name='dog'
-++genericContainer
+++genericContainer multiline
 ++++staticText name='Normal'
 ++++++inlineTextBox name='Normal'
 ++++staticText name='bold' textStyle=1
diff --git a/content/test/data/accessibility/css/font-style-expected-win.txt b/content/test/data/accessibility/css/font-style-expected-win.txt
index ce99610..91f4de0 100644
--- a/content/test/data/accessibility/css/font-style-expected-win.txt
+++ b/content/test/data/accessibility/css/font-style-expected-win.txt
@@ -7,9 +7,9 @@
 ++++ROLE_SYSTEM_STATICTEXT name='jumped' offset:0 font-style:italic font-weight:bold text-line-through-style:none text-line-through-type:none text-underline-style:none text-underline-type:none ia2_hypertext='jumped'
 ++++ROLE_SYSTEM_STATICTEXT name='over' offset:0 font-style:italic font-weight:normal text-line-through-style:none text-line-through-type:none text-underline-style:solid text-underline-type:single ia2_hypertext='over'
 ++++ROLE_SYSTEM_STATICTEXT name='dog' offset:0 font-style:italic font-weight:bold text-line-through-style:none text-line-through-type:none text-underline-style:solid text-underline-type:single ia2_hypertext='dog'
-++IA2_ROLE_SECTION FOCUSABLE offset:0 font-style:normal font-weight:normal text-line-through-style:none text-line-through-type:none text-underline-style:none text-underline-type:none offset:6 font-style:normal font-weight:bold text-line-through-style:none text-line-through-type:none text-underline-style:none text-underline-type:none offset:10 font-style:italic font-weight:normal text-line-through-style:none text-line-through-type:none text-underline-style:none text-underline-type:none offset:16 font-style:normal font-weight:normal text-line-through-style:none text-line-through-type:none text-underline-style:solid text-underline-type:single offset:25 font-style:normal font-weight:normal text-line-through-style:solid text-line-through-type:single text-underline-style:none text-underline-type:none ia2_hypertext='Normalbolditalicunderlineline-through'
+++IA2_ROLE_SECTION FOCUSABLE IA2_STATE_MULTI_LINE offset:0 font-style:normal font-weight:normal text-line-through-style:none text-line-through-type:none text-underline-style:none text-underline-type:none offset:6 font-style:normal font-weight:bold text-line-through-style:none text-line-through-type:none text-underline-style:none text-underline-type:none offset:10 font-style:italic font-weight:normal text-line-through-style:none text-line-through-type:none text-underline-style:none text-underline-type:none offset:16 font-style:normal font-weight:normal text-line-through-style:none text-line-through-type:none text-underline-style:solid text-underline-type:single offset:25 font-style:normal font-weight:normal text-line-through-style:solid text-line-through-type:single text-underline-style:none text-underline-type:none ia2_hypertext='Normalbolditalicunderlineline-through'
 ++++ROLE_SYSTEM_STATICTEXT name='Normal' offset:0 font-style:normal font-weight:normal text-line-through-style:none text-line-through-type:none text-underline-style:none text-underline-type:none ia2_hypertext='Normal'
 ++++ROLE_SYSTEM_STATICTEXT name='bold' offset:0 font-style:normal font-weight:bold text-line-through-style:none text-line-through-type:none text-underline-style:none text-underline-type:none ia2_hypertext='bold'
 ++++ROLE_SYSTEM_STATICTEXT name='italic' offset:0 font-style:italic font-weight:normal text-line-through-style:none text-line-through-type:none text-underline-style:none text-underline-type:none ia2_hypertext='italic'
 ++++ROLE_SYSTEM_STATICTEXT name='underline' offset:0 font-style:normal font-weight:normal text-line-through-style:none text-line-through-type:none text-underline-style:solid text-underline-type:single ia2_hypertext='underline'
-++++ROLE_SYSTEM_STATICTEXT name='line-through' offset:0 font-style:normal font-weight:normal text-line-through-style:solid text-line-through-type:single text-underline-style:none text-underline-type:none ia2_hypertext='line-through'
+++++ROLE_SYSTEM_STATICTEXT name='line-through' offset:0 font-style:normal font-weight:normal text-line-through-style:solid text-line-through-type:single text-underline-style:none text-underline-type:none ia2_hypertext='line-through'
\ No newline at end of file
diff --git a/content/test/data/accessibility/event/aria-combo-box-collapse-expected-win.txt b/content/test/data/accessibility/event/aria-combo-box-collapse-expected-win.txt
index 3badd56..15e2fa9 100644
--- a/content/test/data/accessibility/event/aria-combo-box-collapse-expected-win.txt
+++ b/content/test/data/accessibility/event/aria-combo-box-collapse-expected-win.txt
@@ -1,3 +1,3 @@
 EVENT_OBJECT_STATECHANGE on role=ROLE_SYSTEM_COMBOBOX FOCUSED,COLLAPSED,FOCUSABLE,HASPOPUP IA2_STATE_EDITABLE,IA2_STATE_SUPPORTS_AUTOCOMPLETION,IA2_STATE_VERTICAL
 EVENT_OBJECT_FOCUS on role=ROLE_SYSTEM_COMBOBOX FOCUSED,COLLAPSED,FOCUSABLE,HASPOPUP IA2_STATE_EDITABLE,IA2_STATE_SUPPORTS_AUTOCOMPLETION,IA2_STATE_VERTICAL
-IA2_EVENT_ACTIVE_DESCENDANT_CHANGED on role=ROLE_SYSTEM_COMBOBOX FOCUSED,COLLAPSED,FOCUSABLE,HASPOPUP IA2_STATE_EDITABLE,IA2_STATE_SUPPORTS_AUTOCOMPLETION,IA2_STATE_VERTICAL
+IA2_EVENT_ACTIVE_DESCENDANT_CHANGED on role=ROLE_SYSTEM_COMBOBOX FOCUSED,COLLAPSED,FOCUSABLE,HASPOPUP IA2_STATE_EDITABLE,IA2_STATE_SUPPORTS_AUTOCOMPLETION,IA2_STATE_VERTICAL
\ No newline at end of file
diff --git a/content/test/data/accessibility/event/aria-combo-box-expand-expected-win.txt b/content/test/data/accessibility/event/aria-combo-box-expand-expected-win.txt
index db3df37c..c70c745e4 100644
--- a/content/test/data/accessibility/event/aria-combo-box-expand-expected-win.txt
+++ b/content/test/data/accessibility/event/aria-combo-box-expand-expected-win.txt
@@ -1,3 +1,3 @@
 EVENT_OBJECT_STATECHANGE on role=ROLE_SYSTEM_COMBOBOX EXPANDED,FOCUSABLE,HASPOPUP IA2_STATE_EDITABLE,IA2_STATE_SUPPORTS_AUTOCOMPLETION,IA2_STATE_VERTICAL
 EVENT_OBJECT_FOCUS on role=ROLE_SYSTEM_LISTITEM name="Apple" FOCUSED,FOCUSABLE,SELECTABLE
-IA2_EVENT_ACTIVE_DESCENDANT_CHANGED on role=ROLE_SYSTEM_COMBOBOX EXPANDED,FOCUSABLE,HASPOPUP IA2_STATE_EDITABLE,IA2_STATE_SUPPORTS_AUTOCOMPLETION,IA2_STATE_VERTICAL
+IA2_EVENT_ACTIVE_DESCENDANT_CHANGED on role=ROLE_SYSTEM_COMBOBOX EXPANDED,FOCUSABLE,HASPOPUP IA2_STATE_EDITABLE,IA2_STATE_SUPPORTS_AUTOCOMPLETION,IA2_STATE_VERTICAL
\ No newline at end of file
diff --git a/content/test/data/accessibility/event/aria-combo-box-focus-expected-win.txt b/content/test/data/accessibility/event/aria-combo-box-focus-expected-win.txt
index 6b53ceb..f0b6aea 100644
--- a/content/test/data/accessibility/event/aria-combo-box-focus-expected-win.txt
+++ b/content/test/data/accessibility/event/aria-combo-box-focus-expected-win.txt
@@ -1,2 +1,2 @@
 EVENT_OBJECT_FOCUS on role=ROLE_SYSTEM_LISTITEM name="Apple" FOCUSED,FOCUSABLE,SELECTABLE
-IA2_EVENT_TEXT_CARET_MOVED on role=ROLE_SYSTEM_COMBOBOX EXPANDED,FOCUSABLE,HASPOPUP IA2_STATE_EDITABLE,IA2_STATE_SUPPORTS_AUTOCOMPLETION,IA2_STATE_VERTICAL
+IA2_EVENT_TEXT_CARET_MOVED on role=ROLE_SYSTEM_COMBOBOX EXPANDED,FOCUSABLE,HASPOPUP IA2_STATE_EDITABLE,IA2_STATE_SUPPORTS_AUTOCOMPLETION,IA2_STATE_VERTICAL
\ No newline at end of file
diff --git a/content/test/data/accessibility/event/aria-combo-box-next-expected-win.txt b/content/test/data/accessibility/event/aria-combo-box-next-expected-win.txt
index c68d47f0..71d5544 100644
--- a/content/test/data/accessibility/event/aria-combo-box-next-expected-win.txt
+++ b/content/test/data/accessibility/event/aria-combo-box-next-expected-win.txt
@@ -1,2 +1,2 @@
 EVENT_OBJECT_FOCUS on role=ROLE_SYSTEM_LISTITEM name="Banana" FOCUSED,FOCUSABLE,SELECTABLE
-IA2_EVENT_ACTIVE_DESCENDANT_CHANGED on role=ROLE_SYSTEM_COMBOBOX EXPANDED,FOCUSABLE,HASPOPUP IA2_STATE_EDITABLE,IA2_STATE_SUPPORTS_AUTOCOMPLETION,IA2_STATE_VERTICAL
+IA2_EVENT_ACTIVE_DESCENDANT_CHANGED on role=ROLE_SYSTEM_COMBOBOX EXPANDED,FOCUSABLE,HASPOPUP IA2_STATE_EDITABLE,IA2_STATE_SUPPORTS_AUTOCOMPLETION,IA2_STATE_VERTICAL
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/action-verbs-expected-blink.txt b/content/test/data/accessibility/html/action-verbs-expected-blink.txt
index 814f2e5..b886a2b 100644
--- a/content/test/data/accessibility/html/action-verbs-expected-blink.txt
+++ b/content/test/data/accessibility/html/action-verbs-expected-blink.txt
@@ -12,7 +12,7 @@
 ++textField
 ++checkBox defaultActionVerb=2 checkedState=1
 ++checkBox defaultActionVerb=8 checkedState=2
-++radioButton defaultActionVerb=7 checkedState=3
+++radioButton defaultActionVerb=7 checkedState=1
 ++switch name='ARIA Switch' defaultActionVerb=2 checkedState=1
 ++popUpButton collapsed haspopup defaultActionVerb=5
 ++++menuListPopup invisible activedescendantId=menuListOption
diff --git a/content/test/data/accessibility/html/contenteditable-descendants-expected-blink.txt b/content/test/data/accessibility/html/contenteditable-descendants-expected-blink.txt
index f38d219a..6ac67df 100644
--- a/content/test/data/accessibility/html/contenteditable-descendants-expected-blink.txt
+++ b/content/test/data/accessibility/html/contenteditable-descendants-expected-blink.txt
@@ -1,10 +1,10 @@
 rootWebArea
-++genericContainer editable richlyEditable
+++genericContainer editable multiline richlyEditable
 ++++paragraph editable richlyEditable
 ++++++staticText editable richlyEditable name='A contenteditable with a '
 ++++++++inlineTextBox name='A contenteditable with a '
-++++++link editable richlyEditable name='link'
-++++++++staticText editable richlyEditable name='link'
+++++++link editable linked richlyEditable name='link'
+++++++++staticText editable linked richlyEditable name='link'
 ++++++++++inlineTextBox name='link'
 ++++++staticText editable richlyEditable name=' and an '
 ++++++++inlineTextBox name=' and an '
@@ -29,6 +29,6 @@
 ++paragraph
 ++++staticText name='Non-editable paragraph.'
 ++++++inlineTextBox name='Non-editable paragraph.'
-++paragraph editable richlyEditable
+++paragraph editable multiline richlyEditable
 ++++staticText editable richlyEditable name='Should keep the role but change the state.'
-++++++inlineTextBox name='Should keep the role but change the state.'
\ No newline at end of file
+++++++inlineTextBox name='Should keep the role but change the state.'
diff --git a/content/test/data/accessibility/html/contenteditable-descendants-expected-win.txt b/content/test/data/accessibility/html/contenteditable-descendants-expected-win.txt
index 35b988c..86f8dd9 100644
--- a/content/test/data/accessibility/html/contenteditable-descendants-expected-win.txt
+++ b/content/test/data/accessibility/html/contenteditable-descendants-expected-win.txt
@@ -1,5 +1,5 @@
 ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE ia2_hypertext='<obj0><obj1><obj2>' n_selections=0
-++IA2_ROLE_SECTION FOCUSABLE IA2_STATE_EDITABLE ia2_hypertext='<obj0><obj1><obj2>' n_selections=0
+++IA2_ROLE_SECTION FOCUSABLE IA2_STATE_EDITABLE IA2_STATE_MULTI_LINE ia2_hypertext='<obj0><obj1><obj2>' n_selections=0
 ++++IA2_ROLE_PARAGRAPH IA2_STATE_EDITABLE ia2_hypertext='A contenteditable with a <obj1> and an <obj3> and a <obj5>.' n_selections=0
 ++++++ROLE_SYSTEM_STATICTEXT name='A contenteditable with a ' IA2_STATE_EDITABLE ia2_hypertext='A contenteditable with a ' n_selections=0
 ++++++ROLE_SYSTEM_LINK name='link' LINKED IA2_STATE_EDITABLE ia2_hypertext='link' n_selections=0
@@ -21,5 +21,5 @@
 ++++++++ROLE_SYSTEM_STATICTEXT name='Editable list item.' IA2_STATE_EDITABLE ia2_hypertext='Editable list item.' n_selections=0
 ++IA2_ROLE_PARAGRAPH ia2_hypertext='Non-editable paragraph.' n_selections=0
 ++++ROLE_SYSTEM_STATICTEXT name='Non-editable paragraph.' ia2_hypertext='Non-editable paragraph.' n_selections=0
-++IA2_ROLE_PARAGRAPH FOCUSABLE IA2_STATE_EDITABLE ia2_hypertext='Should keep the role but change the state.' n_selections=0
+++IA2_ROLE_PARAGRAPH FOCUSABLE IA2_STATE_EDITABLE IA2_STATE_MULTI_LINE ia2_hypertext='Should keep the role but change the state.' n_selections=0
 ++++ROLE_SYSTEM_STATICTEXT name='Should keep the role but change the state.' IA2_STATE_EDITABLE ia2_hypertext='Should keep the role but change the state.' n_selections=0
diff --git a/content/test/data/accessibility/html/contenteditable-descendants-with-selection-expected-blink.txt b/content/test/data/accessibility/html/contenteditable-descendants-with-selection-expected-blink.txt
index ea1a5dc..83490c7f 100644
--- a/content/test/data/accessibility/html/contenteditable-descendants-with-selection-expected-blink.txt
+++ b/content/test/data/accessibility/html/contenteditable-descendants-with-selection-expected-blink.txt
@@ -1,5 +1,5 @@
 rootWebArea
-++genericContainer editable richlyEditable
+++genericContainer editable multiline richlyEditable
 ++++paragraph editable richlyEditable
 ++++++staticText editable richlyEditable name='A contenteditable with a ' TreeData.textSelStartOffset=0
 ++++++++inlineTextBox name='A contenteditable with a '
diff --git a/content/test/data/accessibility/html/contenteditable-descendants-with-selection-expected-win.txt b/content/test/data/accessibility/html/contenteditable-descendants-with-selection-expected-win.txt
index 82914951..badd9296 100644
--- a/content/test/data/accessibility/html/contenteditable-descendants-with-selection-expected-win.txt
+++ b/content/test/data/accessibility/html/contenteditable-descendants-with-selection-expected-win.txt
@@ -1,5 +1,5 @@
 ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE ia2_hypertext='<obj0>' caret_offset=1 n_selections=1 selection_start=0 selection_end=1
-++IA2_ROLE_SECTION FOCUSABLE IA2_STATE_EDITABLE ia2_hypertext='<obj0><obj1><obj2>' caret_offset=3 n_selections=1 selection_start=0 selection_end=3
+++IA2_ROLE_SECTION FOCUSABLE IA2_STATE_EDITABLE IA2_STATE_MULTI_LINE ia2_hypertext='<obj0><obj1><obj2>' caret_offset=3 n_selections=1 selection_start=0 selection_end=3
 ++++IA2_ROLE_PARAGRAPH IA2_STATE_EDITABLE ia2_hypertext='A contenteditable with a <obj1> and an <obj3> and a <obj5>.' n_selections=1 selection_start=0 selection_end=44
 ++++++ROLE_SYSTEM_STATICTEXT name='A contenteditable with a ' IA2_STATE_EDITABLE ia2_hypertext='A contenteditable with a ' n_selections=1 selection_start=0 selection_end=25
 ++++++ROLE_SYSTEM_LINK name='link' LINKED IA2_STATE_EDITABLE ia2_hypertext='link' n_selections=1 selection_start=0 selection_end=4
diff --git a/content/test/data/accessibility/html/contenteditable-descendants-with-selection.html b/content/test/data/accessibility/html/contenteditable-descendants-with-selection.html
index 7249361..ee1d1931 100644
--- a/content/test/data/accessibility/html/contenteditable-descendants-with-selection.html
+++ b/content/test/data/accessibility/html/contenteditable-descendants-with-selection.html
@@ -1,5 +1,7 @@
 <!--
 @WIN-ALLOW:IA2_STATE_EDITABLE
+@WIN-ALLOW:IA2_STATE_MULTI_LINE
+@WIN-ALLOW:IA2_STATE_SINGLE_LINE
 @WIN-ALLOW:LINKED
 @WIN-ALLOW:ia2_hypertext=*
 @WIN-ALLOW:caret_offset*
diff --git a/content/test/data/accessibility/html/contenteditable-descendants.html b/content/test/data/accessibility/html/contenteditable-descendants.html
index 2b678a3..822e778d 100644
--- a/content/test/data/accessibility/html/contenteditable-descendants.html
+++ b/content/test/data/accessibility/html/contenteditable-descendants.html
@@ -1,11 +1,14 @@
 <!--
 @WIN-ALLOW:caret_offset*
 @WIN-ALLOW:IA2_STATE_EDITABLE
+@WIN-ALLOW:IA2_STATE_MULTI_LINE
 @WIN-ALLOW:ia2_hypertext=*
 @WIN-ALLOW:LINKED
 @WIN-ALLOW:n_selections*
 @WIN-ALLOW:selection_start*
 @WIN-ALLOW:selection_end*
+@WIN-DENY:value
+@BLINK-ALLOW:linked
 @BLINK-ALLOW:editable*
 @BLINK-ALLOW:richlyEditable*
 @BLINK-ALLOW:textSel*
diff --git a/content/test/data/accessibility/html/contenteditable-with-embedded-contenteditables-expected-blink.txt b/content/test/data/accessibility/html/contenteditable-with-embedded-contenteditables-expected-blink.txt
index 2d02822..521e850 100644
--- a/content/test/data/accessibility/html/contenteditable-with-embedded-contenteditables-expected-blink.txt
+++ b/content/test/data/accessibility/html/contenteditable-with-embedded-contenteditables-expected-blink.txt
@@ -1,5 +1,5 @@
 rootWebArea
-++genericContainer editable richlyEditable
+++genericContainer editable multiline richlyEditable
 ++++paragraph editable richlyEditable
 ++++++staticText editable richlyEditable name='This is editable.'
 ++++++++inlineTextBox name='This is editable.'
@@ -7,9 +7,9 @@
 ++++++inlineTextBox name='This is not editable.'
 ++++lineBreak name='<newline>'
 ++++++inlineTextBox name='<newline>'
-++++paragraph editable richlyEditable
+++++paragraph editable multiline richlyEditable
 ++++++staticText editable richlyEditable name='But this one is.'
 ++++++++inlineTextBox name='But this one is.'
-++++paragraph editable richlyEditable
+++++paragraph editable multiline richlyEditable
 ++++++staticText editable richlyEditable name='So is this one.'
 ++++++++inlineTextBox name='So is this one.'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/contenteditable-with-embedded-contenteditables-expected-win.txt b/content/test/data/accessibility/html/contenteditable-with-embedded-contenteditables-expected-win.txt
index 7d8750a..f6bf3ab 100644
--- a/content/test/data/accessibility/html/contenteditable-with-embedded-contenteditables-expected-win.txt
+++ b/content/test/data/accessibility/html/contenteditable-with-embedded-contenteditables-expected-win.txt
@@ -1,5 +1,5 @@
 ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE ia2_hypertext='<obj0>' n_selections=0
-++IA2_ROLE_SECTION FOCUSABLE IA2_STATE_EDITABLE ia2_hypertext='<obj0>This is not editable.<newline><obj3><obj4>' n_selections=0
+++IA2_ROLE_SECTION FOCUSABLE IA2_STATE_EDITABLE IA2_STATE_MULTI_LINE ia2_hypertext='<obj0>This is not editable.<newline><obj3><obj4>' n_selections=0
 ++++IA2_ROLE_PARAGRAPH IA2_STATE_EDITABLE ia2_hypertext='This is editable.' n_selections=0
 ++++++ROLE_SYSTEM_STATICTEXT name='This is editable.' IA2_STATE_EDITABLE ia2_hypertext='This is editable.' n_selections=0
 ++++ROLE_SYSTEM_STATICTEXT name='This is not editable.' ia2_hypertext='This is not editable.' n_selections=0
diff --git a/content/test/data/accessibility/html/contenteditable-with-no-descendants-expected-blink.txt b/content/test/data/accessibility/html/contenteditable-with-no-descendants-expected-blink.txt
index 81fbe1da..84d0ae9 100644
--- a/content/test/data/accessibility/html/contenteditable-with-no-descendants-expected-blink.txt
+++ b/content/test/data/accessibility/html/contenteditable-with-no-descendants-expected-blink.txt
@@ -1,7 +1,7 @@
 rootWebArea
-++genericContainer editable richlyEditable name='label'
-++genericContainer editable richlyEditable description='description' descriptionFrom=5
-++genericContainer editable richlyEditable name='title'
+++genericContainer editable multiline richlyEditable name='label'
+++genericContainer editable multiline richlyEditable description='description' descriptionFrom=5
+++genericContainer editable multiline richlyEditable name='title'
 ++paragraph
 ++++staticText name='description'
 ++++++inlineTextBox name='description'
diff --git a/content/test/data/accessibility/html/contenteditable-with-no-descendants-expected-win.txt b/content/test/data/accessibility/html/contenteditable-with-no-descendants-expected-win.txt
index bd199c7..2097684 100644
--- a/content/test/data/accessibility/html/contenteditable-with-no-descendants-expected-win.txt
+++ b/content/test/data/accessibility/html/contenteditable-with-no-descendants-expected-win.txt
@@ -1,6 +1,6 @@
 ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE ia2_hypertext='<obj0><obj1><obj2><obj3>'
-++IA2_ROLE_SECTION name='label' FOCUSABLE IA2_STATE_EDITABLE
-++IA2_ROLE_SECTION FOCUSABLE IA2_STATE_EDITABLE description='description'
-++IA2_ROLE_SECTION name='title' FOCUSABLE IA2_STATE_EDITABLE
+++IA2_ROLE_SECTION name='label' FOCUSABLE IA2_STATE_EDITABLE IA2_STATE_MULTI_LINE
+++IA2_ROLE_SECTION FOCUSABLE IA2_STATE_EDITABLE IA2_STATE_MULTI_LINE description='description'
+++IA2_ROLE_SECTION name='title' FOCUSABLE IA2_STATE_EDITABLE IA2_STATE_MULTI_LINE
 ++IA2_ROLE_PARAGRAPH ia2_hypertext='description'
-++++ROLE_SYSTEM_STATICTEXT name='description' ia2_hypertext='description'
+++++ROLE_SYSTEM_STATICTEXT name='description' ia2_hypertext='description'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/input-radio-expected-blink.txt b/content/test/data/accessibility/html/input-radio-expected-blink.txt
index da8eeddd..0c3f3edd 100644
--- a/content/test/data/accessibility/html/input-radio-expected-blink.txt
+++ b/content/test/data/accessibility/html/input-radio-expected-blink.txt
@@ -1,16 +1,16 @@
 rootWebArea
 ++form
-++++radioButton checkedState=3 radioGroupIds=radioButton
+++++radioButton checkedState=1 radioGroupIds=radioButton
 ++++staticText name='Radio1'
 ++++++inlineTextBox name='Radio1'
 ++++lineBreak name='<newline>'
 ++++++inlineTextBox name='<newline>'
-++++radioButton checkedState=3 radioGroupIds=radioButton
+++++radioButton checkedState=1 radioGroupIds=radioButton
 ++++staticText name='Radio2'
 ++++++inlineTextBox name='Radio2'
 ++form
 ++++radioButton name='Radio3' checkedState=1 radioGroupIds=radioButton,radioButton
 ++++radioButton name='Radio4' checkedState=2 radioGroupIds=radioButton,radioButton
 ++form
-++++radioButton name='Radio5' checkedState=3 radioGroupIds=radioButton
+++++radioButton name='Radio5' checkedState=1 radioGroupIds=radioButton
 ++++radioButton name='Radio6' checkedState=2 radioGroupIds=radioButton
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/input-radio-expected-mac.txt b/content/test/data/accessibility/html/input-radio-expected-mac.txt
index c54984b..52457e38 100644
--- a/content/test/data/accessibility/html/input-radio-expected-mac.txt
+++ b/content/test/data/accessibility/html/input-radio-expected-mac.txt
@@ -1,13 +1,13 @@
 AXWebArea
 ++AXGroup
-++++AXRadioButton AXValue='2' AXLinkedUIElements=["AXRadioButton 2"]
+++++AXRadioButton AXValue='0' AXLinkedUIElements=["AXRadioButton 0"]
 ++++AXStaticText AXValue='Radio1'
 ++++AXUnknown AXTitle='<newline>'
-++++AXRadioButton AXValue='2' AXLinkedUIElements=["AXRadioButton 2"]
+++++AXRadioButton AXValue='0' AXLinkedUIElements=["AXRadioButton 0"]
 ++++AXStaticText AXValue='Radio2'
 ++AXGroup
 ++++AXRadioButton AXTitle='Radio3' AXValue='0' AXLinkedUIElements=["AXRadioButton Radio3","AXRadioButton Radio4"]
 ++++AXRadioButton AXTitle='Radio4' AXValue='1' AXLinkedUIElements=["AXRadioButton Radio3","AXRadioButton Radio4"]
 ++AXGroup
-++++AXRadioButton AXTitle='Radio5' AXValue='2' AXLinkedUIElements=["AXRadioButton Radio5"]
+++++AXRadioButton AXTitle='Radio5' AXValue='0' AXLinkedUIElements=["AXRadioButton Radio5"]
 ++++AXRadioButton AXTitle='Radio6' AXValue='1' AXLinkedUIElements=["AXRadioButton Radio6"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/input-radio-expected-win.txt b/content/test/data/accessibility/html/input-radio-expected-win.txt
index 188dcd9..6e763ba 100644
--- a/content/test/data/accessibility/html/input-radio-expected-win.txt
+++ b/content/test/data/accessibility/html/input-radio-expected-win.txt
@@ -1,13 +1,13 @@
 ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE ia2_hypertext='<obj0><obj1><obj2>'
 ++IA2_ROLE_FORM ia2_hypertext='<obj0>Radio1<newline><obj3>Radio2'
-++++ROLE_SYSTEM_RADIOBUTTON MIXED FOCUSABLE IA2_STATE_CHECKABLE checkable:true
+++++ROLE_SYSTEM_RADIOBUTTON FOCUSABLE IA2_STATE_CHECKABLE checkable:true
 ++++ROLE_SYSTEM_STATICTEXT name='Radio1' ia2_hypertext='Radio1'
 ++++ROLE_SYSTEM_WHITESPACE name='<newline>' ia2_hypertext='<newline>'
-++++ROLE_SYSTEM_RADIOBUTTON MIXED FOCUSABLE IA2_STATE_CHECKABLE checkable:true
+++++ROLE_SYSTEM_RADIOBUTTON FOCUSABLE IA2_STATE_CHECKABLE checkable:true
 ++++ROLE_SYSTEM_STATICTEXT name='Radio2' ia2_hypertext='Radio2'
 ++IA2_ROLE_FORM ia2_hypertext='<obj0><obj1>'
 ++++ROLE_SYSTEM_RADIOBUTTON name='Radio3' FOCUSABLE IA2_STATE_CHECKABLE checkable:true ia2_hypertext='Radio3'
 ++++ROLE_SYSTEM_RADIOBUTTON name='Radio4' CHECKED FOCUSABLE IA2_STATE_CHECKABLE checkable:true ia2_hypertext='Radio4'
 ++IA2_ROLE_FORM ia2_hypertext='<obj0><obj1>'
-++++ROLE_SYSTEM_RADIOBUTTON name='Radio5' MIXED FOCUSABLE IA2_STATE_CHECKABLE checkable:true ia2_hypertext='Radio5'
+++++ROLE_SYSTEM_RADIOBUTTON name='Radio5' FOCUSABLE IA2_STATE_CHECKABLE checkable:true ia2_hypertext='Radio5'
 ++++ROLE_SYSTEM_RADIOBUTTON name='Radio6' CHECKED FOCUSABLE IA2_STATE_CHECKABLE checkable:true ia2_hypertext='Radio6'
diff --git a/content/test/data/accessibility/html/input-text-value-expected-blink.txt b/content/test/data/accessibility/html/input-text-value-expected-blink.txt
index c76afc11..669039a 100644
--- a/content/test/data/accessibility/html/input-text-value-expected-blink.txt
+++ b/content/test/data/accessibility/html/input-text-value-expected-blink.txt
@@ -1,18 +1,18 @@
-rootWebArea
+rootWebArea focusable
 ++genericContainer
 ++++labelText
 ++++++staticText name='l1'
 ++++++++inlineTextBox name='l1'
-++++textField name='l1'
+++++textField focusable name='l1'
 ++++labelText
 ++++++staticText name='l2'
 ++++++++inlineTextBox name='l2'
-++++textField name='l2'
+++++textField focusable name='l2'
 ++++++genericContainer
 ++++++++staticText name='value'
 ++++++++++inlineTextBox name='value'
-++++textField name='l2'
-++++textField name='l2'
+++++textField focusable name='l2'
+++++textField focusable name='l2'
 ++++++genericContainer
 ++++++++staticText name='value'
 ++++++++++inlineTextBox name='value'
@@ -20,13 +20,13 @@
 ++++++staticText name='Email'
 ++++++++inlineTextBox name='Email'
 ++++genericContainer
-++++textField name='Email'
-++++textField name='Email'
+++++textField focusable name='Email'
+++++textField focusable name='Email'
 ++++++genericContainer
 ++++++++staticText name='value'
 ++++++++++inlineTextBox name='value'
-++++textField name='l5'
-++++textField name='l6'
+++++textField focusable multiline name='l5'
+++++textField focusable multiline name='l6'
 ++++++genericContainer
 ++++++++staticText name='Value'
-++++++++++inlineTextBox name='Value'
\ No newline at end of file
+++++++++++inlineTextBox name='Value'
diff --git a/content/test/data/accessibility/html/input-text-value-expected-win.txt b/content/test/data/accessibility/html/input-text-value-expected-win.txt
new file mode 100644
index 0000000..9341528
--- /dev/null
+++ b/content/test/data/accessibility/html/input-text-value-expected-win.txt
@@ -0,0 +1,17 @@
+ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE
+++IA2_ROLE_SECTION
+++++IA2_ROLE_LABEL
+++++++ROLE_SYSTEM_STATICTEXT name='l1'
+++++ROLE_SYSTEM_TEXT name='l1' FOCUSABLE
+++++IA2_ROLE_LABEL
+++++++ROLE_SYSTEM_STATICTEXT name='l2'
+++++ROLE_SYSTEM_TEXT name='l2' FOCUSABLE
+++++ROLE_SYSTEM_TEXT name='l2' FOCUSABLE
+++++ROLE_SYSTEM_TEXT name='l2' FOCUSABLE
+++++IA2_ROLE_LABEL
+++++++ROLE_SYSTEM_STATICTEXT name='Email'
+++++IA2_ROLE_SECTION
+++++ROLE_SYSTEM_TEXT name='Email' FOCUSABLE
+++++ROLE_SYSTEM_TEXT name='Email' FOCUSABLE
+++++ROLE_SYSTEM_TEXT name='l5' FOCUSABLE IA2_STATE_MULTI_LINE
+++++ROLE_SYSTEM_TEXT name='l6' FOCUSABLE IA2_STATE_MULTI_LINE
diff --git a/content/test/data/accessibility/html/input-text-value.html b/content/test/data/accessibility/html/input-text-value.html
index d6f3697b..22814a0 100644
--- a/content/test/data/accessibility/html/input-text-value.html
+++ b/content/test/data/accessibility/html/input-text-value.html
@@ -1,5 +1,9 @@
 <!--
 @WIN-ALLOW:description*
+@WIN-ALLOW:IA2_STATE_MULTI_LINE
+@BLINK-ALLOW:description*
+@BLINK-ALLOW:focus*
+@BLINK-ALLOW:multiline
 -->
 <html>
 <body>
diff --git a/content/test/data/accessibility/html/textarea-expected-blink.txt b/content/test/data/accessibility/html/textarea-expected-blink.txt
index 909da1525..f479e46f 100644
--- a/content/test/data/accessibility/html/textarea-expected-blink.txt
+++ b/content/test/data/accessibility/html/textarea-expected-blink.txt
@@ -1,6 +1,6 @@
 rootWebArea
 ++genericContainer
-++++textField textSelStart=0 textSelEnd=0
+++++textField multiline textSelStart=0 textSelEnd=0
 ++++++genericContainer
 ++++++++staticText name='The textarea tag defines a multi-line text input control.<newline>'
 ++++++++++inlineTextBox name='The textarea tag defines a multi-line text input'
diff --git a/content/test/data/accessibility/html/textarea-read-only-expected-blink.txt b/content/test/data/accessibility/html/textarea-read-only-expected-blink.txt
index b1d7ba46..72ce41e5 100644
--- a/content/test/data/accessibility/html/textarea-read-only-expected-blink.txt
+++ b/content/test/data/accessibility/html/textarea-read-only-expected-blink.txt
@@ -1,6 +1,6 @@
 rootWebArea
 ++genericContainer
-++++textField textSelStart=0 textSelEnd=0
+++++textField multiline textSelStart=0 textSelEnd=0
 ++++++genericContainer
 ++++++++staticText name='The textarea tag defines a multi-line text input control.<newline>'
 ++++++++++inlineTextBox name='The textarea tag defines a multi-line text input'
diff --git a/content/test/data/accessibility/html/textarea-with-selection-expected-blink.txt b/content/test/data/accessibility/html/textarea-with-selection-expected-blink.txt
index f12d086..670d10cb 100644
--- a/content/test/data/accessibility/html/textarea-with-selection-expected-blink.txt
+++ b/content/test/data/accessibility/html/textarea-with-selection-expected-blink.txt
@@ -1,6 +1,6 @@
 rootWebArea
 ++genericContainer
-++++textField textSelStart=0 textSelEnd=58
+++++textField multiline textSelStart=0 textSelEnd=58
 ++++++genericContainer
 ++++++++staticText name='The textarea tag defines a multi-line text input control.<newline>'
 ++++++++++inlineTextBox name='The textarea tag defines a multi-line text input'
diff --git a/content/test/gpu/generate_buildbot_json.py b/content/test/gpu/generate_buildbot_json.py
index 19ec8b91..d78c36a 100755
--- a/content/test/gpu/generate_buildbot_json.py
+++ b/content/test/gpu/generate_buildbot_json.py
@@ -1349,7 +1349,7 @@
   'audio_unittests': {
     'tester_configs': [
       {
-        'predicate': Predicates.FYI_ONLY,
+        'predicate': Predicates.FYI_AND_OPTIONAL,
       }
     ],
     # Don't run these tests on Android.
diff --git a/content/test/test_render_view_host.cc b/content/test/test_render_view_host.cc
index de720ae..71239a1d 100644
--- a/content/test/test_render_view_host.cc
+++ b/content/test/test_render_view_host.cc
@@ -8,6 +8,7 @@
 
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
+#include "cc/surfaces/frame_sink_manager.h"
 #include "cc/surfaces/surface_manager.h"
 #include "content/browser/compositor/image_transport_factory.h"
 #include "content/browser/compositor/surface_utils.h"
@@ -68,12 +69,12 @@
       background_color_(SK_ColorWHITE) {
 #if defined(OS_ANDROID)
   frame_sink_id_ = AllocateFrameSinkId();
-  GetSurfaceManager()->RegisterFrameSinkId(frame_sink_id_);
+  GetFrameSinkManager()->RegisterFrameSinkId(frame_sink_id_);
 #else
   // Not all tests initialize or need an image transport factory.
   if (ImageTransportFactory::GetInstance()) {
     frame_sink_id_ = AllocateFrameSinkId();
-    GetSurfaceManager()->RegisterFrameSinkId(frame_sink_id_);
+    GetFrameSinkManager()->RegisterFrameSinkId(frame_sink_id_);
   }
 #endif
 
@@ -88,7 +89,7 @@
 }
 
 TestRenderWidgetHostView::~TestRenderWidgetHostView() {
-  cc::SurfaceManager* manager = GetSurfaceManager();
+  cc::FrameSinkManager* manager = GetFrameSinkManager();
   if (manager) {
     manager->InvalidateFrameSinkId(frame_sink_id_);
   }
diff --git a/content/test/test_web_contents.cc b/content/test/test_web_contents.cc
index ac14a0b8..7369046 100644
--- a/content/test/test_web_contents.cc
+++ b/content/test/test_web_contents.cc
@@ -389,10 +389,12 @@
 
 void TestWebContents::CreateNewWidget(int32_t render_process_id,
                                       int32_t route_id,
+                                      mojom::WidgetPtr widget,
                                       blink::WebPopupType popup_type) {}
 
 void TestWebContents::CreateNewFullscreenWidget(int32_t render_process_id,
-                                                int32_t route_id) {}
+                                                int32_t route_id,
+                                                mojom::WidgetPtr widget) {}
 
 void TestWebContents::ShowCreatedWindow(int process_id,
                                         int route_id,
diff --git a/content/test/test_web_contents.h b/content/test/test_web_contents.h
index 2d2844e3..f075d52a 100644
--- a/content/test/test_web_contents.h
+++ b/content/test/test_web_contents.h
@@ -159,9 +159,11 @@
       SessionStorageNamespace* session_storage_namespace) override;
   void CreateNewWidget(int32_t render_process_id,
                        int32_t route_id,
+                       mojom::WidgetPtr widget,
                        blink::WebPopupType popup_type) override;
   void CreateNewFullscreenWidget(int32_t render_process_id,
-                                 int32_t route_id) override;
+                                 int32_t route_id,
+                                 mojom::WidgetPtr widget) override;
   void ShowCreatedWindow(int process_id,
                          int route_id,
                          WindowOpenDisposition disposition,
diff --git a/device/geolocation/location_arbitrator.h b/device/geolocation/location_arbitrator.h
index dca5dbe..343d366 100644
--- a/device/geolocation/location_arbitrator.h
+++ b/device/geolocation/location_arbitrator.h
@@ -12,7 +12,6 @@
 #include "base/callback_forward.h"
 #include "base/cancelable_callback.h"
 #include "base/macros.h"
-#include "base/memory/scoped_vector.h"
 #include "base/strings/string16.h"
 #include "base/time/time.h"
 #include "device/geolocation/access_token_store.h"
diff --git a/device/usb/mojo/type_converters.cc b/device/usb/mojo/type_converters.cc
index fbe17d8..89034c9 100644
--- a/device/usb/mojo/type_converters.cc
+++ b/device/usb/mojo/type_converters.cc
@@ -10,8 +10,7 @@
 #include <map>
 #include <utility>
 
-#include "base/logging.h"
-#include "base/strings/utf_string_conversions.h"
+#include "device/usb/usb_descriptors.h"
 #include "device/usb/usb_device.h"
 
 namespace mojo {
@@ -111,9 +110,9 @@
   info->device_version_major = device.device_version() >> 8;
   info->device_version_minor = device.device_version() >> 4 & 0xf;
   info->device_version_subminor = device.device_version() & 0xf;
-  info->manufacturer_name = base::UTF16ToUTF8(device.manufacturer_string());
-  info->product_name = base::UTF16ToUTF8(device.product_string());
-  info->serial_number = base::UTF16ToUTF8(device.serial_number());
+  info->manufacturer_name = device.manufacturer_string();
+  info->product_name = device.product_string();
+  info->serial_number = device.serial_number();
   const device::UsbConfigDescriptor* config = device.active_configuration();
   info->active_configuration = config ? config->configuration_value : 0;
   info->configurations =
diff --git a/device/usb/mojo/type_converters.h b/device/usb/mojo/type_converters.h
index fc26bb75..afd4188 100644
--- a/device/usb/mojo/type_converters.h
+++ b/device/usb/mojo/type_converters.h
@@ -9,7 +9,6 @@
 
 #include "device/usb/public/interfaces/device.mojom.h"
 #include "device/usb/public/interfaces/device_manager.mojom.h"
-#include "device/usb/usb_descriptors.h"
 #include "device/usb/usb_device_handle.h"
 #include "mojo/public/cpp/bindings/type_converter.h"
 
@@ -18,7 +17,10 @@
 // that uses these conversions explicitly or implicitly.
 
 namespace device {
+struct UsbConfigDescriptor;
 class UsbDevice;
+struct UsbEndpointDescriptor;
+struct UsbInterfaceDescriptor;
 }
 
 namespace mojo {
diff --git a/device/usb/public/cpp/BUILD.gn b/device/usb/public/cpp/BUILD.gn
index 717c2a9..c555039 100644
--- a/device/usb/public/cpp/BUILD.gn
+++ b/device/usb/public/cpp/BUILD.gn
@@ -9,7 +9,6 @@
   ]
 
   deps = [
-    "//base",
     "//device/usb",
     "//device/usb/public/interfaces",
   ]
diff --git a/device/usb/public/cpp/filter_utils.cc b/device/usb/public/cpp/filter_utils.cc
index 021af336..e8ac314 100644
--- a/device/usb/public/cpp/filter_utils.cc
+++ b/device/usb/public/cpp/filter_utils.cc
@@ -4,7 +4,6 @@
 
 #include "device/usb/public/cpp/filter_utils.h"
 
-#include "base/strings/utf_string_conversions.h"
 #include "device/usb/usb_device.h"
 
 namespace device {
@@ -19,10 +18,8 @@
       return false;
   }
 
-  if (filter.serial_number &&
-      device.serial_number() != base::UTF8ToUTF16(*filter.serial_number)) {
+  if (filter.serial_number && device.serial_number() != *filter.serial_number)
     return false;
-  }
 
   if (filter.has_class_code) {
     for (const UsbConfigDescriptor& config : device.configurations()) {
diff --git a/device/usb/public/cpp/filter_utils_unittest.cc b/device/usb/public/cpp/filter_utils_unittest.cc
index eeaf007..2cf8f63 100644
--- a/device/usb/public/cpp/filter_utils_unittest.cc
+++ b/device/usb/public/cpp/filter_utils_unittest.cc
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <string>
 #include <vector>
 
 #include "base/memory/ref_counted.h"
+#include "base/strings/utf_string_conversions.h"
 #include "device/usb/mock_usb_device.h"
 #include "device/usb/public/cpp/filter_utils.h"
 #include "device/usb/usb_descriptors.h"
@@ -125,7 +125,7 @@
 
 TEST_F(UsbFilterTest, MatchSerialNumber) {
   auto filter = mojom::UsbDeviceFilter::New();
-  filter->serial_number = std::string("ABC123");
+  filter->serial_number = base::ASCIIToUTF16("ABC123");
   EXPECT_TRUE(UsbDeviceFilterMatches(*filter, *android_phone_));
   filter->has_vendor_id = true;
   filter->vendor_id = 0x18d1;
@@ -133,7 +133,7 @@
   filter->vendor_id = 0x18d2;
   EXPECT_FALSE(UsbDeviceFilterMatches(*filter, *android_phone_));
   filter->vendor_id = 0x18d1;
-  filter->serial_number = std::string("DIFFERENT");
+  filter->serial_number = base::ASCIIToUTF16("DIFFERENT");
   EXPECT_FALSE(UsbDeviceFilterMatches(*filter, *android_phone_));
 }
 
diff --git a/device/usb/public/interfaces/BUILD.gn b/device/usb/public/interfaces/BUILD.gn
index 2ff08c00..ea8c6a5 100644
--- a/device/usb/public/interfaces/BUILD.gn
+++ b/device/usb/public/interfaces/BUILD.gn
@@ -11,6 +11,10 @@
     "device_manager.mojom",
   ]
 
+  deps = [
+    "//mojo/common:common_custom_types",
+  ]
+
   # TODO(crbug.com/714018): Convert the implementation to use OnceCallback.
   use_once_callback = false
 }
diff --git a/device/usb/public/interfaces/device.mojom b/device/usb/public/interfaces/device.mojom
index d1c139a..11ded46 100644
--- a/device/usb/public/interfaces/device.mojom
+++ b/device/usb/public/interfaces/device.mojom
@@ -4,6 +4,8 @@
 
 module device.mojom;
 
+import "mojo/common/string16.mojom";
+
 enum UsbOpenDeviceError {
   // Opening the device succeeded.
   OK,
@@ -55,7 +57,7 @@
   uint8 class_code;
   uint8 subclass_code;
   uint8 protocol_code;
-  string? interface_name;
+  mojo.common.mojom.String16? interface_name;
   array<UsbEndpointInfo> endpoints;
 };
 
@@ -66,7 +68,7 @@
 
 struct UsbConfigurationInfo {
   uint8 configuration_value;
-  string? configuration_name;
+  mojo.common.mojom.String16? configuration_name;
   array<UsbInterfaceInfo> interfaces;
 };
 
@@ -83,9 +85,9 @@
   uint8 device_version_major;
   uint8 device_version_minor;
   uint8 device_version_subminor;
-  string? manufacturer_name;
-  string? product_name;
-  string? serial_number;
+  mojo.common.mojom.String16? manufacturer_name;
+  mojo.common.mojom.String16? product_name;
+  mojo.common.mojom.String16? serial_number;
   uint8 active_configuration;
   array<UsbConfigurationInfo> configurations;
 };
diff --git a/device/usb/public/interfaces/device_manager.mojom b/device/usb/public/interfaces/device_manager.mojom
index 4a4ec723..99853d5d 100644
--- a/device/usb/public/interfaces/device_manager.mojom
+++ b/device/usb/public/interfaces/device_manager.mojom
@@ -4,7 +4,8 @@
 
 module device.mojom;
 
-import "device.mojom";
+import "device/usb/public/interfaces/device.mojom";
+import "mojo/common/string16.mojom";
 
 struct UsbDeviceFilter {
   bool has_vendor_id;
@@ -22,7 +23,7 @@
   bool has_protocol_code;
   uint8 protocol_code;
 
-  string? serial_number;
+  mojo.common.mojom.String16? serial_number;
 };
 
 struct UsbEnumerationOptions {
diff --git a/device/vr/android/gvr/gvr_delegate.h b/device/vr/android/gvr/gvr_delegate.h
index d9562c52..fbf6f46 100644
--- a/device/vr/android/gvr/gvr_delegate.h
+++ b/device/vr/android/gvr/gvr_delegate.h
@@ -47,8 +47,8 @@
                                                      uint32_t device_id);
 
   virtual void SetWebVRSecureOrigin(bool secure_origin) = 0;
-  virtual void UpdateVSyncInterval(int64_t timebase_nanos,
-                                   double interval_seconds) = 0;
+  virtual void UpdateVSyncInterval(base::TimeTicks vsync_timebase,
+                                   base::TimeDelta vsync_interval) = 0;
   virtual void CreateVRDisplayInfo(
       const base::Callback<void(mojom::VRDisplayInfoPtr)>& callback,
       uint32_t device_id) = 0;
diff --git a/extensions/browser/extension_util.cc b/extensions/browser/extension_util.cc
index 5ed98fc..1863b1c 100644
--- a/extensions/browser/extension_util.cc
+++ b/extensions/browser/extension_util.cc
@@ -29,23 +29,6 @@
 
 }  // namespace
 
-bool HasIsolatedStorage(const ExtensionInfo& info) {
-  if (!info.extension_manifest.get())
-    return false;
-
-  std::string error;
-  scoped_refptr<const Extension> extension(Extension::Create(
-      info.extension_path,
-      info.extension_location,
-      *info.extension_manifest,
-      Extension::NO_FLAGS,
-      info.extension_id,
-      &error));
-
-  return extension.get() &&
-         AppIsolationInfo::HasIsolatedStorage(extension.get());
-}
-
 bool SiteHasIsolatedStorage(const GURL& extension_site_url,
                             content::BrowserContext* context) {
   const Extension* extension = ExtensionRegistry::Get(context)->
diff --git a/extensions/browser/extension_util.h b/extensions/browser/extension_util.h
index 274c1a7..c831f719 100644
--- a/extensions/browser/extension_util.h
+++ b/extensions/browser/extension_util.h
@@ -16,7 +16,6 @@
 
 namespace extensions {
 class Extension;
-struct ExtensionInfo;
 
 namespace util {
 
@@ -24,9 +23,6 @@
 // chrome/browser/extensions/extension_util.h/cc that are only dependent on
 // extensions/ here.
 
-// Returns true if the extension has isolated storage.
-bool HasIsolatedStorage(const ExtensionInfo& info);
-
 // Returns true if the site URL corresponds to an extension or app and has
 // isolated storage.
 bool SiteHasIsolatedStorage(const GURL& extension_site_url,
diff --git a/gin/modules/module_registry.cc b/gin/modules/module_registry.cc
index 99783d37..9681a0e 100644
--- a/gin/modules/module_registry.cc
+++ b/gin/modules/module_registry.cc
@@ -258,7 +258,7 @@
 bool ModuleRegistry::AttemptToLoad(Isolate* isolate,
                                    std::unique_ptr<PendingModule> pending) {
   if (!CheckDependencies(pending.get())) {
-    pending_modules_.push_back(pending.release());
+    pending_modules_.push_back(std::move(pending));
     return false;
   }
   return Load(isolate, std::move(pending));
@@ -279,7 +279,7 @@
     PendingModuleVector pending_modules;
     pending_modules.swap(pending_modules_);
     for (size_t i = 0; i < pending_modules.size(); ++i) {
-      std::unique_ptr<PendingModule> pending(pending_modules[i]);
+      std::unique_ptr<PendingModule> pending(std::move(pending_modules[i]));
       pending_modules[i] = NULL;
       if (AttemptToLoad(isolate, std::move(pending)))
         keep_trying = true;
diff --git a/gin/modules/module_registry.h b/gin/modules/module_registry.h
index 9bbfd6e0..c1d3a00 100644
--- a/gin/modules/module_registry.h
+++ b/gin/modules/module_registry.h
@@ -10,11 +10,11 @@
 #include <memory>
 #include <set>
 #include <string>
+#include <vector>
 
 #include "base/callback.h"
 #include "base/compiler_specific.h"
 #include "base/macros.h"
-#include "base/memory/scoped_vector.h"
 #include "base/observer_list.h"
 #include "gin/gin_export.h"
 #include "v8/include/v8.h"
@@ -77,7 +77,7 @@
   }
 
  private:
-  typedef ScopedVector<PendingModule> PendingModuleVector;
+  typedef std::vector<std::unique_ptr<PendingModule>> PendingModuleVector;
   typedef std::multimap<std::string, LoadModuleCallback> LoadModuleCallbackMap;
 
   explicit ModuleRegistry(v8::Isolate* isolate);
diff --git a/gpu/ipc/common/memory_stats_struct_traits.h b/gpu/ipc/common/memory_stats_struct_traits.h
index 286801a..b3564cc 100644
--- a/gpu/ipc/common/memory_stats_struct_traits.h
+++ b/gpu/ipc/common/memory_stats_struct_traits.h
@@ -5,6 +5,7 @@
 #ifndef GPU_IPC_COMMON_MEMORY_STATS_STRUCT_TRAITS_H_
 #define GPU_IPC_COMMON_MEMORY_STATS_STRUCT_TRAITS_H_
 
+#include "build/build_config.h"
 #include "gpu/ipc/common/memory_stats.h"
 #include "gpu/ipc/common/memory_stats.mojom-shared.h"
 
@@ -36,7 +37,7 @@
                     gpu::VideoMemoryUsageStats> {
   static std::map<int32_t, gpu::VideoMemoryUsageStats::ProcessStats>
   process_map(const gpu::VideoMemoryUsageStats& stats) {
-#if defined(OS_WIN)
+#if defined(OS_WIN) || defined(OS_FUCHSIA)
     std::map<int32_t, gpu::VideoMemoryUsageStats::ProcessStats> map;
     for (const auto& pair : stats.process_map)
       map[static_cast<int32_t>(pair.first)] = pair.second;
@@ -52,7 +53,7 @@
 
   static bool Read(gpu::mojom::VideoMemoryUsageStatsDataView data,
                    gpu::VideoMemoryUsageStats* out) {
-#if defined(OS_WIN)
+#if defined(OS_WIN) || defined(OS_FUCHSIA)
     std::map<int32_t, gpu::VideoMemoryUsageStats::ProcessStats> process_map;
     if (!data.ReadProcessMap(&process_map))
       return false;
diff --git a/gpu/ipc/common/surface_handle.h b/gpu/ipc/common/surface_handle.h
index 70659ab..ecebc066 100644
--- a/gpu/ipc/common/surface_handle.h
+++ b/gpu/ipc/common/surface_handle.h
@@ -27,10 +27,13 @@
 // handle generated by that.
 // On NaCl, we don't have native surfaces per se, but we need SurfaceHandle to
 // be defined, because some APIs that use it are referenced there.
+//
+// TODO(fuchsia): Figure out the right approach for Fuchsia.
 #if defined(GPU_SURFACE_HANDLE_IS_ACCELERATED_WINDOW)
 using SurfaceHandle = gfx::AcceleratedWidget;
 constexpr SurfaceHandle kNullSurfaceHandle = gfx::kNullAcceleratedWidget;
-#elif defined(OS_MACOSX) || defined(OS_ANDROID) || defined(OS_NACL)
+#elif defined(OS_MACOSX) || defined(OS_ANDROID) || defined(OS_NACL) || \
+    defined(OS_FUCHSIA)
 using SurfaceHandle = int32_t;
 constexpr SurfaceHandle kNullSurfaceHandle = 0;
 #else
diff --git a/headless/lib/frame_id_browsertest.cc b/headless/lib/frame_id_browsertest.cc
index 6a383333..5a03b39 100644
--- a/headless/lib/frame_id_browsertest.cc
+++ b/headless/lib/frame_id_browsertest.cc
@@ -203,8 +203,8 @@
     if (EnableInterception()) {
       devtools_client_->GetNetwork()
           ->GetExperimental()
-          ->EnableRequestInterception(
-              network::EnableRequestInterceptionParams::Builder()
+          ->SetRequestInterceptionEnabled(
+              network::SetRequestInterceptionEnabledParams::Builder()
                   .SetEnabled(true)
                   .Build());
     }
diff --git a/headless/lib/headless_devtools_client_browsertest.cc b/headless/lib/headless_devtools_client_browsertest.cc
index 635aeea..5d97e60e 100644
--- a/headless/lib/headless_devtools_client_browsertest.cc
+++ b/headless/lib/headless_devtools_client_browsertest.cc
@@ -1378,8 +1378,8 @@
     devtools_client_->GetNetwork()->Enable();
     devtools_client_->GetNetwork()
         ->GetExperimental()
-        ->EnableRequestInterception(
-            network::EnableRequestInterceptionParams::Builder()
+        ->SetRequestInterceptionEnabled(
+            network::SetRequestInterceptionEnabledParams::Builder()
                 .SetEnabled(true)
                 .Build());
 
diff --git a/infra/config/cq.cfg b/infra/config/cq.cfg
index 3b46ddb..c4f49c6 100644
--- a/infra/config/cq.cfg
+++ b/infra/config/cq.cfg
@@ -72,8 +72,12 @@
     }
     buckets {
       name: "master.tryserver.chromium.mac"
+      # https://crbug.com/739556; make this non-experimental ASAP.
+      builders {
+        name: "ios-device"
+        experiment_percentage: 100
+      }
       # https://crbug.com/739556
-      # builders { name: "ios-device" }
       # builders { name: "ios-device-xcode-clang" }
       builders { name: "ios-simulator" }
       # builders { name: "ios-simulator-xcode-clang" }
diff --git a/ios/chrome/browser/browser_state/chrome_browser_state.h b/ios/chrome/browser/browser_state/chrome_browser_state.h
index 6f4287a..9d2a2dc 100644
--- a/ios/chrome/browser/browser_state/chrome_browser_state.h
+++ b/ios/chrome/browser/browser_state/chrome_browser_state.h
@@ -13,7 +13,6 @@
 #include "base/macros.h"
 #include "base/memory/linked_ptr.h"
 #include "base/memory/ref_counted.h"
-#include "base/memory/scoped_vector.h"
 #include "ios/chrome/browser/net/net_types.h"
 #include "ios/web/public/browser_state.h"
 #include "net/url_request/url_request_job_factory.h"
diff --git a/ios/chrome/browser/google/google_logo_service.mm b/ios/chrome/browser/google/google_logo_service.mm
index d7da9c3..1d58dfe 100644
--- a/ios/chrome/browser/google/google_logo_service.mm
+++ b/ios/chrome/browser/google/google_logo_service.mm
@@ -8,14 +8,12 @@
 
 #include "base/bind.h"
 #include "base/strings/sys_string_conversions.h"
-#include "base/threading/sequenced_worker_pool.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/search_engines/template_url_service.h"
 #include "components/search_provider_logos/google_logo_api.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/search_engines/template_url_service_factory.h"
 #include "ios/chrome/browser/search_engines/ui_thread_search_terms_data.h"
-#include "ios/web/public/web_thread.h"
 #include "net/url_request/url_request_context_getter.h"
 #include "ui/gfx/image/image.h"
 
@@ -100,12 +98,9 @@
     return;
 
   if (!logo_tracker_) {
-    logo_tracker_.reset(new LogoTracker(
-        DoodleDirectory(),
-        web::WebThread::GetTaskRunnerForThread(web::WebThread::FILE),
-        web::WebThread::GetBlockingPool(), browser_state_->GetRequestContext(),
-        std::unique_ptr<search_provider_logos::LogoDelegate>(
-            new IOSChromeLogoDelegate())));
+    logo_tracker_ = base::MakeUnique<LogoTracker>(
+        DoodleDirectory(), browser_state_->GetRequestContext(),
+        base::MakeUnique<IOSChromeLogoDelegate>());
   }
 
   logo_tracker_->SetServerAPI(
diff --git a/ios/chrome/browser/payments/BUILD.gn b/ios/chrome/browser/payments/BUILD.gn
index e1ed767..33bd463 100644
--- a/ios/chrome/browser/payments/BUILD.gn
+++ b/ios/chrome/browser/payments/BUILD.gn
@@ -47,7 +47,9 @@
     "//components/autofill/core/browser:test_support",
     "//components/payments/core",
     "//ios/chrome/browser",
+    "//ios/chrome/browser/browser_state:test_support",
     "//ios/web",
+    "//ios/web/public/test/fakes",
     "//testing/gmock",
     "//testing/gtest",
   ]
diff --git a/ios/chrome/browser/payments/payment_request.h b/ios/chrome/browser/payments/payment_request.h
index bd1b74a..d05b33b 100644
--- a/ios/chrome/browser/payments/payment_request.h
+++ b/ios/chrome/browser/payments/payment_request.h
@@ -14,6 +14,7 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "components/autofill/core/browser/credit_card.h"
+#include "components/payments/core/journey_logger.h"
 #include "components/payments/core/payment_options_provider.h"
 #include "components/payments/core/payment_request_base_delegate.h"
 #include "components/payments/core/payments_profile_comparator.h"
@@ -37,6 +38,10 @@
 class ChromeBrowserState;
 }  // namepsace ios
 
+namespace web {
+class WebState;
+}  // namespace web
+
 // A protocol implementd by any UI classes that the PaymentRequest object
 // needs to communicate with in order to perform certain actions such as
 // initiating UI to request full card details for payment.
@@ -55,14 +60,16 @@
 // Has a copy of web::PaymentRequest as provided by the page invoking the
 // PaymentRequest API. Also caches credit cards and addresses provided by the
 // |personal_data_manager| and manages shared resources and user selections for
-// the current PaymentRequest flow. It must be initialized with a non-null
-// instance of |personal_data_manager| that outlives this class.
+// the current PaymentRequest flow. It must be initialized with non-null
+// instances of |browser_state|, |web_state|, and |personal_data_manager| that
+// outlive this class.
 class PaymentRequest : public PaymentOptionsProvider,
                        public PaymentRequestBaseDelegate {
  public:
   // |personal_data_manager| should not be null and should outlive this object.
   PaymentRequest(const web::PaymentRequest& web_payment_request,
-                 ios::ChromeBrowserState* browser_state_,
+                 ios::ChromeBrowserState* browser_state,
+                 web::WebState* web_state,
                  autofill::PersonalDataManager* personal_data_manager,
                  id<PaymentRequestUIDelegate> payment_request_ui_delegate);
   ~PaymentRequest() override;
@@ -93,6 +100,9 @@
     return web_payment_request_.details;
   }
 
+  // Returns the JourneyLogger for this instance.
+  const JourneyLogger& journey_logger() const { return journey_logger_; }
+
   // Updates the payment details of the |web_payment_request_|. It also updates
   // the cached references to the shipping options in |web_payment_request_| as
   // well as the reference to the selected shipping option.
@@ -244,6 +254,9 @@
   ios::ChromeBrowserState* browser_state_;
 
   // Never null and outlives this object.
+  web::WebState* web_state_;
+
+  // Never null and outlives this object.
   autofill::PersonalDataManager* personal_data_manager_;
 
   // The PaymentRequestUIDelegate as provided by the UI object that originally
@@ -299,6 +312,9 @@
 
   PaymentsProfileComparator profile_comparator_;
 
+  // Keeps track of different stats during the lifetime of this object.
+  JourneyLogger journey_logger_;
+
   DISALLOW_COPY_AND_ASSIGN(PaymentRequest);
 };
 
diff --git a/ios/chrome/browser/payments/payment_request.mm b/ios/chrome/browser/payments/payment_request.mm
index f3b5aba..900fe519 100644
--- a/ios/chrome/browser/payments/payment_request.mm
+++ b/ios/chrome/browser/payments/payment_request.mm
@@ -28,6 +28,7 @@
 #import "ios/chrome/browser/payments/payment_request_util.h"
 #include "ios/chrome/browser/signin/signin_manager_factory.h"
 #include "ios/web/public/payments/payment_request.h"
+#include "ios/web/public/web_state/web_state.h"
 #include "third_party/libaddressinput/chromium/chrome_metadata_source.h"
 #include "third_party/libaddressinput/src/cpp/include/libaddressinput/source.h"
 #include "third_party/libaddressinput/src/cpp/include/libaddressinput/storage.h"
@@ -56,10 +57,12 @@
 PaymentRequest::PaymentRequest(
     const web::PaymentRequest& web_payment_request,
     ios::ChromeBrowserState* browser_state,
+    web::WebState* web_state,
     autofill::PersonalDataManager* personal_data_manager,
     id<PaymentRequestUIDelegate> payment_request_ui_delegate)
     : web_payment_request_(web_payment_request),
       browser_state_(browser_state),
+      web_state_(web_state),
       personal_data_manager_(personal_data_manager),
       payment_request_ui_delegate_(payment_request_ui_delegate),
       address_normalizer_(new AddressNormalizerImpl(
@@ -70,8 +73,8 @@
       selected_contact_profile_(nullptr),
       selected_payment_method_(nullptr),
       selected_shipping_option_(nullptr),
-      profile_comparator_(GetApplicationContext()->GetApplicationLocale(),
-                          *this) {
+      profile_comparator_(GetApplicationLocale(), *this),
+      journey_logger_(IsIncognito(), GetLastCommittedURL(), GetUkmRecorder()) {
   PopulateAvailableShippingOptions();
   PopulateProfileCache();
   PopulateAvailableProfiles();
@@ -129,8 +132,7 @@
 }
 
 const GURL& PaymentRequest::GetLastCommittedURL() const {
-  NOTREACHED() << "Implementation is never used";
-  return GURL::EmptyGURL();
+  return web_state_->GetLastCommittedURL();
 }
 
 void PaymentRequest::DoFullCardRequest(
@@ -150,8 +152,7 @@
       GetAddressInputSource(
           personal_data_manager_->GetURLRequestContextGetter())
           .release(),
-      GetAddressInputStorage().release(),
-      GetApplicationContext()->GetApplicationLocale());
+      GetAddressInputStorage().release(), GetApplicationLocale());
 }
 
 ukm::UkmRecorder* PaymentRequest::GetUkmRecorder() {
@@ -203,7 +204,7 @@
         base::UTF16ToASCII(web_payment_request_.details.total.amount.currency),
         base::UTF16ToASCII(
             web_payment_request_.details.total.amount.currency_system),
-        GetApplicationContext()->GetApplicationLocale()));
+        GetApplicationLocale()));
   }
   return currency_formatter_.get();
 }
@@ -287,8 +288,7 @@
   // effectively owned by this object.
   payment_method_cache_.push_back(base::MakeUnique<AutofillPaymentInstrument>(
       method_name, credit_card, matches_merchant_card_type_exactly,
-      billing_profiles(), GetApplicationContext()->GetApplicationLocale(),
-      this));
+      billing_profiles(), GetApplicationLocale(), this));
 
   PopulateAvailablePaymentMethods();
 
diff --git a/ios/chrome/browser/payments/payment_request_unittest.mm b/ios/chrome/browser/payments/payment_request_unittest.mm
index 61e584b..d406b7f 100644
--- a/ios/chrome/browser/payments/payment_request_unittest.mm
+++ b/ios/chrome/browser/payments/payment_request_unittest.mm
@@ -4,6 +4,8 @@
 
 #include "ios/chrome/browser/payments/payment_request.h"
 
+#include <memory>
+
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/scoped_task_environment.h"
 #include "components/autofill/core/browser/autofill_test_utils.h"
@@ -14,9 +16,11 @@
 #include "components/payments/core/currency_formatter.h"
 #include "components/payments/core/payment_method_data.h"
 #include "ios/chrome/browser/application_context.h"
+#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #include "ios/chrome/browser/payments/payment_request_test_util.h"
 #include "ios/chrome/browser/payments/test_payment_request.h"
 #include "ios/web/public/payments/payment_request.h"
+#import "ios/web/public/test/fakes/test_web_state.h"
 #include "testing/gmock/include/gmock/gmock-matchers.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -43,6 +47,9 @@
 
 class PaymentRequestTest : public testing::Test {
  protected:
+  PaymentRequestTest()
+      : chrome_browser_state_(TestChromeBrowserState::Builder().Build()) {}
+
   // Returns PaymentDetails with one shipping option that's selected.
   web::PaymentDetails CreateDetailsWithShippingOption() {
     web::PaymentDetails details;
@@ -69,19 +76,23 @@
   }
 
   base::test::ScopedTaskEnvironment scoped_task_environment_;
+
+  web::TestWebState web_state_;
+  std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
 };
 
 // Tests that the payments::CurrencyFormatter is constructed with the correct
 // currency code and currency system.
 TEST_F(PaymentRequestTest, CreatesCurrencyFormatterCorrectly) {
-  ASSERT_EQ("en", GetApplicationContext()->GetApplicationLocale());
 
   web::PaymentRequest web_payment_request;
   autofill::TestPersonalDataManager personal_data_manager;
 
   web_payment_request.details.total.amount.currency = base::ASCIIToUTF16("USD");
   TestPaymentRequest payment_request1(web_payment_request,
+                                      chrome_browser_state_.get(), &web_state_,
                                       &personal_data_manager);
+  ASSERT_EQ("en", payment_request1.GetApplicationLocale());
   CurrencyFormatter* currency_formatter =
       payment_request1.GetOrCreateCurrencyFormatter();
   EXPECT_EQ(base::UTF8ToUTF16("$55.00"), currency_formatter->Format("55.00"));
@@ -89,7 +100,9 @@
 
   web_payment_request.details.total.amount.currency = base::ASCIIToUTF16("JPY");
   TestPaymentRequest payment_request2(web_payment_request,
+                                      chrome_browser_state_.get(), &web_state_,
                                       &personal_data_manager);
+  ASSERT_EQ("en", payment_request2.GetApplicationLocale());
   currency_formatter = payment_request2.GetOrCreateCurrencyFormatter();
   EXPECT_EQ(base::UTF8ToUTF16("¥55"), currency_formatter->Format("55.00"));
   EXPECT_EQ("JPY", currency_formatter->formatted_currency_code());
@@ -98,7 +111,9 @@
       base::ASCIIToUTF16("NOT_ISO4217");
   web_payment_request.details.total.amount.currency = base::ASCIIToUTF16("USD");
   TestPaymentRequest payment_request3(web_payment_request,
+                                      chrome_browser_state_.get(), &web_state_,
                                       &personal_data_manager);
+  ASSERT_EQ("en", payment_request3.GetApplicationLocale());
   currency_formatter = payment_request3.GetOrCreateCurrencyFormatter();
   EXPECT_EQ(base::UTF8ToUTF16("55.00"), currency_formatter->Format("55.00"));
   EXPECT_EQ("USD", currency_formatter->formatted_currency_code());
@@ -117,6 +132,7 @@
   web_payment_request.method_data.push_back(method_datum2);
 
   TestPaymentRequest payment_request(web_payment_request,
+                                     chrome_browser_state_.get(), &web_state_,
                                      &personal_data_manager);
   ASSERT_EQ(2U, payment_request.supported_card_networks().size());
   EXPECT_EQ("visa", payment_request.supported_card_networks()[0]);
@@ -138,6 +154,7 @@
   web_payment_request.method_data.push_back(method_datum1);
 
   TestPaymentRequest payment_request(web_payment_request,
+                                     chrome_browser_state_.get(), &web_state_,
                                      &personal_data_manager);
   ASSERT_EQ(2U, payment_request.supported_card_networks().size());
   EXPECT_EQ("visa", payment_request.supported_card_networks()[0]);
@@ -164,6 +181,7 @@
   web_payment_request.method_data.push_back(method_datum4);
 
   TestPaymentRequest payment_request(web_payment_request,
+                                     chrome_browser_state_.get(), &web_state_,
                                      &personal_data_manager);
   ASSERT_EQ(2U, payment_request.supported_card_networks().size());
   EXPECT_EQ("visa", payment_request.supported_card_networks()[0]);
@@ -180,6 +198,7 @@
   web_payment_request.method_data.push_back(method_datum1);
 
   TestPaymentRequest payment_request(web_payment_request,
+                                     chrome_browser_state_.get(), &web_state_,
                                      &personal_data_manager);
 
   // All of the basic card networks are supported.
@@ -206,6 +225,7 @@
   web_payment_request.method_data.push_back(method_datum1);
 
   TestPaymentRequest payment_request(web_payment_request,
+                                     chrome_browser_state_.get(), &web_state_,
                                      &personal_data_manager);
 
   // All of the basic card networks are supported, but JCB is first because it
@@ -239,6 +259,7 @@
   web_payment_request.method_data.push_back(method_datum2);
 
   TestPaymentRequest payment_request(web_payment_request,
+                                     chrome_browser_state_.get(), &web_state_,
                                      &personal_data_manager);
 
   EXPECT_EQ(3u, payment_request.supported_card_networks().size());
@@ -260,6 +281,7 @@
   web_payment_request.method_data.push_back(method_datum1);
 
   TestPaymentRequest payment_request(web_payment_request,
+                                     chrome_browser_state_.get(), &web_state_,
                                      &personal_data_manager);
 
   // Only the specified networks are supported.
@@ -284,6 +306,7 @@
   personal_data_manager.AddTestingCreditCard(&credit_card_1);
 
   TestPaymentRequest payment_request(web_payment_request,
+                                     chrome_browser_state_.get(), &web_state_,
                                      &personal_data_manager);
   EXPECT_EQ(1U, payment_request.payment_methods().size());
 
@@ -308,6 +331,7 @@
   personal_data_manager.AddTestingProfile(&profile_1);
 
   TestPaymentRequest payment_request(web_payment_request,
+                                     chrome_browser_state_.get(), &web_state_,
                                      &personal_data_manager);
   EXPECT_EQ(1U, payment_request.shipping_profiles().size());
   EXPECT_EQ(1U, payment_request.contact_profiles().size());
@@ -344,6 +368,7 @@
   web_payment_request.details = std::move(details);
 
   TestPaymentRequest payment_request(web_payment_request,
+                                     chrome_browser_state_.get(), &web_state_,
                                      &personal_data_manager);
   // The last one marked "selected" should be selected.
   EXPECT_EQ(base::UTF8ToUTF16("option:3"),
@@ -367,6 +392,7 @@
 
   // No profiles are selected because none are available!
   TestPaymentRequest payment_request(web_payment_request,
+                                     chrome_browser_state_.get(), &web_state_,
                                      &personal_data_manager);
   EXPECT_EQ(nullptr, payment_request.selected_shipping_profile());
   EXPECT_EQ(nullptr, payment_request.selected_contact_profile());
@@ -390,6 +416,7 @@
 
   // address2 is selected because it has the most use count (Frecency model).
   TestPaymentRequest payment_request(web_payment_request,
+                                     chrome_browser_state_.get(), &web_state_,
                                      &personal_data_manager);
   EXPECT_EQ(address2.guid(),
             payment_request.selected_shipping_profile()->guid());
@@ -415,6 +442,7 @@
   // No shipping profile is selected because the merchant has not selected a
   // shipping option. However there is a suitable contact profile.
   TestPaymentRequest payment_request(web_payment_request,
+                                     chrome_browser_state_.get(), &web_state_,
                                      &personal_data_manager);
   EXPECT_EQ(nullptr, payment_request.selected_shipping_profile());
   EXPECT_EQ(address.guid(), payment_request.selected_contact_profile()->guid());
@@ -443,6 +471,7 @@
   // Even though address1 has more use counts, address2 is selected because it
   // is complete.
   TestPaymentRequest payment_request(web_payment_request,
+                                     chrome_browser_state_.get(), &web_state_,
                                      &personal_data_manager);
   EXPECT_EQ(address2.guid(),
             payment_request.selected_shipping_profile()->guid());
@@ -479,6 +508,7 @@
   // phone. address2 is selected as the shipping profile because it's the most
   // complete for shipping.
   TestPaymentRequest payment_request(web_payment_request,
+                                     chrome_browser_state_.get(), &web_state_,
                                      &personal_data_manager);
   EXPECT_EQ(address2.guid(),
             payment_request.selected_shipping_profile()->guid());
@@ -494,6 +524,7 @@
 
   // No payment methods are selected because none are available!
   TestPaymentRequest payment_request(web_payment_request,
+                                     chrome_browser_state_.get(), &web_state_,
                                      &personal_data_manager);
   EXPECT_EQ(nullptr, payment_request.selected_payment_method());
 }
@@ -513,6 +544,7 @@
 
   // credit_card is selected because expired cards are valid for payment.
   TestPaymentRequest payment_request(web_payment_request,
+                                     chrome_browser_state_.get(), &web_state_,
                                      &personal_data_manager);
   EXPECT_EQ(payment_request.selected_payment_method()->type(),
             PaymentInstrument::Type::AUTOFILL);
@@ -542,6 +574,7 @@
   // credit_card2 is selected because it has the most use count (Frecency
   // model).
   TestPaymentRequest payment_request(web_payment_request,
+                                     chrome_browser_state_.get(), &web_state_,
                                      &personal_data_manager);
   AutofillPaymentInstrument* payment_instrument =
       static_cast<AutofillPaymentInstrument*>(
@@ -568,6 +601,7 @@
   // Even though credit_card2 has more use counts, credit_card is selected
   // because it is complete.
   TestPaymentRequest payment_request(web_payment_request,
+                                     chrome_browser_state_.get(), &web_state_,
                                      &personal_data_manager);
   AutofillPaymentInstrument* payment_instrument =
       static_cast<AutofillPaymentInstrument*>(
@@ -598,6 +632,7 @@
       payment_request_test_util::CreateTestWebPaymentRequest();
 
   TestPaymentRequest payment_request(web_payment_request,
+                                     chrome_browser_state_.get(), &web_state_,
                                      &personal_data_manager);
   AutofillPaymentInstrument* payment_instrument =
       static_cast<AutofillPaymentInstrument*>(
@@ -634,6 +669,7 @@
       payment_request_test_util::CreateTestWebPaymentRequest();
 
   TestPaymentRequest payment_request(web_payment_request,
+                                     chrome_browser_state_.get(), &web_state_,
                                      &personal_data_manager);
   AutofillPaymentInstrument* payment_instrument =
       static_cast<AutofillPaymentInstrument*>(
@@ -671,6 +707,7 @@
   web_payment_request.options.request_payer_phone = false;
 
   TestPaymentRequest payment_request(web_payment_request,
+                                     chrome_browser_state_.get(), &web_state_,
                                      &personal_data_manager);
   AutofillPaymentInstrument* payment_instrument =
       static_cast<AutofillPaymentInstrument*>(
@@ -704,6 +741,7 @@
   web_payment_request.options.request_shipping = false;
 
   TestPaymentRequest payment_request(web_payment_request,
+                                     chrome_browser_state_.get(), &web_state_,
                                      &personal_data_manager);
   AutofillPaymentInstrument* payment_instrument =
       static_cast<AutofillPaymentInstrument*>(
@@ -739,6 +777,7 @@
   web_payment_request.options.request_payer_phone = false;
 
   TestPaymentRequest payment_request(web_payment_request,
+                                     chrome_browser_state_.get(), &web_state_,
                                      &personal_data_manager);
   AutofillPaymentInstrument* payment_instrument =
       static_cast<AutofillPaymentInstrument*>(
diff --git a/ios/chrome/browser/payments/test_payment_request.h b/ios/chrome/browser/payments/test_payment_request.h
index 2d119851..b669e245 100644
--- a/ios/chrome/browser/payments/test_payment_request.h
+++ b/ios/chrome/browser/payments/test_payment_request.h
@@ -24,6 +24,7 @@
 namespace web {
 class PaymentRequest;
 class PaymentShippingOption;
+class WebState;
 }  // namespace web
 
 class PrefService;
@@ -33,13 +34,16 @@
 // PaymentRequest for use in tests.
 class TestPaymentRequest : public PaymentRequest {
  public:
-  // |personal_data_manager| should not be null and should outlive this object.
+  // |browser_state|, |web_state|, and |personal_data_manager| should not be
+  // null and should outlive this object.
   TestPaymentRequest(const web::PaymentRequest& web_payment_request,
                      ios::ChromeBrowserState* browser_state,
+                     web::WebState* web_state,
                      autofill::PersonalDataManager* personal_data_manager,
                      id<PaymentRequestUIDelegate> payment_request_ui_delegate)
       : PaymentRequest(web_payment_request,
                        browser_state,
+                       web_state,
                        personal_data_manager,
                        payment_request_ui_delegate),
         region_data_loader_(nullptr),
@@ -48,16 +52,11 @@
 
   TestPaymentRequest(const web::PaymentRequest& web_payment_request,
                      ios::ChromeBrowserState* browser_state,
+                     web::WebState* web_state,
                      autofill::PersonalDataManager* personal_data_manager)
       : TestPaymentRequest(web_payment_request,
                            browser_state,
-                           personal_data_manager,
-                           nil) {}
-
-  TestPaymentRequest(const web::PaymentRequest& web_payment_request,
-                     autofill::PersonalDataManager* personal_data_manager)
-      : TestPaymentRequest(web_payment_request,
-                           nil,
+                           web_state,
                            personal_data_manager,
                            nil) {}
 
diff --git a/ios/chrome/browser/ui/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view_controller.mm
index 6ceff6a..e2389e0 100644
--- a/ios/chrome/browser/ui/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view_controller.mm
@@ -3533,6 +3533,7 @@
              bridge:bridge
         sourceFrame:[sourceView convertRect:[sourceView bounds] toView:view]
          parentView:view];
+  _pageInfoController.dispatcher = self.dispatcher;
   bridge->set_controller(_pageInfoController);
 }
 
@@ -3970,9 +3971,7 @@
 - (IBAction)locationBarBeganEdit:(id)sender {
   // On handsets, if a page is currently loading it should be stopped.
   if (!IsIPadIdiom() && _toolbarModelIOS->IsLoading()) {
-    GenericChromeCommand* command =
-        [[GenericChromeCommand alloc] initWithTag:IDC_STOP];
-    [self chromeExecuteCommand:command];
+    [self.dispatcher stopLoading];
     _locationBarEditCancelledLoad = YES;
   }
 }
@@ -4031,6 +4030,20 @@
   [[_model currentTab] goForward];
 }
 
+- (void)stopLoading {
+  [_model currentTab].webState->Stop();
+}
+
+- (void)reload {
+  web::WebState* webState = [_model currentTab].webState;
+  if (webState) {
+    // |check_for_repost| is true because the reload is explicitly initiated
+    // by the user.
+    webState->GetNavigationManager()->Reload(web::ReloadType::NORMAL,
+                                             true /* check_for_repost */);
+  }
+}
+
 #pragma mark - Command Handling
 
 - (IBAction)chromeExecuteCommand:(id)sender {
@@ -4105,12 +4118,8 @@
       }
       break;
     case IDC_RELOAD: {
-      web::WebState* webState = [_model currentTab].webState;
-      if (webState)
-        // |check_for_repost| is true because the reload is explicitly initiated
-        // by the user.
-        webState->GetNavigationManager()->Reload(web::ReloadType::NORMAL,
-                                                 true /* check_for_repost */);
+      // Route to dispatcher until downstream code is migrated.
+      [self.dispatcher reload];
       break;
     }
     case IDC_SHARE_PAGE:
@@ -4154,9 +4163,6 @@
       }
       break;
     }
-    case IDC_STOP:
-      [_model currentTab].webState->Stop();
-      break;
 #if !defined(NDEBUG)
     case IDC_VIEW_SOURCE:
       [self viewSource];
diff --git a/ios/chrome/browser/ui/commands/browser_commands.h b/ios/chrome/browser/ui/commands/browser_commands.h
index ee911d0..3c97d78b 100644
--- a/ios/chrome/browser/ui/commands/browser_commands.h
+++ b/ios/chrome/browser/ui/commands/browser_commands.h
@@ -18,6 +18,12 @@
 // Navigates forwards in the current tab's history.
 - (void)goForward;
 
+// Stops loading the current web page.
+- (void)stopLoading;
+
+// Reloads the current web page
+- (void)reload;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_COMMANDS_BROWSER_COMMANDS_H_
diff --git a/ios/chrome/browser/ui/commands/ios_command_ids.h b/ios/chrome/browser/ui/commands/ios_command_ids.h
index 58a4aa3..ad25284 100644
--- a/ios/chrome/browser/ui/commands/ios_command_ids.h
+++ b/ios/chrome/browser/ui/commands/ios_command_ids.h
@@ -14,7 +14,6 @@
 
 // clang-format off
 #define IDC_RELOAD                                     33002
-#define IDC_STOP                                       33006
 #define IDC_NEW_TAB                                    34014
 #define IDC_FULLSCREEN                                 34030
 #define IDC_BOOKMARK_PAGE                              35000
diff --git a/ios/chrome/browser/ui/key_commands_provider.mm b/ios/chrome/browser/ui/key_commands_provider.mm
index e09cb7c..06d711d4 100644
--- a/ios/chrome/browser/ui/key_commands_provider.mm
+++ b/ios/chrome/browser/ui/key_commands_provider.mm
@@ -156,7 +156,7 @@
                                      title:l10n_util::GetNSStringWithFixup(
                                                IDS_IOS_ACCNAME_RELOAD)
                                     action:^{
-                                      execute(IDC_RELOAD);
+                                      [weakDispatcher reload];
                                     }],
     ]];
 
@@ -247,7 +247,7 @@
                              modifierFlags:UIKeyModifierCommand
                                      title:nil
                                     action:^{
-                                      execute(IDC_STOP);
+                                      [weakDispatcher stopLoading];
                                     }],
       [UIKeyCommand cr_keyCommandWithInput:@"?"
                              modifierFlags:UIKeyModifierCommand
diff --git a/ios/chrome/browser/ui/omnibox/page_info_view_controller.h b/ios/chrome/browser/ui/omnibox/page_info_view_controller.h
index 50fb230..a64984d 100644
--- a/ios/chrome/browser/ui/omnibox/page_info_view_controller.h
+++ b/ios/chrome/browser/ui/omnibox/page_info_view_controller.h
@@ -15,6 +15,7 @@
 #import "ios/chrome/browser/ui/popup_menu/popup_menu_controller.h"
 
 @class BidiContainerView;
+@protocol BrowserCommands;
 class PageInfoModel;
 
 // TODO(crbug.com/227827) Merge 178763: PageInfoModel has been removed in
@@ -30,6 +31,9 @@
         sourceFrame:(CGRect)source
          parentView:(UIView*)parent;
 
+// Dispatcher for browser commands.
+@property(nonatomic, weak) id<BrowserCommands> dispatcher;
+
 // Dismisses the view.
 - (void)dismiss;
 
diff --git a/ios/chrome/browser/ui/omnibox/page_info_view_controller.mm b/ios/chrome/browser/ui/omnibox/page_info_view_controller.mm
index baf87c3..051bb5e 100644
--- a/ios/chrome/browser/ui/omnibox/page_info_view_controller.mm
+++ b/ios/chrome/browser/ui/omnibox/page_info_view_controller.mm
@@ -201,6 +201,7 @@
 
 @synthesize containerView = containerView_;
 @synthesize popupContainer = popupContainer_;
+@synthesize dispatcher = dispatcher_;
 
 - (id)initWithModel:(PageInfoModel*)model
              bridge:(PageInfoModelObserver*)bridge
@@ -406,6 +407,7 @@
 }
 
 - (void)close {
+  // Refactoring note: _containerView.tag is IDC_HIDE_PAGE_INFO.
   [containerView_ chromeExecuteCommand:containerView_];
 }
 
@@ -471,8 +473,7 @@
     return nil;
   }
   UIButton* button = [[UIButton alloc] initWithFrame:CGRectZero];
-  int messageId = IDS_IOS_PAGE_INFO_RELOAD;
-  NSInteger tag = IDC_RELOAD;
+  int messageId;
   NSString* accessibilityID = @"Reload button";
   switch (buttonAction) {
     case PageInfoModel::BUTTON_NONE:
@@ -480,26 +481,27 @@
       return nil;
     case PageInfoModel::BUTTON_SHOW_SECURITY_HELP:
       messageId = IDS_LEARN_MORE;
-      tag = IDC_SHOW_SECURITY_HELP;
+      button.tag = IDC_SHOW_SECURITY_HELP;
       accessibilityID = @"Learn more";
+      [button addTarget:nil
+                    action:@selector(chromeExecuteCommand:)
+          forControlEvents:UIControlEventTouchUpInside];
       break;
     case PageInfoModel::BUTTON_RELOAD:
       messageId = IDS_IOS_PAGE_INFO_RELOAD;
-      tag = IDC_RELOAD;
       accessibilityID = @"Reload button";
       [button addTarget:self
                     action:@selector(close)
           forControlEvents:UIControlEventTouchUpInside];
+      [button addTarget:self.dispatcher
+                    action:@selector(reload)
+          forControlEvents:UIControlEventTouchUpInside];
       break;
   };
 
   NSString* title = l10n_util::GetNSStringWithFixup(messageId);
   SetA11yLabelAndUiAutomationName(button, messageId, accessibilityID);
   [button setTitle:title forState:UIControlStateNormal];
-  [button setTag:tag];
-  [button addTarget:nil
-                action:@selector(chromeExecuteCommand:)
-      forControlEvents:UIControlEventTouchUpInside];
   return button;
 }
 
diff --git a/ios/chrome/browser/ui/payments/BUILD.gn b/ios/chrome/browser/ui/payments/BUILD.gn
index 148d730..21f86ef 100644
--- a/ios/chrome/browser/ui/payments/BUILD.gn
+++ b/ios/chrome/browser/ui/payments/BUILD.gn
@@ -207,6 +207,7 @@
     "//ios/third_party/material_components_ios",
     "//ios/web",
     "//ios/web/public/test",
+    "//ios/web/public/test/fakes",
     "//testing/gmock",
     "//testing/gtest",
     "//third_party/ocmock",
diff --git a/ios/chrome/browser/ui/payments/address_edit_coordinator.mm b/ios/chrome/browser/ui/payments/address_edit_coordinator.mm
index 77865e8..4e48fa85 100644
--- a/ios/chrome/browser/ui/payments/address_edit_coordinator.mm
+++ b/ios/chrome/browser/ui/payments/address_edit_coordinator.mm
@@ -15,7 +15,6 @@
 #include "components/autofill/core/common/autofill_constants.h"
 #include "components/payments/core/payments_profile_comparator.h"
 #include "components/strings/grit/components_strings.h"
-#include "ios/chrome/browser/application_context.h"
 #include "ios/chrome/browser/payments/payment_request.h"
 #import "ios/chrome/browser/ui/autofill/autofill_ui_type_util.h"
 #import "ios/chrome/browser/ui/payments/address_edit_mediator.h"
@@ -151,7 +150,7 @@
     address.SetInfo(autofill::AutofillType(
                         AutofillTypeFromAutofillUIType(field.autofillUIType)),
                     base::SysNSStringToUTF16(field.value),
-                    GetApplicationContext()->GetApplicationLocale());
+                    self.paymentRequest->GetApplicationLocale());
   }
 
   if (!self.address) {
diff --git a/ios/chrome/browser/ui/payments/address_edit_coordinator_unittest.mm b/ios/chrome/browser/ui/payments/address_edit_coordinator_unittest.mm
index 3e44150..2d996d7d 100644
--- a/ios/chrome/browser/ui/payments/address_edit_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/payments/address_edit_coordinator_unittest.mm
@@ -17,12 +17,13 @@
 #include "components/autofill/core/browser/test_region_data_loader.h"
 #include "components/payments/core/payments_profile_comparator.h"
 #include "components/prefs/pref_service.h"
-#include "ios/chrome/browser/application_context.h"
+#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #include "ios/chrome/browser/payments/payment_request_test_util.h"
 #include "ios/chrome/browser/payments/test_payment_request.h"
 #import "ios/chrome/browser/ui/payments/payment_request_edit_view_controller.h"
 #import "ios/chrome/browser/ui/payments/payment_request_editor_field.h"
 #import "ios/chrome/test/scoped_key_window.h"
+#import "ios/web/public/test/fakes/test_web_state.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
@@ -53,8 +54,12 @@
 class MockTestPaymentRequest : public payments::TestPaymentRequest {
  public:
   MockTestPaymentRequest(web::PaymentRequest web_payment_request,
+                         ios::ChromeBrowserState* browser_state,
+                         web::WebState* web_state,
                          autofill::PersonalDataManager* personal_data_manager)
       : payments::TestPaymentRequest(web_payment_request,
+                                     browser_state,
+                                     web_state,
                                      personal_data_manager) {}
   MOCK_METHOD1(AddAutofillProfile,
                autofill::AutofillProfile*(const autofill::AutofillProfile&));
@@ -104,16 +109,17 @@
 class PaymentRequestAddressEditCoordinatorTest : public PlatformTest {
  protected:
   PaymentRequestAddressEditCoordinatorTest()
-      : pref_service_(autofill::test::PrefServiceForTesting()) {
+      : pref_service_(autofill::test::PrefServiceForTesting()),
+        chrome_browser_state_(TestChromeBrowserState::Builder().Build()) {
     autofill::CountryNames::SetLocaleString("en-US");
     personal_data_manager_.SetTestingPrefService(pref_service_.get());
+
     payment_request_ = base::MakeUnique<MockTestPaymentRequest>(
         payment_request_test_util::CreateTestWebPaymentRequest(),
-        &personal_data_manager_);
+        chrome_browser_state_.get(), &web_state_, &personal_data_manager_);
 
     profile_comparator_ = base::MakeUnique<MockPaymentsProfileComparator>(
-        GetApplicationContext()->GetApplicationLocale(),
-        *payment_request_.get());
+        payment_request_->GetApplicationLocale(), *payment_request_.get());
     payment_request_->SetProfileComparator(profile_comparator_.get());
 
     test_region_data_loader_.set_synchronous_callback(true);
@@ -126,10 +132,12 @@
 
   base::test::ScopedTaskEnvironment scoped_task_evironment_;
 
+  web::TestWebState web_state_;
   std::unique_ptr<PrefService> pref_service_;
   MockTestPersonalDataManager personal_data_manager_;
   autofill::TestRegionDataLoader test_region_data_loader_;
   std::unique_ptr<MockPaymentsProfileComparator> profile_comparator_;
+  std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
   std::unique_ptr<MockTestPaymentRequest> payment_request_;
 };
 
diff --git a/ios/chrome/browser/ui/payments/address_edit_mediator.mm b/ios/chrome/browser/ui/payments/address_edit_mediator.mm
index 1590f90..0a2ae33 100644
--- a/ios/chrome/browser/ui/payments/address_edit_mediator.mm
+++ b/ios/chrome/browser/ui/payments/address_edit_mediator.mm
@@ -23,7 +23,6 @@
 #include "components/autofill/core/browser/personal_data_manager.h"
 #include "components/payments/core/payment_request_data_util.h"
 #include "components/strings/grit/components_strings.h"
-#include "ios/chrome/browser/application_context.h"
 #include "ios/chrome/browser/payments/payment_request.h"
 #import "ios/chrome/browser/ui/autofill/autofill_ui_type.h"
 #import "ios/chrome/browser/ui/autofill/autofill_ui_type_util.h"
@@ -173,7 +172,7 @@
   autofill::CountryComboboxModel countryModel;
   countryModel.SetCountries(*_paymentRequest->GetPersonalDataManager(),
                             base::Callback<bool(const std::string&)>(),
-                            GetApplicationContext()->GetApplicationLocale());
+                            _paymentRequest->GetApplicationLocale());
   const autofill::CountryComboboxModel::CountryVector& countriesVector =
       countryModel.countries();
 
@@ -227,8 +226,7 @@
   std::string unused;
   autofill::GetAddressComponents(
       base::SysNSStringToUTF8(self.selectedCountryCode),
-      GetApplicationContext()->GetApplicationLocale(), &addressComponents,
-      &unused);
+      _paymentRequest->GetApplicationLocale(), &addressComponents, &unused);
 
   for (size_t lineIndex = 0; lineIndex < addressComponents.GetSize();
        ++lineIndex) {
@@ -328,8 +326,7 @@
         self.address
             ? base::SysUTF16ToNSString(
                   payments::data_util::GetFormattedPhoneNumberForDisplay(
-                      *self.address,
-                      GetApplicationContext()->GetApplicationLocale()))
+                      *self.address, _paymentRequest->GetApplicationLocale()))
             : nil;
     field = [[EditorField alloc]
         initWithAutofillUIType:AutofillUITypeProfileHomePhoneWholeNumber
@@ -352,7 +349,7 @@
                          fieldType:(autofill::ServerFieldType)fieldType {
   return profile ? base::SysUTF16ToNSString(profile->GetInfo(
                        autofill::AutofillType(fieldType),
-                       GetApplicationContext()->GetApplicationLocale()))
+                       _paymentRequest->GetApplicationLocale()))
                  : nil;
 }
 
diff --git a/ios/chrome/browser/ui/payments/billing_address_selection_coordinator_unittest.mm b/ios/chrome/browser/ui/payments/billing_address_selection_coordinator_unittest.mm
index d7fb8e70..3558927 100644
--- a/ios/chrome/browser/ui/payments/billing_address_selection_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/payments/billing_address_selection_coordinator_unittest.mm
@@ -15,9 +15,11 @@
 #include "components/autofill/core/browser/test_personal_data_manager.h"
 #include "components/autofill/core/browser/test_region_data_loader.h"
 #include "components/prefs/pref_service.h"
+#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #include "ios/chrome/browser/payments/payment_request_test_util.h"
 #include "ios/chrome/browser/payments/test_payment_request.h"
 #import "ios/chrome/browser/ui/payments/payment_request_selector_view_controller.h"
+#import "ios/web/public/test/fakes/test_web_state.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
 #include "third_party/ocmock/OCMock/OCMock.h"
@@ -33,7 +35,8 @@
   PaymentRequestBillingAddressSelectionCoordinatorTest()
       : autofill_profile1_(autofill::test::GetFullProfile()),
         autofill_profile2_(autofill::test::GetFullProfile2()),
-        pref_service_(autofill::test::PrefServiceForTesting()) {
+        pref_service_(autofill::test::PrefServiceForTesting()),
+        chrome_browser_state_(TestChromeBrowserState::Builder().Build()) {
     personal_data_manager_.SetTestingPrefService(pref_service_.get());
     // Add testing profiles to autofill::TestPersonalDataManager. Make the less
     // frequently used one incomplete.
@@ -44,9 +47,10 @@
         autofill::AutofillType(autofill::PHONE_HOME_WHOLE_NUMBER),
         base::string16(), "en-US");
     personal_data_manager_.AddTestingProfile(&autofill_profile2_);
+
     payment_request_ = base::MakeUnique<payments::TestPaymentRequest>(
         payment_request_test_util::CreateTestWebPaymentRequest(),
-        &personal_data_manager_);
+        chrome_browser_state_.get(), &web_state_, &personal_data_manager_);
 
     test_region_data_loader_.set_synchronous_callback(true);
     payment_request_->SetRegionDataLoader(&test_region_data_loader_);
@@ -79,9 +83,11 @@
 
   autofill::AutofillProfile autofill_profile1_;
   autofill::AutofillProfile autofill_profile2_;
+  web::TestWebState web_state_;
   std::unique_ptr<PrefService> pref_service_;
   autofill::TestPersonalDataManager personal_data_manager_;
   autofill::TestRegionDataLoader test_region_data_loader_;
+  std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
   std::unique_ptr<payments::TestPaymentRequest> payment_request_;
 };
 
diff --git a/ios/chrome/browser/ui/payments/billing_address_selection_mediator_unittest.mm b/ios/chrome/browser/ui/payments/billing_address_selection_mediator_unittest.mm
index 988a417..ec3af68 100644
--- a/ios/chrome/browser/ui/payments/billing_address_selection_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/payments/billing_address_selection_mediator_unittest.mm
@@ -11,11 +11,12 @@
 #include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/browser/test_personal_data_manager.h"
 #include "components/payments/core/payments_profile_comparator.h"
-#include "ios/chrome/browser/application_context.h"
+#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #include "ios/chrome/browser/payments/payment_request_test_util.h"
 #import "ios/chrome/browser/payments/payment_request_util.h"
 #include "ios/chrome/browser/payments/test_payment_request.h"
 #import "ios/chrome/browser/ui/payments/cells/autofill_profile_item.h"
+#import "ios/web/public/test/fakes/test_web_state.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
 #include "third_party/ocmock/gtest_support.h"
@@ -45,18 +46,19 @@
       : autofill_profile_1_(autofill::test::GetFullProfile()),
         autofill_profile_2_(autofill::test::GetFullProfile2()),
         autofill_profile_3_(autofill::test::GetIncompleteProfile1()),
-        autofill_profile_4_(autofill::test::GetIncompleteProfile2()) {
+        autofill_profile_4_(autofill::test::GetIncompleteProfile2()),
+        chrome_browser_state_(TestChromeBrowserState::Builder().Build()) {
     // Add testing profiles to autofill::TestPersonalDataManager.
     personal_data_manager_.AddTestingProfile(&autofill_profile_1_);
     personal_data_manager_.AddTestingProfile(&autofill_profile_2_);
     personal_data_manager_.AddTestingProfile(&autofill_profile_3_);
     personal_data_manager_.AddTestingProfile(&autofill_profile_4_);
+
     payment_request_ = base::MakeUnique<payments::TestPaymentRequest>(
         payment_request_test_util::CreateTestWebPaymentRequest(),
-        &personal_data_manager_);
+        chrome_browser_state_.get(), &web_state_, &personal_data_manager_);
     profile_comparator_ = base::MakeUnique<FakePaymentsProfileComparator>(
-        GetApplicationContext()->GetApplicationLocale(),
-        *payment_request_.get());
+        payment_request_->GetApplicationLocale(), *payment_request_.get());
     payment_request_->SetProfileComparator(profile_comparator_.get());
   }
 
@@ -76,9 +78,11 @@
   autofill::AutofillProfile autofill_profile_2_;
   autofill::AutofillProfile autofill_profile_3_;
   autofill::AutofillProfile autofill_profile_4_;
+  web::TestWebState web_state_;
   autofill::TestPersonalDataManager personal_data_manager_;
-  std::unique_ptr<payments::TestPaymentRequest> payment_request_;
   std::unique_ptr<FakePaymentsProfileComparator> profile_comparator_;
+  std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
+  std::unique_ptr<payments::TestPaymentRequest> payment_request_;
 };
 
 // Tests that the expected selectable items are created and that the index of
diff --git a/ios/chrome/browser/ui/payments/contact_info_edit_coordinator.mm b/ios/chrome/browser/ui/payments/contact_info_edit_coordinator.mm
index aa06914..ee359bf1 100644
--- a/ios/chrome/browser/ui/payments/contact_info_edit_coordinator.mm
+++ b/ios/chrome/browser/ui/payments/contact_info_edit_coordinator.mm
@@ -15,7 +15,6 @@
 #include "components/autofill/core/common/autofill_constants.h"
 #include "components/payments/core/payments_profile_comparator.h"
 #include "components/strings/grit/components_strings.h"
-#include "ios/chrome/browser/application_context.h"
 #include "ios/chrome/browser/payments/payment_request.h"
 #import "ios/chrome/browser/ui/autofill/autofill_ui_type_util.h"
 #import "ios/chrome/browser/ui/payments/contact_info_edit_mediator.h"
@@ -98,7 +97,7 @@
       case AutofillUITypeProfileHomePhoneWholeNumber: {
         const std::string countryCode =
             autofill::AutofillCountry::CountryCodeForLocale(
-                GetApplicationContext()->GetApplicationLocale());
+                self.paymentRequest->GetApplicationLocale());
         if (!autofill::IsValidPhoneNumber(base::SysNSStringToUTF16(field.value),
                                           countryCode)) {
           return l10n_util::GetNSString(
@@ -140,7 +139,7 @@
     profile.SetInfo(autofill::AutofillType(
                         AutofillTypeFromAutofillUIType(field.autofillUIType)),
                     base::SysNSStringToUTF16(field.value),
-                    GetApplicationContext()->GetApplicationLocale());
+                    self.paymentRequest->GetApplicationLocale());
   }
 
   if (!self.profile) {
diff --git a/ios/chrome/browser/ui/payments/contact_info_edit_coordinator_unittest.mm b/ios/chrome/browser/ui/payments/contact_info_edit_coordinator_unittest.mm
index 95adbe5..3776c12 100644
--- a/ios/chrome/browser/ui/payments/contact_info_edit_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/payments/contact_info_edit_coordinator_unittest.mm
@@ -16,12 +16,13 @@
 #include "components/autofill/core/browser/test_region_data_loader.h"
 #include "components/payments/core/payments_profile_comparator.h"
 #include "components/prefs/pref_service.h"
-#include "ios/chrome/browser/application_context.h"
+#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #include "ios/chrome/browser/payments/payment_request_test_util.h"
 #include "ios/chrome/browser/payments/test_payment_request.h"
 #import "ios/chrome/browser/ui/payments/payment_request_edit_view_controller.h"
 #import "ios/chrome/browser/ui/payments/payment_request_editor_field.h"
 #import "ios/chrome/test/scoped_key_window.h"
+#import "ios/web/public/test/fakes/test_web_state.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
@@ -52,8 +53,12 @@
 class MockTestPaymentRequest : public payments::TestPaymentRequest {
  public:
   MockTestPaymentRequest(web::PaymentRequest web_payment_request,
+                         ios::ChromeBrowserState* browser_state,
+                         web::WebState* web_state,
                          autofill::PersonalDataManager* personal_data_manager)
       : payments::TestPaymentRequest(web_payment_request,
+                                     browser_state,
+                                     web_state,
                                      personal_data_manager) {}
   MOCK_METHOD1(AddAutofillProfile,
                autofill::AutofillProfile*(const autofill::AutofillProfile&));
@@ -94,15 +99,16 @@
 class PaymentRequestContactInfoEditCoordinatorTest : public PlatformTest {
  protected:
   PaymentRequestContactInfoEditCoordinatorTest()
-      : pref_service_(autofill::test::PrefServiceForTesting()) {
+      : pref_service_(autofill::test::PrefServiceForTesting()),
+        chrome_browser_state_(TestChromeBrowserState::Builder().Build()) {
     personal_data_manager_.SetTestingPrefService(pref_service_.get());
+
     payment_request_ = base::MakeUnique<MockTestPaymentRequest>(
         payment_request_test_util::CreateTestWebPaymentRequest(),
-        &personal_data_manager_);
+        chrome_browser_state_.get(), &web_state_, &personal_data_manager_);
 
     profile_comparator_ = base::MakeUnique<MockPaymentsProfileComparator>(
-        GetApplicationContext()->GetApplicationLocale(),
-        *payment_request_.get());
+        payment_request_->GetApplicationLocale(), *payment_request_.get());
     payment_request_->SetProfileComparator(profile_comparator_.get());
 
     test_region_data_loader_.set_synchronous_callback(true);
@@ -115,10 +121,12 @@
 
   base::test::ScopedTaskEnvironment scoped_task_evironment_;
 
+  web::TestWebState web_state_;
   std::unique_ptr<PrefService> pref_service_;
   MockTestPersonalDataManager personal_data_manager_;
   autofill::TestRegionDataLoader test_region_data_loader_;
   std::unique_ptr<MockPaymentsProfileComparator> profile_comparator_;
+  std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
   std::unique_ptr<MockTestPaymentRequest> payment_request_;
 };
 
diff --git a/ios/chrome/browser/ui/payments/contact_info_edit_mediator.mm b/ios/chrome/browser/ui/payments/contact_info_edit_mediator.mm
index 70a210f..af5c782 100644
--- a/ios/chrome/browser/ui/payments/contact_info_edit_mediator.mm
+++ b/ios/chrome/browser/ui/payments/contact_info_edit_mediator.mm
@@ -12,7 +12,6 @@
 #include "components/autofill/core/browser/field_types.h"
 #include "components/payments/core/payment_request_data_util.h"
 #include "components/strings/grit/components_strings.h"
-#include "ios/chrome/browser/application_context.h"
 #include "ios/chrome/browser/payments/payment_request.h"
 #import "ios/chrome/browser/ui/autofill/autofill_ui_type.h"
 #import "ios/chrome/browser/ui/payments/payment_request_edit_consumer.h"
@@ -80,7 +79,7 @@
   if (field.autofillUIType == AutofillUITypeProfileHomePhoneWholeNumber) {
     const std::string countryCode =
         autofill::AutofillCountry::CountryCodeForLocale(
-            GetApplicationContext()->GetApplicationLocale());
+            _paymentRequest->GetApplicationLocale());
     field.value =
         base::SysUTF8ToNSString(payments::data_util::FormatPhoneForDisplay(
             base::SysNSStringToUTF8(field.value), countryCode));
@@ -119,8 +118,7 @@
         self.profile
             ? base::SysUTF16ToNSString(
                   payments::data_util::GetFormattedPhoneNumberForDisplay(
-                      *self.profile,
-                      GetApplicationContext()->GetApplicationLocale()))
+                      *self.profile, _paymentRequest->GetApplicationLocale()))
             : nil;
     EditorField* phoneField = [[EditorField alloc]
         initWithAutofillUIType:AutofillUITypeProfileHomePhoneWholeNumber
@@ -163,7 +161,7 @@
                          fieldType:(autofill::ServerFieldType)fieldType {
   return profile ? base::SysUTF16ToNSString(profile->GetInfo(
                        autofill::AutofillType(fieldType),
-                       GetApplicationContext()->GetApplicationLocale()))
+                       _paymentRequest->GetApplicationLocale()))
                  : nil;
 }
 
diff --git a/ios/chrome/browser/ui/payments/contact_info_edit_mediator_unittest.mm b/ios/chrome/browser/ui/payments/contact_info_edit_mediator_unittest.mm
index 8439a02..0dbdd7b 100644
--- a/ios/chrome/browser/ui/payments/contact_info_edit_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/payments/contact_info_edit_mediator_unittest.mm
@@ -11,11 +11,13 @@
 #include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/browser/test_personal_data_manager.h"
 #include "components/strings/grit/components_strings.h"
+#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #include "ios/chrome/browser/payments/payment_request_test_util.h"
 #include "ios/chrome/browser/payments/test_payment_request.h"
 #import "ios/chrome/browser/ui/autofill/autofill_ui_type_util.h"
 #import "ios/chrome/browser/ui/payments/payment_request_edit_consumer.h"
 #import "ios/chrome/browser/ui/payments/payment_request_editor_field.h"
+#import "ios/web/public/test/fakes/test_web_state.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
 #include "third_party/ocmock/OCMock/OCMock.h"
@@ -29,13 +31,18 @@
 class PaymentRequestContactInfoEditMediatorTest : public PlatformTest {
  protected:
   PaymentRequestContactInfoEditMediatorTest()
-      : payment_request_(base::MakeUnique<payments::TestPaymentRequest>(
+      : chrome_browser_state_(TestChromeBrowserState::Builder().Build()),
+        payment_request_(base::MakeUnique<payments::TestPaymentRequest>(
             payment_request_test_util::CreateTestWebPaymentRequest(),
+            chrome_browser_state_.get(),
+            &web_state_,
             &personal_data_manager_)) {}
 
   base::test::ScopedTaskEnvironment scoped_task_evironment_;
 
+  web::TestWebState web_state_;
   autofill::TestPersonalDataManager personal_data_manager_;
+  std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
   std::unique_ptr<payments::TestPaymentRequest> payment_request_;
 };
 
diff --git a/ios/chrome/browser/ui/payments/contact_info_selection_coordinator_unittest.mm b/ios/chrome/browser/ui/payments/contact_info_selection_coordinator_unittest.mm
index 4957c513..ca6627e 100644
--- a/ios/chrome/browser/ui/payments/contact_info_selection_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/payments/contact_info_selection_coordinator_unittest.mm
@@ -11,10 +11,12 @@
 #include "components/autofill/core/browser/autofill_profile.h"
 #include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/browser/test_personal_data_manager.h"
+#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #include "ios/chrome/browser/payments/payment_request.h"
 #include "ios/chrome/browser/payments/payment_request_test_util.h"
 #include "ios/chrome/browser/payments/test_payment_request.h"
 #import "ios/chrome/browser/ui/payments/payment_request_selector_view_controller.h"
+#import "ios/web/public/test/fakes/test_web_state.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
 #include "third_party/ocmock/OCMock/OCMock.h"
@@ -28,20 +30,24 @@
  protected:
   PaymentRequestContactInfoSelectionCoordinatorTest()
       : autofill_profile_1_(autofill::test::GetFullProfile()),
-        autofill_profile_2_(autofill::test::GetFullProfile2()) {
+        autofill_profile_2_(autofill::test::GetFullProfile2()),
+        chrome_browser_state_(TestChromeBrowserState::Builder().Build()) {
     // Add testing profiles to autofill::TestPersonalDataManager.
     personal_data_manager_.AddTestingProfile(&autofill_profile_1_);
     personal_data_manager_.AddTestingProfile(&autofill_profile_2_);
+
     payment_request_ = base::MakeUnique<payments::TestPaymentRequest>(
         payment_request_test_util::CreateTestWebPaymentRequest(),
-        &personal_data_manager_);
+        chrome_browser_state_.get(), &web_state_, &personal_data_manager_);
   }
 
   base::test::ScopedTaskEnvironment scoped_task_evironment_;
 
   autofill::AutofillProfile autofill_profile_1_;
   autofill::AutofillProfile autofill_profile_2_;
+  web::TestWebState web_state_;
   autofill::TestPersonalDataManager personal_data_manager_;
+  std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
   std::unique_ptr<payments::TestPaymentRequest> payment_request_;
 };
 
diff --git a/ios/chrome/browser/ui/payments/contact_info_selection_mediator_unittest.mm b/ios/chrome/browser/ui/payments/contact_info_selection_mediator_unittest.mm
index de1506b..8105cfa 100644
--- a/ios/chrome/browser/ui/payments/contact_info_selection_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/payments/contact_info_selection_mediator_unittest.mm
@@ -10,10 +10,12 @@
 #include "components/autofill/core/browser/autofill_profile.h"
 #include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/browser/test_personal_data_manager.h"
+#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #include "ios/chrome/browser/payments/payment_request_test_util.h"
 #import "ios/chrome/browser/payments/payment_request_util.h"
 #include "ios/chrome/browser/payments/test_payment_request.h"
 #import "ios/chrome/browser/ui/payments/cells/autofill_profile_item.h"
+#import "ios/web/public/test/fakes/test_web_state.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
 #include "third_party/ocmock/gtest_support.h"
@@ -32,13 +34,15 @@
  protected:
   PaymentRequestContactInfoSelectionMediatorTest()
       : autofill_profile_1_(autofill::test::GetFullProfile()),
-        autofill_profile_2_(autofill::test::GetFullProfile2()) {
+        autofill_profile_2_(autofill::test::GetFullProfile2()),
+        chrome_browser_state_(TestChromeBrowserState::Builder().Build()) {
     // Add testing profiles to autofill::TestPersonalDataManager.
     personal_data_manager_.AddTestingProfile(&autofill_profile_1_);
     personal_data_manager_.AddTestingProfile(&autofill_profile_2_);
+
     payment_request_ = base::MakeUnique<payments::TestPaymentRequest>(
         payment_request_test_util::CreateTestWebPaymentRequest(),
-        &personal_data_manager_);
+        chrome_browser_state_.get(), &web_state_, &personal_data_manager_);
     // Override the selected contact profile.
     payment_request_->set_selected_contact_profile(
         payment_request_->contact_profiles()[1]);
@@ -57,7 +61,9 @@
 
   autofill::AutofillProfile autofill_profile_1_;
   autofill::AutofillProfile autofill_profile_2_;
+  web::TestWebState web_state_;
   autofill::TestPersonalDataManager personal_data_manager_;
+  std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
   std::unique_ptr<payments::TestPaymentRequest> payment_request_;
 };
 
diff --git a/ios/chrome/browser/ui/payments/credit_card_edit_coordinator.mm b/ios/chrome/browser/ui/payments/credit_card_edit_coordinator.mm
index a84724d9..8ac85445 100644
--- a/ios/chrome/browser/ui/payments/credit_card_edit_coordinator.mm
+++ b/ios/chrome/browser/ui/payments/credit_card_edit_coordinator.mm
@@ -18,7 +18,6 @@
 #include "components/payments/core/autofill_payment_instrument.h"
 #include "components/payments/core/payment_instrument.h"
 #include "components/strings/grit/components_strings.h"
-#include "ios/chrome/browser/application_context.h"
 #include "ios/chrome/browser/payments/payment_request.h"
 #import "ios/chrome/browser/ui/autofill/autofill_ui_type_util.h"
 #import "ios/chrome/browser/ui/payments/credit_card_edit_mediator.h"
@@ -223,12 +222,12 @@
       creditCard.SetInfo(
           autofill::AutofillType(autofill::CREDIT_CARD_EXP_MONTH),
           base::SysNSStringToUTF16(expMonth),
-          GetApplicationContext()->GetApplicationLocale());
+          _paymentRequest->GetApplicationLocale());
       NSString* expYear = fieldComponents[1];
       creditCard.SetInfo(
           autofill::AutofillType(autofill::CREDIT_CARD_EXP_4_DIGIT_YEAR),
           base::SysNSStringToUTF16(expYear),
-          GetApplicationContext()->GetApplicationLocale());
+          _paymentRequest->GetApplicationLocale());
     } else if (field.autofillUIType == AutofillUITypeCreditCardSaveToChrome) {
       saveCreditCard = [field.value boolValue];
     } else if (field.autofillUIType == AutofillUITypeCreditCardBillingAddress) {
@@ -237,7 +236,7 @@
       creditCard.SetInfo(autofill::AutofillType(AutofillTypeFromAutofillUIType(
                              field.autofillUIType)),
                          base::SysNSStringToUTF16(field.value),
-                         GetApplicationContext()->GetApplicationLocale());
+                         _paymentRequest->GetApplicationLocale());
     }
   }
 
diff --git a/ios/chrome/browser/ui/payments/credit_card_edit_coordinator_unittest.mm b/ios/chrome/browser/ui/payments/credit_card_edit_coordinator_unittest.mm
index 73e990b..cad7274 100644
--- a/ios/chrome/browser/ui/payments/credit_card_edit_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/payments/credit_card_edit_coordinator_unittest.mm
@@ -14,11 +14,13 @@
 #include "components/autofill/core/browser/test_personal_data_manager.h"
 #include "components/payments/core/autofill_payment_instrument.h"
 #include "components/payments/core/payment_instrument.h"
+#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #include "ios/chrome/browser/payments/payment_request_test_util.h"
 #include "ios/chrome/browser/payments/test_payment_request.h"
 #import "ios/chrome/browser/ui/autofill/autofill_ui_type.h"
 #import "ios/chrome/browser/ui/payments/payment_request_editor_field.h"
 #import "ios/chrome/test/scoped_key_window.h"
+#import "ios/web/public/test/fakes/test_web_state.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
@@ -40,8 +42,12 @@
 class MockPaymentRequest : public payments::TestPaymentRequest {
  public:
   MockPaymentRequest(web::PaymentRequest web_payment_request,
+                     ios::ChromeBrowserState* browser_state,
+                     web::WebState* web_state,
                      autofill::PersonalDataManager* personal_data_manager)
       : payments::TestPaymentRequest(web_payment_request,
+                                     browser_state,
+                                     web_state,
                                      personal_data_manager) {}
   MOCK_METHOD1(
       AddAutofillPaymentInstrument,
@@ -109,15 +115,18 @@
 
 class PaymentRequestCreditCardEditCoordinatorTest : public PlatformTest {
  protected:
-  PaymentRequestCreditCardEditCoordinatorTest() {
+  PaymentRequestCreditCardEditCoordinatorTest()
+      : chrome_browser_state_(TestChromeBrowserState::Builder().Build()) {
     payment_request_ = base::MakeUnique<MockPaymentRequest>(
         payment_request_test_util::CreateTestWebPaymentRequest(),
-        &personal_data_manager_);
+        chrome_browser_state_.get(), &web_state_, &personal_data_manager_);
   }
 
   base::test::ScopedTaskEnvironment scoped_task_evironment_;
 
+  web::TestWebState web_state_;
   MockTestPersonalDataManager personal_data_manager_;
+  std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
   std::unique_ptr<MockPaymentRequest> payment_request_;
 };
 
diff --git a/ios/chrome/browser/ui/payments/credit_card_edit_mediator.mm b/ios/chrome/browser/ui/payments/credit_card_edit_mediator.mm
index 6d6c970..cf087c9d 100644
--- a/ios/chrome/browser/ui/payments/credit_card_edit_mediator.mm
+++ b/ios/chrome/browser/ui/payments/credit_card_edit_mediator.mm
@@ -13,7 +13,6 @@
 #include "components/payments/core/payment_request_data_util.h"
 #include "components/payments/core/strings_util.h"
 #include "components/strings/grit/components_strings.h"
-#include "ios/chrome/browser/application_context.h"
 #include "ios/chrome/browser/payments/payment_request.h"
 #import "ios/chrome/browser/payments/payment_request_util.h"
 #import "ios/chrome/browser/ui/autofill/autofill_ui_type.h"
@@ -266,10 +265,9 @@
 
   // Card holder name field.
   NSString* creditCardName =
-      _creditCard
-          ? autofill::GetCreditCardName(
-                *_creditCard, GetApplicationContext()->GetApplicationLocale())
-          : nil;
+      _creditCard ? autofill::GetCreditCardName(
+                        *_creditCard, _paymentRequest->GetApplicationLocale())
+                  : nil;
   fieldKey = [NSNumber numberWithInt:AutofillUITypeCreditCardHolderFullName];
   EditorField* creditCardNameField = self.fieldsMap[fieldKey];
   if (!creditCardNameField) {
diff --git a/ios/chrome/browser/ui/payments/full_card_requester_unittest.mm b/ios/chrome/browser/ui/payments/full_card_requester_unittest.mm
index d84f5e08..118a7fa 100644
--- a/ios/chrome/browser/ui/payments/full_card_requester_unittest.mm
+++ b/ios/chrome/browser/ui/payments/full_card_requester_unittest.mm
@@ -78,10 +78,8 @@
 class PaymentRequestFullCardRequesterTest : public ChromeWebTest {
  protected:
   PaymentRequestFullCardRequesterTest()
-      : credit_card_(autofill::test::GetCreditCard()) {
-    TestChromeBrowserState::Builder test_cbs_builder;
-    chrome_browser_state_ = test_cbs_builder.Build();
-  }
+      : credit_card_(autofill::test::GetCreditCard()),
+        chrome_browser_state_(TestChromeBrowserState::Builder().Build()) {}
 
   void SetUp() override {
     ChromeWebTest::SetUp();
diff --git a/ios/chrome/browser/ui/payments/payment_items_display_coordinator_unittest.mm b/ios/chrome/browser/ui/payments/payment_items_display_coordinator_unittest.mm
index ea41a4f4..5f6ebbe2 100644
--- a/ios/chrome/browser/ui/payments/payment_items_display_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/payments/payment_items_display_coordinator_unittest.mm
@@ -11,11 +11,13 @@
 #include "components/autofill/core/browser/autofill_profile.h"
 #include "components/autofill/core/browser/credit_card.h"
 #include "components/autofill/core/browser/test_personal_data_manager.h"
+#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #include "ios/chrome/browser/payments/payment_request.h"
 #include "ios/chrome/browser/payments/payment_request_test_util.h"
 #include "ios/chrome/browser/payments/test_payment_request.h"
 #import "ios/chrome/browser/ui/payments/payment_items_display_view_controller.h"
 #include "ios/web/public/payments/payment_request.h"
+#import "ios/web/public/test/fakes/test_web_state.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
 #include "third_party/ocmock/OCMock/OCMock.h"
@@ -27,15 +29,18 @@
 
 class PaymentRequestPaymentItemsDisplayCoordinatorTest : public PlatformTest {
  protected:
-  PaymentRequestPaymentItemsDisplayCoordinatorTest() {
+  PaymentRequestPaymentItemsDisplayCoordinatorTest()
+      : chrome_browser_state_(TestChromeBrowserState::Builder().Build()) {
     payment_request_ = base::MakeUnique<payments::TestPaymentRequest>(
         payment_request_test_util::CreateTestWebPaymentRequest(),
-        &personal_data_manager_);
+        chrome_browser_state_.get(), &web_state_, &personal_data_manager_);
   }
 
   base::test::ScopedTaskEnvironment scoped_task_evironment_;
 
+  web::TestWebState web_state_;
   autofill::TestPersonalDataManager personal_data_manager_;
+  std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
   std::unique_ptr<payments::TestPaymentRequest> payment_request_;
 };
 
diff --git a/ios/chrome/browser/ui/payments/payment_items_display_view_controller_unittest.mm b/ios/chrome/browser/ui/payments/payment_items_display_view_controller_unittest.mm
index 5d3a679..ef7d488 100644
--- a/ios/chrome/browser/ui/payments/payment_items_display_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/payments/payment_items_display_view_controller_unittest.mm
@@ -9,6 +9,7 @@
 #include "base/test/scoped_task_environment.h"
 #include "components/autofill/core/browser/test_personal_data_manager.h"
 #include "components/strings/grit/components_strings.h"
+#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #include "ios/chrome/browser/payments/payment_request.h"
 #import "ios/chrome/browser/payments/payment_request_test_util.h"
 #include "ios/chrome/browser/payments/test_payment_request.h"
@@ -17,6 +18,7 @@
 #import "ios/chrome/browser/ui/payments/payment_items_display_view_controller_data_source.h"
 #include "ios/chrome/grit/ios_strings.h"
 #include "ios/web/public/payments/payment_request.h"
+#import "ios/web/public/test/fakes/test_web_state.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -45,10 +47,13 @@
 class PaymentRequestPaymentItemsDisplayViewControllerTest
     : public CollectionViewControllerTest {
  protected:
+  PaymentRequestPaymentItemsDisplayViewControllerTest()
+      : chrome_browser_state_(TestChromeBrowserState::Builder().Build()) {}
+
   CollectionViewController* InstantiateController() override {
     payment_request_ = base::MakeUnique<payments::TestPaymentRequest>(
         payment_request_test_util::CreateTestWebPaymentRequest(),
-        &personal_data_manager_);
+        chrome_browser_state_.get(), &web_state_, &personal_data_manager_);
     mediator_ = [[TestPaymentItemsDisplayMediator alloc] init];
     PaymentItemsDisplayViewController* viewController = [
         [PaymentItemsDisplayViewController alloc] initWithPayButtonEnabled:YES];
@@ -63,7 +68,9 @@
 
   base::test::ScopedTaskEnvironment scoped_task_evironment_;
 
+  web::TestWebState web_state_;
   autofill::TestPersonalDataManager personal_data_manager_;
+  std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
   std::unique_ptr<payments::TestPaymentRequest> payment_request_;
   TestPaymentItemsDisplayMediator* mediator_ = nil;
 };
diff --git a/ios/chrome/browser/ui/payments/payment_method_selection_coordinator_unittest.mm b/ios/chrome/browser/ui/payments/payment_method_selection_coordinator_unittest.mm
index 616e75ca..d3c6296 100644
--- a/ios/chrome/browser/ui/payments/payment_method_selection_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/payments/payment_method_selection_coordinator_unittest.mm
@@ -13,11 +13,13 @@
 #include "components/autofill/core/browser/credit_card.h"
 #include "components/autofill/core/browser/test_personal_data_manager.h"
 #include "components/payments/core/payment_instrument.h"
+#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #include "ios/chrome/browser/payments/payment_request.h"
 #include "ios/chrome/browser/payments/payment_request_test_util.h"
 #include "ios/chrome/browser/payments/test_payment_request.h"
 #import "ios/chrome/browser/ui/payments/payment_request_selector_view_controller.h"
 #include "ios/web/public/payments/payment_request.h"
+#import "ios/web/public/test/fakes/test_web_state.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
 #include "third_party/ocmock/OCMock/OCMock.h"
@@ -33,7 +35,8 @@
   PaymentRequestPaymentMethodSelectionCoordinatorTest()
       : autofill_profile_(autofill::test::GetFullProfile()),
         credit_card1_(autofill::test::GetCreditCard()),
-        credit_card2_(autofill::test::GetCreditCard2()) {
+        credit_card2_(autofill::test::GetCreditCard2()),
+        chrome_browser_state_(TestChromeBrowserState::Builder().Build()) {
     // Add testing credit cards to autofill::TestPersonalDataManager. Make the
     // less frequently used one incomplete.
     credit_card1_.set_use_count(10U);
@@ -44,7 +47,7 @@
     personal_data_manager_.AddTestingCreditCard(&credit_card2_);
     payment_request_ = base::MakeUnique<payments::TestPaymentRequest>(
         payment_request_test_util::CreateTestWebPaymentRequest(),
-        &personal_data_manager_);
+        chrome_browser_state_.get(), &web_state_, &personal_data_manager_);
   }
 
   base::test::ScopedTaskEnvironment scoped_task_evironment_;
@@ -52,7 +55,9 @@
   autofill::AutofillProfile autofill_profile_;
   autofill::CreditCard credit_card1_;
   autofill::CreditCard credit_card2_;
+  web::TestWebState web_state_;
   autofill::TestPersonalDataManager personal_data_manager_;
+  std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
   std::unique_ptr<payments::TestPaymentRequest> payment_request_;
 };
 
diff --git a/ios/chrome/browser/ui/payments/payment_request_coordinator_unittest.mm b/ios/chrome/browser/ui/payments/payment_request_coordinator_unittest.mm
index f307efdb..266bb6b 100644
--- a/ios/chrome/browser/ui/payments/payment_request_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/payments/payment_request_coordinator_unittest.mm
@@ -31,6 +31,7 @@
 #import "ios/chrome/test/scoped_key_window.h"
 #import "ios/testing/ocmock_complex_type_helper.h"
 #include "ios/web/public/payments/payment_request.h"
+#import "ios/web/public/test/fakes/test_web_state.h"
 #import "ios/web/public/test/test_web_thread_bundle.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
@@ -100,11 +101,11 @@
     TestChromeBrowserState::Builder test_cbs_builder;
     test_cbs_builder.AddTestingFactory(ios::SigninManagerFactory::GetInstance(),
                                        &ios::BuildFakeSigninManager);
-    browser_state_ = test_cbs_builder.Build();
+    chrome_browser_state_ = test_cbs_builder.Build();
 
     payment_request_ = base::MakeUnique<payments::TestPaymentRequest>(
         payment_request_test_util::CreateTestWebPaymentRequest(),
-        browser_state_.get(), &personal_data_manager_);
+        chrome_browser_state_.get(), &web_state_, &personal_data_manager_);
     payment_request_->SetPrefService(pref_service_.get());
   }
 
@@ -112,10 +113,11 @@
 
   autofill::AutofillProfile autofill_profile_;
   autofill::CreditCard credit_card_;
+  web::TestWebState web_state_;
   std::unique_ptr<PrefService> pref_service_;
   autofill::TestPersonalDataManager personal_data_manager_;
+  std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
   std::unique_ptr<payments::TestPaymentRequest> payment_request_;
-  std::unique_ptr<ios::ChromeBrowserState> browser_state_;
 };
 
 // Tests that invoking start and stop on the coordinator presents and
@@ -129,7 +131,7 @@
   PaymentRequestCoordinator* coordinator = [[PaymentRequestCoordinator alloc]
       initWithBaseViewController:base_view_controller];
   [coordinator setPaymentRequest:payment_request_.get()];
-  [coordinator setBrowserState:browser_state_.get()];
+  [coordinator setBrowserState:chrome_browser_state_.get()];
 
   [coordinator start];
   // Spin the run loop to trigger the animation.
@@ -296,7 +298,7 @@
          EXPECT_EQ(coordinator, callerCoordinator);
        }];
   [coordinator setDelegate:delegate_mock];
-  [coordinator setBrowserState:browser_state_.get()];
+  [coordinator setBrowserState:chrome_browser_state_.get()];
 
   [coordinator start];
   // Spin the run loop to trigger the animation.
diff --git a/ios/chrome/browser/ui/payments/payment_request_manager.mm b/ios/chrome/browser/ui/payments/payment_request_manager.mm
index c4c252b..fb191ef 100644
--- a/ios/chrome/browser/ui/payments/payment_request_manager.mm
+++ b/ios/chrome/browser/ui/payments/payment_request_manager.mm
@@ -29,7 +29,6 @@
 #include "components/payments/core/payment_request_base_delegate.h"
 #include "components/payments/core/payment_request_data_util.h"
 #include "components/prefs/pref_service.h"
-#include "ios/chrome/browser/application_context.h"
 #include "ios/chrome/browser/autofill/personal_data_manager_factory.h"
 #include "ios/chrome/browser/autofill/validation_rules_storage_factory.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
@@ -380,7 +379,7 @@
 
   const std::string default_country_code =
       autofill::AutofillCountry::CountryCodeForLocale(
-          GetApplicationContext()->GetApplicationLocale());
+          _paymentRequest->GetApplicationLocale());
 
   _addressNormalizationManager =
       base::MakeUnique<payments::AddressNormalizationManager>(
@@ -410,7 +409,7 @@
   }
 
   _paymentRequest = base::MakeUnique<payments::PaymentRequest>(
-      webPaymentRequest, _browserState, _personalDataManager, self);
+      webPaymentRequest, _browserState, _webState, _personalDataManager, self);
 
   return YES;
 }
@@ -734,7 +733,7 @@
     paymentResponse.shipping_address =
         payments::data_util::GetPaymentAddressFromAutofillProfile(
             _pendingPaymentResponse.shippingAddress,
-            GetApplicationContext()->GetApplicationLocale());
+            _paymentRequest->GetApplicationLocale());
 
     web::PaymentShippingOption* shippingOption =
         _paymentRequest->selected_shipping_option();
@@ -745,7 +744,7 @@
   if (_paymentRequest->request_payer_name()) {
     paymentResponse.payer_name = _pendingPaymentResponse.contactAddress.GetInfo(
         autofill::AutofillType(autofill::NAME_FULL),
-        GetApplicationContext()->GetApplicationLocale());
+        _paymentRequest->GetApplicationLocale());
   }
 
   if (_paymentRequest->request_payer_email()) {
@@ -777,7 +776,7 @@
              (const autofill::AutofillProfile&)shippingAddress {
   payments::PaymentAddress address =
       payments::data_util::GetPaymentAddressFromAutofillProfile(
-          shippingAddress, GetApplicationContext()->GetApplicationLocale());
+          shippingAddress, _paymentRequest->GetApplicationLocale());
   [_paymentRequestJsManager updateShippingAddress:address
                                 completionHandler:nil];
   [self setUnblockEventQueueTimer];
diff --git a/ios/chrome/browser/ui/payments/payment_request_mediator_unittest.mm b/ios/chrome/browser/ui/payments/payment_request_mediator_unittest.mm
index 1533918..4a5bbdb 100644
--- a/ios/chrome/browser/ui/payments/payment_request_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/payments/payment_request_mediator_unittest.mm
@@ -34,6 +34,7 @@
 #import "ios/chrome/browser/ui/payments/cells/payments_text_item.h"
 #import "ios/chrome/browser/ui/payments/cells/price_item.h"
 #include "ios/web/public/payments/payment_request.h"
+#import "ios/web/public/test/fakes/test_web_state.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -68,7 +69,7 @@
 
     payment_request_ = base::MakeUnique<payments::TestPaymentRequest>(
         payment_request_test_util::CreateTestWebPaymentRequest(),
-        chrome_browser_state_.get(), &personal_data_manager_);
+        chrome_browser_state_.get(), &web_state_, &personal_data_manager_);
     payment_request_->SetPrefService(pref_service_.get());
 
     mediator_ = [[PaymentRequestMediator alloc]
@@ -81,10 +82,11 @@
 
   autofill::AutofillProfile autofill_profile_;
   autofill::CreditCard credit_card_;
+  web::TestWebState web_state_;
   std::unique_ptr<PrefService> pref_service_;
   autofill::TestPersonalDataManager personal_data_manager_;
-  std::unique_ptr<payments::TestPaymentRequest> payment_request_;
   std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
+  std::unique_ptr<payments::TestPaymentRequest> payment_request_;
   PaymentRequestMediator* mediator_;
 };
 
diff --git a/ios/chrome/browser/ui/payments/payment_request_view_controller_unittest.mm b/ios/chrome/browser/ui/payments/payment_request_view_controller_unittest.mm
index 42f7cbf5..b944235 100644
--- a/ios/chrome/browser/ui/payments/payment_request_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/payments/payment_request_view_controller_unittest.mm
@@ -15,6 +15,7 @@
 #include "components/autofill/core/browser/credit_card.h"
 #include "components/autofill/core/browser/test_personal_data_manager.h"
 #include "components/strings/grit/components_strings.h"
+#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #include "ios/chrome/browser/payments/payment_request_test_util.h"
 #include "ios/chrome/browser/payments/test_payment_request.h"
 #import "ios/chrome/browser/ui/autofill/cells/status_item.h"
@@ -29,6 +30,7 @@
 #import "ios/chrome/browser/ui/payments/payment_request_view_controller_data_source.h"
 #include "ios/chrome/grit/ios_strings.h"
 #include "ios/web/public/payments/payment_request.h"
+#import "ios/web/public/test/fakes/test_web_state.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/l10n/l10n_util.h"
 
@@ -141,14 +143,15 @@
  protected:
   PaymentRequestViewControllerTest()
       : autofill_profile_(autofill::test::GetFullProfile()),
-        credit_card_(autofill::test::GetCreditCard()) {
+        credit_card_(autofill::test::GetCreditCard()),
+        chrome_browser_state_(TestChromeBrowserState::Builder().Build()) {
     // Add testing profile and credit card to autofill::TestPersonalDataManager.
     personal_data_manager_.AddTestingProfile(&autofill_profile_);
     personal_data_manager_.AddTestingCreditCard(&credit_card_);
 
     payment_request_ = base::MakeUnique<payments::TestPaymentRequest>(
         payment_request_test_util::CreateTestWebPaymentRequest(),
-        &personal_data_manager_);
+        chrome_browser_state_.get(), &web_state_, &personal_data_manager_);
 
     mediator_ = [[TestPaymentRequestMediator alloc] init];
   }
@@ -169,7 +172,9 @@
 
   autofill::AutofillProfile autofill_profile_;
   autofill::CreditCard credit_card_;
+  web::TestWebState web_state_;
   autofill::TestPersonalDataManager personal_data_manager_;
+  std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
   std::unique_ptr<payments::TestPaymentRequest> payment_request_;
   TestPaymentRequestMediator* mediator_;
 };
diff --git a/ios/chrome/browser/ui/payments/shipping_address_selection_coordinator_unittest.mm b/ios/chrome/browser/ui/payments/shipping_address_selection_coordinator_unittest.mm
index 4eb6957..7c94a2e 100644
--- a/ios/chrome/browser/ui/payments/shipping_address_selection_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/payments/shipping_address_selection_coordinator_unittest.mm
@@ -15,9 +15,11 @@
 #include "components/autofill/core/browser/test_personal_data_manager.h"
 #include "components/autofill/core/browser/test_region_data_loader.h"
 #include "components/prefs/pref_service.h"
+#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #include "ios/chrome/browser/payments/payment_request_test_util.h"
 #include "ios/chrome/browser/payments/test_payment_request.h"
 #import "ios/chrome/browser/ui/payments/payment_request_selector_view_controller.h"
+#import "ios/web/public/test/fakes/test_web_state.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
 #include "third_party/ocmock/OCMock/OCMock.h"
@@ -33,7 +35,8 @@
   PaymentRequestShippingAddressSelectionCoordinatorTest()
       : autofill_profile1_(autofill::test::GetFullProfile()),
         autofill_profile2_(autofill::test::GetFullProfile2()),
-        pref_service_(autofill::test::PrefServiceForTesting()) {
+        pref_service_(autofill::test::PrefServiceForTesting()),
+        chrome_browser_state_(TestChromeBrowserState::Builder().Build()) {
     personal_data_manager_.SetTestingPrefService(pref_service_.get());
     // Add testing profiles to autofill::TestPersonalDataManager. Make the less
     // frequently used one incomplete.
@@ -44,9 +47,10 @@
         autofill::AutofillType(autofill::PHONE_HOME_WHOLE_NUMBER),
         base::string16(), "en-US");
     personal_data_manager_.AddTestingProfile(&autofill_profile2_);
+
     payment_request_ = base::MakeUnique<payments::TestPaymentRequest>(
         payment_request_test_util::CreateTestWebPaymentRequest(),
-        &personal_data_manager_);
+        chrome_browser_state_.get(), &web_state_, &personal_data_manager_);
 
     test_region_data_loader_.set_synchronous_callback(true);
     payment_request_->SetRegionDataLoader(&test_region_data_loader_);
@@ -60,9 +64,11 @@
 
   autofill::AutofillProfile autofill_profile1_;
   autofill::AutofillProfile autofill_profile2_;
+  web::TestWebState web_state_;
   std::unique_ptr<PrefService> pref_service_;
   autofill::TestPersonalDataManager personal_data_manager_;
   autofill::TestRegionDataLoader test_region_data_loader_;
+  std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
   std::unique_ptr<payments::TestPaymentRequest> payment_request_;
 };
 
diff --git a/ios/chrome/browser/ui/payments/shipping_option_selection_coordinator_unittest.mm b/ios/chrome/browser/ui/payments/shipping_option_selection_coordinator_unittest.mm
index f11631f..e65ce1b5 100644
--- a/ios/chrome/browser/ui/payments/shipping_option_selection_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/payments/shipping_option_selection_coordinator_unittest.mm
@@ -11,11 +11,13 @@
 #include "components/autofill/core/browser/autofill_profile.h"
 #include "components/autofill/core/browser/credit_card.h"
 #include "components/autofill/core/browser/test_personal_data_manager.h"
+#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #include "ios/chrome/browser/payments/payment_request.h"
 #include "ios/chrome/browser/payments/payment_request_test_util.h"
 #include "ios/chrome/browser/payments/test_payment_request.h"
 #import "ios/chrome/browser/ui/payments/payment_request_selector_view_controller.h"
 #include "ios/web/public/payments/payment_request.h"
+#import "ios/web/public/test/fakes/test_web_state.h"
 #include "ios/web/public/test/test_web_thread_bundle.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
@@ -29,15 +31,18 @@
 class PaymentRequestShippingOptionSelectionCoordinatorTest
     : public PlatformTest {
  protected:
-  PaymentRequestShippingOptionSelectionCoordinatorTest() {
+  PaymentRequestShippingOptionSelectionCoordinatorTest()
+      : chrome_browser_state_(TestChromeBrowserState::Builder().Build()) {
     payment_request_ = base::MakeUnique<payments::TestPaymentRequest>(
         payment_request_test_util::CreateTestWebPaymentRequest(),
-        &personal_data_manager_);
+        chrome_browser_state_.get(), &web_state_, &personal_data_manager_);
   }
 
   base::test::ScopedTaskEnvironment scoped_task_evironment_;
 
+  web::TestWebState web_state_;
   autofill::TestPersonalDataManager personal_data_manager_;
+  std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
   std::unique_ptr<payments::TestPaymentRequest> payment_request_;
 };
 
diff --git a/ios/chrome/browser/ui/popup_menu/popup_menu_controller.h b/ios/chrome/browser/ui/popup_menu/popup_menu_controller.h
index a3fdafb..31ab294f 100644
--- a/ios/chrome/browser/ui/popup_menu/popup_menu_controller.h
+++ b/ios/chrome/browser/ui/popup_menu/popup_menu_controller.h
@@ -7,6 +7,7 @@
 
 #import <UIKit/UIKit.h>
 
+@protocol BrowserCommands;
 @class PopupMenuController;
 @class PopupMenuView;
 
@@ -32,6 +33,8 @@
 @property(nonatomic, readonly, strong) UIButton* backgroundButton;
 // Delegate for the popup menu.
 @property(nonatomic, weak) id<PopupMenuDelegate> delegate;
+// Dispatcher for browser commands.
+@property(nonatomic, weak) id<BrowserCommands> dispatcher;
 
 // Initializes the PopupMenuController and adds its views inside of parent.
 - (id)initWithParentView:(UIView*)parent;
diff --git a/ios/chrome/browser/ui/popup_menu/popup_menu_controller.mm b/ios/chrome/browser/ui/popup_menu/popup_menu_controller.mm
index 2ae1512..c4f80a6 100644
--- a/ios/chrome/browser/ui/popup_menu/popup_menu_controller.mm
+++ b/ios/chrome/browser/ui/popup_menu/popup_menu_controller.mm
@@ -67,6 +67,7 @@
 @synthesize backgroundButton = backgroundButton_;
 @synthesize popupContainer = popupContainer_;
 @synthesize delegate = delegate_;
+@synthesize dispatcher = dispatcher_;
 
 - (id)initWithParentView:(UIView*)parent {
   return [self initWithParentView:parent
diff --git a/ios/chrome/browser/ui/qr_scanner/README.md b/ios/chrome/browser/ui/qr_scanner/README.md
index 91c3f2c..d3f7b13 100644
--- a/ios/chrome/browser/ui/qr_scanner/README.md
+++ b/ios/chrome/browser/ui/qr_scanner/README.md
@@ -258,6 +258,8 @@
 
 The following metrics are collected:
 
+*   `IOS.Spotlight.Action` when the user opens the QR scanner from searching for
+    it in Spotlight.
 *   `ApplicationShortcut.ScanQRCodePressed` when the user opens the QR scanner
     from 3D Touch application shortcuts.
 *   `MobileQRScannerClose` when the user closes the QR scanner without scanning
diff --git a/ios/chrome/browser/ui/reader_mode/reader_mode_checker.mm b/ios/chrome/browser/ui/reader_mode/reader_mode_checker.mm
index 8c207d6..7c66279 100644
--- a/ios/chrome/browser/ui/reader_mode/reader_mode_checker.mm
+++ b/ios/chrome/browser/ui/reader_mode/reader_mode_checker.mm
@@ -148,6 +148,7 @@
       CheckIsDistillableOG(receiver);
       break;
     case dom_distiller::DistillerHeuristicsType::ADABOOST_MODEL:
+    case dom_distiller::DistillerHeuristicsType::ALL_ARTICLES:
       CheckIsDistillableDetector(receiver);
       break;
     case dom_distiller::DistillerHeuristicsType::ALWAYS_TRUE:
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_egtest.mm b/ios/chrome/browser/ui/reading_list/reading_list_egtest.mm
index 97b6ec12..898ffc46 100644
--- a/ios/chrome/browser/ui/reading_list/reading_list_egtest.mm
+++ b/ios/chrome/browser/ui/reading_list/reading_list_egtest.mm
@@ -345,10 +345,7 @@
         waitForWebViewContainingText:base::SysNSStringToUTF8(contentToKeep)];
     [ChromeEarlGrey waitForStaticHTMLViewNotContainingText:contentToKeep];
   } else {
-    // TODO(crbug.com/714157): Remove matcher that asserts grey_nil().
-    [[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewContainingText(
-                                            kContentToKeep)]
-        assertWithMatcher:grey_nil()];
+    [ChromeEarlGrey waitForWebViewNotContainingText:kContentToKeep];
     [ChromeEarlGrey waitForStaticHTMLViewContainingText:contentToKeep];
   }
 
diff --git a/ios/chrome/browser/ui/toolbar/toolbar_controller.mm b/ios/chrome/browser/ui/toolbar/toolbar_controller.mm
index d3cdf8d1..9b526c8 100644
--- a/ios/chrome/browser/ui/toolbar/toolbar_controller.mm
+++ b/ios/chrome/browser/ui/toolbar/toolbar_controller.mm
@@ -594,7 +594,8 @@
 
   [configuration setToolsMenuButton:toolsMenuButton_];
   toolsPopupController_ =
-      [[ToolsPopupController alloc] initWithConfiguration:configuration];
+      [[ToolsPopupController alloc] initWithConfiguration:configuration
+                                               dispatcher:self.dispatcher];
 
   [toolsPopupController_ setDelegate:self];
 
diff --git a/ios/chrome/browser/ui/toolbar/web_toolbar_controller.mm b/ios/chrome/browser/ui/toolbar/web_toolbar_controller.mm
index d29d4d4..a067d08 100644
--- a/ios/chrome/browser/ui/toolbar/web_toolbar_controller.mm
+++ b/ios/chrome/browser/ui/toolbar/web_toolbar_controller.mm
@@ -508,8 +508,6 @@
 
     // Assign tags before calling -setUpButton, since only buttons with tags
     // have -chromeExecuteCommand added as a target.
-    [_reloadButton setTag:IDC_RELOAD];
-    [_stopButton setTag:IDC_STOP];
     [_starButton setTag:IDC_BOOKMARK_PAGE];
     [_voiceSearchButton setTag:IDC_VOICE_SEARCH];
 
@@ -538,6 +536,14 @@
         hasDisabledImage:YES
            synchronously:NO];
     [_stopButton setHidden:YES];
+
+    // Assign targets for buttons using the dispatcher.
+    [_stopButton addTarget:self.dispatcher
+                    action:@selector(stopLoading)
+          forControlEvents:UIControlEventTouchUpInside];
+    [_reloadButton addTarget:self.dispatcher
+                      action:@selector(reload)
+            forControlEvents:UIControlEventTouchUpInside];
   } else {
     [_forwardButton setAlpha:0.0];
   }
diff --git a/ios/chrome/browser/ui/tools_menu/request_desktop_mobile_site_egtest.mm b/ios/chrome/browser/ui/tools_menu/request_desktop_mobile_site_egtest.mm
index cf2c07b..2c6b5a8 100644
--- a/ios/chrome/browser/ui/tools_menu/request_desktop_mobile_site_egtest.mm
+++ b/ios/chrome/browser/ui/tools_menu/request_desktop_mobile_site_egtest.mm
@@ -27,8 +27,6 @@
 #error "This file requires ARC support."
 #endif
 
-using chrome_test_util::WebViewContainingText;
-
 namespace {
 
 const char kUserAgentTestURL[] =
@@ -95,20 +93,17 @@
 
   [ChromeEarlGrey loadURL:web::test::HttpServer::MakeUrl("http://1.com")];
   // Verify initial reception of the mobile site.
-  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kMobileSiteLabel)]
-      assertWithMatcher:grey_notNil()];
+  [ChromeEarlGrey waitForWebViewContainingText:kMobileSiteLabel];
 
   // Request and verify reception of the desktop site.
   [ChromeEarlGreyUI openToolsMenu];
   [[EarlGrey selectElementWithMatcher:RequestDesktopButton()]
       performAction:grey_tap()];
-  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kDesktopSiteLabel)]
-      assertWithMatcher:grey_notNil()];
+  [ChromeEarlGrey waitForWebViewContainingText:kDesktopSiteLabel];
 
   // Verify that desktop user agent propagates.
   [ChromeEarlGrey loadURL:web::test::HttpServer::MakeUrl("http://2.com")];
-  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kDesktopSiteLabel)]
-      assertWithMatcher:grey_notNil()];
+  [ChromeEarlGrey waitForWebViewContainingText:kDesktopSiteLabel];
 }
 
 // Tests that requesting desktop site of a page works and desktop user agent
@@ -120,21 +115,18 @@
 
   [ChromeEarlGrey loadURL:web::test::HttpServer::MakeUrl("http://1.com")];
   // Verify initial reception of the mobile site.
-  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kMobileSiteLabel)]
-      assertWithMatcher:grey_notNil()];
+  [ChromeEarlGrey waitForWebViewContainingText:kMobileSiteLabel];
 
   // Request and verify reception of the desktop site.
   [ChromeEarlGreyUI openToolsMenu];
   [[EarlGrey selectElementWithMatcher:RequestDesktopButton()]
       performAction:grey_tap()];
-  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kDesktopSiteLabel)]
-      assertWithMatcher:grey_notNil()];
+  [ChromeEarlGrey waitForWebViewContainingText:kDesktopSiteLabel];
 
   // Verify that desktop user agent does not propagate to new tab.
   [ChromeEarlGreyUI openNewTab];
   [ChromeEarlGrey loadURL:web::test::HttpServer::MakeUrl("http://2.com")];
-  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kMobileSiteLabel)]
-      assertWithMatcher:grey_notNil()];
+  [ChromeEarlGrey waitForWebViewContainingText:kMobileSiteLabel];
 }
 
 // Tests that requesting desktop site of a page works and going back re-opens
@@ -146,21 +138,18 @@
 
   [ChromeEarlGrey loadURL:web::test::HttpServer::MakeUrl("http://1.com")];
   // Verify initial reception of the mobile site.
-  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kMobileSiteLabel)]
-      assertWithMatcher:grey_notNil()];
+  [ChromeEarlGrey waitForWebViewContainingText:kMobileSiteLabel];
 
   // Request and verify reception of the desktop site.
   [ChromeEarlGreyUI openToolsMenu];
   [[EarlGrey selectElementWithMatcher:RequestDesktopButton()]
       performAction:grey_tap()];
-  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kDesktopSiteLabel)]
-      assertWithMatcher:grey_notNil()];
+  [ChromeEarlGrey waitForWebViewContainingText:kDesktopSiteLabel];
 
   // Verify that going back returns to the mobile site.
   [[EarlGrey selectElementWithMatcher:chrome_test_util::BackButton()]
       performAction:grey_tap()];
-  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kMobileSiteLabel)]
-      assertWithMatcher:grey_notNil()];
+  [ChromeEarlGrey waitForWebViewContainingText:kMobileSiteLabel];
 }
 
 // Tests that requesting mobile site of a page works and the user agent
@@ -176,27 +165,23 @@
 
   [ChromeEarlGrey loadURL:web::test::HttpServer::MakeUrl("http://1.com")];
   // Verify initial reception of the mobile site.
-  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kMobileSiteLabel)]
-      assertWithMatcher:grey_notNil()];
+  [ChromeEarlGrey waitForWebViewContainingText:kMobileSiteLabel];
 
   // Request and verify reception of the desktop site.
   [ChromeEarlGreyUI openToolsMenu];
   [[EarlGrey selectElementWithMatcher:RequestDesktopButton()]
       performAction:grey_tap()];
-  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kDesktopSiteLabel)]
-      assertWithMatcher:grey_notNil()];
+  [ChromeEarlGrey waitForWebViewContainingText:kDesktopSiteLabel];
 
   // Request and verify reception of the mobile site.
   [ChromeEarlGreyUI openToolsMenu];
   [[EarlGrey selectElementWithMatcher:RequestMobileButton()]
       performAction:grey_tap()];
-  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kMobileSiteLabel)]
-      assertWithMatcher:grey_notNil()];
+  [ChromeEarlGrey waitForWebViewContainingText:kMobileSiteLabel];
 
   // Verify that mobile user agent propagates.
   [ChromeEarlGrey loadURL:web::test::HttpServer::MakeUrl("http://2.com")];
-  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kMobileSiteLabel)]
-      assertWithMatcher:grey_notNil()];
+  [ChromeEarlGrey waitForWebViewContainingText:kMobileSiteLabel];
 }
 
 // Tests that requesting mobile site of a page works and going back re-opens
@@ -212,28 +197,24 @@
 
   [ChromeEarlGrey loadURL:web::test::HttpServer::MakeUrl("http://1.com")];
   // Verify initial reception of the mobile site.
-  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kMobileSiteLabel)]
-      assertWithMatcher:grey_notNil()];
+  [ChromeEarlGrey waitForWebViewContainingText:kMobileSiteLabel];
 
   // Request and verify reception of the desktop site.
   [ChromeEarlGreyUI openToolsMenu];
   [[EarlGrey selectElementWithMatcher:RequestDesktopButton()]
       performAction:grey_tap()];
-  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kDesktopSiteLabel)]
-      assertWithMatcher:grey_notNil()];
+  [ChromeEarlGrey waitForWebViewContainingText:kDesktopSiteLabel];
 
   // Request and verify reception of the mobile site.
   [ChromeEarlGreyUI openToolsMenu];
   [[EarlGrey selectElementWithMatcher:RequestMobileButton()]
       performAction:grey_tap()];
-  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kMobileSiteLabel)]
-      assertWithMatcher:grey_notNil()];
+  [ChromeEarlGrey waitForWebViewContainingText:kMobileSiteLabel];
 
   // Verify that going back returns to the desktop site.
   [[EarlGrey selectElementWithMatcher:chrome_test_util::BackButton()]
       performAction:grey_tap()];
-  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kDesktopSiteLabel)]
-      assertWithMatcher:grey_notNil()];
+  [ChromeEarlGrey waitForWebViewContainingText:kDesktopSiteLabel];
 }
 
 // Tests that requesting desktop site button is not enabled on new tab pages.
@@ -264,15 +245,13 @@
   web::test::SetUpFileBasedHttpServer();
   [ChromeEarlGrey loadURL:web::test::HttpServer::MakeUrl(kUserAgentTestURL)];
   // Verify initial reception of the mobile site.
-  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kMobileSiteLabel)]
-      assertWithMatcher:grey_notNil()];
+  [ChromeEarlGrey waitForWebViewContainingText:kMobileSiteLabel];
 
   // Request and verify reception of the desktop site.
   [ChromeEarlGreyUI openToolsMenu];
   [[EarlGrey selectElementWithMatcher:RequestDesktopButton()]
       performAction:grey_tap()];
-  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kDesktopSiteLabel)]
-      assertWithMatcher:grey_notNil()];
+  [ChromeEarlGrey waitForWebViewContainingText:kDesktopSiteLabel];
 }
 
 // Tests that navigator.appVersion JavaScript API returns correct string for
@@ -285,22 +264,19 @@
   web::test::SetUpFileBasedHttpServer();
   [ChromeEarlGrey loadURL:web::test::HttpServer::MakeUrl(kUserAgentTestURL)];
   // Verify initial reception of the mobile site.
-  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kMobileSiteLabel)]
-      assertWithMatcher:grey_notNil()];
+  [ChromeEarlGrey waitForWebViewContainingText:kMobileSiteLabel];
 
   // Request and verify reception of the desktop site.
   [ChromeEarlGreyUI openToolsMenu];
   [[EarlGrey selectElementWithMatcher:RequestDesktopButton()]
       performAction:grey_tap()];
-  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kDesktopSiteLabel)]
-      assertWithMatcher:grey_notNil()];
+  [ChromeEarlGrey waitForWebViewContainingText:kDesktopSiteLabel];
 
   // Request and verify reception of the mobile site.
   [ChromeEarlGreyUI openToolsMenu];
   [[EarlGrey selectElementWithMatcher:RequestMobileButton()]
       performAction:grey_tap()];
-  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kMobileSiteLabel)]
-      assertWithMatcher:grey_notNil()];
+  [ChromeEarlGrey waitForWebViewContainingText:kMobileSiteLabel];
 }
 
 @end
diff --git a/ios/chrome/browser/ui/tools_menu/tools_menu_constants.h b/ios/chrome/browser/ui/tools_menu/tools_menu_constants.h
index 318b8bf..b7904b6 100644
--- a/ios/chrome/browser/ui/tools_menu/tools_menu_constants.h
+++ b/ios/chrome/browser/ui/tools_menu/tools_menu_constants.h
@@ -38,4 +38,11 @@
 // Request mobile item accessibility Identifier.
 extern NSString* const kToolsMenuRequestMobileId;
 
+// Identifiers for tools menu items (for metrics purposes).
+typedef NS_ENUM(int, ToolsMenuItemID) {
+  // All of these values must be < 0.
+  TOOLS_STOP_ITEM = -1,
+  TOOLS_RELOAD_ITEM = -2,
+};
+
 #endif  // IOS_CHROME_BROWSER_UI_TOOLS_MENU_TOOLS_MENU_CONSTANTS_H_
diff --git a/ios/chrome/browser/ui/tools_menu/tools_menu_view_controller.h b/ios/chrome/browser/ui/tools_menu/tools_menu_view_controller.h
index 5c3905e..f4d9918 100644
--- a/ios/chrome/browser/ui/tools_menu/tools_menu_view_controller.h
+++ b/ios/chrome/browser/ui/tools_menu/tools_menu_view_controller.h
@@ -7,6 +7,7 @@
 
 #import <UIKit/UIKit.h>
 
+@protocol BrowserCommands;
 @class ToolsMenuConfiguration;
 
 // TODO(crbug.com/228521): Remove this once the new command/metric handling is
@@ -59,6 +60,9 @@
 
 @property(nonatomic, weak) id<ToolsPopupTableDelegate> delegate;
 
+// Dispatcher for browser commands.
+@property(nonatomic, weak) id<BrowserCommands> dispatcher;
+
 // Initializes the Tools popup menu.
 - (void)initializeMenuWithConfiguration:(ToolsMenuConfiguration*)configuration;
 
diff --git a/ios/chrome/browser/ui/tools_menu/tools_menu_view_controller.mm b/ios/chrome/browser/ui/tools_menu/tools_menu_view_controller.mm
index 8f0e860..3928cfaa 100644
--- a/ios/chrome/browser/ui/tools_menu/tools_menu_view_controller.mm
+++ b/ios/chrome/browser/ui/tools_menu/tools_menu_view_controller.mm
@@ -14,6 +14,7 @@
 #include "ios/chrome/browser/experimental_flags.h"
 #import "ios/chrome/browser/ui/animation_util.h"
 #import "ios/chrome/browser/ui/commands/UIKit+ChromeExecuteCommand.h"
+#import "ios/chrome/browser/ui/commands/browser_commands.h"
 #include "ios/chrome/browser/ui/commands/ios_command_ids.h"
 #import "ios/chrome/browser/ui/reading_list/reading_list_menu_notification_delegate.h"
 #import "ios/chrome/browser/ui/reading_list/reading_list_menu_notifier.h"
@@ -133,6 +134,7 @@
 @synthesize toolbarType = _toolbarType;
 @synthesize menuItems = _menuItems;
 @synthesize delegate = _delegate;
+@synthesize dispatcher = _dispatcher;
 @synthesize requestStartTime = _requestStartTime;
 
 #pragma mark Public methods
@@ -459,16 +461,23 @@
   [toolsButton removeTarget:self
                      action:@selector(buttonPressed:)
            forControlEvents:UIControlEventTouchUpInside];
-  for (UIButton* button in [[self toolsCell] allButtons]) {
+  ToolsMenuViewToolsCell* toolsCell = [self toolsCell];
+  for (UIButton* button in [toolsCell allButtons]) {
     [button removeTarget:self
                   action:@selector(buttonPressed:)
         forControlEvents:UIControlEventTouchUpInside];
   }
+  [toolsCell.stopButton removeTarget:self.dispatcher
+                              action:@selector(stopLoading)
+                    forControlEvents:UIControlEventTouchUpInside];
+  [toolsCell.reloadButton removeTarget:self.dispatcher
+                                action:@selector(reload)
+                      forControlEvents:UIControlEventTouchUpInside];
 }
 
 #pragma mark - Button event handling
 
-- (IBAction)buttonPressed:(id)sender {
+- (void)buttonPressed:(id)sender {
   int commandId = [sender tag];
   DCHECK(commandId);
   // The bookmark command workaround is only needed for metrics; remap it
@@ -478,12 +487,15 @@
   if (commandId == IDC_TEMP_EDIT_BOOKMARK)
     [sender setTag:IDC_BOOKMARK_PAGE];
   // Do nothing when tapping the tools menu a second time.
-  if (commandId != IDC_SHOW_TOOLS_MENU) {
+  // Do not use -chromeExecuteCommand: for tags < 0 -- that is, items that have
+  // been refactored to use the dispatcher.
+  if (commandId != IDC_SHOW_TOOLS_MENU && commandId > 0) {
     [self chromeExecuteCommand:sender];
   }
   if (commandId == IDC_TEMP_EDIT_BOOKMARK)
     [sender setTag:IDC_TEMP_EDIT_BOOKMARK];
 
+  // Do any metrics logging for the command, and then close the menu.
   [_delegate commandWasSelected:commandId];
 }
 
@@ -576,11 +588,22 @@
     ToolsMenuViewToolsCell* cell =
         [view dequeueReusableCellWithReuseIdentifier:kToolsItemCellID
                                         forIndexPath:path];
+    // Add specific target/action dispatch for buttons refactored away from
+    // ChromeExecuteCommand. These need to be added *before* -buttonPressed:,
+    // because -buttonPressed: closes the popup menu, which will usually
+    // destroy the buttons before any other actions can be called.
+    [cell.stopButton addTarget:self.dispatcher
+                        action:@selector(stopLoading)
+              forControlEvents:UIControlEventTouchUpInside];
+    [cell.reloadButton addTarget:self.dispatcher
+                          action:@selector(reload)
+                forControlEvents:UIControlEventTouchUpInside];
     for (UIButton* button in [cell allButtons]) {
       [button addTarget:self
                     action:@selector(buttonPressed:)
           forControlEvents:UIControlEventTouchUpInside];
     }
+
     return cell;
   }
 
diff --git a/ios/chrome/browser/ui/tools_menu/tools_menu_view_tools_cell.mm b/ios/chrome/browser/ui/tools_menu/tools_menu_view_tools_cell.mm
index 2ae9d1b..7d4c069 100644
--- a/ios/chrome/browser/ui/tools_menu/tools_menu_view_tools_cell.mm
+++ b/ios/chrome/browser/ui/tools_menu/tools_menu_view_tools_cell.mm
@@ -8,6 +8,7 @@
 #include "ios/chrome/browser/ui/commands/ios_command_ids.h"
 #include "ios/chrome/browser/ui/rtl_geometry.h"
 #include "ios/chrome/browser/ui/toolbar/toolbar_resource_macros.h"
+#include "ios/chrome/browser/ui/tools_menu/tools_menu_constants.h"
 #import "ios/chrome/browser/ui/uikit_ui_util.h"
 #include "ios/chrome/grit/ios_strings.h"
 
@@ -64,14 +65,14 @@
 
   int reload[2][3] = TOOLBAR_IDR_TWO_STATE(RELOAD);
   _reloadButton = [self newButtonForImageIds:reload
-                                   commandID:IDC_RELOAD
+                                   commandID:TOOLS_RELOAD_ITEM
                         accessibilityLabelID:IDS_IOS_ACCNAME_RELOAD
                               automationName:@"Reload"
                                reverseForRTL:YES];
 
   int stop[2][3] = TOOLBAR_IDR_TWO_STATE(STOP);
   _stopButton = [self newButtonForImageIds:stop
-                                 commandID:IDC_STOP
+                                 commandID:TOOLS_STOP_ITEM
                       accessibilityLabelID:IDS_IOS_ACCNAME_STOP
                             automationName:@"Stop"];
 
diff --git a/ios/chrome/browser/ui/tools_menu/tools_popup_controller.h b/ios/chrome/browser/ui/tools_menu/tools_popup_controller.h
index e91c6441ed..4bc35c7 100644
--- a/ios/chrome/browser/ui/tools_menu/tools_popup_controller.h
+++ b/ios/chrome/browser/ui/tools_menu/tools_popup_controller.h
@@ -12,6 +12,7 @@
 // The a11y ID of the tools menu table view (used by integration tests).
 extern NSString* const kToolsMenuTableViewId;
 
+@protocol BrowserCommands;
 @class ToolsMenuConfiguration;
 
 // The view controller for the tools menu within the top toolbar.
@@ -23,7 +24,8 @@
 
 // Initializes the popup with the given |configuration|, a set of information
 // used to determine the appearance of the menu and the entries displayed.
-- (instancetype)initWithConfiguration:(ToolsMenuConfiguration*)configuration;
+- (instancetype)initWithConfiguration:(ToolsMenuConfiguration*)configuration
+                           dispatcher:(id<BrowserCommands>)dispatcher;
 
 // Called when the current tab loading state changes.
 - (void)setIsTabLoading:(BOOL)isTabLoading;
diff --git a/ios/chrome/browser/ui/tools_menu/tools_popup_controller.mm b/ios/chrome/browser/ui/tools_menu/tools_popup_controller.mm
index 96928dc..02d7b2f6 100644
--- a/ios/chrome/browser/ui/tools_menu/tools_popup_controller.mm
+++ b/ios/chrome/browser/ui/tools_menu/tools_popup_controller.mm
@@ -12,6 +12,7 @@
 #include "ios/chrome/browser/ui/commands/ios_command_ids.h"
 #import "ios/chrome/browser/ui/popup_menu/popup_menu_view.h"
 #include "ios/chrome/browser/ui/rtl_geometry.h"
+#import "ios/chrome/browser/ui/tools_menu/tools_menu_constants.h"
 #import "ios/chrome/browser/ui/tools_menu/tools_menu_view_controller.h"
 #import "ios/chrome/browser/ui/uikit_ui_util.h"
 #import "ios/shared/chrome/browser/ui/tools_menu/tools_menu_configuration.h"
@@ -46,11 +47,16 @@
 @implementation ToolsPopupController
 @synthesize isCurrentPageBookmarked = _isCurrentPageBookmarked;
 
-- (instancetype)initWithConfiguration:(ToolsMenuConfiguration*)configuration {
+- (instancetype)initWithConfiguration:(ToolsMenuConfiguration*)configuration
+                           dispatcher:(id<BrowserCommands>)dispatcher {
   DCHECK(configuration.displayView);
   self = [super initWithParentView:configuration.displayView];
   if (self) {
+    // Set superclass dispatcher property.
+    self.dispatcher = dispatcher;
     _toolsMenuViewController = [[ToolsMenuViewController alloc] init];
+    _toolsMenuViewController.dispatcher = self.dispatcher;
+
     _toolsTableViewContainer = [_toolsMenuViewController view];
     [_toolsTableViewContainer layer].cornerRadius = 2;
     [_toolsTableViewContainer layer].masksToBounds = YES;
@@ -184,7 +190,7 @@
     case IDC_OPTIONS:
       base::RecordAction(UserMetricsAction("MobileMenuSettings"));
       break;
-    case IDC_RELOAD:
+    case TOOLS_RELOAD_ITEM:
       base::RecordAction(UserMetricsAction("MobileMenuReload"));
       break;
     case IDC_SHARE_PAGE:
@@ -208,7 +214,7 @@
     case IDC_SHOW_OTHER_DEVICES:
       base::RecordAction(UserMetricsAction("MobileMenuRecentTabs"));
       break;
-    case IDC_STOP:
+    case TOOLS_STOP_ITEM:
       base::RecordAction(UserMetricsAction("MobileMenuStop"));
       break;
     case IDC_PRINT:
diff --git a/ios/chrome/browser/web/cache_egtest.mm b/ios/chrome/browser/web/cache_egtest.mm
index 60805bd..86770251 100644
--- a/ios/chrome/browser/web/cache_egtest.mm
+++ b/ios/chrome/browser/web/cache_egtest.mm
@@ -28,7 +28,6 @@
 #error "This file requires ARC support."
 #endif
 
-using chrome_test_util::WebViewContainingText;
 using web::test::HttpServer;
 
 namespace {
@@ -159,9 +158,7 @@
 // Reloads the web view and waits for the loading to complete.
 // TODO(crbug.com/638674): Evaluate if this can move to shared code
 - (void)reloadPage {
-  GenericChromeCommand* reloadCommand =
-      [[GenericChromeCommand alloc] initWithTag:IDC_RELOAD];
-  chrome_test_util::RunCommandWithActiveViewController(reloadCommand);
+  [chrome_test_util::BrowserCommandDispatcherForMainBVC() reload];
 
   [ChromeEarlGrey waitForPageToFinishLoading];
 }
diff --git a/ios/chrome/browser/web/forms_egtest.mm b/ios/chrome/browser/web/forms_egtest.mm
index 00c468f..7cd2562 100644
--- a/ios/chrome/browser/web/forms_egtest.mm
+++ b/ios/chrome/browser/web/forms_egtest.mm
@@ -30,7 +30,6 @@
 
 using chrome_test_util::ButtonWithAccessibilityLabelId;
 using chrome_test_util::OmniboxText;
-using chrome_test_util::WebViewContainingText;
 
 namespace {
 
@@ -198,16 +197,14 @@
 
   [ChromeEarlGrey loadURL:GetFormUrl()];
   chrome_test_util::TapWebViewElementWithId(kSubmitButtonLabel);
-  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kDestinationText)]
-      assertWithMatcher:grey_notNil()];
+  [ChromeEarlGrey waitForWebViewContainingText:kDestinationText];
   [[EarlGrey selectElementWithMatcher:OmniboxText(destinationURL.GetContent())]
       assertWithMatcher:grey_notNil()];
 
   [ChromeEarlGrey reload];
   [self confirmResendWarning];
 
-  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kDestinationText)]
-      assertWithMatcher:grey_notNil()];
+  [ChromeEarlGrey waitForWebViewContainingText:kDestinationText];
   [[EarlGrey selectElementWithMatcher:OmniboxText(destinationURL.GetContent())]
       assertWithMatcher:grey_notNil()];
 }
@@ -220,8 +217,7 @@
 
   [ChromeEarlGrey loadURL:GetFormUrl()];
   chrome_test_util::TapWebViewElementWithId(kSubmitButtonLabel);
-  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kDestinationText)]
-      assertWithMatcher:grey_notNil()];
+  [ChromeEarlGrey waitForWebViewContainingText:kDestinationText];
   [[EarlGrey selectElementWithMatcher:OmniboxText(destinationURL.GetContent())]
       assertWithMatcher:grey_notNil()];
 
@@ -229,8 +225,7 @@
   [ChromeEarlGrey loadURL:GetGenericUrl()];
   [ChromeEarlGrey goBack];
   [self confirmResendWarning];
-  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kDestinationText)]
-      assertWithMatcher:grey_notNil()];
+  [ChromeEarlGrey waitForWebViewContainingText:kDestinationText];
   [[EarlGrey selectElementWithMatcher:OmniboxText(destinationURL.GetContent())]
       assertWithMatcher:grey_notNil()];
 }
@@ -243,16 +238,14 @@
 
   [ChromeEarlGrey loadURL:GetFormUrl()];
   chrome_test_util::TapWebViewElementWithId(kSubmitButtonLabel);
-  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kDestinationText)]
-      assertWithMatcher:grey_notNil()];
+  [ChromeEarlGrey waitForWebViewContainingText:kDestinationText];
   [[EarlGrey selectElementWithMatcher:OmniboxText(destinationURL.GetContent())]
       assertWithMatcher:grey_notNil()];
 
   [ChromeEarlGrey goBack];
   [ChromeEarlGrey goForward];
   [self confirmResendWarning];
-  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kDestinationText)]
-      assertWithMatcher:grey_notNil()];
+  [ChromeEarlGrey waitForWebViewContainingText:kDestinationText];
   [[EarlGrey selectElementWithMatcher:OmniboxText(destinationURL.GetContent())]
       assertWithMatcher:grey_notNil()];
 }
@@ -265,8 +258,7 @@
 
   [ChromeEarlGrey loadURL:GetFormUrl()];
   chrome_test_util::TapWebViewElementWithId(kSubmitButtonLabel);
-  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kDestinationText)]
-      assertWithMatcher:grey_notNil()];
+  [ChromeEarlGrey waitForWebViewContainingText:kDestinationText];
   [[EarlGrey selectElementWithMatcher:OmniboxText(destinationURL.GetContent())]
       assertWithMatcher:grey_notNil()];
 
@@ -280,8 +272,7 @@
   [ChromeEarlGrey waitForPageToFinishLoading];
 
   [self confirmResendWarning];
-  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kDestinationText)]
-      assertWithMatcher:grey_notNil()];
+  [ChromeEarlGrey waitForWebViewContainingText:kDestinationText];
   [[EarlGrey selectElementWithMatcher:OmniboxText(destinationURL.GetContent())]
       assertWithMatcher:grey_notNil()];
 }
@@ -293,8 +284,7 @@
 
   [ChromeEarlGrey loadURL:GetFormUrl()];
   chrome_test_util::TapWebViewElementWithId(kSubmitButtonLabel);
-  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kDestinationText)]
-      assertWithMatcher:grey_notNil()];
+  [ChromeEarlGrey waitForWebViewContainingText:kDestinationText];
   [[EarlGrey selectElementWithMatcher:OmniboxText(destinationURL.GetContent())]
       assertWithMatcher:grey_notNil()];
 
@@ -323,8 +313,7 @@
   [ChromeEarlGrey waitForPageToFinishLoading];
 
   // Verify that navigation was cancelled, and forward navigation is possible.
-  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kSubmitButtonLabel)]
-      assertWithMatcher:grey_notNil()];
+  [ChromeEarlGrey waitForWebViewContainingText:kSubmitButtonLabel];
   [[EarlGrey selectElementWithMatcher:OmniboxText(GetFormUrl().GetContent())]
       assertWithMatcher:grey_notNil()];
   [[EarlGrey selectElementWithMatcher:chrome_test_util::ForwardButton()]
@@ -339,15 +328,13 @@
 
   [ChromeEarlGrey loadURL:GetFormUrl()];
   chrome_test_util::TapWebViewElementWithId(kSubmitButtonLabel);
-  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kDestinationText)]
-      assertWithMatcher:grey_notNil()];
+  [ChromeEarlGrey waitForWebViewContainingText:kDestinationText];
   [[EarlGrey selectElementWithMatcher:OmniboxText(destinationURL.GetContent())]
       assertWithMatcher:grey_notNil()];
 
   // Go back and verify the browser navigates to the original URL.
   [ChromeEarlGrey goBack];
-  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kSubmitButtonLabel)]
-      assertWithMatcher:grey_notNil()];
+  [ChromeEarlGrey waitForWebViewContainingText:kSubmitButtonLabel];
   [[EarlGrey selectElementWithMatcher:OmniboxText(GetFormUrl().GetContent())]
       assertWithMatcher:grey_notNil()];
 }
@@ -363,8 +350,7 @@
   chrome_test_util::TapWebViewElementWithId(kSubmitButtonLabel);
 
   // Check that the redirect changes the POST to a GET.
-  [[EarlGrey selectElementWithMatcher:WebViewContainingText("GET")]
-      assertWithMatcher:grey_notNil()];
+  [ChromeEarlGrey waitForWebViewContainingText:"GET"];
   [[EarlGrey selectElementWithMatcher:OmniboxText(destinationURL.GetContent())]
       assertWithMatcher:grey_notNil()];
 
@@ -375,8 +361,7 @@
       ButtonWithAccessibilityLabelId(IDS_HTTP_POST_WARNING_RESEND);
   [[EarlGrey selectElementWithMatcher:resendWarning]
       assertWithMatcher:grey_nil()];
-  [[EarlGrey selectElementWithMatcher:WebViewContainingText("GET")]
-      assertWithMatcher:grey_notNil()];
+  [ChromeEarlGrey waitForWebViewContainingText:"GET"];
   [[EarlGrey selectElementWithMatcher:OmniboxText(destinationURL.GetContent())]
       assertWithMatcher:grey_notNil()];
 }
diff --git a/ios/chrome/test/earl_grey/BUILD.gn b/ios/chrome/test/earl_grey/BUILD.gn
index 07a9fd3..57f2156 100644
--- a/ios/chrome/test/earl_grey/BUILD.gn
+++ b/ios/chrome/test/earl_grey/BUILD.gn
@@ -156,8 +156,6 @@
     "accessibility_util.mm",
     "chrome_actions.h",
     "chrome_actions.mm",
-    "chrome_assertions.h",
-    "chrome_assertions.mm",
     "chrome_earl_grey.h",
     "chrome_earl_grey.mm",
     "chrome_earl_grey_ui.h",
diff --git a/ios/chrome/test/earl_grey/chrome_assertions.h b/ios/chrome/test/earl_grey/chrome_assertions.h
deleted file mode 100644
index fcd1689..0000000
--- a/ios/chrome/test/earl_grey/chrome_assertions.h
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_TEST_EARL_GREY_CHROME_ASSERTIONS_H_
-#define IOS_CHROME_TEST_EARL_GREY_CHROME_ASSERTIONS_H_
-
-#import <Foundation/Foundation.h>
-
-namespace chrome_test_util {
-
-// Generates a failure if the actual number of non-incognito tabs is different
-// from |expected_tab_count|.
-void AssertMainTabCount(NSUInteger expected_tab_count);
-
-// Generates a failure if the actual number of incognito tabs is different from
-// |expected_tab_count|.
-void AssertIncognitoTabCount(NSUInteger expected_tab_count);
-
-}  // namespace chrome_test_util
-
-#endif  // IOS_CHROME_TEST_EARL_GREY_CHROME_ASSERTIONS_H_
diff --git a/ios/chrome/test/earl_grey/chrome_assertions.mm b/ios/chrome/test/earl_grey/chrome_assertions.mm
deleted file mode 100644
index 2cdbc3cb..0000000
--- a/ios/chrome/test/earl_grey/chrome_assertions.mm
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/test/earl_grey/chrome_assertions.h"
-
-#import <EarlGrey/EarlGrey.h>
-
-#include "base/format_macros.h"
-#import "ios/chrome/test/app/tab_test_util.h"
-#import "ios/testing/wait_util.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-namespace chrome_test_util {
-
-void AssertMainTabCount(NSUInteger expected_tab_count) {
-  // Allow the UI to become idle, in case any tabs are being opened or closed.
-  [[GREYUIThreadExecutor sharedInstance] drainUntilIdle];
-  GREYAssert(testing::WaitUntilConditionOrTimeout(
-                 testing::kWaitForUIElementTimeout,
-                 ^{
-                   return GetMainTabCount() == expected_tab_count;
-                 }),
-             @"Did not receive %" PRIuNS " tabs", expected_tab_count);
-}
-
-void AssertIncognitoTabCount(NSUInteger expected_tab_count) {
-  // Allow the UI to become idle, in case any tabs are being opened or closed.
-  [[GREYUIThreadExecutor sharedInstance] drainUntilIdle];
-  ConditionBlock condition = ^{
-    return GetIncognitoTabCount() == expected_tab_count;
-  };
-  GREYAssert(testing::WaitUntilConditionOrTimeout(
-                 testing::kWaitForUIElementTimeout, condition),
-             @"Did not receive %" PRIuNS " incognito tabs", expected_tab_count);
-}
-
-}  // namespace chrome_test_util
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey.mm b/ios/chrome/test/earl_grey/chrome_earl_grey.mm
index c0aba2f..5e46264 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey.mm
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey.mm
@@ -117,9 +117,7 @@
 }
 
 + (void)reload {
-  base::scoped_nsobject<GenericChromeCommand> reloadCommand(
-      [[GenericChromeCommand alloc] initWithTag:IDC_RELOAD]);
-  chrome_test_util::RunCommandWithActiveViewController(reloadCommand);
+  [chrome_test_util::BrowserCommandDispatcherForMainBVC() reload];
   [ChromeEarlGrey waitForPageToFinishLoading];
 }
 
diff --git a/ios/chrome/test/earl_grey/chrome_matchers.h b/ios/chrome/test/earl_grey/chrome_matchers.h
index d9002c72..6c4fb37 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers.h
+++ b/ios/chrome/test/earl_grey/chrome_matchers.h
@@ -31,9 +31,6 @@
 // accessibility trait UIAccessibilityTraitStaticText.
 id<GREYMatcher> StaticTextWithAccessibilityLabel(NSString* label);
 
-// Returns matcher for webview containing |text|.
-id<GREYMatcher> WebViewContainingText(std::string text);
-
 // Returns matcher for webview not containing |text|.
 id<GREYMatcher> WebViewNotContainingText(std::string text);
 
diff --git a/ios/chrome/test/earl_grey/chrome_matchers.mm b/ios/chrome/test/earl_grey/chrome_matchers.mm
index c9aa5e42..3a951c9 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers.mm
+++ b/ios/chrome/test/earl_grey/chrome_matchers.mm
@@ -95,10 +95,6 @@
       l10n_util::GetNSStringWithFixup(message_id));
 }
 
-id<GREYMatcher> WebViewContainingText(std::string text) {
-  return web::WebViewContainingText(std::move(text), GetCurrentWebState());
-}
-
 id<GREYMatcher> WebViewNotContainingText(std::string text) {
   return web::WebViewNotContainingText(std::move(text), GetCurrentWebState());
 }
diff --git a/ios/chrome/test/earl_grey/device_check_egtest.mm b/ios/chrome/test/earl_grey/device_check_egtest.mm
index 0fee2395..e9f596f 100644
--- a/ios/chrome/test/earl_grey/device_check_egtest.mm
+++ b/ios/chrome/test/earl_grey/device_check_egtest.mm
@@ -5,7 +5,6 @@
 #import <XCTest/XCTest.h>
 
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
-#import "ios/chrome/test/earl_grey/chrome_matchers.h"
 #import "ios/chrome/test/earl_grey/chrome_test_case.h"
 #include "url/gurl.h"
 
@@ -13,8 +12,6 @@
 #error "This file requires ARC support."
 #endif
 
-using chrome_test_util::WebViewContainingText;
-
 // Test suite to verify Internet connectivity.
 @interface DeviceCheckTestCase : ChromeTestCase
 @end
diff --git a/ios/clean/chrome/browser/ui/tab/BUILD.gn b/ios/clean/chrome/browser/ui/tab/BUILD.gn
index 43b3865..3788862 100644
--- a/ios/clean/chrome/browser/ui/tab/BUILD.gn
+++ b/ios/clean/chrome/browser/ui/tab/BUILD.gn
@@ -63,6 +63,7 @@
     "//ios/shared/chrome/browser/ui/browser_list",
     "//ios/shared/chrome/browser/ui/coordinators",
     "//ios/shared/chrome/browser/ui/coordinators:test_support",
+    "//ios/shared/chrome/browser/ui/tab:test_support",
     "//ios/shared/chrome/browser/ui/toolbar:test_support",
     "//ios/web/public/test",
     "//testing/gtest",
diff --git a/ios/clean/chrome/browser/ui/tab/tab_coordinator_unittest.mm b/ios/clean/chrome/browser/ui/tab/tab_coordinator_unittest.mm
index 6da44dbb..cdacb9cd 100644
--- a/ios/clean/chrome/browser/ui/tab/tab_coordinator_unittest.mm
+++ b/ios/clean/chrome/browser/ui/tab/tab_coordinator_unittest.mm
@@ -11,6 +11,7 @@
 #import "ios/shared/chrome/browser/ui/browser_list/browser.h"
 #import "ios/shared/chrome/browser/ui/coordinators/browser_coordinator+internal.h"
 #import "ios/shared/chrome/browser/ui/coordinators/browser_coordinator_test.h"
+#import "ios/shared/chrome/browser/ui/tab/tab_test_util.h"
 #import "ios/shared/chrome/browser/ui/toolbar/toolbar_test_util.h"
 #include "ios/web/public/test/test_web_thread.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -21,24 +22,6 @@
 
 namespace {
 
-class StubNavigationManager : public web::TestNavigationManager {
- public:
-  int GetItemCount() const override { return item_count_; }
-  bool CanGoForward() const override { return false; }
-  bool CanGoBack() const override { return false; }
-
-  void LoadURLWithParams(const NavigationManager::WebLoadParams&) override {
-    has_loaded_url_ = true;
-  }
-
-  void SetItemCount(int count) { item_count_ = count; }
-  bool GetHasLoadedUrl() { return has_loaded_url_; }
-
- private:
-  int item_count_;
-  bool has_loaded_url_;
-};
-
 class TabCoordinatorTest : public BrowserCoordinatorTest {
  protected:
   TabCoordinatorTest()
@@ -49,7 +32,7 @@
         objectForKey:@"EnableBottomToolbar"];
 
     // Initialize the web state.
-    auto navigation_manager = base::MakeUnique<StubNavigationManager>();
+    auto navigation_manager = base::MakeUnique<TabNavigationManager>();
     navigation_manager->SetItemCount(0);
     web_state_.SetNavigationManager(std::move(navigation_manager));
 
diff --git a/ios/clean/chrome/browser/ui/web_contents/BUILD.gn b/ios/clean/chrome/browser/ui/web_contents/BUILD.gn
index 452ec28..7cc6f98 100644
--- a/ios/clean/chrome/browser/ui/web_contents/BUILD.gn
+++ b/ios/clean/chrome/browser/ui/web_contents/BUILD.gn
@@ -50,6 +50,7 @@
     "//base",
     "//base/test:test_support",
     "//ios/chrome/test/base",
+    "//ios/shared/chrome/browser/ui/tab:test_support",
     "//ios/web/public/test/fakes",
     "//testing/gtest",
   ]
diff --git a/ios/clean/chrome/browser/ui/web_contents/web_contents_mediator_unittest.mm b/ios/clean/chrome/browser/ui/web_contents/web_contents_mediator_unittest.mm
index 5576ba4..b1f6f7f7 100644
--- a/ios/clean/chrome/browser/ui/web_contents/web_contents_mediator_unittest.mm
+++ b/ios/clean/chrome/browser/ui/web_contents/web_contents_mediator_unittest.mm
@@ -6,7 +6,7 @@
 
 #include "base/memory/ptr_util.h"
 #import "ios/clean/chrome/browser/ui/web_contents/web_contents_consumer.h"
-#import "ios/web/public/test/fakes/test_navigation_manager.h"
+#import "ios/shared/chrome/browser/ui/tab/tab_test_util.h"
 #import "ios/web/public/test/fakes/test_web_state.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/gtest_mac.h"
@@ -31,31 +31,15 @@
 
 namespace {
 
-class StubNavigationManager : public web::TestNavigationManager {
- public:
-  int GetItemCount() const override { return item_count_; }
-
-  void LoadURLWithParams(const NavigationManager::WebLoadParams&) override {
-    has_loaded_url_ = true;
-  }
-
-  void SetItemCount(int count) { item_count_ = count; }
-  bool GetHasLoadedUrl() { return has_loaded_url_; }
-
- private:
-  int item_count_;
-  bool has_loaded_url_;
-};
-
 class WebContentsMediatorTest : public PlatformTest {
  public:
   WebContentsMediatorTest() {
-    auto navigation_manager = base::MakeUnique<StubNavigationManager>();
+    auto navigation_manager = base::MakeUnique<TabNavigationManager>();
     navigation_manager->SetItemCount(0);
     test_web_state_.SetView([[UIView alloc] init]);
     test_web_state_.SetNavigationManager(std::move(navigation_manager));
 
-    auto new_navigation_manager = base::MakeUnique<StubNavigationManager>();
+    auto new_navigation_manager = base::MakeUnique<TabNavigationManager>();
     new_test_web_state_.SetView([[UIView alloc] init]);
     new_test_web_state_.SetNavigationManager(std::move(new_navigation_manager));
 
@@ -63,13 +47,13 @@
   }
   ~WebContentsMediatorTest() override { [mediator_ disconnect]; }
 
-  StubNavigationManager* navigation_manager() {
-    return static_cast<StubNavigationManager*>(
+  TabNavigationManager* navigation_manager() {
+    return static_cast<TabNavigationManager*>(
         test_web_state_.GetNavigationManager());
   }
 
-  StubNavigationManager* new_navigation_manager() {
-    return static_cast<StubNavigationManager*>(
+  TabNavigationManager* new_navigation_manager() {
+    return static_cast<TabNavigationManager*>(
         new_test_web_state_.GetNavigationManager());
   }
 
diff --git a/ios/shared/chrome/browser/ui/tab/BUILD.gn b/ios/shared/chrome/browser/ui/tab/BUILD.gn
new file mode 100644
index 0000000..857b808
--- /dev/null
+++ b/ios/shared/chrome/browser/ui/tab/BUILD.gn
@@ -0,0 +1,15 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("test_support") {
+  testonly = true
+  sources = [
+    "tab_test_util.h",
+    "tab_test_util.mm",
+  ]
+  deps = [
+    "//ios/web/public/test/fakes",
+  ]
+  configs += [ "//build/config/compiler:enable_arc" ]
+}
diff --git a/ios/shared/chrome/browser/ui/tab/tab_test_util.h b/ios/shared/chrome/browser/ui/tab/tab_test_util.h
new file mode 100644
index 0000000..86e13547
--- /dev/null
+++ b/ios/shared/chrome/browser/ui/tab/tab_test_util.h
@@ -0,0 +1,25 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_SHARED_CHROME_BROWSER_UI_TAB_TAB_TEST_UTIL_H_
+#define IOS_SHARED_CHROME_BROWSER_UI_TAB_TAB_TEST_UTIL_H_
+
+#import "ios/web/public/test/fakes/test_navigation_manager.h"
+
+class TabNavigationManager : public web::TestNavigationManager {
+ public:
+  int GetItemCount() const override;
+  bool CanGoForward() const override;
+  bool CanGoBack() const override;
+
+  void LoadURLWithParams(const NavigationManager::WebLoadParams&) override;
+  void SetItemCount(int count);
+  bool GetHasLoadedUrl();
+
+ private:
+  int item_count_;
+  bool has_loaded_url_;
+};
+
+#endif  // IOS_SHARED_CHROME_BROWSER_UI_TAB_TAB_TEST_UTIL_H_
diff --git a/ios/shared/chrome/browser/ui/tab/tab_test_util.mm b/ios/shared/chrome/browser/ui/tab/tab_test_util.mm
new file mode 100644
index 0000000..69c33fb
--- /dev/null
+++ b/ios/shared/chrome/browser/ui/tab/tab_test_util.mm
@@ -0,0 +1,34 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/shared/chrome/browser/ui/tab/tab_test_util.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+int TabNavigationManager::GetItemCount() const {
+  return item_count_;
+}
+
+bool TabNavigationManager::CanGoBack() const {
+  return false;
+}
+
+bool TabNavigationManager::CanGoForward() const {
+  return false;
+}
+
+void TabNavigationManager::LoadURLWithParams(
+    const NavigationManager::WebLoadParams&) {
+  has_loaded_url_ = true;
+}
+
+void TabNavigationManager::SetItemCount(int count) {
+  item_count_ = count;
+}
+
+bool TabNavigationManager::GetHasLoadedUrl() {
+  return has_loaded_url_;
+}
diff --git a/ios/web_view/internal/translate/web_view_translate_accept_languages_factory.cc b/ios/web_view/internal/translate/web_view_translate_accept_languages_factory.cc
index 1e24463..5a2f062 100644
--- a/ios/web_view/internal/translate/web_view_translate_accept_languages_factory.cc
+++ b/ios/web_view/internal/translate/web_view_translate_accept_languages_factory.cc
@@ -76,4 +76,10 @@
       browser_state->GetPrefs());
 }
 
+web::BrowserState* WebViewTranslateAcceptLanguagesFactory::GetBrowserStateToUse(
+    web::BrowserState* context) const {
+  // Override to enable this service for off the record browser states.
+  return context;
+}
+
 }  // namespace ios_web_view
diff --git a/ios/web_view/internal/translate/web_view_translate_accept_languages_factory.h b/ios/web_view/internal/translate/web_view_translate_accept_languages_factory.h
index 08d0e6b..119b81b 100644
--- a/ios/web_view/internal/translate/web_view_translate_accept_languages_factory.h
+++ b/ios/web_view/internal/translate/web_view_translate_accept_languages_factory.h
@@ -42,6 +42,8 @@
   // BrowserStateKeyedServiceFactory implementation.
   std::unique_ptr<KeyedService> BuildServiceInstanceFor(
       web::BrowserState* context) const override;
+  web::BrowserState* GetBrowserStateToUse(
+      web::BrowserState* context) const override;
 
   DISALLOW_COPY_AND_ASSIGN(WebViewTranslateAcceptLanguagesFactory);
 };
diff --git a/ios/web_view/internal/translate/web_view_translate_ranker_factory.cc b/ios/web_view/internal/translate/web_view_translate_ranker_factory.cc
index 8c701968..4efa919 100644
--- a/ios/web_view/internal/translate/web_view_translate_ranker_factory.cc
+++ b/ios/web_view/internal/translate/web_view_translate_ranker_factory.cc
@@ -53,4 +53,10 @@
   return std::move(ranker);
 }
 
+web::BrowserState* WebViewTranslateRankerFactory::GetBrowserStateToUse(
+    web::BrowserState* context) const {
+  // Override to enable this service for off the record browser states.
+  return context;
+}
+
 }  // namespace ios_web_view
diff --git a/ios/web_view/internal/translate/web_view_translate_ranker_factory.h b/ios/web_view/internal/translate/web_view_translate_ranker_factory.h
index b51ab276..6aa7902 100644
--- a/ios/web_view/internal/translate/web_view_translate_ranker_factory.h
+++ b/ios/web_view/internal/translate/web_view_translate_ranker_factory.h
@@ -39,6 +39,8 @@
   // BrowserStateKeyedServiceFactory implementation.
   std::unique_ptr<KeyedService> BuildServiceInstanceFor(
       web::BrowserState* context) const override;
+  web::BrowserState* GetBrowserStateToUse(
+      web::BrowserState* context) const override;
 
   DISALLOW_COPY_AND_ASSIGN(WebViewTranslateRankerFactory);
 };
diff --git a/media/BUILD.gn b/media/BUILD.gn
index bd12c8a..8b4c81d 100644
--- a/media/BUILD.gn
+++ b/media/BUILD.gn
@@ -466,6 +466,7 @@
     ":unit_tests",
     "//media/audio:unit_tests",
     "//media/base:unit_tests",
+    "//media/gpu:unit_tests",
     "//media/mojo:unit_tests",
     "//media/test:pipeline_integration_tests",
     "//media/test:run_all_unittests",
diff --git a/media/base/android/media_player_bridge.cc b/media/base/android/media_player_bridge.cc
index d496b6c..1acdd05 100644
--- a/media/base/android/media_player_bridge.cc
+++ b/media/base/android/media_player_bridge.cc
@@ -85,6 +85,11 @@
 
 void MediaPlayerBridge::Initialize() {
   cookies_.clear();
+  if (url_.SchemeIsBlob()) {
+    NOTREACHED();
+    return;
+  }
+
   if (url_.SchemeIsFile() || url_.SchemeIs("data")) {
     ExtractMediaMetadata(url_.spec());
     return;
@@ -92,11 +97,10 @@
 
   media::MediaResourceGetter* resource_getter =
       manager()->GetMediaResourceGetter();
-  if (url_.SchemeIsFileSystem() || url_.SchemeIsBlob()) {
+  if (url_.SchemeIsFileSystem()) {
     resource_getter->GetPlatformPathFromURL(
-        url_,
-        base::Bind(&MediaPlayerBridge::ExtractMediaMetadata,
-                   weak_factory_.GetWeakPtr()));
+        url_, base::BindOnce(&MediaPlayerBridge::ExtractMediaMetadata,
+                             weak_factory_.GetWeakPtr()));
     return;
   }
 
@@ -144,12 +148,18 @@
 
 void MediaPlayerBridge::Prepare() {
   DCHECK(j_media_player_bridge_.is_null());
+
+  if (url_.SchemeIsBlob()) {
+    NOTREACHED();
+    return;
+  }
+
   CreateJavaMediaPlayerBridge();
-  if (url_.SchemeIsFileSystem() || url_.SchemeIsBlob()) {
+
+  if (url_.SchemeIsFileSystem()) {
     manager()->GetMediaResourceGetter()->GetPlatformPathFromURL(
-        url_,
-        base::Bind(&MediaPlayerBridge::SetDataSource,
-                   weak_factory_.GetWeakPtr()));
+        url_, base::BindOnce(&MediaPlayerBridge::SetDataSource,
+                             weak_factory_.GetWeakPtr()));
     return;
   }
 
diff --git a/media/base/android/media_resource_getter.h b/media/base/android/media_resource_getter.h
index 81987c9..65c3aa5 100644
--- a/media/base/android/media_resource_getter.h
+++ b/media/base/android/media_resource_getter.h
@@ -10,7 +10,6 @@
 #include <string>
 
 #include "base/callback.h"
-#include "base/files/file_path.h"
 #include "base/strings/string16.h"
 #include "base/time/time.h"
 #include "media/base/media_export.h"
@@ -23,49 +22,47 @@
 class MEDIA_EXPORT MediaResourceGetter {
  public:
   // Callback to get the cookies. Args: cookies string.
-  typedef base::Callback<void(const std::string&)> GetCookieCB;
+  typedef base::OnceCallback<void(const std::string&)> GetCookieCB;
 
   // Callback to get the platform path. Args: platform path.
-  typedef base::Callback<void(const std::string&)> GetPlatformPathCB;
+  typedef base::OnceCallback<void(const std::string&)> GetPlatformPathCB;
 
   // Callback to get the auth credentials. Args: username and password.
-  typedef base::Callback<void(const base::string16&, const base::string16&)>
+  typedef base::OnceCallback<void(const base::string16&, const base::string16&)>
       GetAuthCredentialsCB;
 
   // Callback to get the media metadata. Args: duration, width, height, and
   // whether the information is retrieved successfully.
-  typedef base::Callback<void(base::TimeDelta, int, int, bool)>
+  typedef base::OnceCallback<void(base::TimeDelta, int, int, bool)>
       ExtractMediaMetadataCB;
   virtual ~MediaResourceGetter();
 
   // Method for getting the auth credentials for a URL.
   virtual void GetAuthCredentials(const GURL& url,
-                                  const GetAuthCredentialsCB& callback) = 0;
+                                  GetAuthCredentialsCB callback) = 0;
 
   // Method for getting the cookies for a given URL.
   virtual void GetCookies(const GURL& url,
                           const GURL& first_party_for_cookies,
-                          const GetCookieCB& callback) = 0;
+                          GetCookieCB callback) = 0;
 
-  // Method for getting the platform path from a file system or blob URL.
-  virtual void GetPlatformPathFromURL(
-      const GURL& url,
-      const GetPlatformPathCB& callback) = 0;
+  // Method for getting the platform path from a file system URL.
+  virtual void GetPlatformPathFromURL(const GURL& url,
+                                      GetPlatformPathCB callback) = 0;
 
   // Extracts the metadata from a media URL. Once completed, the provided
   // callback function will be run.
-  virtual void ExtractMediaMetadata(
-      const std::string& url,
-      const std::string& cookies,
-      const std::string& user_agent,
-      const ExtractMediaMetadataCB& callback) = 0;
+  virtual void ExtractMediaMetadata(const std::string& url,
+                                    const std::string& cookies,
+                                    const std::string& user_agent,
+                                    ExtractMediaMetadataCB callback) = 0;
 
   // Extracts the metadata from a file descriptor. Once completed, the
   // provided callback function will be run.
   virtual void ExtractMediaMetadata(const int fd,
                                     const int64_t offset,
                                     const int64_t size,
-                                    const ExtractMediaMetadataCB& callback) = 0;
+                                    ExtractMediaMetadataCB callback) = 0;
 };
 
 }  // namespace media
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc
index 7031e095..61e797b 100644
--- a/media/base/media_switches.cc
+++ b/media/base/media_switches.cc
@@ -257,6 +257,9 @@
 const base::Feature kExternalClearKeyForTesting{
     "external-clear-key-for-testing", base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kSupportExperimentalCdmInterface{
+    "SupportExperimentalCdmInterface", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Enables low-delay video rendering in media pipeline on "live" stream.
 const base::Feature kLowDelayVideoRenderingOnLiveStream{
     "low-delay-video-rendering-on-live-stream",
diff --git a/media/base/media_switches.h b/media/base/media_switches.h
index 8691c07..5ec97b28 100644
--- a/media/base/media_switches.h
+++ b/media/base/media_switches.h
@@ -108,6 +108,7 @@
 MEDIA_EXPORT extern const base::Feature kExternalClearKeyForTesting;
 MEDIA_EXPORT extern const base::Feature kLowDelayVideoRenderingOnLiveStream;
 MEDIA_EXPORT extern const base::Feature kMediaCastOverlayButton;
+MEDIA_EXPORT extern const base::Feature kMediaEngagement;
 MEDIA_EXPORT extern const base::Feature kMemoryPressureBasedSourceBufferGC;
 MEDIA_EXPORT extern const base::Feature kMojoCdm;
 MEDIA_EXPORT extern const base::Feature kNewAudioRenderingMixingStrategy;
@@ -115,12 +116,12 @@
 MEDIA_EXPORT extern const base::Feature kOverlayFullscreenVideo;
 MEDIA_EXPORT extern const base::Feature kResumeBackgroundVideo;
 MEDIA_EXPORT extern const base::Feature kSpecCompliantCanPlayThrough;
+MEDIA_EXPORT extern const base::Feature kSupportExperimentalCdmInterface;
 MEDIA_EXPORT extern const base::Feature kUseAndroidOverlay;
 MEDIA_EXPORT extern const base::Feature kUseNewMediaCache;
 MEDIA_EXPORT extern const base::Feature kVideoBlitColorAccuracy;
 MEDIA_EXPORT extern const base::Feature kVideoColorManagement;
 MEDIA_EXPORT extern const base::Feature kUseSurfaceLayerForVideo;
-MEDIA_EXPORT extern const base::Feature kMediaEngagement;
 
 #if defined(OS_ANDROID)
 MEDIA_EXPORT extern const base::Feature kVideoFullscreenOrientationLock;
diff --git a/media/base/pipeline_impl.cc b/media/base/pipeline_impl.cc
index 9f074c0..d21c201 100644
--- a/media/base/pipeline_impl.cc
+++ b/media/base/pipeline_impl.cc
@@ -650,14 +650,20 @@
   shared_state_.statistics.audio_memory_usage += stats.audio_memory_usage;
   shared_state_.statistics.video_memory_usage += stats.video_memory_usage;
 
-  base::TimeDelta old_average =
+  if (stats.video_frame_duration_average != kNoTimestamp) {
+    shared_state_.statistics.video_frame_duration_average =
+        stats.video_frame_duration_average;
+  }
+
+  base::TimeDelta old_key_frame_distance_average =
       shared_state_.statistics.video_keyframe_distance_average;
   if (stats.video_keyframe_distance_average != kNoTimestamp) {
     shared_state_.statistics.video_keyframe_distance_average =
         stats.video_keyframe_distance_average;
   }
 
-  if (shared_state_.statistics.video_keyframe_distance_average != old_average) {
+  if (shared_state_.statistics.video_keyframe_distance_average !=
+      old_key_frame_distance_average) {
     main_task_runner_->PostTask(
         FROM_HERE,
         base::Bind(&PipelineImpl::OnVideoAverageKeyframeDistanceUpdate,
diff --git a/media/base/pipeline_status.h b/media/base/pipeline_status.h
index b393c79..5ef61423 100644
--- a/media/base/pipeline_status.h
+++ b/media/base/pipeline_status.h
@@ -61,6 +61,7 @@
   int64_t audio_memory_usage = 0;
   int64_t video_memory_usage = 0;
   base::TimeDelta video_keyframe_distance_average = kNoTimestamp;
+  base::TimeDelta video_frame_duration_average = kNoTimestamp;
 };
 
 // Used for updating pipeline statistics; the passed value should be a delta
diff --git a/media/base/video_frame.cc b/media/base/video_frame.cc
index 8e3eaff..7636cc4 100644
--- a/media/base/video_frame.cc
+++ b/media/base/video_frame.cc
@@ -790,7 +790,7 @@
   done_callbacks_.push_back(callback);
 }
 
-void VideoFrame::UpdateReleaseSyncToken(SyncTokenClient* client) {
+gpu::SyncToken VideoFrame::UpdateReleaseSyncToken(SyncTokenClient* client) {
   DCHECK(HasTextures());
   base::AutoLock locker(release_sync_token_lock_);
   // Must wait on the previous sync point before inserting a new sync point so
@@ -799,6 +799,7 @@
   if (release_sync_token_.HasData())
     client->WaitSyncToken(release_sync_token_);
   client->GenerateSyncToken(&release_sync_token_);
+  return release_sync_token_;
 }
 
 std::string VideoFrame::AsHumanReadableString() {
diff --git a/media/base/video_frame.h b/media/base/video_frame.h
index 5895253..8f56892 100644
--- a/media/base/video_frame.h
+++ b/media/base/video_frame.h
@@ -390,11 +390,11 @@
     timestamp_ = timestamp;
   }
 
-  // It uses |client| to insert a new sync point and potentially waits on a
-  // older sync point. The final sync point will be used to release this
-  // VideoFrame.
+  // It uses |client| to insert a new sync token and potentially waits on an
+  // older sync token. The final sync point will be used to release this
+  // VideoFrame. Also returns the new sync token.
   // This method is thread safe. Both blink and compositor threads can call it.
-  void UpdateReleaseSyncToken(SyncTokenClient* client);
+  gpu::SyncToken UpdateReleaseSyncToken(SyncTokenClient* client);
 
   // Returns a human-readable string describing |*this|.
   std::string AsHumanReadableString();
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index 2986a27c8..28911b1 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -60,6 +60,7 @@
 #include "third_party/WebKit/public/platform/WebSecurityOrigin.h"
 #include "third_party/WebKit/public/platform/WebSize.h"
 #include "third_party/WebKit/public/platform/WebString.h"
+#include "third_party/WebKit/public/platform/WebSurfaceLayerBridge.h"
 #include "third_party/WebKit/public/platform/WebURL.h"
 #include "third_party/WebKit/public/web/WebDocument.h"
 #include "third_party/WebKit/public/web/WebFrame.h"
@@ -259,6 +260,8 @@
           params->enable_instant_source_buffer_gc()),
       embedded_media_experience_enabled_(
           params->embedded_media_experience_enabled()),
+      surface_layer_for_video_enabled_(
+          base::FeatureList::IsEnabled(media::kUseSurfaceLayerForVideo)),
       request_routing_token_cb_(params->request_routing_token_cb()),
       overlay_routing_token_(OverlayInfo::RoutingToken()) {
   DVLOG(1) << __func__;
@@ -267,6 +270,9 @@
   DCHECK(client_);
   DCHECK(delegate_);
 
+  if (surface_layer_for_video_enabled_)
+    bridge_ = base::WrapUnique(blink::WebSurfaceLayerBridge::Create());
+
   force_video_overlays_ = base::CommandLine::ForCurrentProcess()->HasSwitch(
       switches::kForceVideoOverlays);
 
@@ -323,8 +329,11 @@
 
   // Destruct compositor resources in the proper order.
   client_->SetWebLayer(nullptr);
-  if (video_weblayer_)
+  if (!surface_layer_for_video_enabled_ && video_weblayer_) {
     static_cast<cc::VideoLayer*>(video_weblayer_->layer())->StopUsingProvider();
+  }
+  // TODO(lethalantidote): Handle destruction of compositor for surface layer.
+  // https://crbug/739854.
   compositor_task_runner_->DeleteSoon(FROM_HERE, compositor_);
 
   media_log_->AddEvent(
@@ -1349,12 +1358,20 @@
         surface_manager_->NaturalSizeChanged(pipeline_metadata_.natural_size);
     }
 
-    DCHECK(!video_weblayer_);
-    video_weblayer_.reset(new cc_blink::WebLayerImpl(cc::VideoLayer::Create(
-        compositor_, pipeline_metadata_.video_rotation)));
-    video_weblayer_->layer()->SetContentsOpaque(opaque_);
-    video_weblayer_->SetContentsOpaqueIsFixed(true);
-    client_->SetWebLayer(video_weblayer_.get());
+    if (!surface_layer_for_video_enabled_) {
+      DCHECK(!video_weblayer_);
+      video_weblayer_.reset(new cc_blink::WebLayerImpl(cc::VideoLayer::Create(
+          compositor_, pipeline_metadata_.video_rotation)));
+      video_weblayer_->layer()->SetContentsOpaque(opaque_);
+      video_weblayer_->SetContentsOpaqueIsFixed(true);
+      client_->SetWebLayer(video_weblayer_.get());
+    } else if (bridge_->GetWebLayer()) {
+      bridge_->GetWebLayer()->CcLayer()->SetContentsOpaque(opaque_);
+      // TODO(lethalantidote): Figure out how to persist opaque setting
+      // without calling WebLayerImpl's SetContentsOpaueIsFixed;
+      // https://crbug/739859.
+      client_->SetWebLayer(bridge_->GetWebLayer());
+    }
   }
 
   if (observer_)
@@ -1548,8 +1565,12 @@
   opaque_ = opaque;
   // Modify content opaqueness of cc::Layer directly so that
   // SetContentsOpaqueIsFixed is ignored.
-  if (video_weblayer_)
-    video_weblayer_->layer()->SetContentsOpaque(opaque_);
+  if (!surface_layer_for_video_enabled_) {
+    if (video_weblayer_)
+      video_weblayer_->layer()->SetContentsOpaque(opaque_);
+  } else if (bridge_->GetWebLayer()) {
+    bridge_->GetWebLayer()->CcLayer()->SetContentsOpaque(opaque_);
+  }
 }
 
 void WebMediaPlayerImpl::OnVideoAverageKeyframeDistanceUpdate() {
@@ -1723,10 +1744,16 @@
 }
 
 gfx::Size WebMediaPlayerImpl::GetCanvasSize() const {
-  if (!video_weblayer_)
+  if (!surface_layer_for_video_enabled_) {
+    if (!video_weblayer_)
+      return pipeline_metadata_.natural_size;
+
+    return video_weblayer_->Bounds();
+  }
+  if (!bridge_->GetWebLayer())
     return pipeline_metadata_.natural_size;
 
-  return video_weblayer_->Bounds();
+  return bridge_->GetWebLayer()->Bounds();
 }
 
 void WebMediaPlayerImpl::SetDeviceScaleFactor(float scale_factor) {
diff --git a/media/blink/webmediaplayer_impl.h b/media/blink/webmediaplayer_impl.h
index da0d8b6..d16807c 100644
--- a/media/blink/webmediaplayer_impl.h
+++ b/media/blink/webmediaplayer_impl.h
@@ -44,6 +44,7 @@
 #include "third_party/WebKit/public/platform/WebAudioSourceProvider.h"
 #include "third_party/WebKit/public/platform/WebContentDecryptionModuleResult.h"
 #include "third_party/WebKit/public/platform/WebMediaPlayer.h"
+#include "third_party/WebKit/public/platform/WebSurfaceLayerBridge.h"
 #include "url/gurl.h"
 
 #if defined(OS_ANDROID)  // WMPI_CAST
@@ -721,6 +722,10 @@
   // Monitors the player events.
   base::WeakPtr<MediaObserver> observer_;
 
+  // Owns the weblayer and obtains/maintains SurfaceIds for
+  // kUseSurfaceLayerForVideo feature.
+  std::unique_ptr<blink::WebSurfaceLayerBridge> bridge_;
+
   // The maximum video keyframe distance that allows triggering background
   // playback optimizations (non-MSE).
   base::TimeDelta max_keyframe_distance_to_disable_background_video_;
@@ -757,6 +762,9 @@
   // Whether embedded media experience is currently enabled.
   bool embedded_media_experience_enabled_ = false;
 
+  // Whether the use of a surface layer instead of a video layer is enabled.
+  bool surface_layer_for_video_enabled_ = false;
+
   gfx::Size last_uploaded_frame_size_;
   base::TimeDelta last_uploaded_frame_timestamp_;
 
diff --git a/media/cdm/cdm_adapter.cc b/media/cdm/cdm_adapter.cc
index 3003d9211..3a32303 100644
--- a/media/cdm/cdm_adapter.cc
+++ b/media/cdm/cdm_adapter.cc
@@ -66,26 +66,40 @@
   return cdm::kKeyIds;
 }
 
-CdmPromise::Exception ToMediaExceptionType(cdm::Error error) {
+CdmPromise::Exception ToMediaExceptionType(cdm::Exception exception) {
+  switch (exception) {
+    case cdm::kExceptionTypeError:
+      return CdmPromise::INVALID_ACCESS_ERROR;
+    case cdm::kExceptionNotSupportedError:
+      return CdmPromise::NOT_SUPPORTED_ERROR;
+    case cdm::kExceptionInvalidStateError:
+      return CdmPromise::INVALID_STATE_ERROR;
+    case cdm::kExceptionQuotaExceededError:
+      return CdmPromise::QUOTA_EXCEEDED_ERROR;
+  }
+
+  NOTREACHED() << "Unexpected cdm::Exception " << exception;
+  return CdmPromise::INVALID_STATE_ERROR;
+}
+
+cdm::Exception ToCdmExceptionType(cdm::Error error) {
   switch (error) {
     case cdm::kNotSupportedError:
-      return CdmPromise::NOT_SUPPORTED_ERROR;
+      return cdm::kExceptionNotSupportedError;
     case cdm::kInvalidStateError:
-      return CdmPromise::INVALID_STATE_ERROR;
+      return cdm::kExceptionTypeError;
     case cdm::kInvalidAccessError:
-      return CdmPromise::INVALID_ACCESS_ERROR;
+      return cdm::kExceptionInvalidStateError;
     case cdm::kQuotaExceededError:
-      return CdmPromise::QUOTA_EXCEEDED_ERROR;
+      return cdm::kExceptionQuotaExceededError;
     case cdm::kUnknownError:
-      return CdmPromise::UNKNOWN_ERROR;
     case cdm::kClientError:
-      return CdmPromise::CLIENT_ERROR;
     case cdm::kOutputError:
-      return CdmPromise::OUTPUT_ERROR;
+      break;
   }
 
   NOTREACHED() << "Unexpected cdm::Error " << error;
-  return CdmPromise::UNKNOWN_ERROR;
+  return cdm::kExceptionInvalidStateError;
 }
 
 CdmMessageType ToMediaMessageType(cdm::MessageType message_type) {
@@ -220,7 +234,7 @@
       return Decryptor::kError;
     case cdm::kDecodeError:
       return Decryptor::kError;
-    case cdm::kSessionError:
+    case cdm::kInitializationError:
     case cdm::kDeferredInitialization:
       break;
   }
@@ -294,7 +308,7 @@
     return nullptr;
 
   static_assert(
-      cdm::ContentDecryptionModule::Host::kVersion == cdm::Host_8::kVersion,
+      cdm::ContentDecryptionModule::Host::kVersion == cdm::Host_9::kVersion,
       "update the code below");
 
   // Ensure IsSupportedCdmHostVersion matches implementation of this function.
@@ -304,10 +318,11 @@
 
   DCHECK(
       // Future version is not supported.
-      !IsSupportedCdmHostVersion(cdm::Host_8::kVersion + 1) &&
+      !IsSupportedCdmHostVersion(cdm::Host_9::kVersion + 1) &&
       // Current version is supported.
-      IsSupportedCdmHostVersion(cdm::Host_8::kVersion) &&
+      IsSupportedCdmHostVersion(cdm::Host_9::kVersion) &&
       // Include all previous supported versions (if any) here.
+      IsSupportedCdmHostVersion(cdm::Host_8::kVersion) &&
       // One older than the oldest supported version is not supported.
       !IsSupportedCdmHostVersion(cdm::Host_8::kVersion - 1));
   DCHECK(IsSupportedCdmHostVersion(host_interface_version));
@@ -317,6 +332,8 @@
   switch (host_interface_version) {
     case cdm::Host_8::kVersion:
       return static_cast<cdm::Host_8*>(cdm_adapter);
+    case cdm::Host_9::kVersion:
+      return static_cast<cdm::Host_9*>(cdm_adapter);
     default:
       NOTREACHED() << "Unexpected host interface version "
                    << host_interface_version;
@@ -734,6 +751,15 @@
   return base::Time::Now().ToDoubleT();
 }
 
+void CdmAdapter::OnResolveKeyStatusPromise(uint32_t promise_id,
+                                           cdm::KeyStatus key_status) {
+  // TODO(xhwang): Implement HDCP Policy Check. https://crbug.com/709348.
+  NOTIMPLEMENTED();
+  cdm_promise_adapter_.RejectPromise(promise_id,
+                                     CdmPromise::NOT_SUPPORTED_ERROR, 0,
+                                     "HDCP Policy Check not implemented.");
+}
+
 void CdmAdapter::OnResolvePromise(uint32_t promise_id) {
   DCHECK(task_runner_->BelongsToCurrentThread());
   cdm_promise_adapter_.ResolvePromise(promise_id);
@@ -748,32 +774,51 @@
 }
 
 void CdmAdapter::OnRejectPromise(uint32_t promise_id,
-                                 cdm::Error error,
+                                 cdm::Exception exception,
                                  uint32_t system_code,
                                  const char* error_message,
                                  uint32_t error_message_size) {
   DCHECK(task_runner_->BelongsToCurrentThread());
   cdm_promise_adapter_.RejectPromise(
-      promise_id, ToMediaExceptionType(error), system_code,
+      promise_id, ToMediaExceptionType(exception), system_code,
       std::string(error_message, error_message_size));
 }
 
+void CdmAdapter::OnRejectPromise(uint32_t promise_id,
+                                 cdm::Error error,
+                                 uint32_t system_code,
+                                 const char* error_message,
+                                 uint32_t error_message_size) {
+  // cdm::Host_8 version. Remove when CDM_8 no longer supported.
+  // https://crbug.com/737296.
+  OnRejectPromise(promise_id, ToCdmExceptionType(error), system_code,
+                  error_message, error_message_size);
+}
+
+void CdmAdapter::OnSessionMessage(const char* session_id,
+                                  uint32_t session_id_size,
+                                  cdm::MessageType message_type,
+                                  const char* message,
+                                  uint32_t message_size) {
+  DCHECK(task_runner_->BelongsToCurrentThread());
+  const uint8_t* message_ptr = reinterpret_cast<const uint8_t*>(message);
+  session_message_cb_.Run(
+      std::string(session_id, session_id_size),
+      ToMediaMessageType(message_type),
+      std::vector<uint8_t>(message_ptr, message_ptr + message_size));
+}
+
 void CdmAdapter::OnSessionMessage(const char* session_id,
                                   uint32_t session_id_size,
                                   cdm::MessageType message_type,
                                   const char* message,
                                   uint32_t message_size,
-                                  const char* legacy_destination_url,
-                                  uint32_t legacy_destination_url_size) {
-  DCHECK(task_runner_->BelongsToCurrentThread());
-  // |legacy_destination_url| is obsolete and will be removed as part of
-  // https://crbug.com/570216.
-
-  const uint8_t* message_ptr = reinterpret_cast<const uint8_t*>(message);
-  session_message_cb_.Run(
-      std::string(session_id, session_id_size),
-      ToMediaMessageType(message_type),
-      std::vector<uint8_t>(message_ptr, message_ptr + message_size));
+                                  const char* /* legacy_destination_url */,
+                                  uint32_t /* legacy_destination_url_size */) {
+  // cdm::Host_8 version. Remove when CDM_8 no longer supported.
+  // https://crbug.com/737296.
+  OnSessionMessage(session_id, session_id_size, message_type, message,
+                   message_size);
 }
 
 void CdmAdapter::OnSessionKeysChange(const char* session_id,
@@ -827,8 +872,9 @@
                                       uint32_t system_code,
                                       const char* error_message,
                                       uint32_t error_message_size) {
+  // cdm::Host_8 version. Remove when CDM_8 no longer supported.
+  // https://crbug.com/737296.
   DCHECK(task_runner_->BelongsToCurrentThread());
-  // Obsolete and will be removed as part of https://crbug.com/570216.
 }
 
 void CdmAdapter::SendPlatformChallenge(const char* service_id,
@@ -888,6 +934,12 @@
   return file_io.release();
 }
 
+void CdmAdapter::RequestStorageId() {
+  // TODO(jrummell): Implement Storage Id. https://crbug.com/478960.
+  NOTIMPLEMENTED();
+  cdm_->OnStorageId(nullptr, 0);
+}
+
 bool CdmAdapter::AudioFramesDataToAudioFrames(
     std::unique_ptr<AudioFramesImpl> audio_frames,
     Decryptor::AudioFrames* result_frames) {
diff --git a/media/cdm/cdm_adapter.h b/media/cdm/cdm_adapter.h
index 7eca3403..3210a7c 100644
--- a/media/cdm/cdm_adapter.h
+++ b/media/cdm/cdm_adapter.h
@@ -48,7 +48,8 @@
 class MEDIA_EXPORT CdmAdapter : public ContentDecryptionModule,
                                 public CdmContext,
                                 public Decryptor,
-                                NON_EXPORTED_BASE(public cdm::Host_8) {
+                                NON_EXPORTED_BASE(public cdm::Host_8),
+                                NON_EXPORTED_BASE(public cdm::Host_9) {
  public:
   // Create the CDM using |cdm_path| and initialize it using |key_system| and
   // |cdm_config|. |allocator| is to be used whenever the CDM needs memory
@@ -110,15 +111,49 @@
   void ResetDecoder(StreamType stream_type) final;
   void DeinitializeDecoder(StreamType stream_type) final;
 
-  // cdm::Host_8 implementation.
+  // cdm::Host_9 implementation.
   cdm::Buffer* Allocate(uint32_t capacity) override;
   void SetTimer(int64_t delay_ms, void* context) override;
   cdm::Time GetCurrentWallTime() override;
+  void OnResolveKeyStatusPromise(uint32_t promise_id,
+                                 cdm::KeyStatus key_status) override;
   void OnResolveNewSessionPromise(uint32_t promise_id,
                                   const char* session_id,
                                   uint32_t session_id_size) override;
   void OnResolvePromise(uint32_t promise_id) override;
   void OnRejectPromise(uint32_t promise_id,
+                       cdm::Exception exception,
+                       uint32_t system_code,
+                       const char* error_message,
+                       uint32_t error_message_size) override;
+  void OnSessionMessage(const char* session_id,
+                        uint32_t session_id_size,
+                        cdm::MessageType message_type,
+                        const char* message,
+                        uint32_t message_size) override;
+  void OnSessionKeysChange(const char* session_id,
+                           uint32_t session_id_size,
+                           bool has_additional_usable_key,
+                           const cdm::KeyInformation* keys_info,
+                           uint32_t keys_info_count) override;
+  void OnExpirationChange(const char* session_id,
+                          uint32_t session_id_size,
+                          cdm::Time new_expiry_time) override;
+  void OnSessionClosed(const char* session_id,
+                       uint32_t session_id_size) override;
+  void SendPlatformChallenge(const char* service_id,
+                             uint32_t service_id_size,
+                             const char* challenge,
+                             uint32_t challenge_size) override;
+  void EnableOutputProtection(uint32_t desired_protection_mask) override;
+  void QueryOutputProtectionStatus() override;
+  void OnDeferredInitializationDone(cdm::StreamType stream_type,
+                                    cdm::Status decoder_status) override;
+  cdm::FileIO* CreateFileIO(cdm::FileIOClient* client) override;
+  void RequestStorageId() override;
+
+  // cdm::Host_8 specific implementation.
+  void OnRejectPromise(uint32_t promise_id,
                        cdm::Error error,
                        uint32_t system_code,
                        const char* error_message,
@@ -130,31 +165,12 @@
                         uint32_t message_size,
                         const char* legacy_destination_url,
                         uint32_t legacy_destination_url_size) override;
-  void OnSessionKeysChange(const char* session_id,
-                           uint32_t session_id_size,
-                           bool has_additional_usable_key,
-                           const cdm::KeyInformation* keys_info,
-                           uint32_t keys_info_count) override;
-  void OnExpirationChange(const char* session_id,
-                          uint32_t session_id_size,
-                          cdm::Time new_expiry_time) override;
-  void OnSessionClosed(const char* session_id,
-                       uint32_t session_id_size) override;
   void OnLegacySessionError(const char* session_id,
                             uint32_t session_id_size,
                             cdm::Error error,
                             uint32_t system_code,
                             const char* error_message,
                             uint32_t error_message_size) override;
-  void SendPlatformChallenge(const char* service_id,
-                             uint32_t service_id_size,
-                             const char* challenge,
-                             uint32_t challenge_size) override;
-  void EnableOutputProtection(uint32_t desired_protection_mask) override;
-  void QueryOutputProtectionStatus() override;
-  void OnDeferredInitializationDone(cdm::StreamType stream_type,
-                                    cdm::Status decoder_status) override;
-  cdm::FileIO* CreateFileIO(cdm::FileIOClient* client) override;
 
  private:
   CdmAdapter(const std::string& key_system,
diff --git a/media/cdm/cdm_wrapper.h b/media/cdm/cdm_wrapper.h
index ed88f49..28c4ce8 100644
--- a/media/cdm/cdm_wrapper.h
+++ b/media/cdm/cdm_wrapper.h
@@ -19,11 +19,28 @@
 #define PLATFORM_DCHECK PP_DCHECK
 #else
 #include "base/logging.h"
+#include "media/base/media_switches.h"  // nogncheck
 #define PLATFORM_DCHECK DCHECK
 #endif
 
 namespace media {
 
+namespace {
+
+bool IsExperimentalCdmInterfaceSupported() {
+#if defined(USE_PPAPI_CDM_ADAPTER)
+#if defined(SUPPORT_EXPERIMENTAL_CDM_INTERFACE)
+  return true;
+#else
+  return false;
+#endif  // defined(SUPPORT_EXPERIMENTAL_CDM_INTERFACE)
+#else
+  return base::FeatureList::IsEnabled(media::kSupportExperimentalCdmInterface);
+#endif  // defined(USE_PPAPI_CDM_ADAPTER)
+}
+
+}  // namespace
+
 // Returns a pointer to the requested CDM upon success.
 // Returns NULL if an error occurs or the requested |cdm_interface_version| or
 // |key_system| is not supported or another error occurs.
@@ -109,6 +126,8 @@
       cdm::QueryResult result,
       uint32_t link_mask,
       uint32_t output_protection_mask) = 0;
+  virtual void OnStorageId(const uint8_t* storage_id,
+                           uint32_t storage_id_size) = 0;
 
  protected:
   CdmWrapper() {}
@@ -238,6 +257,10 @@
                                         output_protection_mask);
   }
 
+  void OnStorageId(const uint8_t* storage_id, uint32_t storage_id_size) {
+    cdm_->OnStorageId(storage_id, storage_id_size);
+  }
+
  private:
   CdmWrapperImpl(CdmInterface* cdm) : cdm_(cdm) { PLATFORM_DCHECK(cdm_); }
 
@@ -246,13 +269,22 @@
   DISALLOW_COPY_AND_ASSIGN(CdmWrapperImpl);
 };
 
+// Overrides for cdm::Host_8 methods.
+// TODO(jrummell): Remove when CDM_8 no longer supported.
+// https://crbug.com/737296.
+
+template <>
+void CdmWrapperImpl<cdm::ContentDecryptionModule_8>::OnStorageId(
+    const uint8_t* storage_id,
+    uint32_t storage_id_size) {}
+
 CdmWrapper* CdmWrapper::Create(CreateCdmFunc create_cdm_func,
                                const char* key_system,
                                uint32_t key_system_size,
                                GetCdmHostFunc get_cdm_host_func,
                                void* user_data) {
   static_assert(cdm::ContentDecryptionModule::kVersion ==
-                    cdm::ContentDecryptionModule_8::kVersion,
+                    cdm::ContentDecryptionModule_9::kVersion,
                 "update the code below");
 
   // Ensure IsSupportedCdmInterfaceVersion() matches this implementation.
@@ -260,17 +292,32 @@
   // If this check fails, update this function and DCHECK or update
   // IsSupportedCdmInterfaceVersion().
   PLATFORM_DCHECK(!IsSupportedCdmInterfaceVersion(
-                      cdm::ContentDecryptionModule_8::kVersion + 1) &&
+                      cdm::ContentDecryptionModule_9::kVersion + 1) &&
+                  IsSupportedCdmInterfaceVersion(
+                      cdm::ContentDecryptionModule_9::kVersion) &&
                   IsSupportedCdmInterfaceVersion(
                       cdm::ContentDecryptionModule_8::kVersion) &&
                   !IsSupportedCdmInterfaceVersion(
                       cdm::ContentDecryptionModule_8::kVersion - 1));
 
   // Try to create the CDM using the latest CDM interface version.
-  CdmWrapper* cdm_wrapper =
-      CdmWrapperImpl<cdm::ContentDecryptionModule>::Create(
-          create_cdm_func, key_system, key_system_size, get_cdm_host_func,
-          user_data);
+  // This is only attempted if requested. For pepper plugins, this is done
+  // at compile time. For mojo, it is done using a media feature setting.
+  CdmWrapper* cdm_wrapper = nullptr;
+
+  if (IsExperimentalCdmInterfaceSupported()) {
+    cdm_wrapper = CdmWrapperImpl<cdm::ContentDecryptionModule_9>::Create(
+        create_cdm_func, key_system, key_system_size, get_cdm_host_func,
+        user_data);
+  }
+
+  // If |cdm_wrapper| is NULL, try to create the CDM using older supported
+  // versions of the CDM interface here.
+  if (!cdm_wrapper) {
+    cdm_wrapper = CdmWrapperImpl<cdm::ContentDecryptionModule_8>::Create(
+        create_cdm_func, key_system, key_system_size, get_cdm_host_func,
+        user_data);
+  }
 
   return cdm_wrapper;
 }
@@ -280,7 +327,7 @@
 // does not have.
 // Also update supported_cdm_versions.h.
 static_assert(cdm::ContentDecryptionModule::kVersion ==
-                  cdm::ContentDecryptionModule_8::kVersion,
+                  cdm::ContentDecryptionModule_9::kVersion,
               "ensure cdm wrapper templates have old version support");
 
 }  // namespace media
diff --git a/media/cdm/ppapi/BUILD.gn b/media/cdm/ppapi/BUILD.gn
index 0b6d6b0..29f8465 100644
--- a/media/cdm/ppapi/BUILD.gn
+++ b/media/cdm/ppapi/BUILD.gn
@@ -65,7 +65,10 @@
   output_dir = "$root_out_dir/$clearkey_cdm_path"
 
   # Check whether the plugin's origin URL is valid.
-  defines = [ "CHECK_DOCUMENT_URL" ]
+  defines = [
+    "CHECK_DOCUMENT_URL",
+    "SUPPORT_EXPERIMENTAL_CDM_INTERFACE",
+  ]
   deps = [
     ":clearkeycdm",
     ":clearkeycdmadapter_resources",
diff --git a/media/cdm/ppapi/external_clear_key/clear_key_cdm.cc b/media/cdm/ppapi/external_clear_key/clear_key_cdm.cc
index e44d50f..cca58bd 100644
--- a/media/cdm/ppapi/external_clear_key/clear_key_cdm.cc
+++ b/media/cdm/ppapi/external_clear_key/clear_key_cdm.cc
@@ -548,14 +548,14 @@
 cdm::Status ClearKeyCdm::InitializeAudioDecoder(
     const cdm::AudioDecoderConfig& audio_decoder_config) {
   if (key_system_ == kExternalClearKeyDecryptOnlyKeySystem)
-    return cdm::kSessionError;
+    return cdm::kInitializationError;
 
 #if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
   if (!audio_decoder_)
     audio_decoder_.reset(new media::FFmpegCdmAudioDecoder(host_));
 
   if (!audio_decoder_->Initialize(audio_decoder_config))
-    return cdm::kSessionError;
+    return cdm::kInitializationError;
 
   return cdm::kSuccess;
 #elif defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER)
@@ -572,17 +572,17 @@
 cdm::Status ClearKeyCdm::InitializeVideoDecoder(
     const cdm::VideoDecoderConfig& video_decoder_config) {
   if (key_system_ == kExternalClearKeyDecryptOnlyKeySystem)
-    return cdm::kSessionError;
+    return cdm::kInitializationError;
 
   if (video_decoder_ && video_decoder_->is_initialized()) {
     DCHECK(!video_decoder_->is_initialized());
-    return cdm::kSessionError;
+    return cdm::kInitializationError;
   }
 
   // Any uninitialized decoder will be replaced.
   video_decoder_ = CreateVideoDecoder(host_, video_decoder_config);
   if (!video_decoder_)
-    return cdm::kSessionError;
+    return cdm::kInitializationError;
 
   return cdm::kSuccess;
 }
diff --git a/media/cdm/ppapi/ppapi_cdm_adapter.cc b/media/cdm/ppapi/ppapi_cdm_adapter.cc
index aaa39e2..4698653 100644
--- a/media/cdm/ppapi/ppapi_cdm_adapter.cc
+++ b/media/cdm/ppapi/ppapi_cdm_adapter.cc
@@ -107,11 +107,11 @@
       return PP_DECRYPTRESULT_DECRYPT_ERROR;
     case cdm::kDecodeError:
       return PP_DECRYPTRESULT_DECODE_ERROR;
-    case cdm::kSessionError:
+    case cdm::kInitializationError:
     case cdm::kDeferredInitialization:
-      // kSessionError and kDeferredInitialization are only used by the
-      // Initialize* methods internally and never returned. Deliver*
-      // methods should never use these values.
+      // kInitializationError and kDeferredInitialization are only used by the
+      // Initialize* methods internally and never returned. Deliver* methods
+      // should never use these values.
       break;
   }
 
@@ -255,26 +255,41 @@
   return cdm::kKeyIds;
 }
 
-PP_CdmExceptionCode CdmExceptionTypeToPpCdmExceptionType(cdm::Error error) {
-  switch (error) {
-    case cdm::kNotSupportedError:
+PP_CdmExceptionCode CdmExceptionTypeToPpCdmExceptionType(
+    cdm::Exception exception) {
+  switch (exception) {
+    case cdm::Exception::kExceptionNotSupportedError:
       return PP_CDMEXCEPTIONCODE_NOTSUPPORTEDERROR;
-    case cdm::kInvalidStateError:
+    case cdm::Exception::kExceptionInvalidStateError:
       return PP_CDMEXCEPTIONCODE_INVALIDSTATEERROR;
-    case cdm::kInvalidAccessError:
+    case cdm::Exception::kExceptionTypeError:
       return PP_CDMEXCEPTIONCODE_INVALIDACCESSERROR;
-    case cdm::kQuotaExceededError:
+    case cdm::Exception::kExceptionQuotaExceededError:
       return PP_CDMEXCEPTIONCODE_QUOTAEXCEEDEDERROR;
-    case cdm::kUnknownError:
-      return PP_CDMEXCEPTIONCODE_UNKNOWNERROR;
-    case cdm::kClientError:
-      return PP_CDMEXCEPTIONCODE_CLIENTERROR;
-    case cdm::kOutputError:
-      return PP_CDMEXCEPTIONCODE_OUTPUTERROR;
   }
 
   PP_NOTREACHED();
-  return PP_CDMEXCEPTIONCODE_UNKNOWNERROR;
+  return PP_CDMEXCEPTIONCODE_INVALIDSTATEERROR;
+}
+
+cdm::Exception CdmErrorTypeToCdmExceptionType(cdm::Error error) {
+  switch (error) {
+    case cdm::kNotSupportedError:
+      return cdm::Exception::kExceptionNotSupportedError;
+    case cdm::kInvalidStateError:
+      return cdm::Exception::kExceptionInvalidStateError;
+    case cdm::kInvalidAccessError:
+      return cdm::Exception::kExceptionTypeError;
+    case cdm::kQuotaExceededError:
+      return cdm::Exception::kExceptionQuotaExceededError;
+    case cdm::kUnknownError:
+    case cdm::kClientError:
+    case cdm::kOutputError:
+      break;
+  }
+
+  PP_NOTREACHED();
+  return cdm::Exception::kExceptionInvalidStateError;
 }
 
 PP_CdmMessageType CdmMessageTypeToPpMessageType(cdm::MessageType message) {
@@ -380,7 +395,7 @@
   PP_URLComponents_Dev url_components = {};
   const pp::URLUtil_Dev* url_util = pp::URLUtil_Dev::Get();
   if (!url_util) {
-    RejectPromise(promise_id, cdm::kUnknownError, 0,
+    RejectPromise(promise_id, cdm::Exception::kExceptionInvalidStateError, 0,
                   "Unable to determine origin.");
     return;
   }
@@ -401,7 +416,7 @@
 
   cdm_ = make_linked_ptr(CreateCdmInstance(key_system));
   if (!cdm_) {
-    RejectPromise(promise_id, cdm::kInvalidAccessError, 0,
+    RejectPromise(promise_id, cdm::Exception::kExceptionInvalidStateError, 0,
                   "Unable to create CDM.");
     return;
   }
@@ -423,7 +438,7 @@
   if (!server_certificate_ptr ||
       server_certificate_size < media::limits::kMinCertificateLength ||
       server_certificate_size > media::limits::kMaxCertificateLength) {
-    RejectPromise(promise_id, cdm::kInvalidAccessError, 0,
+    RejectPromise(promise_id, cdm::Exception::kExceptionTypeError, 0,
                   "Incorrect certificate.");
     return;
   }
@@ -509,7 +524,7 @@
     pp::Buffer_Dev extra_data_buffer) {
   PP_DCHECK(!deferred_initialize_audio_decoder_);
   PP_DCHECK(deferred_audio_decoder_config_id_ == 0);
-  cdm::Status status = cdm::kSessionError;
+  cdm::Status status = cdm::kInitializationError;
   if (cdm_) {
     cdm::AudioDecoderConfig cdm_decoder_config;
     cdm_decoder_config.codec =
@@ -539,7 +554,7 @@
     pp::Buffer_Dev extra_data_buffer) {
   PP_DCHECK(!deferred_initialize_video_decoder_);
   PP_DCHECK(deferred_video_decoder_config_id_ == 0);
-  cdm::Status status = cdm::kSessionError;
+  cdm::Status status = cdm::kInitializationError;
   if (cdm_) {
     cdm::VideoDecoderConfig cdm_decoder_config;
     cdm_decoder_config.codec =
@@ -658,6 +673,14 @@
   return pp::Module::Get()->core()->GetTime();
 }
 
+void PpapiCdmAdapter::OnResolveKeyStatusPromise(uint32_t promise_id,
+                                                cdm::KeyStatus key_status) {
+  // TODO(xhwang): Implement HDCP Policy Check. https://crbug.com/709348.
+  PP_NOTREACHED();
+  RejectPromise(promise_id, cdm::Exception::kExceptionNotSupportedError, 0,
+                "HDCP Policy Check not implemented.");
+}
+
 void PpapiCdmAdapter::OnResolveNewSessionPromise(uint32_t promise_id,
                                                  const char* session_id,
                                                  uint32_t session_id_size) {
@@ -672,7 +695,7 @@
 }
 
 void PpapiCdmAdapter::OnRejectPromise(uint32_t promise_id,
-                                      cdm::Error error,
+                                      cdm::Exception exception,
                                       uint32_t system_code,
                                       const char* error_message,
                                       uint32_t error_message_size) {
@@ -685,17 +708,37 @@
                                         kSizeKBMax, kSizeKBBuckets);
   }
 
-  RejectPromise(promise_id, error, system_code,
+  RejectPromise(promise_id, exception, system_code,
                 std::string(error_message, error_message_size));
 }
 
+void PpapiCdmAdapter::OnRejectPromise(uint32_t promise_id,
+                                      cdm::Error error,
+                                      uint32_t system_code,
+                                      const char* error_message,
+                                      uint32_t error_message_size) {
+  OnRejectPromise(promise_id, CdmErrorTypeToCdmExceptionType(error),
+                  system_code, error_message, error_message_size);
+}
+
 void PpapiCdmAdapter::RejectPromise(uint32_t promise_id,
-                                    cdm::Error error,
+                                    cdm::Exception exception,
                                     uint32_t system_code,
                                     const std::string& error_message) {
   PostOnMain(callback_factory_.NewCallback(
       &PpapiCdmAdapter::SendPromiseRejectedInternal, promise_id,
-      SessionError(error, system_code, error_message)));
+      SessionError(exception, system_code, error_message)));
+}
+
+void PpapiCdmAdapter::OnSessionMessage(const char* session_id,
+                                       uint32_t session_id_size,
+                                       cdm::MessageType message_type,
+                                       const char* message,
+                                       uint32_t message_size) {
+  PostOnMain(callback_factory_.NewCallback(
+      &PpapiCdmAdapter::SendSessionMessageInternal,
+      SessionMessage(std::string(session_id, session_id_size), message_type,
+                     message, message_size)));
 }
 
 void PpapiCdmAdapter::OnSessionMessage(const char* session_id,
@@ -710,11 +753,8 @@
   // License requests should not specify |legacy_destination_url|.
   PP_DCHECK(legacy_destination_url_size == 0 ||
             message_type != cdm::MessageType::kLicenseRequest);
-
-  PostOnMain(callback_factory_.NewCallback(
-      &PpapiCdmAdapter::SendSessionMessageInternal,
-      SessionMessage(std::string(session_id, session_id_size), message_type,
-                     message, message_size)));
+  OnSessionMessage(session_id, session_id_size, message_type, message,
+                   message_size);
 }
 
 void PpapiCdmAdapter::OnSessionKeysChange(const char* session_id,
@@ -794,7 +834,7 @@
                                                   const SessionError& error) {
   PP_DCHECK(result == PP_OK);
   pp::ContentDecryptor_Private::PromiseRejected(
-      promise_id, CdmExceptionTypeToPpCdmExceptionType(error.error),
+      promise_id, CdmExceptionTypeToPpCdmExceptionType(error.exception),
       error.system_code, error.error_description);
 }
 
@@ -1117,6 +1157,12 @@
   }
 }
 
+void PpapiCdmAdapter::RequestStorageId() {
+  // TODO(jrummell): Implement Storage Id. https://crbug.com/478960.
+  PP_NOTREACHED();
+  cdm_->OnStorageId(nullptr, 0);
+}
+
 // The CDM owns the returned object and must call FileIO::Close() to release it.
 cdm::FileIO* PpapiCdmAdapter::CreateFileIO(cdm::FileIOClient* client) {
   if (!allow_persistent_state_) {
@@ -1232,10 +1278,10 @@
 }
 
 PpapiCdmAdapter::SessionError::SessionError(
-    cdm::Error error,
+    cdm::Exception exception,
     uint32_t system_code,
     const std::string& error_description)
-    : error(error),
+    : exception(exception),
       system_code(system_code),
       error_description(error_description) {}
 
@@ -1252,7 +1298,7 @@
     return NULL;
 
   static_assert(
-      cdm::ContentDecryptionModule::Host::kVersion == cdm::Host_8::kVersion,
+      cdm::ContentDecryptionModule::Host::kVersion == cdm::Host_9::kVersion,
       "update the code below");
 
   // Ensure IsSupportedCdmHostVersion matches implementation of this function.
@@ -1262,10 +1308,11 @@
 
   PP_DCHECK(
       // Future version is not supported.
-      !IsSupportedCdmHostVersion(cdm::Host_8::kVersion + 1) &&
+      !IsSupportedCdmHostVersion(cdm::Host_9::kVersion + 1) &&
       // Current version is supported.
-      IsSupportedCdmHostVersion(cdm::Host_8::kVersion) &&
+      IsSupportedCdmHostVersion(cdm::Host_9::kVersion) &&
       // Include all previous supported versions (if any) here.
+      IsSupportedCdmHostVersion(cdm::Host_8::kVersion) &&
       // One older than the oldest supported version is not supported.
       !IsSupportedCdmHostVersion(cdm::Host_8::kVersion - 1));
   PP_DCHECK(IsSupportedCdmHostVersion(host_interface_version));
@@ -1275,6 +1322,8 @@
   switch (host_interface_version) {
     case cdm::Host_8::kVersion:
       return static_cast<cdm::Host_8*>(cdm_adapter);
+    case cdm::Host_9::kVersion:
+      return static_cast<cdm::Host_9*>(cdm_adapter);
     default:
       PP_NOTREACHED();
       return NULL;
diff --git a/media/cdm/ppapi/ppapi_cdm_adapter.h b/media/cdm/ppapi/ppapi_cdm_adapter.h
index bd83aa3..91ddf97 100644
--- a/media/cdm/ppapi/ppapi_cdm_adapter.h
+++ b/media/cdm/ppapi/ppapi_cdm_adapter.h
@@ -35,7 +35,8 @@
 // Content Decryption Module (CDM).
 class PpapiCdmAdapter : public pp::Instance,
                         public pp::ContentDecryptor_Private,
-                        public cdm::Host_8 {
+                        public cdm::Host_8,
+                        public cdm::Host_9 {
  public:
   PpapiCdmAdapter(PP_Instance instance, pp::Module* module);
   virtual ~PpapiCdmAdapter();
@@ -82,16 +83,18 @@
       pp::Buffer_Dev encrypted_buffer,
       const PP_EncryptedBlockInfo& encrypted_block_info) override;
 
-  // cdm::Host_8 implementation.
+  // cdm::Host_9 implementation.
   cdm::Buffer* Allocate(uint32_t capacity) override;
   void SetTimer(int64_t delay_ms, void* context) override;
   cdm::Time GetCurrentWallTime() override;
+  void OnResolveKeyStatusPromise(uint32_t promise_id,
+                                 cdm::KeyStatus key_status) override;
   void OnResolveNewSessionPromise(uint32_t promise_id,
                                   const char* session_id,
                                   uint32_t session_id_size) override;
   void OnResolvePromise(uint32_t promise_id) override;
   void OnRejectPromise(uint32_t promise_id,
-                       cdm::Error error,
+                       cdm::Exception exception,
                        uint32_t system_code,
                        const char* error_message,
                        uint32_t error_message_size) override;
@@ -99,9 +102,7 @@
                         uint32_t session_id_size,
                         cdm::MessageType message_type,
                         const char* message,
-                        uint32_t message_size,
-                        const char* legacy_destination_url,
-                        uint32_t legacy_destination_url_size) override;
+                        uint32_t message_size) override;
   void OnSessionKeysChange(const char* session_id,
                            uint32_t session_id_size,
                            bool has_additional_usable_key,
@@ -112,12 +113,6 @@
                           cdm::Time new_expiry_time) override;
   void OnSessionClosed(const char* session_id,
                        uint32_t session_id_size) override;
-  void OnLegacySessionError(const char* session_id,
-                            uint32_t session_id_size,
-                            cdm::Error error,
-                            uint32_t system_code,
-                            const char* error_message,
-                            uint32_t error_message_size) override;
   void SendPlatformChallenge(const char* service_id,
                              uint32_t service_id_size,
                              const char* challenge,
@@ -126,8 +121,29 @@
   void QueryOutputProtectionStatus() override;
   void OnDeferredInitializationDone(cdm::StreamType stream_type,
                                     cdm::Status decoder_status) override;
+  void RequestStorageId() override;
   cdm::FileIO* CreateFileIO(cdm::FileIOClient* client) override;
 
+  // cdm::Host_8 implementation (differences from Host_9).
+  void OnSessionMessage(const char* session_id,
+                        uint32_t session_id_size,
+                        cdm::MessageType message_type,
+                        const char* message,
+                        uint32_t message_size,
+                        const char* legacy_destination_url,
+                        uint32_t legacy_destination_url_size) override;
+  void OnRejectPromise(uint32_t promise_id,
+                       cdm::Error error,
+                       uint32_t system_code,
+                       const char* error_message,
+                       uint32_t error_message_size) override;
+  void OnLegacySessionError(const char* session_id,
+                            uint32_t session_id_size,
+                            cdm::Error error,
+                            uint32_t system_code,
+                            const char* error_message,
+                            uint32_t error_message_size) override;
+
  private:
   // These are reported to UMA server. Do not change the existing values!
   enum OutputProtectionStatus {
@@ -142,10 +158,10 @@
   typedef linked_ptr<AudioFramesImpl> LinkedAudioFrames;
 
   struct SessionError {
-    SessionError(cdm::Error error,
+    SessionError(cdm::Exception exception,
                  uint32_t system_code,
                  const std::string& error_description);
-    cdm::Error error;
+    cdm::Exception exception;
     uint32_t system_code;
     std::string error_description;
   };
@@ -187,7 +203,7 @@
                                     const std::string& session_id,
                                     cdm::Time new_expiry_time);
   void RejectPromise(uint32_t promise_id,
-                     cdm::Error error,
+                     cdm::Exception exception,
                      uint32_t system_code,
                      const std::string& error_message);
 
diff --git a/media/cdm/supported_cdm_versions.cc b/media/cdm/supported_cdm_versions.cc
index 281ca7e..fe9b161 100644
--- a/media/cdm/supported_cdm_versions.cc
+++ b/media/cdm/supported_cdm_versions.cc
@@ -20,10 +20,11 @@
 
 bool IsSupportedCdmInterfaceVersion(int version) {
   static_assert(cdm::ContentDecryptionModule::kVersion ==
-                    cdm::ContentDecryptionModule_8::kVersion,
+                    cdm::ContentDecryptionModule_9::kVersion,
                 "update the code below");
   switch (version) {
     // Supported versions in decreasing order.
+    case cdm::ContentDecryptionModule_9::kVersion:
     case cdm::ContentDecryptionModule_8::kVersion:
       return true;
     default:
@@ -33,10 +34,11 @@
 
 bool IsSupportedCdmHostVersion(int version) {
   static_assert(cdm::ContentDecryptionModule::Host::kVersion ==
-                    cdm::ContentDecryptionModule_8::Host::kVersion,
+                    cdm::ContentDecryptionModule_9::Host::kVersion,
                 "update the code below");
   switch (version) {
     // Supported versions in decreasing order.
+    case cdm::Host_9::kVersion:
     case cdm::Host_8::kVersion:
       return true;
     default:
diff --git a/media/gpu/BUILD.gn b/media/gpu/BUILD.gn
index 1e5b1d0..6066206a 100644
--- a/media/gpu/BUILD.gn
+++ b/media/gpu/BUILD.gn
@@ -128,6 +128,7 @@
   defines = [ "MEDIA_GPU_IMPLEMENTATION" ]
 
   sources = [
+    "accelerated_video_decoder.h",
     "fake_jpeg_decode_accelerator.cc",
     "fake_jpeg_decode_accelerator.h",
     "fake_video_decode_accelerator.cc",
@@ -139,6 +140,10 @@
     "gpu_video_decode_accelerator_helpers.h",
     "gpu_video_encode_accelerator_factory.cc",
     "gpu_video_encode_accelerator_factory.h",
+    "h264_decoder.cc",
+    "h264_decoder.h",
+    "h264_dpb.cc",
+    "h264_dpb.h",
     "shared_memory_region.cc",
     "shared_memory_region.h",
   ]
@@ -242,15 +247,6 @@
     }
   }
 
-  if (is_chromeos || is_win) {
-    sources += [
-      "accelerated_video_decoder.h",
-      "h264_decoder.cc",
-      "h264_decoder.h",
-      "h264_dpb.cc",
-      "h264_dpb.h",
-    ]
-  }
   if (is_chromeos) {
     sources += [
       "vp8_decoder.cc",
@@ -565,6 +561,20 @@
   }
 }
 
+source_set("unit_tests") {
+  testonly = true
+  deps = [
+    "//base",
+    "//media/base:test_support",
+    "//media/gpu",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+  sources = [
+    "h264_decoder_unittest.cc",
+  ]
+}
+
 test("video_decode_accelerator_service_unittest") {
   sources = [
     "ipc/service/gpu_jpeg_decode_accelerator_unittest.cc",
diff --git a/media/gpu/h264_decoder_unittest.cc b/media/gpu/h264_decoder_unittest.cc
new file mode 100644
index 0000000..fdd01f37
--- /dev/null
+++ b/media/gpu/h264_decoder_unittest.cc
@@ -0,0 +1,332 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdint.h>
+#include <string.h>
+
+#include <memory>
+#include <queue>
+#include <string>
+
+#include "base/command_line.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "media/base/test_data_util.h"
+#include "media/gpu/h264_decoder.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+using ::testing::Expectation;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::MakeMatcher;
+using ::testing::Matcher;
+using ::testing::MatcherInterface;
+using ::testing::MatchResultListener;
+using ::testing::Mock;
+using ::testing::Return;
+
+namespace media {
+namespace {
+
+const std::string kBaselineFrame0 = "bear-320x192-baseline-frame-0.h264";
+const std::string kBaselineFrame1 = "bear-320x192-baseline-frame-1.h264";
+const std::string kBaselineFrame2 = "bear-320x192-baseline-frame-2.h264";
+const std::string kBaselineFrame3 = "bear-320x192-baseline-frame-3.h264";
+const std::string kHighFrame0 = "bear-320x192-high-frame-0.h264";
+const std::string kHighFrame1 = "bear-320x192-high-frame-1.h264";
+const std::string kHighFrame2 = "bear-320x192-high-frame-2.h264";
+const std::string kHighFrame3 = "bear-320x192-high-frame-3.h264";
+
+class MockH264Accelerator : public H264Decoder::H264Accelerator {
+ public:
+  MockH264Accelerator() {}
+
+  MOCK_METHOD0(CreateH264Picture, scoped_refptr<H264Picture>());
+  MOCK_METHOD1(SubmitDecode, bool(const scoped_refptr<H264Picture>& pic));
+  MOCK_METHOD1(OutputPicture, bool(const scoped_refptr<H264Picture>& pic));
+
+  bool SubmitFrameMetadata(const H264SPS* sps,
+                           const H264PPS* pps,
+                           const H264DPB& dpb,
+                           const H264Picture::Vector& ref_pic_listp0,
+                           const H264Picture::Vector& ref_pic_listb0,
+                           const H264Picture::Vector& ref_pic_listb1,
+                           const scoped_refptr<H264Picture>& pic) override {
+    return true;
+  }
+
+  bool SubmitSlice(const H264PPS* pps,
+                   const H264SliceHeader* slice_hdr,
+                   const H264Picture::Vector& ref_pic_list0,
+                   const H264Picture::Vector& ref_pic_list1,
+                   const scoped_refptr<H264Picture>& pic,
+                   const uint8_t* data,
+                   size_t size) override {
+    return true;
+  }
+
+  void Reset() override {}
+};
+
+// Test H264Decoder by feeding different of h264 frame sequences and make
+// sure it behaves as expected.
+class H264DecoderTest : public ::testing::Test {
+ public:
+  H264DecoderTest() = default;
+
+  void SetUp() override;
+
+  // Sets the bitstreams to be decoded, frame by frame. The content of each
+  // file is the encoded bitstream of a single video frame.
+  void SetInputFrameFiles(const std::vector<std::string>& frame_files);
+
+  // Keeps decoding the input bitstream set at |SetInputFrameFiles| until the
+  // decoder has consumed all bitstreams or returned from
+  // |H264Decoder::Decode|. Returns the same result as |H264Decoder::Decode|.
+  AcceleratedVideoDecoder::DecodeResult Decode();
+
+ protected:
+  std::unique_ptr<H264Decoder> decoder_;
+  MockH264Accelerator accelerator_;
+
+ private:
+  std::queue<std::string> input_frame_files_;
+  std::string bitstream_;
+};
+
+void H264DecoderTest::SetUp() {
+  decoder_.reset(new H264Decoder(&accelerator_));
+
+  // Sets default behaviors for mock methods for convenience.
+  ON_CALL(accelerator_, CreateH264Picture()).WillByDefault(Invoke([]() {
+    return new H264Picture();
+  }));
+  ON_CALL(accelerator_, SubmitDecode(_)).WillByDefault(Return(true));
+  ON_CALL(accelerator_, OutputPicture(_)).WillByDefault(Return(true));
+}
+
+void H264DecoderTest::SetInputFrameFiles(
+    const std::vector<std::string>& input_frame_files) {
+  for (auto f : input_frame_files)
+    input_frame_files_.push(f);
+}
+
+AcceleratedVideoDecoder::DecodeResult H264DecoderTest::Decode() {
+  while (true) {
+    auto result = decoder_->Decode();
+    if (result != AcceleratedVideoDecoder::kRanOutOfStreamData ||
+        input_frame_files_.empty())
+      return result;
+    auto input_file = GetTestDataFilePath(input_frame_files_.front());
+    input_frame_files_.pop();
+    CHECK(base::ReadFileToString(input_file, &bitstream_));
+    decoder_->SetStream(reinterpret_cast<const uint8_t*>(bitstream_.data()),
+                        bitstream_.size());
+  }
+}
+
+// To have better description on mismatch.
+class WithPocMatcher
+    : public MatcherInterface<const scoped_refptr<H264Picture>&> {
+ public:
+  explicit WithPocMatcher(int expected_poc) : expected_poc_(expected_poc) {}
+
+  bool MatchAndExplain(const scoped_refptr<H264Picture>& p,
+                       MatchResultListener* listener) const override {
+    if (p->pic_order_cnt == expected_poc_)
+      return true;
+    *listener << "with poc: " << p->pic_order_cnt;
+    return false;
+  }
+
+  void DescribeTo(std::ostream* os) const override {
+    *os << "with poc " << expected_poc_;
+  }
+
+ private:
+  int expected_poc_;
+};
+
+inline Matcher<const scoped_refptr<H264Picture>&> WithPoc(int expected_poc) {
+  return MakeMatcher(new WithPocMatcher(expected_poc));
+}
+
+// Test Cases
+
+TEST_F(H264DecoderTest, DecodeSingleFrame) {
+  SetInputFrameFiles({kBaselineFrame0});
+  ASSERT_EQ(AcceleratedVideoDecoder::kAllocateNewSurfaces, Decode());
+  EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
+  EXPECT_LE(9u, decoder_->GetRequiredNumOfPictures());
+
+  EXPECT_CALL(accelerator_, CreateH264Picture()).WillOnce(Return(nullptr));
+  ASSERT_EQ(AcceleratedVideoDecoder::kRanOutOfSurfaces, Decode());
+  ASSERT_TRUE(Mock::VerifyAndClearExpectations(&accelerator_));
+
+  {
+    InSequence sequence;
+    EXPECT_CALL(accelerator_, CreateH264Picture());
+    EXPECT_CALL(accelerator_, SubmitDecode(_));
+    EXPECT_CALL(accelerator_, OutputPicture(_));
+  }
+  ASSERT_EQ(AcceleratedVideoDecoder::kRanOutOfStreamData, Decode());
+  ASSERT_TRUE(decoder_->Flush());
+}
+
+TEST_F(H264DecoderTest, SkipNonIDRFrames) {
+  SetInputFrameFiles({kBaselineFrame1, kBaselineFrame2, kBaselineFrame0});
+  ASSERT_EQ(AcceleratedVideoDecoder::kAllocateNewSurfaces, Decode());
+  EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
+  EXPECT_LE(9u, decoder_->GetRequiredNumOfPictures());
+  {
+    InSequence sequence;
+    EXPECT_CALL(accelerator_, CreateH264Picture());
+    EXPECT_CALL(accelerator_, SubmitDecode(_));
+    EXPECT_CALL(accelerator_, OutputPicture(WithPoc(0)));
+  }
+  ASSERT_EQ(AcceleratedVideoDecoder::kRanOutOfStreamData, Decode());
+  ASSERT_TRUE(decoder_->Flush());
+}
+
+TEST_F(H264DecoderTest, DecodeProfileBaseline) {
+  SetInputFrameFiles({
+      kBaselineFrame0, kBaselineFrame1, kBaselineFrame2, kBaselineFrame3,
+  });
+  ASSERT_EQ(AcceleratedVideoDecoder::kAllocateNewSurfaces, Decode());
+  EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
+  EXPECT_LE(9u, decoder_->GetRequiredNumOfPictures());
+
+  EXPECT_CALL(accelerator_, CreateH264Picture()).Times(4);
+  Expectation decode_poc0, decode_poc2, decode_poc4, decode_poc6;
+  {
+    InSequence decode_order;
+    decode_poc0 = EXPECT_CALL(accelerator_, SubmitDecode(WithPoc(0)));
+    decode_poc2 = EXPECT_CALL(accelerator_, SubmitDecode(WithPoc(2)));
+    decode_poc4 = EXPECT_CALL(accelerator_, SubmitDecode(WithPoc(4)));
+    decode_poc6 = EXPECT_CALL(accelerator_, SubmitDecode(WithPoc(6)));
+  }
+  {
+    InSequence display_order;
+    EXPECT_CALL(accelerator_, OutputPicture(WithPoc(0))).After(decode_poc0);
+    EXPECT_CALL(accelerator_, OutputPicture(WithPoc(2))).After(decode_poc2);
+    EXPECT_CALL(accelerator_, OutputPicture(WithPoc(4))).After(decode_poc4);
+    EXPECT_CALL(accelerator_, OutputPicture(WithPoc(6))).After(decode_poc6);
+  }
+  ASSERT_EQ(AcceleratedVideoDecoder::kRanOutOfStreamData, Decode());
+  ASSERT_TRUE(decoder_->Flush());
+}
+
+TEST_F(H264DecoderTest, DecodeProfileHigh) {
+  SetInputFrameFiles({kHighFrame0, kHighFrame1, kHighFrame2, kHighFrame3});
+  ASSERT_EQ(AcceleratedVideoDecoder::kAllocateNewSurfaces, Decode());
+  EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
+  EXPECT_LE(16u, decoder_->GetRequiredNumOfPictures());
+
+  // Two pictures will be kept in DPB for reordering. The first picture should
+  // be outputted after feeding the third frame.
+  EXPECT_CALL(accelerator_, CreateH264Picture()).Times(4);
+  Expectation decode_poc0, decode_poc2, decode_poc4, decode_poc6;
+  {
+    InSequence decode_order;
+    decode_poc0 = EXPECT_CALL(accelerator_, SubmitDecode(WithPoc(0)));
+    decode_poc4 = EXPECT_CALL(accelerator_, SubmitDecode(WithPoc(4)));
+    decode_poc2 = EXPECT_CALL(accelerator_, SubmitDecode(WithPoc(2)));
+    decode_poc6 = EXPECT_CALL(accelerator_, SubmitDecode(WithPoc(6)));
+  }
+  {
+    InSequence display_order;
+    EXPECT_CALL(accelerator_, OutputPicture(WithPoc(0))).After(decode_poc0);
+    EXPECT_CALL(accelerator_, OutputPicture(WithPoc(2))).After(decode_poc2);
+    EXPECT_CALL(accelerator_, OutputPicture(WithPoc(4))).After(decode_poc4);
+    EXPECT_CALL(accelerator_, OutputPicture(WithPoc(6))).After(decode_poc6);
+  }
+  ASSERT_EQ(AcceleratedVideoDecoder::kRanOutOfStreamData, Decode());
+  ASSERT_TRUE(decoder_->Flush());
+}
+
+TEST_F(H264DecoderTest, SwitchBaselineToHigh) {
+  SetInputFrameFiles({
+      kBaselineFrame0, kHighFrame0, kHighFrame1, kHighFrame2, kHighFrame3,
+  });
+  ASSERT_EQ(AcceleratedVideoDecoder::kAllocateNewSurfaces, Decode());
+  EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
+  EXPECT_LE(9u, decoder_->GetRequiredNumOfPictures());
+
+  EXPECT_CALL(accelerator_, CreateH264Picture());
+  {
+    InSequence sequence;
+    EXPECT_CALL(accelerator_, SubmitDecode(_));
+    EXPECT_CALL(accelerator_, OutputPicture(WithPoc(0)));
+  }
+  ASSERT_EQ(AcceleratedVideoDecoder::kAllocateNewSurfaces, Decode());
+  EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
+  EXPECT_LE(16u, decoder_->GetRequiredNumOfPictures());
+
+  ASSERT_TRUE(Mock::VerifyAndClearExpectations(&accelerator_));
+
+  EXPECT_CALL(accelerator_, CreateH264Picture()).Times(4);
+  Expectation decode_poc0, decode_poc2, decode_poc4, decode_poc6;
+  {
+    InSequence decode_order;
+    decode_poc0 = EXPECT_CALL(accelerator_, SubmitDecode(WithPoc(0)));
+    decode_poc4 = EXPECT_CALL(accelerator_, SubmitDecode(WithPoc(4)));
+    decode_poc2 = EXPECT_CALL(accelerator_, SubmitDecode(WithPoc(2)));
+    decode_poc6 = EXPECT_CALL(accelerator_, SubmitDecode(WithPoc(6)));
+  }
+  {
+    InSequence display_order;
+    EXPECT_CALL(accelerator_, OutputPicture(WithPoc(0))).After(decode_poc0);
+    EXPECT_CALL(accelerator_, OutputPicture(WithPoc(2))).After(decode_poc2);
+    EXPECT_CALL(accelerator_, OutputPicture(WithPoc(4))).After(decode_poc4);
+    EXPECT_CALL(accelerator_, OutputPicture(WithPoc(6))).After(decode_poc6);
+  }
+  ASSERT_EQ(AcceleratedVideoDecoder::kRanOutOfStreamData, Decode());
+  ASSERT_TRUE(decoder_->Flush());
+}
+
+TEST_F(H264DecoderTest, SwitchHighToBaseline) {
+  SetInputFrameFiles({
+      kHighFrame0, kBaselineFrame0, kBaselineFrame1, kBaselineFrame2,
+      kBaselineFrame3,
+  });
+  ASSERT_EQ(AcceleratedVideoDecoder::kAllocateNewSurfaces, Decode());
+  EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
+  EXPECT_LE(16u, decoder_->GetRequiredNumOfPictures());
+
+  EXPECT_CALL(accelerator_, CreateH264Picture());
+  {
+    InSequence sequence;
+    EXPECT_CALL(accelerator_, SubmitDecode(_));
+    EXPECT_CALL(accelerator_, OutputPicture(WithPoc(0)));
+  }
+  ASSERT_EQ(AcceleratedVideoDecoder::kAllocateNewSurfaces, Decode());
+  EXPECT_EQ(gfx::Size(320, 192), decoder_->GetPicSize());
+  EXPECT_LE(9u, decoder_->GetRequiredNumOfPictures());
+
+  ASSERT_TRUE(Mock::VerifyAndClearExpectations(&accelerator_));
+
+  EXPECT_CALL(accelerator_, CreateH264Picture()).Times(4);
+  Expectation decode_poc0, decode_poc2, decode_poc4, decode_poc6;
+  {
+    InSequence decode_order;
+    decode_poc0 = EXPECT_CALL(accelerator_, SubmitDecode(WithPoc(0)));
+    decode_poc2 = EXPECT_CALL(accelerator_, SubmitDecode(WithPoc(2)));
+    decode_poc4 = EXPECT_CALL(accelerator_, SubmitDecode(WithPoc(4)));
+    decode_poc6 = EXPECT_CALL(accelerator_, SubmitDecode(WithPoc(6)));
+  }
+  {
+    InSequence display_order;
+    EXPECT_CALL(accelerator_, OutputPicture(WithPoc(0))).After(decode_poc0);
+    EXPECT_CALL(accelerator_, OutputPicture(WithPoc(2))).After(decode_poc2);
+    EXPECT_CALL(accelerator_, OutputPicture(WithPoc(4))).After(decode_poc4);
+    EXPECT_CALL(accelerator_, OutputPicture(WithPoc(6))).After(decode_poc6);
+  }
+  ASSERT_EQ(AcceleratedVideoDecoder::kRanOutOfStreamData, Decode());
+  ASSERT_TRUE(decoder_->Flush());
+}
+
+}  // namespace
+}  // namespace media
diff --git a/media/gpu/h264_dpb.h b/media/gpu/h264_dpb.h
index e8f119f..2af8a77 100644
--- a/media/gpu/h264_dpb.h
+++ b/media/gpu/h264_dpb.h
@@ -15,6 +15,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "media/filters/h264_parser.h"
+#include "media/gpu/media_gpu_export.h"
 #include "ui/gfx/geometry/rect.h"
 
 namespace media {
@@ -24,7 +25,7 @@
 
 // A picture (a frame or a field) in the H.264 spec sense.
 // See spec at http://www.itu.int/rec/T-REC-H.264
-class H264Picture : public base::RefCounted<H264Picture> {
+class MEDIA_GPU_EXPORT H264Picture : public base::RefCounted<H264Picture> {
  public:
   using Vector = std::vector<scoped_refptr<H264Picture>>;
 
diff --git a/media/mojo/interfaces/BUILD.gn b/media/mojo/interfaces/BUILD.gn
index 0d98b3c..9055e38e 100644
--- a/media/mojo/interfaces/BUILD.gn
+++ b/media/mojo/interfaces/BUILD.gn
@@ -52,10 +52,36 @@
   ]
 }
 
+mojom("remoting_common") {
+  sources = [
+    "remoting_common.mojom",
+  ]
+
+  # TODO(crbug.com/699569): Convert to use the new JS bindings.
+  use_new_js_bindings = false
+}
+
+mojom("mirror_service_remoting") {
+  sources = [
+    "mirror_service_remoting.mojom",
+  ]
+
+  public_deps = [
+    ":remoting_common",
+  ]
+
+  # TODO(crbug.com/699569): Convert to use the new JS bindings.
+  use_new_js_bindings = false
+}
+
 mojom("remoting") {
   sources = [
     "remoting.mojom",
   ]
+
+  public_deps = [
+    ":remoting_common",
+  ]
 }
 
 mojom("test_interfaces") {
diff --git a/media/mojo/interfaces/mirror_service_remoting.mojom b/media/mojo/interfaces/mirror_service_remoting.mojom
new file mode 100644
index 0000000..0c76dbb
--- /dev/null
+++ b/media/mojo/interfaces/mirror_service_remoting.mojom
@@ -0,0 +1,56 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module media.mojom;
+
+import "media/mojo/interfaces/remoting_common.mojom";
+
+// Interface used by the source to start/stop remoting and send data to the
+// sink.
+interface MirrorServiceRemoter {
+  // Starts a remoting session. Always assumes the remoting session will be
+  // stared successfully. If any failure happens,
+  // MirrorServiceRemotingSource::OnError() will be called.
+  Start();
+
+  // Starts remoting the media data streams. This is called after Start() to
+  // indicate audio/video bitstream data is ready to be consumed. Returns
+  // audio/video stream IDs. A valid stream ID should be greater than 0. When
+  // there is no audio/video, or if the data stream is not successfully started,
+  // the returned stream ID is -1.
+  StartDataStreams(bool has_audio, bool has_video)
+      => (int32 audio_stream_id, int32 video_stream_id);
+
+  // Stops remoting media. Messages in both directions will be dropped after
+  // this point as well as any pending or in-transit media bitstream data.
+  Stop(RemotingStopReason reason);
+
+  // Sends|message| to the sink. |message| is a serialized protobuf from
+  // src/media/remoting/proto.
+  SendMessageToSink(array<uint8> message);
+};
+
+// Interface used for sending notifications back to the source's control logic,
+// and to pass messages from the sink back to the source.
+interface MirrorServiceRemotingSource {
+  // Notifies the source that the sink is now available to start remoting and
+  // passes the receiver's capabilities. It is up to the source's control logic
+  // to decide whether/when to start remoting.
+  OnSinkAvailable(SinkCapabilities capabilities);
+
+  // Passes a |message| from the sink back to the source. The |message| consists
+  // of a serialized protobuf from src/media/remoting/proto. This will only be
+  // called after OnStarted() and before OnStopped().
+  OnMessageFromSink(array<uint8> message);
+
+  // Notifies the source that remoting has terminated. This may or may not be in
+  // response to a MirrorServiceRemoter.Stop() call, as other events (possibly
+  // external) may have caused remoting to end.
+  OnStopped(RemotingStopReason reason);
+
+  // Notifies the source that a fatal error has occurred. Remoting session will
+  // be stopped immediately once this is called.
+  // TODO(xjz): Add error codes in future to indicate different errors.
+  OnError();
+};
diff --git a/media/mojo/interfaces/remoting.mojom b/media/mojo/interfaces/remoting.mojom
index ba218f3..8960cb4 100644
--- a/media/mojo/interfaces/remoting.mojom
+++ b/media/mojo/interfaces/remoting.mojom
@@ -4,6 +4,8 @@
 
 module media.mojom;
 
+import "media/mojo/interfaces/remoting_common.mojom";
+
 interface RemoterFactory {
   // Create a new Remoter associated with the given RemotingSource and bind it
   // to the given interface request. The RemotingSource will be notified when
@@ -32,7 +34,7 @@
   // provide all of the frame's data.
   SendFrame();
 
-  // Cancel the transmission of all in-flight data to the remote, up to and
+  // Cancels the transmission of all in-flight data to the remote, up to and
   // including the last SendFrame() call; and also discard any data chunks
   // that were consumed from the data pipe for the next frame. This is used to
   // optimize seeking, when it is known that any in-flight data is no longer
@@ -41,26 +43,17 @@
   CancelInFlightData();
 };
 
-enum RemotingStopReason {
-  ROUTE_TERMINATED,  // User-initiated disconnect, etc.
-  LOCAL_PLAYBACK,  // Media switched back to local playback.
-  SOURCE_GONE,  // RemotingSource has been destroyed.
-  MESSAGE_SEND_FAILED,  // Failed to send a message to the sink.
-  DATA_SEND_FAILED,  // Failed to consume from a data pipe or send to the sink.
-  UNEXPECTED_FAILURE,  // Unexpected failure or inconsistent state encountered.
-};
-
 // Interface used by the source to start/stop remoting and send data to the
 // sink.
 interface Remoter {
-  // Start a remoting session (once the sink has been reported to be available;
+  // Starts a remoting session (once the sink has been reported to be available;
   // see RemotingSource). Either RemotingSource.OnStarted() or OnStartFailed()
   // will be called to indicate success or failure. Once OnStarted() has been
   // invoked, the source may then make calls to SendMessageToSink() and expect
   // messages from the remote via RemotingSource.OnMessageFromSink().
   Start();
 
-  // Start remoting the media data streams. This is called after Start() to
+  // Starts remoting the media data streams. This is called after Start() to
   // provide data pipes from which the audio/video bitstream data is consumed
   // and then transported to the remote device. RemotingDataStreamSenders are
   // used by the source to control when data should be consumed from the data
@@ -71,37 +64,34 @@
                    RemotingDataStreamSender&? audio_sender,
                    RemotingDataStreamSender&? video_sender);
 
-  // Stop remoting media. Messages in both directions will be dropped after this
+  // Stops remoting media. Messages in both directions will be dropped after this
   // point as well as any pending or in-transit media bitstream data.
   Stop(RemotingStopReason reason);
 
-  // Send |message| to the sink. |message| is a serialized protobuf from
+  // Sends |message| to the sink. |message| is a serialized protobuf from
   // src/media/remoting/proto.
   SendMessageToSink(array<uint8> message);
 };
 
+// TODO(xjz): Replace this with the SinkCapabilities struct defined in
+// remoting_common.mojom.
 enum RemotingSinkCapabilities {
   NONE,
   RENDERING_ONLY,
   CONTENT_DECRYPTION_AND_RENDERING,
 };
 
-enum RemotingStartFailReason {
-  CANNOT_START_MULTIPLE,  // Remoting was already active.
-  ROUTE_TERMINATED,  // User-initated disconnect while starting remoting.
-};
-
 // Interface used for sending notifications back to the local source's control
 // logic, and to pass messages from the sink back to the local media pipeline.
 interface RemotingSource {
-  // Notify the source that the sink is now available to start remoting. It is
+  // Notifies the source that the sink is now available to start remoting. It is
   // up to the source's control logic to decide whether/when to start remoting.
   //
   // TODO(miu): In a later change, pass detailed information about the sink's
   // capabilities (e.g., codec support, DRM keysystem support, etc.).
   OnSinkAvailable(RemotingSinkCapabilities capabilities);
 
-  // Notify the source that the sink is no longer available for remoting. This
+  // Notifies the source that the sink is no longer available for remoting. This
   // may happen, for example, because the sink has been shut down, or because
   // another source has started remoting.
   OnSinkGone();
@@ -114,12 +104,12 @@
   OnStarted();
   OnStartFailed(RemotingStartFailReason reason);
 
-  // Pass a |message| from the sink back to the source. The |message| consists
+  // Passes a |message| from the sink back to the source. The |message| consists
   // of a serialized protobuf from src/media/remoting/proto. This will only be
   // called after OnStarted() and before OnStopped().
   OnMessageFromSink(array<uint8> message);
 
-  // Notify the source that remoting has terminated. This may or may not be in
+  // Notifies the source that remoting has terminated. This may or may not be in
   // response to a Remoter.Stop() call, as other events (possibly external) may
   // have caused remoting to end.
   OnStopped(RemotingStopReason reason);
diff --git a/media/mojo/interfaces/remoting_common.mojom b/media/mojo/interfaces/remoting_common.mojom
new file mode 100644
index 0000000..fe3536e
--- /dev/null
+++ b/media/mojo/interfaces/remoting_common.mojom
@@ -0,0 +1,47 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module media.mojom;
+
+enum RemotingStopReason {
+  ROUTE_TERMINATED,  // User-initiated disconnect, etc.
+  LOCAL_PLAYBACK,  // Media switched back to local playback.
+  SOURCE_GONE,  // RemotingSource has been destroyed.
+  MESSAGE_SEND_FAILED,  // Failed to send a message to the sink.
+  DATA_SEND_FAILED,  // Failed to consume from a data pipe or send to the sink.
+  UNEXPECTED_FAILURE,  // Unexpected failure or inconsistent state encountered.
+  SERVICE_GONE,  // Mirror service disconnected.
+};
+
+enum RemotingStartFailReason {
+  CANNOT_START_MULTIPLE,  // Remoting was already active.
+  ROUTE_TERMINATED,  // User-initated disconnect while starting remoting.
+  SERVICE_NOT_CONNECTED,  // Mirror service was not connected.
+};
+
+enum RemotingSinkFeatures {
+  RENDERING,
+  CONTENT_DECRYPTION,
+};
+
+enum RemotingSinkAudioCapabilities {
+  CODEC_BASELINE_SET,
+  CODEC_AAC,
+  CODEC_OPUS,
+};
+
+enum RemotingSinkVideoCapabilities {
+  SUPPORT_4K,
+  CODEC_BASELINE_SET,
+  CODEC_H264,
+  CODEC_VP8,
+  CODEC_VP9,
+  CODEC_HEVC,
+};
+
+struct SinkCapabilities {
+  array<RemotingSinkFeatures>  features;
+  array<RemotingSinkAudioCapabilities> audio;
+  array<RemotingSinkVideoCapabilities> video;
+};
diff --git a/media/remoting/proto_utils.cc b/media/remoting/proto_utils.cc
index 55e1ab5..3920163 100644
--- a/media/remoting/proto_utils.cc
+++ b/media/remoting/proto_utils.cc
@@ -11,6 +11,7 @@
 #include "base/time/time.h"
 #include "base/values.h"
 #include "media/base/encryption_scheme.h"
+#include "media/base/timestamp_constants.h"
 #include "media/remoting/proto_enum_utils.h"
 
 namespace media {
@@ -346,6 +347,13 @@
   // HACK: Set the following to prevent "disable video when hidden" logic in
   // media::blink::WebMediaPlayerImpl.
   stats->video_keyframe_distance_average = base::TimeDelta::Max();
+
+  // This field was added after the initial message definition. Check that
+  // sender provided the value.
+  if (stats_message.has_video_frame_duration_average_usec()) {
+    stats->video_frame_duration_average = base::TimeDelta::FromMicroseconds(
+        stats_message.video_frame_duration_average_usec());
+  }
 }
 
 void ConvertCdmKeyInfoToProto(
diff --git a/media/remoting/proto_utils_unittest.cc b/media/remoting/proto_utils_unittest.cc
index c974c0f8..12aae106 100644
--- a/media/remoting/proto_utils_unittest.cc
+++ b/media/remoting/proto_utils_unittest.cc
@@ -142,6 +142,7 @@
   original.audio_memory_usage = 32;
   original.video_memory_usage = 43;
   original.video_keyframe_distance_average = base::TimeDelta::Max();
+  original.video_frame_duration_average = base::TimeDelta::Max();
 
   // There is no convert-to-proto function, so just do that here.
   pb::PipelineStatistics pb_stats;
@@ -151,6 +152,8 @@
   pb_stats.set_video_frames_dropped(original.video_frames_dropped);
   pb_stats.set_audio_memory_usage(original.audio_memory_usage);
   pb_stats.set_video_memory_usage(original.video_memory_usage);
+  pb_stats.set_video_frame_duration_average_usec(
+      original.video_frame_duration_average.InMicroseconds());
 
   PipelineStatistics converted;
   memset(&converted, ~0xcd, sizeof(converted));  // See note above.
diff --git a/media/remoting/renderer_controller.cc b/media/remoting/renderer_controller.cc
index 998fa99..176c930 100644
--- a/media/remoting/renderer_controller.cc
+++ b/media/remoting/renderer_controller.cc
@@ -118,6 +118,7 @@
 }
 
 void RendererController::OnSetCdm(CdmContext* cdm_context) {
+  VLOG(2) << __func__;
   DCHECK(thread_checker_.CalledOnValidThread());
 
   auto* remoting_cdm_context = RemotingCdmContext::From(cdm_context);
@@ -131,6 +132,7 @@
 }
 
 void RendererController::OnRemotePlaybackDisabled(bool disabled) {
+  VLOG(2) << __func__ << ": disabled = " << disabled;
   DCHECK(thread_checker_.CalledOnValidThread());
 
   is_remote_playback_disabled_ = disabled;
@@ -290,6 +292,10 @@
     case mojom::RemotingSinkCapabilities::RENDERING_ONLY:
     case mojom::RemotingSinkCapabilities::CONTENT_DECRYPTION_AND_RENDERING:
       break;  // The sink is capable of remote rendering.
+    default:
+      // TODO(xjz): Will be changed in a coming CL that passes the receiver's
+      // capabilities.
+      NOTREACHED();
   }
 
   if ((!has_audio() && !has_video()) ||
@@ -352,6 +358,7 @@
 }
 
 void RendererController::OnRendererFatalError(StopTrigger stop_trigger) {
+  VLOG(2) << __func__ << ": stop_trigger= " << stop_trigger;
   DCHECK(thread_checker_.CalledOnValidThread());
 
   // Do not act on errors caused by things like Mojo pipes being closed during
diff --git a/media/remoting/rpc.proto b/media/remoting/rpc.proto
index f8f0cd6..aaab7b39 100644
--- a/media/remoting/rpc.proto
+++ b/media/remoting/rpc.proto
@@ -254,6 +254,7 @@
   optional uint32 video_frames_dropped = 4;
   optional int64 audio_memory_usage = 5;
   optional int64 video_memory_usage = 6;
+  optional int64 video_frame_duration_average_usec = 7;
 };
 
 message CdmKeyInformation {
diff --git a/media/renderers/video_renderer_impl.cc b/media/renderers/video_renderer_impl.cc
index fbd82d7b..5f3ada1d 100644
--- a/media/renderers/video_renderer_impl.cc
+++ b/media/renderers/video_renderer_impl.cc
@@ -696,6 +696,9 @@
     const size_t memory_usage = algorithm_->GetMemoryUsage();
     statistics.video_memory_usage = memory_usage - last_video_memory_usage_;
 
+    statistics.video_frame_duration_average =
+        algorithm_->average_frame_duration();
+
     task_runner_->PostTask(FROM_HERE,
                            base::Bind(&VideoRendererImpl::OnStatisticsUpdate,
                                       weak_factory_.GetWeakPtr(), statistics));
diff --git a/media/test/data/bear-320x192-baseline-frame-0.h264 b/media/test/data/bear-320x192-baseline-frame-0.h264
new file mode 100644
index 0000000..a8d6a14d
--- /dev/null
+++ b/media/test/data/bear-320x192-baseline-frame-0.h264
Binary files differ
diff --git a/media/test/data/bear-320x192-baseline-frame-1.h264 b/media/test/data/bear-320x192-baseline-frame-1.h264
new file mode 100644
index 0000000..21176c4
--- /dev/null
+++ b/media/test/data/bear-320x192-baseline-frame-1.h264
Binary files differ
diff --git a/media/test/data/bear-320x192-baseline-frame-2.h264 b/media/test/data/bear-320x192-baseline-frame-2.h264
new file mode 100644
index 0000000..10c93782
--- /dev/null
+++ b/media/test/data/bear-320x192-baseline-frame-2.h264
Binary files differ
diff --git a/media/test/data/bear-320x192-baseline-frame-3.h264 b/media/test/data/bear-320x192-baseline-frame-3.h264
new file mode 100644
index 0000000..9e7e37f
--- /dev/null
+++ b/media/test/data/bear-320x192-baseline-frame-3.h264
Binary files differ
diff --git a/media/test/data/bear-320x192-high-frame-0.h264 b/media/test/data/bear-320x192-high-frame-0.h264
new file mode 100644
index 0000000..1379b224c
--- /dev/null
+++ b/media/test/data/bear-320x192-high-frame-0.h264
Binary files differ
diff --git a/media/test/data/bear-320x192-high-frame-1.h264 b/media/test/data/bear-320x192-high-frame-1.h264
new file mode 100644
index 0000000..b06a1dfa
--- /dev/null
+++ b/media/test/data/bear-320x192-high-frame-1.h264
Binary files differ
diff --git a/media/test/data/bear-320x192-high-frame-2.h264 b/media/test/data/bear-320x192-high-frame-2.h264
new file mode 100644
index 0000000..36ccb65
--- /dev/null
+++ b/media/test/data/bear-320x192-high-frame-2.h264
Binary files differ
diff --git a/media/test/data/bear-320x192-high-frame-3.h264 b/media/test/data/bear-320x192-high-frame-3.h264
new file mode 100644
index 0000000..a53c98f8
--- /dev/null
+++ b/media/test/data/bear-320x192-high-frame-3.h264
Binary files differ
diff --git a/net/cookies/cookie_monster.cc b/net/cookies/cookie_monster.cc
index 85b658e..d084a2397 100644
--- a/net/cookies/cookie_monster.cc
+++ b/net/cookies/cookie_monster.cc
@@ -1937,15 +1937,6 @@
   static_assert(arraysize(kChangeCauseMapping) == DELETE_COOKIE_LAST_ENTRY + 1,
                 "kChangeCauseMapping size should match DeletionCause size");
 
-  // See InitializeHistograms() for details.
-  DeletionCause deletion_cause_to_record = deletion_cause;
-  if (deletion_cause >= DELETE_COOKIE_CREATED_BETWEEN &&
-      deletion_cause <= DELETE_COOKIE_CANONICAL) {
-    deletion_cause_to_record = DELETE_COOKIE_EXPLICIT;
-  }
-  if (deletion_cause != DELETE_COOKIE_DONT_RECORD)
-    histogram_cookie_deletion_cause_->Add(deletion_cause_to_record);
-
   CanonicalCookie* cc = it->second.get();
   VLOG(kVlogSetCookies) << "InternalDeleteCookie()"
                         << ", cause:" << deletion_cause
@@ -2202,8 +2193,6 @@
   DCHECK(thread_checker_.CalledOnValidThread());
 
   for (CookieItVector::iterator it = it_begin; it != it_end; it++) {
-    histogram_evicted_last_access_minutes_->Add(
-        (current - (*it)->second->LastAccessDate()).InMinutes());
     InternalDeleteCookie((*it), true, cause);
   }
   return it_end - it_begin;
@@ -2348,16 +2337,10 @@
   histogram_expiration_duration_minutes_ = base::Histogram::FactoryGet(
       "Cookie.ExpirationDurationMinutes", 1, kMinutesInTenYears, 50,
       base::Histogram::kUmaTargetedHistogramFlag);
-  histogram_evicted_last_access_minutes_ = base::Histogram::FactoryGet(
-      "Cookie.EvictedLastAccessMinutes", 1, kMinutesInTenYears, 50,
-      base::Histogram::kUmaTargetedHistogramFlag);
   histogram_count_ = base::Histogram::FactoryGet(
       "Cookie.Count", 1, 4000, 50, base::Histogram::kUmaTargetedHistogramFlag);
 
   // From UMA_HISTOGRAM_ENUMERATION
-  histogram_cookie_deletion_cause_ = base::LinearHistogram::FactoryGet(
-      "Cookie.DeletionCause", 1, DELETE_COOKIE_LAST_ENTRY - 1,
-      DELETE_COOKIE_LAST_ENTRY, base::Histogram::kUmaTargetedHistogramFlag);
   histogram_cookie_type_ = base::LinearHistogram::FactoryGet(
       "Cookie.Type", 1, (1 << COOKIE_TYPE_LAST_ENTRY) - 1,
       1 << COOKIE_TYPE_LAST_ENTRY, base::Histogram::kUmaTargetedHistogramFlag);
diff --git a/net/cookies/cookie_monster.h b/net/cookies/cookie_monster.h
index 6d8a533..7fb91e44 100644
--- a/net/cookies/cookie_monster.h
+++ b/net/cookies/cookie_monster.h
@@ -675,9 +675,7 @@
   // Histogram variables; see CookieMonster::InitializeHistograms() in
   // cookie_monster.cc for details.
   base::HistogramBase* histogram_expiration_duration_minutes_;
-  base::HistogramBase* histogram_evicted_last_access_minutes_;
   base::HistogramBase* histogram_count_;
-  base::HistogramBase* histogram_cookie_deletion_cause_;
   base::HistogramBase* histogram_cookie_type_;
   base::HistogramBase* histogram_cookie_source_scheme_;
   base::HistogramBase* histogram_cookie_delete_equivalent_;
diff --git a/net/dns/dns_config_service.h b/net/dns/dns_config_service.h
index d8845d1b..603fb4cc 100644
--- a/net/dns/dns_config_service.h
+++ b/net/dns/dns_config_service.h
@@ -11,7 +11,6 @@
 #include <vector>
 
 #include "base/macros.h"
-#include "base/memory/scoped_vector.h"
 #include "base/threading/thread_checker.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
diff --git a/net/socket/client_socket_pool_base_unittest.cc b/net/socket/client_socket_pool_base_unittest.cc
index 81fc5d5..fa86ecc 100644
--- a/net/socket/client_socket_pool_base_unittest.cc
+++ b/net/socket/client_socket_pool_base_unittest.cc
@@ -15,7 +15,6 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
-#include "base/memory/scoped_vector.h"
 #include "base/memory/weak_ptr.h"
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
diff --git a/net/socket/socket_test_util.h b/net/socket/socket_test_util.h
index 9790f49..d961dde 100644
--- a/net/socket/socket_test_util.h
+++ b/net/socket/socket_test_util.h
@@ -19,7 +19,6 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/ref_counted.h"
-#include "base/memory/scoped_vector.h"
 #include "base/memory/weak_ptr.h"
 #include "base/strings/string16.h"
 #include "base/time/time.h"
diff --git a/net/ssl/default_channel_id_store.h b/net/ssl/default_channel_id_store.h
index b07495c6e..bc5ab07 100644
--- a/net/ssl/default_channel_id_store.h
+++ b/net/ssl/default_channel_id_store.h
@@ -14,7 +14,6 @@
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
-#include "base/memory/scoped_vector.h"
 #include "base/memory/weak_ptr.h"
 #include "net/base/net_export.h"
 #include "net/ssl/channel_id_store.h"
diff --git a/net/url_request/url_request_context_builder.h b/net/url_request/url_request_context_builder.h
index c38dea98..5bd634c9 100644
--- a/net/url_request/url_request_context_builder.h
+++ b/net/url_request/url_request_context_builder.h
@@ -250,56 +250,6 @@
   void SetSpdyAndQuicEnabled(bool spdy_enabled,
                              bool quic_enabled);
 
-  void set_quic_connection_options(
-      const QuicTagVector& quic_connection_options) {
-    http_network_session_params_.quic_connection_options =
-        quic_connection_options;
-  }
-
-  void set_quic_user_agent_id(const std::string& quic_user_agent_id) {
-    http_network_session_params_.quic_user_agent_id = quic_user_agent_id;
-  }
-
-  void set_quic_max_server_configs_stored_in_properties(
-      int quic_max_server_configs_stored_in_properties) {
-    http_network_session_params_.quic_max_server_configs_stored_in_properties =
-        quic_max_server_configs_stored_in_properties;
-  }
-
-  void set_quic_idle_connection_timeout_seconds(
-      int quic_idle_connection_timeout_seconds) {
-    http_network_session_params_.quic_idle_connection_timeout_seconds =
-        quic_idle_connection_timeout_seconds;
-  }
-
-  void set_quic_close_sessions_on_ip_change(
-      bool quic_close_sessions_on_ip_change) {
-    http_network_session_params_.quic_close_sessions_on_ip_change =
-        quic_close_sessions_on_ip_change;
-  }
-
-  void set_quic_migrate_sessions_on_network_change(
-      bool quic_migrate_sessions_on_network_change) {
-    http_network_session_params_.quic_migrate_sessions_on_network_change =
-        quic_migrate_sessions_on_network_change;
-  }
-
-  void set_quic_migrate_sessions_early(bool quic_migrate_sessions_early) {
-    http_network_session_params_.quic_migrate_sessions_early =
-        quic_migrate_sessions_early;
-  }
-
-  void set_quic_disable_bidirectional_streams(
-      bool quic_disable_bidirectional_streams) {
-    http_network_session_params_.quic_disable_bidirectional_streams =
-        quic_disable_bidirectional_streams;
-  }
-
-  void set_quic_race_cert_verification(bool quic_race_cert_verification) {
-    http_network_session_params_.quic_race_cert_verification =
-        quic_race_cert_verification;
-  }
-
   void set_throttling_enabled(bool throttling_enabled) {
     throttling_enabled_ = throttling_enabled;
   }
diff --git a/remoting/host/BUILD.gn b/remoting/host/BUILD.gn
index 0c43ddcc..8007319 100644
--- a/remoting/host/BUILD.gn
+++ b/remoting/host/BUILD.gn
@@ -172,6 +172,7 @@
     "host_session_options.h",
     "host_status_logger.cc",
     "host_status_logger.h",
+    "host_status_monitor.cc",
     "host_status_monitor.h",
     "host_status_observer.h",
     "host_window.cc",
@@ -411,7 +412,6 @@
     "fake_desktop_environment.h",
     "fake_host_extension.cc",
     "fake_host_extension.h",
-    "fake_host_status_monitor.h",
     "fake_mouse_cursor_monitor.cc",
     "fake_mouse_cursor_monitor.h",
     "host_mock_objects.cc",
diff --git a/remoting/host/chromoting_host.cc b/remoting/host/chromoting_host.cc
index 3e2ce47..d165dd6 100644
--- a/remoting/host/chromoting_host.cc
+++ b/remoting/host/chromoting_host.cc
@@ -75,7 +75,7 @@
       transport_context_(transport_context),
       audio_task_runner_(audio_task_runner),
       video_encode_task_runner_(video_encode_task_runner),
-      started_(false),
+      status_monitor_(new HostStatusMonitor()),
       login_backoff_(&kDefaultBackoffPolicy),
       desktop_environment_options_(options),
       weak_factory_(this) {
@@ -96,7 +96,7 @@
 
   // Notify observers.
   if (started_) {
-    for (auto& observer : status_observers_)
+    for (auto& observer : status_monitor_->observers())
       observer.OnShutdown();
   }
 }
@@ -107,23 +107,13 @@
 
   HOST_LOG << "Starting host";
   started_ = true;
-  for (auto& observer : status_observers_)
+  for (auto& observer : status_monitor_->observers())
     observer.OnStart(host_owner_email);
 
   session_manager_->AcceptIncoming(
       base::Bind(&ChromotingHost::OnIncomingSession, base::Unretained(this)));
 }
 
-void ChromotingHost::AddStatusObserver(HostStatusObserver* observer) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  status_observers_.AddObserver(observer);
-}
-
-void ChromotingHost::RemoveStatusObserver(HostStatusObserver* observer) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  status_observers_.RemoveObserver(observer);
-}
-
 void ChromotingHost::AddExtension(std::unique_ptr<HostExtension> extension) {
   extensions_.push_back(std::move(extension));
 }
@@ -176,7 +166,7 @@
   DCHECK(clients_.front().get() == client);
 
   // Notify observers that there is at least one authenticated client.
-  for (auto& observer : status_observers_)
+  for (auto& observer : status_monitor_->observers())
     observer.OnClientAuthenticated(client->client_jid());
 }
 
@@ -184,7 +174,7 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   // Notify observers.
-  for (auto& observer : status_observers_)
+  for (auto& observer : status_monitor_->observers())
     observer.OnClientConnected(client->client_jid());
 }
 
@@ -192,7 +182,7 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   // Notify observers.
-  for (auto& observer : status_observers_)
+  for (auto& observer : status_monitor_->observers())
     observer.OnAccessDenied(client->client_jid());
 }
 
@@ -211,7 +201,7 @@
   clients_.erase(it);
 
   if (was_authenticated) {
-    for (auto& observer : status_observers_)
+    for (auto& observer : status_monitor_->observers())
       observer.OnClientDisconnected(jid);
   }
 }
@@ -221,7 +211,7 @@
     const std::string& channel_name,
     const protocol::TransportRoute& route) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  for (auto& observer : status_observers_)
+  for (auto& observer : status_monitor_->observers())
     observer.OnClientRouteChange(session->client_jid(), channel_name, route);
 }
 
diff --git a/remoting/host/chromoting_host.h b/remoting/host/chromoting_host.h
index 049c120..d907fa20 100644
--- a/remoting/host/chromoting_host.h
+++ b/remoting/host/chromoting_host.h
@@ -62,8 +62,7 @@
 //    all pending tasks to complete. After all of that completed we
 //    return to the idle state. We then go to step (2) if there a new
 //    incoming connection.
-class ChromotingHost : public ClientSession::EventHandler,
-                       public HostStatusMonitor {
+class ChromotingHost : public ClientSession::EventHandler {
  public:
   typedef std::vector<std::unique_ptr<ClientSession>> ClientSessions;
 
@@ -85,9 +84,7 @@
   // This method can only be called once during the lifetime of this object.
   void Start(const std::string& host_owner);
 
-  // HostStatusMonitor interface.
-  void AddStatusObserver(HostStatusObserver* observer) override;
-  void RemoveStatusObserver(HostStatusObserver* observer) override;
+  scoped_refptr<HostStatusMonitor> status_monitor() { return status_monitor_; }
 
   // Registers a host extension.
   void AddExtension(std::unique_ptr<HostExtension> extension);
@@ -133,10 +130,6 @@
 
   const ClientSessions& client_sessions_for_tests() { return clients_; }
 
-  base::WeakPtr<ChromotingHost> AsWeakPtr() {
-    return weak_factory_.GetWeakPtr();
-  }
-
   scoped_refptr<protocol::TransportContext> transport_context_for_tests() {
     return transport_context_;
   }
@@ -154,14 +147,13 @@
   scoped_refptr<base::SingleThreadTaskRunner> audio_task_runner_;
   scoped_refptr<base::SingleThreadTaskRunner> video_encode_task_runner_;
 
-  // Must be used on the network thread only.
-  base::ObserverList<HostStatusObserver> status_observers_;
+  scoped_refptr<HostStatusMonitor> status_monitor_;
 
   // The connections to remote clients.
   ClientSessions clients_;
 
   // True if the host has been started.
-  bool started_;
+  bool started_ = false;
 
   // Login backoff state.
   net::BackoffEntry login_backoff_;
diff --git a/remoting/host/chromoting_host_unittest.cc b/remoting/host/chromoting_host_unittest.cc
index 5fc75e40..d8519e8 100644
--- a/remoting/host/chromoting_host_unittest.cc
+++ b/remoting/host/chromoting_host_unittest.cc
@@ -70,10 +70,10 @@
     host_.reset(new ChromotingHost(
         desktop_environment_factory_.get(), base::WrapUnique(session_manager_),
         protocol::TransportContext::ForTests(protocol::TransportRole::SERVER),
-        task_runner_,    // Audio
+        task_runner_,  // Audio
         task_runner_,
         DesktopEnvironmentOptions::CreateDefault()));  // Video encode
-    host_->AddStatusObserver(&host_status_observer_);
+    host_->status_monitor()->AddStatusObserver(&host_status_observer_);
 
     xmpp_login_ = "host@domain";
     session1_ = new MockSession();
diff --git a/remoting/host/daemon_process.cc b/remoting/host/daemon_process.cc
index cc7ec8e..40b5c72 100644
--- a/remoting/host/daemon_process.cc
+++ b/remoting/host/daemon_process.cc
@@ -46,10 +46,8 @@
 DaemonProcess::~DaemonProcess() {
   DCHECK(caller_task_runner()->BelongsToCurrentThread());
 
-  host_event_logger_.reset();
-  weak_factory_.InvalidateWeakPtrs();
-
-  config_watcher_.reset();
+  host_event_logger_ = nullptr;
+  config_watcher_ = nullptr;
   DeleteAllDesktopSessions();
 }
 
@@ -69,18 +67,6 @@
   Stop();
 }
 
-void DaemonProcess::AddStatusObserver(HostStatusObserver* observer) {
-  DCHECK(caller_task_runner()->BelongsToCurrentThread());
-
-  status_observers_.AddObserver(observer);
-}
-
-void DaemonProcess::RemoveStatusObserver(HostStatusObserver* observer) {
-  DCHECK(caller_task_runner()->BelongsToCurrentThread());
-
-  status_observers_.RemoveObserver(observer);
-}
-
 void DaemonProcess::OnChannelConnected(int32_t peer_pid) {
   DCHECK(caller_task_runner()->BelongsToCurrentThread());
 
@@ -191,8 +177,8 @@
       io_task_runner_(io_task_runner),
       next_terminal_id_(0),
       stopped_callback_(stopped_callback),
-      current_process_stats_("DaemonProcess"),
-      weak_factory_(this) {
+      status_monitor_(new HostStatusMonitor()),
+      current_process_stats_("DaemonProcess") {
   DCHECK(caller_task_runner->BelongsToCurrentThread());
   // TODO(sammc): On OSX, mojo::edk::SetMachPortProvider() should be called with
   // a base::PortProvider implementation. Add it here when this code is used on
@@ -290,7 +276,7 @@
       caller_task_runner(), io_task_runner(), config_path));
   config_watcher_->Watch(this);
   host_event_logger_ =
-      HostEventLogger::Create(weak_factory_.GetWeakPtr(), kApplicationName);
+      HostEventLogger::Create(status_monitor_, kApplicationName);
 
   // Launch the process.
   LaunchNetworkProcess();
diff --git a/remoting/host/daemon_process.h b/remoting/host/daemon_process.h
index 562b1a2d..8047f29 100644
--- a/remoting/host/daemon_process.h
+++ b/remoting/host/daemon_process.h
@@ -14,7 +14,6 @@
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "base/process/process.h"
 #include "base/time/time.h"
@@ -46,7 +45,6 @@
 // sessions.
 class DaemonProcess
     : public ConfigWatcher::Delegate,
-      public HostStatusMonitor,
       public WorkerProcessIpcDelegate,
       public protocol::ProcessStatsStub {
  public:
@@ -67,9 +65,7 @@
   void OnConfigUpdated(const std::string& serialized_config) override;
   void OnConfigWatcherError() override;
 
-  // HostStatusMonitor interface.
-  void AddStatusObserver(HostStatusObserver* observer) override;
-  void RemoveStatusObserver(HostStatusObserver* observer) override;
+  scoped_refptr<HostStatusMonitor> status_monitor() { return status_monitor_; }
 
   // WorkerProcessIpcDelegate implementation.
   void OnChannelConnected(int32_t peer_pid) override;
@@ -201,6 +197,8 @@
   // Writes host status updates to the system event log.
   std::unique_ptr<HostEventLogger> host_event_logger_;
 
+  scoped_refptr<HostStatusMonitor> status_monitor_;
+
   // Reports process statistic data to network process.
   std::unique_ptr<ProcessStatsSender> stats_sender_;
 
@@ -215,8 +213,6 @@
 
   CurrentProcessStatsAgent current_process_stats_;
 
-  base::WeakPtrFactory<DaemonProcess> weak_factory_;
-
   DISALLOW_COPY_AND_ASSIGN(DaemonProcess);
 };
 
diff --git a/remoting/host/desktop_process.cc b/remoting/host/desktop_process.cc
index 3b2ae57..6505892 100644
--- a/remoting/host/desktop_process.cc
+++ b/remoting/host/desktop_process.cc
@@ -38,7 +38,8 @@
     : caller_task_runner_(caller_task_runner),
       input_task_runner_(input_task_runner),
       io_task_runner_(io_task_runner),
-      daemon_channel_handle_(std::move(daemon_channel_handle)) {
+      daemon_channel_handle_(std::move(daemon_channel_handle)),
+      weak_factory_(this) {
   DCHECK(caller_task_runner_->BelongsToCurrentThread());
   DCHECK(base::MessageLoopForUI::IsCurrent());
 }
@@ -66,7 +67,7 @@
   daemon_channel_->Send(new ChromotingDesktopDaemonMsg_InjectSas());
 }
 
-void DesktopProcess::LockWorkStation() {
+void DesktopProcess::LockWorkstation() {
   DCHECK(caller_task_runner_->BelongsToCurrentThread());
 #if defined(OS_WIN)
   if (base::win::OSInfo::GetInstance()->version_type() ==
@@ -145,7 +146,7 @@
 
   // Start the agent and create an IPC channel to talk to it.
   mojo::ScopedMessagePipeHandle desktop_pipe =
-      desktop_agent_->Start(AsWeakPtr());
+      desktop_agent_->Start(weak_factory_.GetWeakPtr());
 
   // Connect to the daemon.
   daemon_channel_ = IPC::ChannelProxy::Create(daemon_channel_handle_.release(),
diff --git a/remoting/host/desktop_process.h b/remoting/host/desktop_process.h
index 7454590..0c084e92 100644
--- a/remoting/host/desktop_process.h
+++ b/remoting/host/desktop_process.h
@@ -30,8 +30,7 @@
 class DesktopSessionAgent;
 
 class DesktopProcess : public DesktopSessionAgent::Delegate,
-                       public IPC::Listener,
-                       public base::SupportsWeakPtr<DesktopProcess> {
+                       public IPC::Listener {
  public:
   DesktopProcess(scoped_refptr<AutoThreadTaskRunner> caller_task_runner,
                  scoped_refptr<AutoThreadTaskRunner> input_task_runner,
@@ -52,7 +51,7 @@
   void InjectSas();
 
   // Locks the workstation for the current session.
-  void LockWorkStation();
+  void LockWorkstation();
 
   // Creates the desktop agent and required threads and IPC channels. Returns
   // true on success.
@@ -90,6 +89,8 @@
   // the network process.
   scoped_refptr<DesktopSessionAgent> desktop_agent_;
 
+  base::WeakPtrFactory<DesktopProcess> weak_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(DesktopProcess);
 };
 
diff --git a/remoting/host/desktop_process_main.cc b/remoting/host/desktop_process_main.cc
index d898bd0..56c5f6f 100644
--- a/remoting/host/desktop_process_main.cc
+++ b/remoting/host/desktop_process_main.cc
@@ -79,12 +79,15 @@
   // Create a platform-dependent environment factory.
   std::unique_ptr<DesktopEnvironmentFactory> desktop_environment_factory;
 #if defined(OS_WIN)
+  // base::Unretained() is safe here: |desktop_process| outlives run_loop.Run().
+  auto inject_sas_closure = base::Bind(&DesktopProcess::InjectSas,
+                                       base::Unretained(&desktop_process));
+  auto lock_workstation_closure = base::Bind(
+      &DesktopProcess::LockWorkstation, base::Unretained(&desktop_process));
+
   desktop_environment_factory.reset(new SessionDesktopEnvironmentFactory(
       ui_task_runner, video_capture_task_runner, input_task_runner,
-      ui_task_runner,
-      base::Bind(&DesktopProcess::InjectSas, desktop_process.AsWeakPtr()),
-      base::Bind(&DesktopProcess::LockWorkStation,
-                 desktop_process.AsWeakPtr())));
+      ui_task_runner, inject_sas_closure, lock_workstation_closure));
 #else  // !defined(OS_WIN)
   desktop_environment_factory.reset(new Me2MeDesktopEnvironmentFactory(
       ui_task_runner, video_capture_task_runner, input_task_runner,
diff --git a/remoting/host/fake_desktop_environment.cc b/remoting/host/fake_desktop_environment.cc
index 6597f96..6209c23 100644
--- a/remoting/host/fake_desktop_environment.cc
+++ b/remoting/host/fake_desktop_environment.cc
@@ -15,7 +15,7 @@
 
 namespace remoting {
 
-FakeInputInjector::FakeInputInjector() {}
+FakeInputInjector::FakeInputInjector() : weak_factory_(this) {}
 FakeInputInjector::~FakeInputInjector() {}
 
 void FakeInputInjector::Start(
@@ -57,7 +57,9 @@
 FakeDesktopEnvironment::FakeDesktopEnvironment(
     scoped_refptr<base::SingleThreadTaskRunner> capture_thread,
     const DesktopEnvironmentOptions& options)
-    : capture_thread_(std::move(capture_thread)), options_(options) {}
+    : capture_thread_(std::move(capture_thread)),
+      options_(options),
+      weak_factory_(this) {}
 
 FakeDesktopEnvironment::~FakeDesktopEnvironment() = default;
 
@@ -68,7 +70,7 @@
 
 std::unique_ptr<InputInjector> FakeDesktopEnvironment::CreateInputInjector() {
   std::unique_ptr<FakeInputInjector> result(new FakeInputInjector());
-  last_input_injector_ = result->AsWeakPtr();
+  last_input_injector_ = result->weak_factory_.GetWeakPtr();
   return std::move(result);
 }
 
@@ -121,7 +123,7 @@
   std::unique_ptr<FakeDesktopEnvironment> result(
       new FakeDesktopEnvironment(capture_thread_, options));
   result->set_frame_generator(frame_generator_);
-  last_desktop_environment_ = result->AsWeakPtr();
+  last_desktop_environment_ = result->weak_factory_.GetWeakPtr();
   return std::move(result);
 }
 
diff --git a/remoting/host/fake_desktop_environment.h b/remoting/host/fake_desktop_environment.h
index 116678e..28a0406 100644
--- a/remoting/host/fake_desktop_environment.h
+++ b/remoting/host/fake_desktop_environment.h
@@ -21,8 +21,7 @@
 
 namespace remoting {
 
-class FakeInputInjector : public InputInjector,
-                          public base::SupportsWeakPtr<FakeInputInjector> {
+class FakeInputInjector : public InputInjector {
  public:
   FakeInputInjector();
   ~FakeInputInjector() override;
@@ -53,11 +52,17 @@
   }
 
  private:
+  friend class FakeDesktopEnvironment;
+
   std::vector<protocol::KeyEvent>* key_events_ = nullptr;
   std::vector<protocol::TextEvent>* text_events_ = nullptr;
   std::vector<protocol::MouseEvent>* mouse_events_ = nullptr;
   std::vector<protocol::TouchEvent>* touch_events_ = nullptr;
   std::vector<protocol::ClipboardEvent>* clipboard_events_ = nullptr;
+
+  base::WeakPtrFactory<FakeInputInjector> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeInputInjector);
 };
 
 class FakeScreenControls : public ScreenControls {
@@ -69,9 +74,7 @@
   void SetScreenResolution(const ScreenResolution& resolution) override;
 };
 
-class FakeDesktopEnvironment
-    : public DesktopEnvironment,
-      public base::SupportsWeakPtr<FakeDesktopEnvironment> {
+class FakeDesktopEnvironment : public DesktopEnvironment {
  public:
   explicit FakeDesktopEnvironment(
       scoped_refptr<base::SingleThreadTaskRunner> capture_thread,
@@ -103,6 +106,8 @@
   }
 
  private:
+  friend class FakeDesktopEnvironmentFactory;
+
   scoped_refptr<base::SingleThreadTaskRunner> capture_thread_;
   protocol::FakeDesktopCapturer::FrameGenerator frame_generator_;
 
@@ -110,6 +115,8 @@
 
   const DesktopEnvironmentOptions options_;
 
+  base::WeakPtrFactory<FakeDesktopEnvironment> weak_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(FakeDesktopEnvironment);
 };
 
diff --git a/remoting/host/fake_host_status_monitor.h b/remoting/host/fake_host_status_monitor.h
deleted file mode 100644
index 7c9e7ee..0000000
--- a/remoting/host/fake_host_status_monitor.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef REMOTING_HOST_FAKE_HOST_STATUS_MONITOR_H_
-#define REMOTING_HOST_FAKE_HOST_STATUS_MONITOR_H_
-
-#include "base/memory/weak_ptr.h"
-#include "remoting/host/host_status_monitor.h"
-
-namespace remoting {
-
-// An implementation of HostStatusMonitor that does nothing.
-class FakeHostStatusMonitor
-    : public base::SupportsWeakPtr<FakeHostStatusMonitor>,
-      public HostStatusMonitor {
- public:
-  ~FakeHostStatusMonitor() override {}
-
-  // HostStatusMonitor interface.
-  void AddStatusObserver(HostStatusObserver* observer) override {}
-  void RemoveStatusObserver(HostStatusObserver* observer) override {}
-};
-
-}  // namespace remoting
-
-#endif  // REMOTING_HOST_FAKE_HOST_STATUS_MONITOR_H_
diff --git a/remoting/host/host_event_logger.h b/remoting/host/host_event_logger.h
index e7ab5a9..254aa968 100644
--- a/remoting/host/host_event_logger.h
+++ b/remoting/host/host_event_logger.h
@@ -23,7 +23,7 @@
   // Creates an event-logger that monitors host status changes and logs
   // corresponding events to the OS-specific log (syslog/EventLog).
   static std::unique_ptr<HostEventLogger> Create(
-      base::WeakPtr<HostStatusMonitor> monitor,
+      scoped_refptr<HostStatusMonitor> monitor,
       const std::string& application_name);
 
  protected:
diff --git a/remoting/host/host_event_logger_posix.cc b/remoting/host/host_event_logger_posix.cc
index 7b7112ee..2ce48ad9 100644
--- a/remoting/host/host_event_logger_posix.cc
+++ b/remoting/host/host_event_logger_posix.cc
@@ -26,7 +26,7 @@
 
 class HostEventLoggerPosix : public HostEventLogger, public HostStatusObserver {
  public:
-  HostEventLoggerPosix(base::WeakPtr<HostStatusMonitor> monitor,
+  HostEventLoggerPosix(scoped_refptr<HostStatusMonitor> monitor,
                        const std::string& application_name);
 
   ~HostEventLoggerPosix() override;
@@ -45,7 +45,7 @@
  private:
   void Log(const std::string& message);
 
-  base::WeakPtr<HostStatusMonitor> monitor_;
+  scoped_refptr<HostStatusMonitor> monitor_;
   std::string application_name_;
 
   DISALLOW_COPY_AND_ASSIGN(HostEventLoggerPosix);
@@ -54,17 +54,15 @@
 } //namespace
 
 HostEventLoggerPosix::HostEventLoggerPosix(
-    base::WeakPtr<HostStatusMonitor> monitor,
+    scoped_refptr<HostStatusMonitor> monitor,
     const std::string& application_name)
-    : monitor_(monitor),
-      application_name_(application_name) {
+    : monitor_(monitor), application_name_(application_name) {
   openlog(application_name_.c_str(), 0, LOG_USER);
   monitor_->AddStatusObserver(this);
 }
 
 HostEventLoggerPosix::~HostEventLoggerPosix() {
-  if (monitor_.get())
-    monitor_->RemoveStatusObserver(this);
+  monitor_->RemoveStatusObserver(this);
   closelog();
 }
 
@@ -106,7 +104,7 @@
 
 // static
 std::unique_ptr<HostEventLogger> HostEventLogger::Create(
-    base::WeakPtr<HostStatusMonitor> monitor,
+    scoped_refptr<HostStatusMonitor> monitor,
     const std::string& application_name) {
   return base::WrapUnique(new HostEventLoggerPosix(monitor, application_name));
 }
diff --git a/remoting/host/host_event_logger_win.cc b/remoting/host/host_event_logger_win.cc
index 70135aa9a..77326758 100644
--- a/remoting/host/host_event_logger_win.cc
+++ b/remoting/host/host_event_logger_win.cc
@@ -28,7 +28,7 @@
 
 class HostEventLoggerWin : public HostEventLogger, public HostStatusObserver {
  public:
-  HostEventLoggerWin(base::WeakPtr<HostStatusMonitor> monitor,
+  HostEventLoggerWin(scoped_refptr<HostStatusMonitor> monitor,
                      const std::string& application_name);
 
   ~HostEventLoggerWin() override;
@@ -49,20 +49,19 @@
   void LogString(WORD type, DWORD event_id, const std::string& string);
   void Log(WORD type, DWORD event_id, const std::vector<std::string>& strings);
 
-  base::WeakPtr<HostStatusMonitor> monitor_;
+  scoped_refptr<HostStatusMonitor> monitor_;
 
   // The handle of the application event log.
-  HANDLE event_log_;
+  HANDLE event_log_ = nullptr;
 
   DISALLOW_COPY_AND_ASSIGN(HostEventLoggerWin);
 };
 
-} //namespace
+}  // namespace
 
-HostEventLoggerWin::HostEventLoggerWin(base::WeakPtr<HostStatusMonitor> monitor,
+HostEventLoggerWin::HostEventLoggerWin(scoped_refptr<HostStatusMonitor> monitor,
                                        const std::string& application_name)
-    : monitor_(monitor),
-      event_log_(nullptr) {
+    : monitor_(monitor) {
   event_log_ = RegisterEventSourceW(
       nullptr, base::UTF8ToUTF16(application_name).c_str());
   if (event_log_ != nullptr) {
@@ -74,8 +73,7 @@
 
 HostEventLoggerWin::~HostEventLoggerWin() {
   if (event_log_ != nullptr) {
-    if (monitor_)
-      monitor_->RemoveStatusObserver(this);
+    monitor_->RemoveStatusObserver(this);
     DeregisterEventSource(event_log_);
   }
 }
@@ -128,14 +126,8 @@
     raw_strings[i] = utf16_strings[i].c_str();
   }
 
-  if (!ReportEventW(event_log_,
-                    type,
-                    HOST_CATEGORY,
-                    event_id,
-                    nullptr,
-                    static_cast<WORD>(raw_strings.size()),
-                    0,
-                    &raw_strings[0],
+  if (!ReportEventW(event_log_, type, HOST_CATEGORY, event_id, nullptr,
+                    static_cast<WORD>(raw_strings.size()), 0, &raw_strings[0],
                     nullptr)) {
     PLOG(ERROR) << "Failed to write an event to the event log";
   }
@@ -151,9 +143,9 @@
 
 // static
 std::unique_ptr<HostEventLogger> HostEventLogger::Create(
-    base::WeakPtr<HostStatusMonitor> monitor,
+    scoped_refptr<HostStatusMonitor> monitor,
     const std::string& application_name) {
-  return base::WrapUnique(new HostEventLoggerWin(monitor, application_name));
+  return base::MakeUnique<HostEventLoggerWin>(monitor, application_name);
 }
 
 }  // namespace remoting
diff --git a/remoting/host/host_power_save_blocker.cc b/remoting/host/host_power_save_blocker.cc
index c93f71c..a461115d 100644
--- a/remoting/host/host_power_save_blocker.cc
+++ b/remoting/host/host_power_save_blocker.cc
@@ -13,7 +13,7 @@
 namespace remoting {
 
 HostPowerSaveBlocker::HostPowerSaveBlocker(
-    base::WeakPtr<HostStatusMonitor> monitor,
+    scoped_refptr<HostStatusMonitor> monitor,
     const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner,
     const scoped_refptr<base::SingleThreadTaskRunner>& file_task_runner)
     : monitor_(monitor),
@@ -24,18 +24,14 @@
 }
 
 HostPowerSaveBlocker::~HostPowerSaveBlocker() {
-  if (monitor_.get()) {
-    monitor_->RemoveStatusObserver(this);
-  }
+  monitor_->RemoveStatusObserver(this);
 }
 
 void HostPowerSaveBlocker::OnClientConnected(const std::string& jid) {
   blocker_.reset(new device::PowerSaveBlocker(
       device::PowerSaveBlocker::kPowerSaveBlockPreventDisplaySleep,
-      device::PowerSaveBlocker::kReasonOther,
-      "Remoting session is active",
-      ui_task_runner_,
-      file_task_runner_));
+      device::PowerSaveBlocker::kReasonOther, "Remoting session is active",
+      ui_task_runner_, file_task_runner_));
 }
 
 void HostPowerSaveBlocker::OnClientDisconnected(const std::string& jid) {
diff --git a/remoting/host/host_power_save_blocker.h b/remoting/host/host_power_save_blocker.h
index 1f31363..4348293 100644
--- a/remoting/host/host_power_save_blocker.h
+++ b/remoting/host/host_power_save_blocker.h
@@ -28,7 +28,7 @@
 class HostPowerSaveBlocker : public HostStatusObserver {
  public:
   HostPowerSaveBlocker(
-      base::WeakPtr<HostStatusMonitor> monitor,
+      scoped_refptr<HostStatusMonitor> monitor,
       const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner,
       const scoped_refptr<base::SingleThreadTaskRunner>& file_task_runner);
 
@@ -40,7 +40,8 @@
  private:
   friend class HostPowerSaveBlockerTest;
 
-  base::WeakPtr<HostStatusMonitor> monitor_;
+  scoped_refptr<HostStatusMonitor> monitor_;
+
   scoped_refptr<base::SequencedTaskRunner> ui_task_runner_;
   scoped_refptr<base::SingleThreadTaskRunner> file_task_runner_;
   std::unique_ptr<device::PowerSaveBlocker> blocker_;
diff --git a/remoting/host/host_power_save_blocker_unittest.cc b/remoting/host/host_power_save_blocker_unittest.cc
index 77124fc..6b501861 100644
--- a/remoting/host/host_power_save_blocker_unittest.cc
+++ b/remoting/host/host_power_save_blocker_unittest.cc
@@ -9,7 +9,7 @@
 #include "base/logging.h"
 #include "base/message_loop/message_loop.h"
 #include "base/threading/thread.h"
-#include "remoting/host/fake_host_status_monitor.h"
+#include "remoting/host/host_status_monitor.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace remoting {
@@ -25,18 +25,18 @@
 
   base::MessageLoopForUI ui_message_loop_;
   base::Thread blocking_thread_;
-  FakeHostStatusMonitor monitor_;
+  scoped_refptr<HostStatusMonitor> monitor_;
   std::unique_ptr<HostPowerSaveBlocker> blocker_;
 };
 
 HostPowerSaveBlockerTest::HostPowerSaveBlockerTest()
-    : blocking_thread_("block-thread") {}
+    : blocking_thread_("block-thread"), monitor_(new HostStatusMonitor()) {}
 
 void HostPowerSaveBlockerTest::SetUp() {
   ASSERT_TRUE(blocking_thread_.StartWithOptions(
                   base::Thread::Options(base::MessageLoop::TYPE_IO, 0)) &&
               blocking_thread_.WaitUntilThreadStarted());
-  blocker_.reset(new HostPowerSaveBlocker(monitor_.AsWeakPtr(),
+  blocker_.reset(new HostPowerSaveBlocker(monitor_,
                                           ui_message_loop_.task_runner(),
                                           blocking_thread_.task_runner()));
 }
diff --git a/remoting/host/host_status_logger.cc b/remoting/host/host_status_logger.cc
index b81e03297..07dd716 100644
--- a/remoting/host/host_status_logger.cc
+++ b/remoting/host/host_status_logger.cc
@@ -13,7 +13,7 @@
 
 namespace remoting {
 
-HostStatusLogger::HostStatusLogger(base::WeakPtr<HostStatusMonitor> monitor,
+HostStatusLogger::HostStatusLogger(scoped_refptr<HostStatusMonitor> monitor,
                                    ServerLogEntry::Mode mode,
                                    SignalStrategy* signal_strategy,
                                    const std::string& directory_bot_jid)
@@ -24,8 +24,7 @@
 
 HostStatusLogger::~HostStatusLogger() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (monitor_.get())
-    monitor_->RemoveStatusObserver(this);
+  monitor_->RemoveStatusObserver(this);
 }
 
 void HostStatusLogger::LogSessionStateChange(const std::string& jid,
diff --git a/remoting/host/host_status_logger.h b/remoting/host/host_status_logger.h
index 55771cc3..2a3f4582 100644
--- a/remoting/host/host_status_logger.h
+++ b/remoting/host/host_status_logger.h
@@ -23,10 +23,10 @@
 // They do not contain any personally identifiable information.
 class HostStatusLogger : public HostStatusObserver {
  public:
-  HostStatusLogger(base::WeakPtr<HostStatusMonitor> monitor,
-                  ServerLogEntry::Mode mode,
-                  SignalStrategy* signal_strategy,
-                  const std::string& directory_bot_jid);
+  HostStatusLogger(scoped_refptr<HostStatusMonitor> monitor,
+                   ServerLogEntry::Mode mode,
+                   SignalStrategy* signal_strategy,
+                   const std::string& directory_bot_jid);
   ~HostStatusLogger() override;
 
   // Logs a session state change. Currently, this is either
@@ -46,7 +46,7 @@
  private:
   LogToServer log_to_server_;
 
-  base::WeakPtr<HostStatusMonitor> monitor_;
+  scoped_refptr<HostStatusMonitor> monitor_;
 
   // A map from client JID to the route type of that client's connection to
   // this host.
diff --git a/remoting/host/host_status_logger_unittest.cc b/remoting/host/host_status_logger_unittest.cc
index 01160ee..7d2a413 100644
--- a/remoting/host/host_status_logger_unittest.cc
+++ b/remoting/host/host_status_logger_unittest.cc
@@ -6,7 +6,7 @@
 
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
-#include "remoting/host/fake_host_status_monitor.h"
+#include "remoting/host/host_status_monitor.h"
 #include "remoting/signaling/mock_signal_strategy.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gmock_mutant.h"
@@ -124,14 +124,14 @@
 
 class HostStatusLoggerTest : public testing::Test {
  public:
-  HostStatusLoggerTest() : signal_strategy_(SignalingAddress(kHostJid)) {}
+  HostStatusLoggerTest()
+      : signal_strategy_(SignalingAddress(kHostJid)),
+        host_status_monitor_(new HostStatusMonitor()) {}
   void SetUp() override {
     EXPECT_CALL(signal_strategy_, AddListener(_));
     host_status_logger_.reset(
-        new HostStatusLogger(host_status_monitor_.AsWeakPtr(),
-                             ServerLogEntry::ME2ME,
-                             &signal_strategy_,
-                             kTestBotJid));
+        new HostStatusLogger(host_status_monitor_, ServerLogEntry::ME2ME,
+                             &signal_strategy_, kTestBotJid));
     EXPECT_CALL(signal_strategy_, RemoveListener(_));
   }
 
@@ -139,7 +139,7 @@
   base::MessageLoop message_loop_;
   MockSignalStrategy signal_strategy_;
   std::unique_ptr<HostStatusLogger> host_status_logger_;
-  FakeHostStatusMonitor host_status_monitor_;
+  scoped_refptr<HostStatusMonitor> host_status_monitor_;
 };
 
 TEST_F(HostStatusLoggerTest, SendNow) {
diff --git a/remoting/host/host_status_monitor.cc b/remoting/host/host_status_monitor.cc
new file mode 100644
index 0000000..5a547ee
--- /dev/null
+++ b/remoting/host/host_status_monitor.cc
@@ -0,0 +1,20 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "remoting/host/host_status_monitor.h"
+
+namespace remoting {
+
+HostStatusMonitor::HostStatusMonitor() {}
+HostStatusMonitor::~HostStatusMonitor() {}
+
+void HostStatusMonitor::AddStatusObserver(HostStatusObserver* observer) {
+  observers_.AddObserver(observer);
+}
+
+void HostStatusMonitor::RemoveStatusObserver(HostStatusObserver* observer) {
+  observers_.RemoveObserver(observer);
+}
+
+}  // namespace remoting
diff --git a/remoting/host/host_status_monitor.h b/remoting/host/host_status_monitor.h
index 6cc80cb..96e6420 100644
--- a/remoting/host/host_status_monitor.h
+++ b/remoting/host/host_status_monitor.h
@@ -5,18 +5,32 @@
 #ifndef REMOTING_HOST_HOST_STATUS_MONITOR_H_
 #define REMOTING_HOST_HOST_STATUS_MONITOR_H_
 
+#include "base/memory/ref_counted.h"
+#include "base/observer_list.h"
+
 namespace remoting {
 
 class HostStatusObserver;
 
-// Interface for registering host status observers.
-class HostStatusMonitor {
+// Helper used to deliver host status notifications to observers.
+class HostStatusMonitor : public base::RefCountedThreadSafe<HostStatusMonitor> {
  public:
-  virtual ~HostStatusMonitor() {}
+  HostStatusMonitor();
 
   // Add/Remove |observer| to/from the list of status observers.
-  virtual void AddStatusObserver(HostStatusObserver* observer) = 0;
-  virtual void RemoveStatusObserver(HostStatusObserver* observer) = 0;
+  void AddStatusObserver(HostStatusObserver* observer);
+  void RemoveStatusObserver(HostStatusObserver* observer);
+
+  const base::ObserverList<HostStatusObserver>& observers() {
+    return observers_;
+  };
+
+ protected:
+  friend class base::RefCountedThreadSafe<HostStatusMonitor>;
+
+  base::ObserverList<HostStatusObserver> observers_;
+
+  virtual ~HostStatusMonitor();
 };
 
 }  // namespace remoting
diff --git a/remoting/host/host_status_observer.h b/remoting/host/host_status_observer.h
index 2b6282d..349a749 100644
--- a/remoting/host/host_status_observer.h
+++ b/remoting/host/host_status_observer.h
@@ -11,15 +11,15 @@
 
 namespace protocol {
 struct TransportRoute;
-};
+}  // namespace protocol
 
 // Interface for host status observer. All methods are invoked on the
 // network thread. Observers must not tear-down ChromotingHost state
 // on receipt of these callbacks; they are purely informational.
 class HostStatusObserver {
  public:
-  HostStatusObserver() { }
-  virtual ~HostStatusObserver() { }
+  HostStatusObserver() {}
+  virtual ~HostStatusObserver() {}
 
   // Called when an unauthorized user attempts to connect to the host.
   virtual void OnAccessDenied(const std::string& jid) {}
diff --git a/remoting/host/ipc_host_event_logger.cc b/remoting/host/ipc_host_event_logger.cc
index 501e20ca..b3240f60 100644
--- a/remoting/host/ipc_host_event_logger.cc
+++ b/remoting/host/ipc_host_event_logger.cc
@@ -13,18 +13,16 @@
 
 namespace remoting {
 
-IpcHostEventLogger::IpcHostEventLogger(base::WeakPtr<HostStatusMonitor> monitor,
+IpcHostEventLogger::IpcHostEventLogger(scoped_refptr<HostStatusMonitor> monitor,
                                        IPC::Sender* daemon_channel)
-    : daemon_channel_(daemon_channel),
-      monitor_(monitor) {
+    : daemon_channel_(daemon_channel), monitor_(monitor) {
   monitor_->AddStatusObserver(this);
 }
 
 IpcHostEventLogger::~IpcHostEventLogger() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  if (monitor_.get())
-    monitor_->RemoveStatusObserver(this);
+  monitor_->RemoveStatusObserver(this);
 }
 
 void IpcHostEventLogger::OnAccessDenied(const std::string& jid) {
diff --git a/remoting/host/ipc_host_event_logger.h b/remoting/host/ipc_host_event_logger.h
index b292fa45..2157b8f 100644
--- a/remoting/host/ipc_host_event_logger.h
+++ b/remoting/host/ipc_host_event_logger.h
@@ -10,7 +10,7 @@
 
 #include "base/compiler_specific.h"
 #include "base/macros.h"
-#include "base/memory/weak_ptr.h"
+#include "base/memory/ref_counted.h"
 #include "base/sequence_checker.h"
 #include "remoting/host/host_event_logger.h"
 #include "remoting/host/host_status_observer.h"
@@ -26,7 +26,7 @@
 class IpcHostEventLogger : public HostEventLogger, public HostStatusObserver {
  public:
   // Initializes the logger. |daemon_channel| must outlive this object.
-  IpcHostEventLogger(base::WeakPtr<HostStatusMonitor> monitor,
+  IpcHostEventLogger(scoped_refptr<HostStatusMonitor> monitor,
                      IPC::Sender* daemon_channel);
   ~IpcHostEventLogger() override;
 
@@ -45,7 +45,7 @@
   // Used to report host status events to the daemon.
   IPC::Sender* daemon_channel_;
 
-  base::WeakPtr<HostStatusMonitor> monitor_;
+  scoped_refptr<HostStatusMonitor> monitor_;
 
   SEQUENCE_CHECKER(sequence_checker_);
 
diff --git a/remoting/host/it2me/it2me_host.cc b/remoting/host/it2me/it2me_host.cc
index 9575eda..7f49098 100644
--- a/remoting/host/it2me/it2me_host.cc
+++ b/remoting/host/it2me/it2me_host.cc
@@ -179,14 +179,14 @@
                                  host_context_->audio_task_runner(),
                                  host_context_->video_encode_task_runner(),
                                  DesktopEnvironmentOptions::CreateDefault()));
-  host_->AddStatusObserver(this);
+  host_->status_monitor()->AddStatusObserver(this);
   host_status_logger_.reset(
-      new HostStatusLogger(host_->AsWeakPtr(), ServerLogEntry::IT2ME,
+      new HostStatusLogger(host_->status_monitor(), ServerLogEntry::IT2ME,
                            signal_strategy_.get(), directory_bot_jid));
 
   // Create event logger.
   host_event_logger_ =
-      HostEventLogger::Create(host_->AsWeakPtr(), kApplicationName);
+      HostEventLogger::Create(host_->status_monitor(), kApplicationName);
 
   // Connect signaling and start the host.
   signal_strategy_->Connect();
@@ -457,15 +457,15 @@
 
   confirmation_dialog_proxy_.reset();
 
-  host_event_logger_.reset();
   if (host_) {
-    host_->RemoveStatusObserver(this);
-    host_.reset();
+    host_->status_monitor()->RemoveStatusObserver(this);
+    host_ = nullptr;
   }
 
-  register_request_.reset();
-  host_status_logger_.reset();
-  signal_strategy_.reset();
+  register_request_ = nullptr;
+  host_status_logger_ = nullptr;
+  signal_strategy_ = nullptr;
+  host_event_logger_ = nullptr;
 
   // Post tasks to delete UI objects on the UI thread.
   host_context_->ui_task_runner()->DeleteSoon(
diff --git a/remoting/host/linux/certificate_watcher.cc b/remoting/host/linux/certificate_watcher.cc
index c47c462..7b3bd3f 100644
--- a/remoting/host/linux/certificate_watcher.cc
+++ b/remoting/host/linux/certificate_watcher.cc
@@ -210,7 +210,7 @@
   VLOG(1) << "Started watching certificate changes.";
 }
 
-void CertificateWatcher::SetMonitor(base::WeakPtr<HostStatusMonitor> monitor) {
+void CertificateWatcher::SetMonitor(scoped_refptr<HostStatusMonitor> monitor) {
   DCHECK(is_started());
   if (monitor_) {
     monitor_->RemoveStatusObserver(this);
diff --git a/remoting/host/linux/certificate_watcher.h b/remoting/host/linux/certificate_watcher.h
index 720e34d..c243e31 100644
--- a/remoting/host/linux/certificate_watcher.h
+++ b/remoting/host/linux/certificate_watcher.h
@@ -48,7 +48,7 @@
   // the inhibit mode. Should be called after the watcher starts.
   // Adds |this| as an observer to the monitor.
   // Removes |this| as an observer from the old monitor if it is not null.
-  void SetMonitor(base::WeakPtr<HostStatusMonitor> monitor);
+  void SetMonitor(scoped_refptr<HostStatusMonitor> monitor);
 
   // HostStatusObserver interface.
   void OnClientConnected(const std::string& jid) override;
@@ -68,8 +68,8 @@
   // |inhibit_restart_scheduled_| flag is set to true.
   void DatabaseChanged();
 
-  // Reference to the monitor
-  base::WeakPtr<HostStatusMonitor> monitor_;
+  // Reference to the monitor.
+  scoped_refptr<HostStatusMonitor> monitor_;
 
   // Called when a restart is scheduled.
   base::Closure restart_action_;
diff --git a/remoting/host/remoting_me2me_host.cc b/remoting/host/remoting_me2me_host.cc
index 5c10a908..944a92b 100644
--- a/remoting/host/remoting_me2me_host.cc
+++ b/remoting/host/remoting_me2me_host.cc
@@ -744,7 +744,7 @@
           context_->file_task_runner()));
       cert_watcher_->Start();
     }
-    cert_watcher_->SetMonitor(host_->AsWeakPtr());
+    cert_watcher_->SetMonitor(host_->status_monitor());
 #endif
 
     scoped_refptr<protocol::TokenValidatorFactory> token_validator_factory =
@@ -1503,22 +1503,21 @@
   host_change_notification_listener_.reset(new HostChangeNotificationListener(
       this, host_id_, signal_strategy_.get(), directory_bot_jid_));
 
-  host_status_logger_.reset(new HostStatusLogger(
-      host_->AsWeakPtr(), ServerLogEntry::ME2ME,
-      signal_strategy_.get(), directory_bot_jid_));
+  host_status_logger_.reset(
+      new HostStatusLogger(host_->status_monitor(), ServerLogEntry::ME2ME,
+                           signal_strategy_.get(), directory_bot_jid_));
 
   power_save_blocker_.reset(new HostPowerSaveBlocker(
-      host_->AsWeakPtr(),
-      context_->ui_task_runner(),
+      host_->status_monitor(), context_->ui_task_runner(),
       context_->file_task_runner()));
 
   // Set up reporting the host status notifications.
 #if defined(REMOTING_MULTI_PROCESS)
   host_event_logger_.reset(
-      new IpcHostEventLogger(host_->AsWeakPtr(), daemon_channel_.get()));
+      new IpcHostEventLogger(host_->status_monitor(), daemon_channel_.get()));
 #else  // !defined(REMOTING_MULTI_PROCESS)
   host_event_logger_ =
-      HostEventLogger::Create(host_->AsWeakPtr(), kApplicationName);
+      HostEventLogger::Create(host_->status_monitor(), kApplicationName);
 #endif  // !defined(REMOTING_MULTI_PROCESS)
 
   host_->Start(host_owner_email_);
diff --git a/remoting/host/signaling_connector.cc b/remoting/host/signaling_connector.cc
index b338072..9a99435 100644
--- a/remoting/host/signaling_connector.cc
+++ b/remoting/host/signaling_connector.cc
@@ -50,7 +50,8 @@
       auth_failed_callback_(auth_failed_callback),
       dns_blackhole_checker_(std::move(dns_blackhole_checker)),
       oauth_token_getter_(oauth_token_getter),
-      reconnect_attempts_(0) {
+      reconnect_attempts_(0),
+      weak_factory_(this) {
   DCHECK(!auth_failed_callback_.is_null());
   DCHECK(dns_blackhole_checker_.get());
   net::NetworkChangeNotifier::AddConnectionTypeObserver(this);
@@ -184,8 +185,8 @@
 
   if (signal_strategy_->GetState() == SignalStrategy::DISCONNECTED) {
     HOST_LOG << "Attempting to connect signaling.";
-    oauth_token_getter_->CallWithToken(
-        base::Bind(&SignalingConnector::OnAccessToken, AsWeakPtr()));
+    oauth_token_getter_->CallWithToken(base::Bind(
+        &SignalingConnector::OnAccessToken, weak_factory_.GetWeakPtr()));
   }
 }
 
diff --git a/remoting/host/signaling_connector.h b/remoting/host/signaling_connector.h
index b057438..da6e17d1 100644
--- a/remoting/host/signaling_connector.h
+++ b/remoting/host/signaling_connector.h
@@ -24,8 +24,7 @@
 // backoff. It also monitors network state and reconnects signalling
 // whenever connection type changes or IP address changes.
 class SignalingConnector
-    : public base::SupportsWeakPtr<SignalingConnector>,
-      public SignalStrategy::Listener,
+    : public SignalStrategy::Listener,
       public net::NetworkChangeNotifier::ConnectionTypeObserver,
       public net::NetworkChangeNotifier::IPAddressObserver {
  public:
@@ -72,6 +71,8 @@
 
   SEQUENCE_CHECKER(sequence_checker_);
 
+  base::WeakPtrFactory<SignalingConnector> weak_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(SignalingConnector);
 };
 
diff --git a/remoting/ios/app/BUILD.gn b/remoting/ios/app/BUILD.gn
index d941e5a..542aed00 100644
--- a/remoting/ios/app/BUILD.gn
+++ b/remoting/ios/app/BUILD.gn
@@ -46,6 +46,8 @@
     "remoting_theme.mm",
     "remoting_view_controller.h",
     "remoting_view_controller.mm",
+    "session_reconnect_view.h",
+    "session_reconnect_view.mm",
   ]
 
   deps = [
diff --git a/remoting/ios/app/client_connection_view_controller.h b/remoting/ios/app/client_connection_view_controller.h
index efc5b56..866d495 100644
--- a/remoting/ios/app/client_connection_view_controller.h
+++ b/remoting/ios/app/client_connection_view_controller.h
@@ -14,7 +14,9 @@
   ClientViewConnecting,
   ClientViewPinPrompt,
   ClientViewConnected,
+  ClientViewReconnect,
   ClientViewClosed,
+  ClientViewError,
 };
 
 // This is the view that shows the user feedback while the client connection is
diff --git a/remoting/ios/app/client_connection_view_controller.mm b/remoting/ios/app/client_connection_view_controller.mm
index 888ee2c..272fc2f 100644
--- a/remoting/ios/app/client_connection_view_controller.mm
+++ b/remoting/ios/app/client_connection_view_controller.mm
@@ -12,9 +12,11 @@
 #import "ios/third_party/material_components_ios/src/components/ActivityIndicator/src/MDCActivityIndicator.h"
 #import "ios/third_party/material_components_ios/src/components/Buttons/src/MaterialButtons.h"
 #import "ios/third_party/material_components_ios/src/components/NavigationBar/src/MaterialNavigationBar.h"
+#import "ios/third_party/material_components_ios/src/components/Snackbar/src/MaterialSnackbar.h"
 #import "remoting/ios/app/host_view_controller.h"
 #import "remoting/ios/app/pin_entry_view.h"
 #import "remoting/ios/app/remoting_theme.h"
+#import "remoting/ios/app/session_reconnect_view.h"
 #import "remoting/ios/domain/client_session_details.h"
 #import "remoting/ios/domain/host_info.h"
 #import "remoting/ios/facade/remoting_authentication.h"
@@ -31,7 +33,10 @@
 static const CGFloat kPinEntryViewWidth = 240.f;
 static const CGFloat kPinEntryViewHeight = 90.f;
 
-static const CGFloat kCenterShift = -80.f;
+static const CGFloat kReconnectViewWidth = 120.f;
+static const CGFloat kReconnectViewHeight = 90.f;
+
+static const CGFloat kTopPadding = 240.f;
 static const CGFloat kPadding = 20.f;
 static const CGFloat kMargin = 20.f;
 
@@ -39,14 +44,20 @@
 
 static const CGFloat kKeyboardAnimationTime = 0.3;
 
-@interface ClientConnectionViewController ()<PinEntryDelegate> {
+@interface ClientConnectionViewController ()<PinEntryDelegate,
+                                             SessionReconnectViewDelegate> {
   UIImageView* _iconView;
   MDCActivityIndicator* _activityIndicator;
+  NSLayoutConstraint* _activityIndicatorTopConstraintFull;
+  NSLayoutConstraint* _activityIndicatorTopConstraintKeyboard;
   UILabel* _statusLabel;
   MDCNavigationBar* _navBar;
   PinEntryView* _pinEntryView;
+  SessionReconnectView* _reconnectView;
   NSString* _remoteHostName;
   RemotingClient* _client;
+  SessionErrorCode _lastError;
+  HostInfo* _hostInfo;
 }
 @end
 
@@ -57,17 +68,7 @@
 - (instancetype)initWithHostInfo:(HostInfo*)hostInfo {
   self = [super init];
   if (self) {
-    _client = [[RemotingClient alloc] init];
-
-    __weak RemotingClient* weakClient = _client;
-    [RemotingService.instance.authentication
-        callbackWithAccessToken:^(RemotingAuthenticationStatus status,
-                                  NSString* userEmail, NSString* accessToken) {
-          [weakClient connectToHost:hostInfo
-                           username:userEmail
-                        accessToken:accessToken];
-        }];
-
+    _hostInfo = hostInfo;
     _remoteHostName = hostInfo.hostName;
 
     // TODO(yuweih): This logic may be reused by other views.
@@ -102,78 +103,67 @@
           constraintEqualToAnchor:[self.view trailingAnchor]],
       [[_navBar heightAnchor] constraintEqualToConstant:kBarHeight],
     ]];
+
+    [self attemptConnectionToHost];
   }
   return self;
 }
 
-#pragma mark - UIViewController
-
-- (void)loadView {
-  [super loadView];
-
-  self.view.backgroundColor = RemotingTheme.connectionViewBackgroundColor;
-
-  _activityIndicator = [[MDCActivityIndicator alloc] initWithFrame:CGRectZero];
-  [self.view addSubview:_activityIndicator];
-
-  _statusLabel = [[UILabel alloc] initWithFrame:CGRectZero];
-  [self.view addSubview:_statusLabel];
-
-  _iconView = [[UIImageView alloc] initWithFrame:CGRectZero];
-  [self.view addSubview:_iconView];
-
-  _pinEntryView = [[PinEntryView alloc] init];
-  [self.view addSubview:_pinEntryView];
-  _pinEntryView.delegate = self;
+- (void)dealloc {
+  [[NSNotificationCenter defaultCenter] removeObserver:self];
 }
 
+#pragma mark - UIViewController
+
 - (void)viewDidLoad {
   [super viewDidLoad];
+  self.view.backgroundColor = RemotingTheme.connectionViewBackgroundColor;
 
+  _activityIndicator = [[MDCActivityIndicator alloc] initWithFrame:CGRectZero];
+  _activityIndicator.radius = kActivityIndicatorRadius;
+  _activityIndicator.trackEnabled = YES;
+  _activityIndicator.strokeWidth = kActivityIndicatorStrokeWidth;
+  _activityIndicator.cycleColors = @[ UIColor.whiteColor ];
+  _activityIndicator.translatesAutoresizingMaskIntoConstraints = NO;
+  [self.view addSubview:_activityIndicator];
+
+  _statusLabel = [[UILabel alloc] initWithFrame:CGRectZero];
+  _statusLabel.numberOfLines = 1;
+  _statusLabel.lineBreakMode = NSLineBreakByTruncatingTail;
+  _statusLabel.textColor = [UIColor whiteColor];
+  _statusLabel.textAlignment = NSTextAlignmentCenter;
+  _statusLabel.translatesAutoresizingMaskIntoConstraints = NO;
+  [self.view addSubview:_statusLabel];
+
+  _iconView = [[UIImageView alloc] initWithFrame:CGRectZero];
   _iconView.contentMode = UIViewContentModeCenter;
   _iconView.alpha = 0.87f;
   _iconView.backgroundColor = RemotingTheme.onlineHostColor;
   _iconView.layer.cornerRadius = kIconRadius;
   _iconView.layer.masksToBounds = YES;
   _iconView.image = RemotingTheme.desktopIcon;
+  _iconView.translatesAutoresizingMaskIntoConstraints = NO;
+  [self.view addSubview:_iconView];
 
-  _activityIndicator.radius = kActivityIndicatorRadius;
-  _activityIndicator.trackEnabled = YES;
-  _activityIndicator.strokeWidth = kActivityIndicatorStrokeWidth;
-  _activityIndicator.cycleColors = @[ UIColor.whiteColor ];
+  _reconnectView = [[SessionReconnectView alloc] initWithFrame:CGRectZero];
+  _reconnectView.hidden = YES;
+  _reconnectView.translatesAutoresizingMaskIntoConstraints = NO;
+  [self.view addSubview:_reconnectView];
+  _reconnectView.delegate = self;
 
-  _statusLabel.numberOfLines = 1;
-  _statusLabel.lineBreakMode = NSLineBreakByTruncatingTail;
-  _statusLabel.textColor = [UIColor whiteColor];
-  _statusLabel.textAlignment = NSTextAlignmentCenter;
-
+  _pinEntryView = [[PinEntryView alloc] init];
   _pinEntryView.hidden = YES;
-}
+  _pinEntryView.translatesAutoresizingMaskIntoConstraints = NO;
+  [self.view addSubview:_pinEntryView];
+  _pinEntryView.delegate = self;
 
-- (void)viewWillLayoutSubviews {
-  [super viewWillLayoutSubviews];
+  _reconnectView.hidden = YES;
 
-  _iconView.frame = CGRectMake(0, 0, kIconRadius * 2, kIconRadius * 2.f);
-  _iconView.center =
-      CGPointMake(self.view.center.x, self.view.center.y + kCenterShift);
-
-  [_activityIndicator sizeToFit];
-  _activityIndicator.center = _iconView.center;
-
-  _statusLabel.frame =
-      CGRectMake(kMargin, _activityIndicator.center.y + kIconRadius + kPadding,
-                 self.view.frame.size.width - kMargin * 2.f,
-                 _statusLabel.font.pointSize * _statusLabel.numberOfLines);
-
-  _pinEntryView.frame = CGRectMake(
-      (self.view.frame.size.width - kPinEntryViewWidth) / 2.f,
-      _statusLabel.frame.origin.y + _statusLabel.frame.size.height + kPadding,
-      kPinEntryViewWidth, kPinEntryViewHeight);
-}
-
-- (void)viewWillAppear:(BOOL)animated {
-  [super viewWillAppear:animated];
-  [self.navigationController setNavigationBarHidden:YES animated:animated];
+  [self
+      initializeLayoutConstraintsWithViews:NSDictionaryOfVariableBindings(
+                                               _activityIndicator, _statusLabel,
+                                               _iconView, _reconnectView,
+                                               _pinEntryView)];
 
   [[NSNotificationCenter defaultCenter]
       addObserver:self
@@ -182,6 +172,108 @@
            object:nil];
 }
 
+- (void)initializeLayoutConstraintsWithViews:(NSDictionary*)views {
+  // Metrics to use in visual format strings.
+  NSDictionary* layoutMetrics = @{
+    @"padding" : @(kPadding),
+    @"margin" : @(kMargin),
+    @"topPadding" : @(kTopPadding),
+    @"iconDiameter" : @(kIconRadius * 2),
+    @"pinEntryViewWidth" : @(kPinEntryViewWidth),
+    @"pinEntryViewHeight" : @(kPinEntryViewHeight),
+    @"reconnectViewWidth" : @(kReconnectViewWidth),
+    @"reconnectViewHeight" : @(kReconnectViewHeight),
+  };
+  [_activityIndicator sizeToFit];
+  NSString* f;
+
+  // Horizontal constraints:
+  [self.view addConstraints:
+                 [NSLayoutConstraint
+                     constraintsWithVisualFormat:@"H:[_iconView(iconDiameter)]"
+                                         options:0
+                                         metrics:layoutMetrics
+                                           views:views]];
+
+  [self.view addConstraints:[NSLayoutConstraint
+                                constraintsWithVisualFormat:
+                                    @"H:|-margin-[_statusLabel]-margin-|"
+                                                    options:0
+                                                    metrics:layoutMetrics
+                                                      views:views]];
+
+  [self.view addConstraints:[NSLayoutConstraint
+                                constraintsWithVisualFormat:
+                                    @"H:[_pinEntryView(pinEntryViewWidth)]"
+                                                    options:0
+                                                    metrics:layoutMetrics
+                                                      views:views]];
+
+  [self.view addConstraints:[NSLayoutConstraint
+                                constraintsWithVisualFormat:
+                                    @"H:[_reconnectView(reconnectViewWidth)]"
+                                                    options:0
+                                                    metrics:layoutMetrics
+                                                      views:views]];
+
+  // Anchors:
+  _activityIndicatorTopConstraintFull =
+      [_activityIndicator.topAnchor constraintEqualToAnchor:self.view.topAnchor
+                                                   constant:kTopPadding];
+  _activityIndicatorTopConstraintFull.active = YES;
+
+  [_iconView.centerYAnchor
+      constraintEqualToAnchor:_activityIndicator.centerYAnchor]
+      .active = YES;
+
+  // Vertical constraints:
+  [self.view addConstraints:
+                 [NSLayoutConstraint
+                     constraintsWithVisualFormat:@"V:[_iconView(iconDiameter)]"
+                                         options:0
+                                         metrics:layoutMetrics
+                                           views:views]];
+
+  [self.view addConstraints:
+                 [NSLayoutConstraint
+                     constraintsWithVisualFormat:
+                         @"V:[_activityIndicator]-(padding)-[_statusLabel]"
+                                         options:NSLayoutFormatAlignAllCenterX
+                                         metrics:layoutMetrics
+                                           views:views]];
+
+  [self.view addConstraints:
+                 [NSLayoutConstraint
+                     constraintsWithVisualFormat:
+                         @"V:[_iconView]-(padding)-[_statusLabel]"
+                                         options:NSLayoutFormatAlignAllCenterX
+                                         metrics:layoutMetrics
+                                           views:views]];
+
+  f = @"V:[_statusLabel]-(padding)-[_pinEntryView(pinEntryViewHeight)]";
+  [self.view addConstraints:
+                 [NSLayoutConstraint
+                     constraintsWithVisualFormat:f
+                                         options:NSLayoutFormatAlignAllCenterX
+                                         metrics:layoutMetrics
+                                           views:views]];
+
+  f = @"V:[_statusLabel]-padding-[_reconnectView(reconnectViewHeight)]";
+  [self.view addConstraints:
+                 [NSLayoutConstraint
+                     constraintsWithVisualFormat:f
+                                         options:NSLayoutFormatAlignAllCenterX
+                                         metrics:layoutMetrics
+                                           views:views]];
+
+  [self.view setNeedsUpdateConstraints];
+}
+
+- (void)viewWillAppear:(BOOL)animated {
+  [super viewWillAppear:animated];
+  [self.navigationController setNavigationBarHidden:YES animated:animated];
+}
+
 - (void)viewDidAppear:(BOOL)animated {
   [super viewDidAppear:animated];
   [[NSNotificationCenter defaultCenter]
@@ -202,7 +294,6 @@
 - (void)viewWillDisappear:(BOOL)animated {
   [super viewWillDisappear:animated];
   [_activityIndicator stopAnimating];
-  [[NSNotificationCenter defaultCenter] removeObserver:self];
 }
 
 - (BOOL)prefersStatusBarHidden {
@@ -211,35 +302,39 @@
 
 #pragma mark - Keyboard
 
+// TODO(nicholss): We need to listen to screen rotation and re-adjust the
+// topAnchor.
+
 - (void)keyboardWillShow:(NSNotification*)notification {
   CGSize keyboardSize =
       [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey]
           CGRectValue]
           .size;
 
-  [UIView
-      animateWithDuration:kKeyboardAnimationTime
-               animations:^{
-                 CGRect f = self.view.frame;
-                 CGFloat newHeight =
-                     self.view.frame.size.height - keyboardSize.height;
-                 CGFloat overlap =
-                     newHeight - (_pinEntryView.frame.origin.y +
-                                  _pinEntryView.frame.size.height + kPadding);
-                 if (overlap < 0) {
-                   f.origin.y = overlap;
-                   // TODO(yuweih): This may push the navigation bar off screen.
-                   self.view.frame = f;
-                 }
-               }];
+  CGFloat newHeight = self.view.frame.size.height - keyboardSize.height;
+  CGFloat overlap = newHeight - (_pinEntryView.frame.origin.y +
+                                 _pinEntryView.frame.size.height + kPadding);
+  if (overlap > 0) {
+    overlap = 0;
+  }
+  _activityIndicatorTopConstraintKeyboard.active = NO;
+  _activityIndicatorTopConstraintKeyboard = [_activityIndicator.topAnchor
+      constraintEqualToAnchor:self.view.topAnchor
+                     constant:kTopPadding + overlap];
+  _activityIndicatorTopConstraintFull.active = NO;
+  _activityIndicatorTopConstraintKeyboard.active = YES;
+  [UIView animateWithDuration:kKeyboardAnimationTime
+                   animations:^{
+                     [self.view layoutIfNeeded];
+                   }];
 }
 
 - (void)keyboardWillHide:(NSNotification*)notification {
+  _activityIndicatorTopConstraintKeyboard.active = NO;
+  _activityIndicatorTopConstraintFull.active = YES;
   [UIView animateWithDuration:kKeyboardAnimationTime
                    animations:^{
-                     CGRect f = self.view.frame;
-                     f.origin.y = 0.f;
-                     self.view.frame = f;
+                     [self.view layoutIfNeeded];
                    }];
 }
 
@@ -257,23 +352,53 @@
     case ClientViewConnected:
       [self showConnectedState];
       break;
+    case ClientViewReconnect:
+      [self showReconnect];
+      break;
     case ClientViewClosed:
-      [self dismissViewControllerAnimated:YES completion:nil];
+      [self.navigationController popToRootViewControllerAnimated:YES];
+      break;
+    case ClientViewError:
+      [self showError];
       break;
   }
 }
 
+#pragma mark - SessionReconnectViewDelegate
+
+- (void)didTapReconnect {
+  [self attemptConnectionToHost];
+}
+
 #pragma mark - Private
 
+- (void)attemptConnectionToHost {
+  _client = [[RemotingClient alloc] init];
+  __weak RemotingClient* weakClient = _client;
+  __weak HostInfo* weakHostInfo = _hostInfo;
+  [RemotingService.instance.authentication
+      callbackWithAccessToken:^(RemotingAuthenticationStatus status,
+                                NSString* userEmail, NSString* accessToken) {
+        [weakClient connectToHost:weakHostInfo
+                         username:userEmail
+                      accessToken:accessToken];
+      }];
+  [self setState:ClientViewConnecting];
+}
+
 - (void)showConnectingState {
   [_pinEntryView endEditing:YES];
   _statusLabel.text =
       [NSString stringWithFormat:@"Connecting to %@", _remoteHostName];
+
+  _pinEntryView.hidden = YES;
+
+  _reconnectView.hidden = YES;
+
   [_activityIndicator stopAnimating];
   _activityIndicator.cycleColors = @[ [UIColor whiteColor] ];
   _activityIndicator.indicatorMode = MDCActivityIndicatorModeIndeterminate;
   _activityIndicator.hidden = NO;
-  _pinEntryView.hidden = YES;
   [_activityIndicator startAnimating];
 }
 
@@ -281,7 +406,11 @@
   _statusLabel.text = [NSString stringWithFormat:@"%@", _remoteHostName];
   [_activityIndicator stopAnimating];
   _activityIndicator.hidden = YES;
+
   _pinEntryView.hidden = NO;
+  _reconnectView.hidden = YES;
+
+  _reconnectView.hidden = YES;
 
   // TODO(yuweih): This may be called before viewDidAppear and miss the keyboard
   // callback.
@@ -292,24 +421,113 @@
   [_pinEntryView endEditing:YES];
   _statusLabel.text =
       [NSString stringWithFormat:@"Connected to %@", _remoteHostName];
-  _activityIndicator.progress = 0.0;
+
   _pinEntryView.hidden = YES;
+  [_pinEntryView clearPinEntry];
+
+  _activityIndicator.progress = 0.0;
   _activityIndicator.hidden = NO;
   _activityIndicator.indicatorMode = MDCActivityIndicatorModeDeterminate;
   _activityIndicator.cycleColors = @[ [UIColor greenColor] ];
   [_activityIndicator startAnimating];
   _activityIndicator.progress = 1.0;
+  _reconnectView.hidden = YES;
+
+  _reconnectView.hidden = YES;
 
   HostViewController* hostViewController =
       [[HostViewController alloc] initWithClient:_client];
   _client = nil;
 
-  // Replaces current (topmost) view controller with |hostViewController|.
-  NSMutableArray* controllers =
-      [self.navigationController.viewControllers mutableCopy];
-  [controllers removeLastObject];
-  [controllers addObject:hostViewController];
-  [self.navigationController setViewControllers:controllers animated:NO];
+  [self.navigationController pushViewController:hostViewController animated:NO];
+}
+
+- (void)showReconnect {
+  _statusLabel.text =
+      [NSString stringWithFormat:@"Connection closed for %@", _remoteHostName];
+  [_activityIndicator stopAnimating];
+  _activityIndicator.hidden = YES;
+
+  _pinEntryView.hidden = YES;
+
+  _reconnectView.hidden = NO;
+
+  [self.navigationController popToViewController:self animated:YES];
+  [MDCSnackbarManager
+      showMessage:[MDCSnackbarMessage messageWithText:@"Connection Closed."]];
+}
+
+- (void)showError {
+  _statusLabel.text =
+      [NSString stringWithFormat:@"Error connecting to %@", _remoteHostName];
+
+  _pinEntryView.hidden = YES;
+
+  _activityIndicator.indicatorMode = MDCActivityIndicatorModeDeterminate;
+  _activityIndicator.cycleColors = @[ [UIColor redColor] ];
+  _activityIndicator.progress = 1.0;
+  _activityIndicator.hidden = NO;
+  [_activityIndicator startAnimating];
+
+  _reconnectView.hidden = NO;
+
+  MDCSnackbarMessage* message = nil;
+  switch (_lastError) {
+    case SessionErrorOk:
+      // Do nothing.
+      break;
+    case SessionErrorPeerIsOffline:
+      message = [MDCSnackbarMessage
+          messageWithText:@"Error: SessionErrorPeerIsOffline."];
+      break;
+    case SessionErrorSessionRejected:
+      message = [MDCSnackbarMessage
+          messageWithText:@"Error: SessionErrorSessionRejected."];
+      break;
+    case SessionErrorIncompatibleProtocol:
+      message = [MDCSnackbarMessage
+          messageWithText:@"Error: SessionErrorIncompatibleProtocol."];
+      break;
+    case SessionErrorAuthenticationFailed:
+      message = [MDCSnackbarMessage messageWithText:@"Error: Invalid Pin."];
+      [_pinEntryView clearPinEntry];
+      break;
+    case SessionErrorInvalidAccount:
+      message = [MDCSnackbarMessage
+          messageWithText:@"Error: SessionErrorInvalidAccount."];
+      break;
+    case SessionErrorChannelConnectionError:
+      message = [MDCSnackbarMessage
+          messageWithText:@"Error: SessionErrorChannelConnectionError."];
+      break;
+    case SessionErrorSignalingError:
+      message = [MDCSnackbarMessage
+          messageWithText:@"Error: SessionErrorSignalingError."];
+      break;
+    case SessionErrorSignalingTimeout:
+      message = [MDCSnackbarMessage
+          messageWithText:@"Error: SessionErrorSignalingTimeout."];
+      break;
+    case SessionErrorHostOverload:
+      message = [MDCSnackbarMessage
+          messageWithText:@"Error: SessionErrorHostOverload."];
+      break;
+    case SessionErrorMaxSessionLength:
+      message = [MDCSnackbarMessage
+          messageWithText:@"Error: SessionErrorMaxSessionLength."];
+      break;
+    case SessionErrorHostConfigurationError:
+      message = [MDCSnackbarMessage
+          messageWithText:@"Error: SessionErrorHostConfigurationError."];
+      break;
+    case SessionErrorUnknownError:
+      message = [MDCSnackbarMessage
+          messageWithText:@"Error: SessionErrorUnknownError."];
+      break;
+  }
+  if (message.text) {
+    [MDCSnackbarManager showMessage:message];
+  }
 }
 
 - (void)didProvidePin:(NSString*)pin createPairing:(BOOL)createPairing {
@@ -347,14 +565,17 @@
       state = ClientViewConnected;
       break;
     case SessionFailed:
-    // TODO(nicholss): Implement an error screen.
+      state = ClientViewError;
+      break;
     case SessionClosed:
-      state = ClientViewClosed;
+      // If the session closes, offer the user to reconnect.
+      state = ClientViewReconnect;
       break;
     default:
       LOG(ERROR) << "Unknown State for Session, " << sessionDetails.state;
       return;
   }
+  _lastError = sessionDetails.error;
   [[NSOperationQueue mainQueue] addOperationWithBlock:^{
     [self setState:state];
   }];
diff --git a/remoting/ios/app/host_view_controller.mm b/remoting/ios/app/host_view_controller.mm
index 6bf3993..ea36e730 100644
--- a/remoting/ios/app/host_view_controller.mm
+++ b/remoting/ios/app/host_view_controller.mm
@@ -310,7 +310,7 @@
 
   void (^disconnectHandler)(UIAlertAction*) = ^(UIAlertAction*) {
     [_client disconnectFromHost];
-    [self.navigationController popViewControllerAnimated:YES];
+    [self.navigationController popToRootViewControllerAnimated:YES];
     [_actionImageView setActive:NO animated:YES];
   };
   [alert addAction:[UIAlertAction actionWithTitle:@"Disconnect"
diff --git a/remoting/ios/app/pin_entry_view.h b/remoting/ios/app/pin_entry_view.h
index acbf923..926831f 100644
--- a/remoting/ios/app/pin_entry_view.h
+++ b/remoting/ios/app/pin_entry_view.h
@@ -20,6 +20,9 @@
 // passcode.
 @interface PinEntryView : UIView
 
+// Clears the pin entry view.
+- (void)clearPinEntry;
+
 // This delegate will handle interactions on the cells in the collection.
 @property(weak, nonatomic) id<PinEntryDelegate> delegate;
 
diff --git a/remoting/ios/app/pin_entry_view.mm b/remoting/ios/app/pin_entry_view.mm
index 87a5a6b..9037e0f 100644
--- a/remoting/ios/app/pin_entry_view.mm
+++ b/remoting/ios/app/pin_entry_view.mm
@@ -12,10 +12,12 @@
 #import "remoting/ios/app/remoting_theme.h"
 
 static const CGFloat kMargin = 5.f;
-static const CGFloat kPadding = 6.f;
+static const CGFloat kPadding = 8.f;
 static const CGFloat kLineSpace = 12.f;
 
-@interface PinEntryView () {
+static const int kMinPinLength = 6;
+
+@interface PinEntryView ()<UITextFieldDelegate> {
   UISwitch* _pairingSwitch;
   UILabel* _pairingLabel;
   MDCFloatingButton* _pinButton;
@@ -36,6 +38,7 @@
     _pairingSwitch.tintColor =
         [UIColor colorWithRed:1.f green:1.f blue:1.f alpha:0.5];
     _pairingSwitch.transform = CGAffineTransformMakeScale(0.5, 0.5);
+    _pairingSwitch.translatesAutoresizingMaskIntoConstraints = NO;
     [self addSubview:_pairingSwitch];
 
     _pairingLabel = [[UILabel alloc] init];
@@ -43,15 +46,21 @@
         [UIColor colorWithRed:1.f green:1.f blue:1.f alpha:0.5];
     _pairingLabel.font = [UIFont systemFontOfSize:12.f];
     _pairingLabel.text = @"Remember my PIN on this device.";
+    _pairingLabel.translatesAutoresizingMaskIntoConstraints = NO;
     [self addSubview:_pairingLabel];
 
     _pinButton =
         [MDCFloatingButton floatingButtonWithShape:MDCFloatingButtonShapeMini];
-    [_pinButton setImage:RemotingTheme.arrowIcon forState:UIControlStateNormal];
+    [_pinButton
+        setImage:[RemotingTheme
+                         .arrowIcon imageFlippedForRightToLeftLayoutDirection]
+        forState:UIControlStateNormal];
     [_pinButton addTarget:self
                    action:@selector(didTapPinEntry:)
          forControlEvents:UIControlEventTouchUpInside];
     _pinButton.translatesAutoresizingMaskIntoConstraints = NO;
+    _pinButton.enabled = NO;
+    _pinButton.translatesAutoresizingMaskIntoConstraints = NO;
     [self addSubview:_pinButton];
 
     _pinEntry = [[UITextField alloc] init];
@@ -65,11 +74,51 @@
               NSForegroundColorAttributeName :
                   [UIColor colorWithRed:1.f green:1.f blue:1.f alpha:0.5]
             }];
+    _pinEntry.translatesAutoresizingMaskIntoConstraints = NO;
+    _pinEntry.delegate = self;
     [self addSubview:_pinEntry];
+
+    [self
+        initializeLayoutConstraintsWithViews:NSDictionaryOfVariableBindings(
+                                                 _pairingSwitch, _pairingLabel,
+                                                 _pinButton, _pinEntry)];
   }
   return self;
 }
 
+- (void)initializeLayoutConstraintsWithViews:(NSDictionary*)views {
+  // Metrics to use in visual format strings.
+  NSDictionary* layoutMetrics = @{
+    @"margin" : @(kMargin),
+    @"padding" : @(kPadding),
+    @"lineSpace" : @(kLineSpace),
+  };
+
+  [self addConstraints:
+            [NSLayoutConstraint
+                constraintsWithVisualFormat:
+                    @"H:|-[_pinEntry]-(padding)-[_pinButton]-|"
+                                    options:NSLayoutFormatAlignAllCenterY
+                                    metrics:layoutMetrics
+                                      views:views]];
+
+  [self addConstraints:
+            [NSLayoutConstraint
+                constraintsWithVisualFormat:
+                    @"H:|-[_pairingSwitch]-(padding)-[_pairingLabel]-|"
+                                    options:NSLayoutFormatAlignAllCenterY
+                                    metrics:layoutMetrics
+                                      views:views]];
+
+  [self addConstraints:[NSLayoutConstraint
+                           constraintsWithVisualFormat:
+                               @"V:|-[_pinButton]-(lineSpace)-[_pairingSwitch]"
+                                               options:0
+                                               metrics:layoutMetrics
+                                                 views:views]];
+  [self setNeedsUpdateConstraints];
+}
+
 #pragma mark - UIView
 
 - (BOOL)canBecomeFirstResponder {
@@ -84,31 +133,32 @@
   return [_pinEntry endEditing:force];
 }
 
-- (void)layoutSubviews {
-  [super layoutSubviews];
+#pragma mark - UITextFieldDelegate
 
-  [_pinButton sizeToFit];
-  CGFloat buttonSize = _pinButton.frame.size.width;  // Assume circle.
+- (BOOL)textField:(UITextField*)textField
+    shouldChangeCharactersInRange:(NSRange)range
+                replacementString:(NSString*)string {
+  if (textField == _pinEntry) {
+    NSUInteger length = _pinEntry.text.length - range.length + string.length;
+    _pinButton.enabled = length >= kMinPinLength;
+  }
+  return YES;
+}
 
-  _pinEntry.frame =
-      CGRectMake(kMargin, 0.f,
-                 self.frame.size.width - kPadding - kMargin * 2.f - buttonSize,
-                 buttonSize);
+- (BOOL)textFieldShouldReturn:(UITextField*)textField {
+  NSLog(@"textFieldShouldReturn");
+  if ([_pinButton isEnabled]) {
+    [self didTapPinEntry:textField];
+    return YES;
+  }
+  return NO;
+}
 
-  [_pinButton sizeToFit];
-  _pinButton.frame =
-      CGRectMake(self.frame.size.width - kPadding - kMargin - buttonSize, 0.f,
-                 buttonSize, buttonSize);
+#pragma mark - Public
 
-  [_pairingSwitch sizeToFit];
-  _pairingSwitch.center = CGPointMake(
-      kMargin + _pairingSwitch.frame.size.width / 2.f,
-      buttonSize + _pairingSwitch.frame.size.height / 2.f + kLineSpace);
-
-  _pairingLabel.frame =
-      CGRectMake(kMargin + _pairingSwitch.frame.size.width + kPadding,
-                 buttonSize + kLineSpace, 0.f, 0.f);
-  [_pairingLabel sizeToFit];
+- (void)clearPinEntry {
+  _pinEntry.text = @"";
+  _pinButton.enabled = NO;
 }
 
 #pragma mark - Private
diff --git a/remoting/ios/app/remoting_view_controller.mm b/remoting/ios/app/remoting_view_controller.mm
index 637febc..ea9666ea 100644
--- a/remoting/ios/app/remoting_view_controller.mm
+++ b/remoting/ios/app/remoting_view_controller.mm
@@ -36,7 +36,8 @@
 @interface RemotingViewController ()<HostCollectionViewControllerDelegate,
                                      UIViewControllerAnimatedTransitioning,
                                      UIViewControllerTransitioningDelegate> {
-  bool _isAuthenticated;
+  BOOL _isAuthenticated;
+  BOOL _showSignedInAccount;
   MDCDialogTransitionController* _dialogTransitionController;
   MDCAppBar* _appBar;
   HostCollectionViewController* _collectionViewController;
@@ -54,6 +55,7 @@
 
 - (instancetype)init {
   _isAuthenticated = NO;
+  _showSignedInAccount = YES;
   UICollectionViewFlowLayout* layout =
       [[MDCCollectionViewFlowLayout alloc] init];
   layout.minimumInteritemSpacing = 0;
@@ -147,19 +149,9 @@
            object:nil];
 }
 
-- (void)viewWillAppear:(BOOL)animated {
-  [super viewWillAppear:animated];
-
-  [self nowAuthenticated:_remotingService.authentication.user.isAuthenticated];
-  if (_isAuthenticated) {
-    [_remotingService requestHostListFetch];
-  }
-  [self presentStatus];
-}
-
 - (void)viewDidAppear:(BOOL)animated {
   [super viewDidAppear:animated];
-  if (!_isAuthenticated) {
+  if (!_remotingService.authentication.user.isAuthenticated) {
     [AppDelegate.instance presentSignInFlow];
     MDCSnackbarMessage* message = [[MDCSnackbarMessage alloc] init];
     message.text = @"Please login.";
@@ -178,22 +170,24 @@
 }
 
 - (void)userDidUpdateNotification:(NSNotification*)notification {
-  [self nowAuthenticated:_remotingService.authentication.user.isAuthenticated];
+  [[NSOperationQueue mainQueue] addOperationWithBlock:^{
+    [self
+        nowAuthenticated:_remotingService.authentication.user.isAuthenticated];
+  }];
 }
 
 #pragma mark - RemotingAuthenticationDelegate
 
 - (void)nowAuthenticated:(BOOL)authenticated {
+  _isAuthenticated = authenticated;
   if (authenticated) {
-    MDCSnackbarMessage* message = [[MDCSnackbarMessage alloc] init];
-    message.text = @"Logged In!";
-    [MDCSnackbarManager showMessage:message];
+    _showSignedInAccount = YES;
+    [self presentStatus];
   } else {
     MDCSnackbarMessage* message = [[MDCSnackbarMessage alloc] init];
-    message.text = @"Not logged in.";
+    message.text = @"Please sign-in.";
     [MDCSnackbarManager showMessage:message];
   }
-  _isAuthenticated = authenticated;
   [self refreshContent];
 }
 
@@ -268,7 +262,8 @@
 
 - (void)presentStatus {
   MDCSnackbarMessage* message = [[MDCSnackbarMessage alloc] init];
-  if (_isAuthenticated) {
+  if (_isAuthenticated && _showSignedInAccount) {
+    _showSignedInAccount = NO;
     message.text = [NSString
         stringWithFormat:@"Currently signed in as %@.",
                          _remotingService.authentication.user.userEmail];
diff --git a/remoting/ios/app/session_reconnect_view.h b/remoting/ios/app/session_reconnect_view.h
new file mode 100644
index 0000000..2909cd53
--- /dev/null
+++ b/remoting/ios/app/session_reconnect_view.h
@@ -0,0 +1,27 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef REMOTING_IOS_SESSON_RECONNECT_VIEW_H_
+#define REMOTING_IOS_SESSON_RECONNECT_VIEW_H_
+
+#import <UIKit/UIKit.h>
+
+@protocol SessionReconnectViewDelegate<NSObject>
+
+// Notifies the delegate that the user tapped the reconnect button.
+@optional
+- (void)didTapReconnect;
+
+@end
+
+// This view is the container for a session connection error. It will display a
+// reconnect button.
+@interface SessionReconnectView : UIView
+
+// This delegate will handle interactions on the view.
+@property(weak, nonatomic) id<SessionReconnectViewDelegate> delegate;
+
+@end
+
+#endif  // REMOTING_IOS_SESSON_RECONNECT_VIEW_H_
diff --git a/remoting/ios/app/session_reconnect_view.mm b/remoting/ios/app/session_reconnect_view.mm
new file mode 100644
index 0000000..3c3aafe
--- /dev/null
+++ b/remoting/ios/app/session_reconnect_view.mm
@@ -0,0 +1,75 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+#import "remoting/ios/app/session_reconnect_view.h"
+
+#import "ios/third_party/material_components_ios/src/components/Buttons/src/MaterialButtons.h"
+#import "remoting/ios/app/remoting_theme.h"
+
+static const CGFloat kReconnectButtonWidth = 120.f;
+static const CGFloat kReconnectButtonHeight = 30.f;
+
+@interface SessionReconnectView () {
+  MDCRaisedButton* _reconnectButton;
+}
+@end
+
+@implementation SessionReconnectView
+
+@synthesize delegate = _delegate;
+
+- (id)initWithFrame:(CGRect)frame {
+  self = [super initWithFrame:frame];
+  if (self) {
+    self.backgroundColor = [UIColor clearColor];
+
+    _reconnectButton = [[MDCRaisedButton alloc] init];
+    [_reconnectButton setElevation:4.0f forState:UIControlStateNormal];
+    [_reconnectButton setTitle:@"Reconnect" forState:UIControlStateNormal];
+    [_reconnectButton addTarget:self
+                         action:@selector(didTapReconnect:)
+               forControlEvents:UIControlEventTouchUpInside];
+    _reconnectButton.translatesAutoresizingMaskIntoConstraints = NO;
+    [self addSubview:_reconnectButton];
+
+    [self initializeLayoutConstraintsWithViews:NSDictionaryOfVariableBindings(
+                                                   _reconnectButton)];
+  }
+  return self;
+}
+
+- (void)initializeLayoutConstraintsWithViews:(NSDictionary*)views {
+  NSMutableArray* layoutConstraints = [NSMutableArray array];
+
+  [layoutConstraints addObject:[_reconnectButton.centerYAnchor
+                                   constraintEqualToAnchor:self.centerYAnchor]];
+
+  [layoutConstraints addObject:[_reconnectButton.centerXAnchor
+                                   constraintEqualToAnchor:self.centerXAnchor]];
+
+  [layoutConstraints
+      addObject:[_reconnectButton.widthAnchor
+                    constraintEqualToConstant:kReconnectButtonWidth]];
+
+  [layoutConstraints
+      addObject:[_reconnectButton.heightAnchor
+                    constraintEqualToConstant:kReconnectButtonHeight]];
+
+  [NSLayoutConstraint activateConstraints:layoutConstraints];
+  [self setNeedsUpdateConstraints];
+}
+
+#pragma mark - Private
+
+- (void)didTapReconnect:(id)sender {
+  if ([_delegate respondsToSelector:@selector(didTapReconnect)]) {
+    [_delegate didTapReconnect];
+  }
+}
+
+@end
diff --git a/remoting/ios/domain/client_session_details.h b/remoting/ios/domain/client_session_details.h
index 7309892..ee839b8 100644
--- a/remoting/ios/domain/client_session_details.h
+++ b/remoting/ios/domain/client_session_details.h
@@ -22,6 +22,23 @@
   SessionClosed,
 };
 
+// Session states that map to |remoting::protocol::ConnectionToHost::Error|.
+typedef NS_ENUM(NSInteger, SessionErrorCode) {
+  SessionErrorOk = 0,
+  SessionErrorPeerIsOffline,
+  SessionErrorSessionRejected,
+  SessionErrorIncompatibleProtocol,
+  SessionErrorAuthenticationFailed,
+  SessionErrorInvalidAccount,
+  SessionErrorChannelConnectionError,
+  SessionErrorSignalingError,
+  SessionErrorSignalingTimeout,
+  SessionErrorHostOverload,
+  SessionErrorMaxSessionLength,
+  SessionErrorHostConfigurationError,
+  SessionErrorUnknownError,
+};
+
 // The current state of a session and data needed for session context.
 @interface ClientSessionDetails : NSObject
 
@@ -29,6 +46,8 @@
 @property(nonatomic) HostInfo* hostInfo;
 // The current state of the session.
 @property(nonatomic, assign) SessionState state;
+// The error assoiciated to the current state.
+@property(nonatomic, assign) SessionErrorCode error;
 
 @end
 
diff --git a/remoting/ios/domain/client_session_details.mm b/remoting/ios/domain/client_session_details.mm
index 7e73b95..8156c34 100644
--- a/remoting/ios/domain/client_session_details.mm
+++ b/remoting/ios/domain/client_session_details.mm
@@ -14,6 +14,7 @@
 
 @synthesize hostInfo = _hostInfo;
 @synthesize state = _state;
+@synthesize error = _error;
 
 - (NSString*)description {
   return
diff --git a/remoting/ios/session/remoting_client.mm b/remoting/ios/session/remoting_client.mm
index 3dfccc8..3154bdc 100644
--- a/remoting/ios/session/remoting_client.mm
+++ b/remoting/ios/session/remoting_client.mm
@@ -165,10 +165,12 @@
 - (void)hostSessionPinProvided:(NSNotification*)notification {
   NSString* pin = [[notification userInfo] objectForKey:kHostSessionPin];
   if (_secretFetchedCallback) {
+    remoting::protocol::SecretFetchedCallback callback = _secretFetchedCallback;
     _runtime->network_task_runner()->PostTask(
         FROM_HERE, base::BindBlockArc(^{
-          _secretFetchedCallback.Run(base::SysNSStringToUTF8(pin));
+          callback.Run(base::SysNSStringToUTF8(pin));
         }));
+    _secretFetchedCallback.Reset();
   }
 }
 
@@ -192,34 +194,69 @@
                     error:(remoting::protocol::ErrorCode)error {
   switch (state) {
     case remoting::protocol::ConnectionToHost::INITIALIZING:
-      NSLog(@"State --> INITIALIZING");
       _sessionDetails.state = SessionInitializing;
       break;
     case remoting::protocol::ConnectionToHost::CONNECTING:
-      NSLog(@"State --> CONNECTING");
       _sessionDetails.state = SessionConnecting;
       break;
     case remoting::protocol::ConnectionToHost::AUTHENTICATED:
-      NSLog(@"State --> AUTHENTICATED");
       _sessionDetails.state = SessionAuthenticated;
       break;
     case remoting::protocol::ConnectionToHost::CONNECTED:
-      NSLog(@"State --> CONNECTED");
       _sessionDetails.state = SessionConnected;
       break;
     case remoting::protocol::ConnectionToHost::FAILED:
-      NSLog(@"State --> FAILED");
       _sessionDetails.state = SessionFailed;
       break;
     case remoting::protocol::ConnectionToHost::CLOSED:
-      NSLog(@"State --> CLOSED");
       _sessionDetails.state = SessionClosed;
+      [self disconnectFromHost];
       break;
     default:
       LOG(ERROR) << "onConnectionState, unknown state: " << state;
   }
 
-  // TODO(nicholss): Send along the error code when we know what to do about it.
+  switch (error) {
+    case remoting::protocol::ErrorCode::OK:
+      _sessionDetails.error = SessionErrorOk;
+      break;
+    case remoting::protocol::ErrorCode::PEER_IS_OFFLINE:
+      _sessionDetails.error = SessionErrorPeerIsOffline;
+      break;
+    case remoting::protocol::ErrorCode::SESSION_REJECTED:
+      _sessionDetails.error = SessionErrorSessionRejected;
+      break;
+    case remoting::protocol::ErrorCode::INCOMPATIBLE_PROTOCOL:
+      _sessionDetails.error = SessionErrorIncompatibleProtocol;
+      break;
+    case remoting::protocol::ErrorCode::AUTHENTICATION_FAILED:
+      _sessionDetails.error = SessionErrorAuthenticationFailed;
+      break;
+    case remoting::protocol::ErrorCode::INVALID_ACCOUNT:
+      _sessionDetails.error = SessionErrorInvalidAccount;
+      break;
+    case remoting::protocol::ErrorCode::CHANNEL_CONNECTION_ERROR:
+      _sessionDetails.error = SessionErrorChannelConnectionError;
+      break;
+    case remoting::protocol::ErrorCode::SIGNALING_ERROR:
+      _sessionDetails.error = SessionErrorSignalingError;
+      break;
+    case remoting::protocol::ErrorCode::SIGNALING_TIMEOUT:
+      _sessionDetails.error = SessionErrorSignalingTimeout;
+      break;
+    case remoting::protocol::ErrorCode::HOST_OVERLOAD:
+      _sessionDetails.error = SessionErrorHostOverload;
+      break;
+    case remoting::protocol::ErrorCode::MAX_SESSION_LENGTH:
+      _sessionDetails.error = SessionErrorMaxSessionLength;
+      break;
+    case remoting::protocol::ErrorCode::HOST_CONFIGURATION_ERROR:
+      _sessionDetails.error = SessionErrorHostConfigurationError;
+    case remoting::protocol::ErrorCode::UNKNOWN_ERROR:
+      _sessionDetails.error = SessionErrorUnknownError;
+      break;
+  }
+
   [[NSNotificationCenter defaultCenter]
       postNotificationName:kHostSessionStatusChanged
                     object:self
@@ -240,7 +277,7 @@
 }
 
 - (void)setCapabilities:(NSString*)capabilities {
-  NSLog(@"TODO(nicholss): implement this, setCapabilities.");
+  NSLog(@"TODO(nicholss): implement this, setCapabilities. %@", capabilities);
 }
 
 - (void)handleExtensionMessageOfType:(NSString*)type
diff --git a/remoting/protocol/channel_multiplexer.cc b/remoting/protocol/channel_multiplexer.cc
index 5331bc4..2fc2fb3 100644
--- a/remoting/protocol/channel_multiplexer.cc
+++ b/remoting/protocol/channel_multiplexer.cc
@@ -97,8 +97,7 @@
   DISALLOW_COPY_AND_ASSIGN(MuxChannel);
 };
 
-class ChannelMultiplexer::MuxSocket : public P2PStreamSocket,
-                                      public base::SupportsWeakPtr<MuxSocket> {
+class ChannelMultiplexer::MuxSocket : public P2PStreamSocket {
  public:
   MuxSocket(MuxChannel* channel);
   ~MuxSocket() override;
@@ -128,21 +127,20 @@
 
   SEQUENCE_CHECKER(sequence_checker_);
 
+  base::WeakPtrFactory<MuxSocket> weak_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(MuxSocket);
 };
 
-
-ChannelMultiplexer::MuxChannel::MuxChannel(
-    ChannelMultiplexer* multiplexer,
-    const std::string& name,
-    int send_id)
+ChannelMultiplexer::MuxChannel::MuxChannel(ChannelMultiplexer* multiplexer,
+                                           const std::string& name,
+                                           int send_id)
     : multiplexer_(multiplexer),
       name_(name),
       send_id_(send_id),
       id_sent_(false),
       receive_id_(kChannelIdUnknown),
-      socket_(nullptr) {
-}
+      socket_(nullptr) {}
 
 ChannelMultiplexer::MuxChannel::~MuxChannel() {
   // Socket must be destroyed before the channel.
@@ -212,8 +210,8 @@
     : channel_(channel),
       read_buffer_size_(0),
       write_pending_(false),
-      write_result_(0) {
-}
+      write_result_(0),
+      weak_factory_(this) {}
 
 ChannelMultiplexer::MuxSocket::~MuxSocket() {
   channel_->OnSocketDestroyed();
@@ -252,8 +250,9 @@
   packet->mutable_data()->assign(buffer->data(), size);
 
   write_pending_ = true;
-  channel_->DoWrite(std::move(packet), base::Bind(
-      &ChannelMultiplexer::MuxSocket::OnWriteComplete, AsWeakPtr()));
+  channel_->DoWrite(std::move(packet),
+                    base::Bind(&ChannelMultiplexer::MuxSocket::OnWriteComplete,
+                               weak_factory_.GetWeakPtr()));
 
   // OnWriteComplete() might be called above synchronously.
   if (write_pending_) {
diff --git a/remoting/signaling/iq_sender.cc b/remoting/signaling/iq_sender.cc
index efa2528..1e6b494 100644
--- a/remoting/signaling/iq_sender.cc
+++ b/remoting/signaling/iq_sender.cc
@@ -130,12 +130,13 @@
   return true;
 }
 
-IqRequest::IqRequest(IqSender* sender, const IqSender::ReplyCallback& callback,
+IqRequest::IqRequest(IqSender* sender,
+                     const IqSender::ReplyCallback& callback,
                      const std::string& addressee)
     : sender_(sender),
       callback_(callback),
-      addressee_(addressee) {
-}
+      addressee_(addressee),
+      weak_factory_(this) {}
 
 IqRequest::~IqRequest() {
   sender_->RemoveRequest(this);
@@ -143,7 +144,8 @@
 
 void IqRequest::SetTimeout(base::TimeDelta timeout) {
   base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-      FROM_HERE, base::Bind(&IqRequest::OnTimeout, AsWeakPtr()), timeout);
+      FROM_HERE, base::Bind(&IqRequest::OnTimeout, weak_factory_.GetWeakPtr()),
+      timeout);
 }
 
 void IqRequest::CallCallback(const buzz::XmlElement* stanza) {
@@ -160,8 +162,9 @@
   // want to do that, so we post task to invoke the callback later.
   std::unique_ptr<buzz::XmlElement> stanza_copy(new buzz::XmlElement(*stanza));
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::Bind(&IqRequest::DeliverResponse, AsWeakPtr(),
-                            base::Passed(&stanza_copy)));
+      FROM_HERE,
+      base::Bind(&IqRequest::DeliverResponse, weak_factory_.GetWeakPtr(),
+                 base::Passed(&stanza_copy)));
 }
 
 void IqRequest::DeliverResponse(std::unique_ptr<buzz::XmlElement> stanza) {
diff --git a/remoting/signaling/iq_sender.h b/remoting/signaling/iq_sender.h
index be7c986..a1998f7 100644
--- a/remoting/signaling/iq_sender.h
+++ b/remoting/signaling/iq_sender.h
@@ -78,7 +78,7 @@
 };
 
 // This call must only be used on the thread it was created on.
-class IqRequest : public  base::SupportsWeakPtr<IqRequest> {
+class IqRequest {
  public:
   IqRequest(IqSender* sender, const IqSender::ReplyCallback& callback,
             const std::string& addressee);
@@ -103,6 +103,8 @@
   IqSender::ReplyCallback callback_;
   std::string addressee_;
 
+  base::WeakPtrFactory<IqRequest> weak_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(IqRequest);
 };
 
diff --git a/remoting/test/protocol_perftest.cc b/remoting/test/protocol_perftest.cc
index 1491273..15a9bf2 100644
--- a/remoting/test/protocol_perftest.cc
+++ b/remoting/test/protocol_perftest.cc
@@ -318,7 +318,7 @@
             host_pin_hash, nullptr);
     host_->SetAuthenticatorFactory(std::move(auth_factory));
 
-    host_->AddStatusObserver(this);
+    host_->status_monitor()->AddStatusObserver(this);
     host_->Start(kHostOwner);
 
     message_loop_.task_runner()->PostTask(
diff --git a/sandbox/linux/BUILD.gn b/sandbox/linux/BUILD.gn
index e833e4d..dd0910ea 100644
--- a/sandbox/linux/BUILD.gn
+++ b/sandbox/linux/BUILD.gn
@@ -221,6 +221,8 @@
     "bpf_dsl/trap_registry.h",
     "seccomp-bpf-helpers/baseline_policy.cc",
     "seccomp-bpf-helpers/baseline_policy.h",
+    "seccomp-bpf-helpers/baseline_policy_android.cc",
+    "seccomp-bpf-helpers/baseline_policy_android.h",
     "seccomp-bpf-helpers/sigsys_handlers.cc",
     "seccomp-bpf-helpers/sigsys_handlers.h",
     "seccomp-bpf-helpers/syscall_parameters_restrictions.cc",
diff --git a/content/common/sandbox_linux/android/sandbox_bpf_base_policy_android.cc b/sandbox/linux/seccomp-bpf-helpers/baseline_policy_android.cc
similarity index 89%
rename from content/common/sandbox_linux/android/sandbox_bpf_base_policy_android.cc
rename to sandbox/linux/seccomp-bpf-helpers/baseline_policy_android.cc
index aad26f63..761a292 100644
--- a/content/common/sandbox_linux/android/sandbox_bpf_base_policy_android.cc
+++ b/sandbox/linux/seccomp-bpf-helpers/baseline_policy_android.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/common/sandbox_linux/android/sandbox_bpf_base_policy_android.h"
+#include "sandbox/linux/seccomp-bpf-helpers/baseline_policy_android.h"
 
 #include <errno.h>
 #include <fcntl.h>
@@ -28,7 +28,7 @@
 using sandbox::bpf_dsl::Error;
 using sandbox::bpf_dsl::ResultExpr;
 
-namespace content {
+namespace sandbox {
 
 #ifndef SOCK_CLOEXEC
 #define SOCK_CLOEXEC O_CLOEXEC
@@ -58,13 +58,12 @@
 
 }  // namespace
 
-SandboxBPFBasePolicyAndroid::SandboxBPFBasePolicyAndroid()
-    : SandboxBPFBasePolicy(),
-      pid_(getpid()) {}
+BaselinePolicyAndroid::BaselinePolicyAndroid()
+    : BaselinePolicy() {}
 
-SandboxBPFBasePolicyAndroid::~SandboxBPFBasePolicyAndroid() {}
+BaselinePolicyAndroid::~BaselinePolicyAndroid() {}
 
-ResultExpr SandboxBPFBasePolicyAndroid::EvaluateSyscall(int sysno) const {
+ResultExpr BaselinePolicyAndroid::EvaluateSyscall(int sysno) const {
   bool override_and_allow = false;
 
   switch (sysno) {
@@ -150,13 +149,13 @@
   // https://crbug.com/644759
   if (sysno == __NR_rt_tgsigqueueinfo) {
     const Arg<pid_t> tgid(0);
-    return If(tgid == pid_, Allow())
+    return If(tgid == policy_pid(), Allow())
            .Else(Error(EPERM));
   }
 
   // https://crbug.com/655299
   if (sysno == __NR_clock_getres) {
-    return sandbox::RestrictClockID();
+    return RestrictClockID();
   }
 
 #if defined(__x86_64__)
@@ -196,7 +195,7 @@
                           option == SO_RCVTIMEO,
                           option == SO_REUSEADDR)),
               Allow())
-           .Else(SandboxBPFBasePolicy::EvaluateSyscall(sysno));
+           .Else(BaselinePolicy::EvaluateSyscall(sysno));
   }
 #elif defined(__i386__)
   if (sysno == __NR_socketcall) {
@@ -208,14 +207,14 @@
                 SYS_SETSOCKOPT,
                 SYS_GETSOCKOPT),
                Allow())
-        .Default(SandboxBPFBasePolicy::EvaluateSyscall(sysno));
+        .Default(BaselinePolicy::EvaluateSyscall(sysno));
   }
 #endif
 
   if (override_and_allow)
     return Allow();
 
-  return SandboxBPFBasePolicy::EvaluateSyscall(sysno);
+  return BaselinePolicy::EvaluateSyscall(sysno);
 }
 
-}  // namespace content
+}  // namespace sandbox
diff --git a/sandbox/linux/seccomp-bpf-helpers/baseline_policy_android.h b/sandbox/linux/seccomp-bpf-helpers/baseline_policy_android.h
new file mode 100644
index 0000000..77fdedf
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf-helpers/baseline_policy_android.h
@@ -0,0 +1,41 @@
+// 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 SANDBOX_LINUX_SECCOMP_BPF_HELPERS_BASELINE_POLICY_ANDROID_H_
+#define SANDBOX_LINUX_SECCOMP_BPF_HELPERS_BASELINE_POLICY_ANDROID_H_
+
+#include <sys/types.h>
+
+#include "base/macros.h"
+#include "sandbox/linux/seccomp-bpf-helpers/baseline_policy.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+// This class provides a Seccomp-BPF sandbox policy for programs that run
+// in the Android Runtime (Java) environment. It builds upon the Linux
+// BaselinePolicy, which would be suitable for Android shell-based programs,
+// and adds allowances for the JVM.
+//
+// As with the Linux BaselinePolicy, the behavior is largely implementation
+// defined.
+//
+// TODO(rsesek): This policy may currently have allowances for //content-level
+// features. This needs an audit. https://crbug.com/739879
+class SANDBOX_EXPORT BaselinePolicyAndroid : public BaselinePolicy {
+ public:
+  BaselinePolicyAndroid();
+  ~BaselinePolicyAndroid() override;
+
+  // sandbox::BaselinePolicy:
+  sandbox::bpf_dsl::ResultExpr EvaluateSyscall(
+      int system_call_number) const override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(BaselinePolicyAndroid);
+};
+
+}  // namespace sandbox
+
+#endif  // SANDBOX_LINUX_SECCOMP_BPF_HELPERS_BASELINE_POLICY_ANDROID_H_
diff --git a/services/identity/identity_manager.cc b/services/identity/identity_manager.cc
index 627e21f..a63f51a4 100644
--- a/services/identity/identity_manager.cc
+++ b/services/identity/identity_manager.cc
@@ -8,7 +8,6 @@
 
 #include "base/time/time.h"
 #include "components/signin/core/browser/account_tracker_service.h"
-#include "components/signin/core/browser/signin_manager_base.h"
 #include "google_apis/gaia/google_service_auth_error.h"
 
 namespace identity {
@@ -79,10 +78,12 @@
       base::Bind(&IdentityManager::OnConnectionError, base::Unretained(this)));
 
   token_service_->AddObserver(this);
+  signin_manager_->AddObserver(this);
 }
 
 IdentityManager::~IdentityManager() {
   token_service_->RemoveObserver(this);
+  signin_manager_->RemoveObserver(this);
 }
 
 void IdentityManager::GetPrimaryAccountInfo(
@@ -135,13 +136,25 @@
 }
 
 void IdentityManager::OnRefreshTokenAvailable(const std::string& account_id) {
+  OnAccountStateChange(account_id);
+}
+
+void IdentityManager::GoogleSigninSucceeded(const std::string& account_id,
+                                            const std::string& username) {
+  OnAccountStateChange(account_id);
+}
+
+void IdentityManager::OnAccountStateChange(const std::string& account_id) {
   AccountInfo account_info = account_tracker_->GetAccountInfo(account_id);
   AccountState account_state = GetStateOfAccount(account_info);
 
-  if (account_state.is_primary_account) {
+  // Check whether the primary account is available and notify any waiting
+  // consumers if so.
+  if (account_state.is_primary_account && account_state.has_refresh_token) {
     DCHECK(!account_info.account_id.empty());
     DCHECK(!account_info.email.empty());
     DCHECK(!account_info.gaia.empty());
+
     for (auto&& callback : primary_account_available_callbacks_) {
       std::move(callback).Run(account_info, account_state);
     }
diff --git a/services/identity/identity_manager.h b/services/identity/identity_manager.h
index fe49463..3b92c853 100644
--- a/services/identity/identity_manager.h
+++ b/services/identity/identity_manager.h
@@ -8,18 +8,19 @@
 #include "base/callback_list.h"
 #include "components/signin/core/browser/account_info.h"
 #include "components/signin/core/browser/profile_oauth2_token_service.h"
+#include "components/signin/core/browser/signin_manager_base.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "services/identity/public/cpp/account_state.h"
 #include "services/identity/public/cpp/scope_set.h"
 #include "services/identity/public/interfaces/identity_manager.mojom.h"
 
 class AccountTrackerService;
-class SigninManagerBase;
 
 namespace identity {
 
 class IdentityManager : public mojom::IdentityManager,
-                        public OAuth2TokenService::Observer {
+                        public OAuth2TokenService::Observer,
+                        public SigninManagerBase::Observer {
  public:
   static void Create(mojom::IdentityManagerRequest request,
                      AccountTrackerService* account_tracker,
@@ -82,6 +83,14 @@
   // OAuth2TokenService::Observer:
   void OnRefreshTokenAvailable(const std::string& account_id) override;
 
+  // SigninManagerBase::Observer:
+  void GoogleSigninSucceeded(const std::string& account_id,
+                             const std::string& username) override;
+
+  // Notified when there is a change in the state of the account
+  // corresponding to |account_id|.
+  void OnAccountStateChange(const std::string& account_id);
+
   // Deletes |request|.
   void AccessTokenRequestCompleted(AccessTokenRequest* request);
 
diff --git a/services/identity/identity_manager_unittest.cc b/services/identity/identity_manager_unittest.cc
index 1fdb055d..6a3e7a5d 100644
--- a/services/identity/identity_manager_unittest.cc
+++ b/services/identity/identity_manager_unittest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/run_loop.h"
+#include "build/build_config.h"
 #include "components/signin/core/browser/account_info.h"
 #include "components/signin/core/browser/account_tracker_service.h"
 #include "components/signin/core/browser/fake_profile_oauth2_token_service.h"
@@ -23,6 +24,12 @@
 namespace identity {
 namespace {
 
+#if defined(OS_CHROMEOS)
+using SigninManagerForTest = FakeSigninManagerBase;
+#else
+using SigninManagerForTest = FakeSigninManager;
+#endif  // OS_CHROMEOS
+
 const std::string kTestGaiaId = "dummyId";
 const std::string kTestEmail = "me@dummy.com";
 const std::string kTestRefreshToken = "dummy-refresh-token";
@@ -81,7 +88,14 @@
   IdentityManagerTest()
       : ServiceTest("identity_unittests", false),
         signin_client_(&pref_service_),
+#if defined(OS_CHROMEOS)
         signin_manager_(&signin_client_, &account_tracker_) {
+#else
+        signin_manager_(&signin_client_,
+                        &token_service_,
+                        &account_tracker_,
+                        nullptr) {
+#endif
     AccountTrackerService::RegisterPrefs(pref_service_.registry());
     SigninManagerBase::RegisterProfilePrefs(pref_service_.registry());
     SigninManagerBase::RegisterPrefs(pref_service_.registry());
@@ -162,7 +176,7 @@
   sync_preferences::TestingPrefServiceSyncable pref_service_;
   AccountTrackerService account_tracker_;
   TestSigninClient signin_client_;
-  FakeSigninManagerBase signin_manager_;
+  SigninManagerForTest signin_manager_;
   FakeProfileOAuth2TokenService token_service_;
 
   DISALLOW_COPY_AND_ASSIGN(IdentityManagerTest);
@@ -335,6 +349,65 @@
   EXPECT_TRUE(account_state.is_primary_account);
 }
 
+// Check that GetPrimaryAccountWhenAvailable() returns the expected account info
+// in the case where the token is available before the call is received but the
+// account is made authenticated only *after* the call is received. This test is
+// relevant only on non-ChromeOS platforms, as the flow being tested here is not
+// possible on ChromeOS.
+#if !defined(OS_CHROMEOS)
+TEST_F(IdentityManagerTest,
+       GetPrimaryAccountWhenAvailableAuthenticationAvailableLater) {
+  AccountInfo account_info;
+  AccountState account_state;
+
+  // Set the refresh token, but don't sign in yet.
+  std::string account_id_to_use =
+      account_tracker()->SeedAccountInfo(kTestGaiaId, kTestEmail);
+  token_service()->UpdateCredentials(account_id_to_use, kTestRefreshToken);
+  base::RunLoop run_loop;
+  identity_manager_->GetPrimaryAccountWhenAvailable(base::Bind(
+      &IdentityManagerTest::OnPrimaryAccountAvailable, base::Unretained(this),
+      run_loop.QuitClosure(), base::Unretained(&account_info),
+      base::Unretained(&account_state)));
+
+  // Verify that the account is present and has a refresh token, but that the
+  // primary account is not yet considered available (this also serves to ensure
+  // that the preceding call has been received by the Identity Manager before
+  // proceeding).
+  base::RunLoop run_loop2;
+  identity_manager_->GetAccountInfoFromGaiaId(
+      kTestGaiaId,
+      base::Bind(&IdentityManagerTest::OnReceivedAccountInfoFromGaiaId,
+                 base::Unretained(this), run_loop2.QuitClosure()));
+  run_loop2.Run();
+
+  EXPECT_TRUE(account_info_from_gaia_id_);
+  EXPECT_EQ(account_id_to_use, account_info_from_gaia_id_->account_id);
+  EXPECT_EQ(kTestGaiaId, account_info_from_gaia_id_->gaia);
+  EXPECT_EQ(kTestEmail, account_info_from_gaia_id_->email);
+  EXPECT_TRUE(account_state_from_gaia_id_.has_refresh_token);
+  EXPECT_FALSE(account_state_from_gaia_id_.is_primary_account);
+
+  EXPECT_TRUE(account_info.account_id.empty());
+
+  // Sign the user in and check that the callback is invoked as expected (i.e.,
+  // the primary account is now considered available). Note that it is necessary
+  // to call SignIn() here to ensure that GoogleSigninSucceeded() is fired by
+  // the fake signin manager.
+  static_cast<FakeSigninManager*>(signin_manager())
+      ->SignIn(kTestGaiaId, kTestEmail, "password");
+
+  run_loop.Run();
+
+  EXPECT_EQ(signin_manager()->GetAuthenticatedAccountId(),
+            account_info.account_id);
+  EXPECT_EQ(kTestGaiaId, account_info.gaia);
+  EXPECT_EQ(kTestEmail, account_info.email);
+  EXPECT_TRUE(account_state.has_refresh_token);
+  EXPECT_TRUE(account_state.is_primary_account);
+}
+#endif
+
 // Check that GetPrimaryAccountWhenAvailable() returns the expected account
 // info to all callers in the case where the primary account is made available
 // after multiple overlapping calls have been received.
diff --git a/services/metrics/public/cpp/ukm_recorder.h b/services/metrics/public/cpp/ukm_recorder.h
index 1aadef76..fcefa5b 100644
--- a/services/metrics/public/cpp/ukm_recorder.h
+++ b/services/metrics/public/cpp/ukm_recorder.h
@@ -21,6 +21,7 @@
 class ContextualSearchRankerLoggerImpl;
 class PluginInfoMessageFilter;
 class UkmPageLoadMetricsObserver;
+class LocalNetworkRequestsPageLoadMetricsObserver;
 
 namespace autofill {
 class AutofillMetrics;
@@ -90,6 +91,7 @@
   friend ContextualSearchRankerLoggerImpl;
   friend PluginInfoMessageFilter;
   friend UkmPageLoadMetricsObserver;
+  friend LocalNetworkRequestsPageLoadMetricsObserver;
   friend translate::TranslateRankerImpl;
   friend TestRecordingHelper;
   friend UkmInterface;
diff --git a/services/resource_coordinator/BUILD.gn b/services/resource_coordinator/BUILD.gn
index a947735..8c02a1c 100644
--- a/services/resource_coordinator/BUILD.gn
+++ b/services/resource_coordinator/BUILD.gn
@@ -76,10 +76,14 @@
     "coordination_unit/process_coordination_unit_impl_unittest.cc",
     "memory_instrumentation/coordinator_impl_unittest.cc",
     "memory_instrumentation/process_map_unittest.cc",
+    "public/cpp/memory_instrumentation/os_metrics_unittest.cc",
     "public/cpp/memory_instrumentation/process_metrics_memory_dump_provider_unittest.cc",
     "public/cpp/tracing/chrome_trace_event_agent_unittest.cc",
     "tracing/agent_registry_unittest.cc",
+    "tracing/coordinator_unittest.cc",
     "tracing/recorder_unittest.cc",
+    "tracing/test_util.cc",
+    "tracing/test_util.h",
   ]
 
   if (!is_android) {
diff --git a/services/resource_coordinator/public/cpp/BUILD.gn b/services/resource_coordinator/public/cpp/BUILD.gn
index e26cb413..b98ca1a 100644
--- a/services/resource_coordinator/public/cpp/BUILD.gn
+++ b/services/resource_coordinator/public/cpp/BUILD.gn
@@ -12,6 +12,11 @@
     "memory_instrumentation/coordinator.h",
     "memory_instrumentation/memory_instrumentation.cc",
     "memory_instrumentation/memory_instrumentation.h",
+    "memory_instrumentation/os_metrics.cc",
+    "memory_instrumentation/os_metrics.h",
+    "memory_instrumentation/os_metrics_linux.cc",
+    "memory_instrumentation/os_metrics_mac.cc",
+    "memory_instrumentation/os_metrics_win.cc",
     "memory_instrumentation/process_metrics_memory_dump_provider.cc",
     "memory_instrumentation/process_metrics_memory_dump_provider.h",
     "resource_coordinator_features.cc",
@@ -22,6 +27,12 @@
     "tracing/chrome_trace_event_agent.h",
   ]
 
+  if (is_android) {
+    set_sources_assignment_filter([])
+    sources += [ "memory_instrumentation/os_metrics_linux.cc" ]
+    set_sources_assignment_filter(sources_assignment_filter)
+  }
+
   defines = [ "SERVICES_RESOURCE_COORDINATOR_PUBLIC_CPP_IMPLEMENTATION" ]
 
   deps = [
diff --git a/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.cc b/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.cc
new file mode 100644
index 0000000..6e4c92a
--- /dev/null
+++ b/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.cc
@@ -0,0 +1,15 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.h"
+
+namespace memory_instrumentation {
+
+mojom::RawOSMemDumpPtr GetOSMemoryDump(base::ProcessId pid) {
+  mojom::RawOSMemDumpPtr dump = mojom::RawOSMemDump::New();
+  FillOSMemoryDump(pid, dump.get());
+  return dump;
+}
+
+}  // namespace memory_instrumentation
diff --git a/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.h b/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.h
new file mode 100644
index 0000000..09e258ec
--- /dev/null
+++ b/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.h
@@ -0,0 +1,20 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+#ifndef SERVICES_RESOURCE_COORDINATOR_PUBLIC_CPP_MEMORY_INSTRUMENTATION_OS_METRICS_H_
+#define SERVICES_RESOURCE_COORDINATOR_PUBLIC_CPP_MEMORY_INSTRUMENTATION_OS_METRICS_H_
+
+#include "base/process/process_handle.h"
+#include "services/resource_coordinator/public/cpp/resource_coordinator_export.h"
+#include "services/resource_coordinator/public/interfaces/memory_instrumentation/memory_instrumentation.mojom.h"
+
+namespace memory_instrumentation {
+
+mojom::RawOSMemDumpPtr SERVICES_RESOURCE_COORDINATOR_PUBLIC_CPP_EXPORT
+GetOSMemoryDump(base::ProcessId pid);
+
+void FillOSMemoryDump(base::ProcessId pid, mojom::RawOSMemDump* dump);
+
+}  // namespace memory_instrumentation
+
+#endif  // SERVICES_RESOURCE_COORDINATOR_PUBLIC_CPP_MEMORY_INSTRUMENTATION_OS_METRICS_H_
diff --git a/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_linux.cc b/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_linux.cc
new file mode 100644
index 0000000..e818bea
--- /dev/null
+++ b/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_linux.cc
@@ -0,0 +1,83 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <fcntl.h>
+#include <stdint.h>
+#include <memory>
+
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/format_macros.h"
+#include "base/process/process_metrics.h"
+#include "base/strings/string_number_conversions.h"
+#include "build/build_config.h"
+#include "services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.h"
+
+namespace memory_instrumentation {
+
+namespace {
+
+const uint32_t kMaxLineSize = 4096;
+
+base::ScopedFD OpenStatm(base::ProcessId pid) {
+  std::string name =
+      "/proc/" +
+      (pid == base::kNullProcessId ? "self" : base::IntToString(pid)) +
+      "/statm";
+  base::ScopedFD fd = base::ScopedFD(open(name.c_str(), O_RDONLY));
+  DCHECK(fd.is_valid());
+  return fd;
+}
+
+bool GetResidentAndSharedPagesFromStatmFile(int fd,
+                                            uint64_t* resident_pages,
+                                            uint64_t* shared_pages) {
+  lseek(fd, 0, SEEK_SET);
+  char line[kMaxLineSize];
+  int res = read(fd, line, kMaxLineSize - 1);
+  if (res <= 0)
+    return false;
+  line[res] = '\0';
+  int num_scanned =
+      sscanf(line, "%*s %" SCNu64 " %" SCNu64, resident_pages, shared_pages);
+  return num_scanned == 2;
+}
+
+std::unique_ptr<base::ProcessMetrics> CreateProcessMetrics(
+    base::ProcessId pid) {
+  if (pid == base::kNullProcessId) {
+    return base::ProcessMetrics::CreateCurrentProcessMetrics();
+  }
+  return base::ProcessMetrics::CreateProcessMetrics(pid);
+}
+
+}  // namespace
+
+void FillOSMemoryDump(base::ProcessId pid, mojom::RawOSMemDump* dump) {
+  base::ScopedFD autoclose = OpenStatm(pid);
+  int statm_fd = autoclose.get();
+
+  if (statm_fd == -1)
+    return;
+
+  uint64_t resident_pages;
+  uint64_t shared_pages;
+  bool success = GetResidentAndSharedPagesFromStatmFile(
+      statm_fd, &resident_pages, &shared_pages);
+
+  if (!success)
+    return;
+
+  auto process_metrics = CreateProcessMetrics(pid);
+
+  const static size_t page_size = base::GetPageSize();
+  uint64_t rss_anon_bytes = (resident_pages - shared_pages) * page_size;
+  uint64_t vm_swap_bytes = (resident_pages - shared_pages) * page_size;
+
+  dump->platform_private_footprint.rss_anon_bytes = rss_anon_bytes;
+  dump->platform_private_footprint.vm_swap_bytes = vm_swap_bytes;
+  dump->resident_set_kb = process_metrics->GetWorkingSetSize() / 1024;
+}
+
+}  // namespace memory_instrumentation
diff --git a/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_mac.cc b/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_mac.cc
new file mode 100644
index 0000000..a418e861
--- /dev/null
+++ b/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_mac.cc
@@ -0,0 +1,32 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.h"
+
+#include "base/process/process_metrics.h"
+
+namespace memory_instrumentation {
+
+void FillOSMemoryDump(base::ProcessId pid, mojom::RawOSMemDump* dump) {
+  // Creating process metrics for child processes in mac or windows requires
+  // additional information like ProcessHandle or port provider.
+  DCHECK_EQ(base::kNullProcessId, pid);
+  auto process_metrics = base::ProcessMetrics::CreateCurrentProcessMetrics();
+
+  size_t private_bytes;
+  size_t shared_bytes;
+  size_t resident_bytes;
+  size_t locked_bytes;
+  if (!process_metrics->GetMemoryBytes(&private_bytes, &shared_bytes,
+                                       &resident_bytes, &locked_bytes)) {
+    return;
+  }
+  base::ProcessMetrics::TaskVMInfo info = process_metrics->GetTaskVMInfo();
+  dump->platform_private_footprint.phys_footprint_bytes = info.phys_footprint;
+  dump->platform_private_footprint.internal_bytes = info.internal;
+  dump->platform_private_footprint.compressed_bytes = info.compressed;
+  dump->resident_set_kb = resident_bytes / 1024;
+}
+
+}  // namespace memory_instrumentation
diff --git a/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_unittest.cc b/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_unittest.cc
new file mode 100644
index 0000000..61cabc4
--- /dev/null
+++ b/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_unittest.cc
@@ -0,0 +1,24 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+#include "services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.h"
+
+#include "base/process/process_handle.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace memory_instrumentation {
+
+TEST(OSMetricsTest, GivesNonZeroResults) {
+  base::ProcessId pid = base::kNullProcessId;
+  mojom::RawOSMemDumpPtr dump = GetOSMemoryDump(pid);
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+  EXPECT_GT(dump->platform_private_footprint.rss_anon_bytes, 0u);
+#elif defined(OS_WIN)
+  EXPECT_GT(dump->platform_private_footprint.private_bytes, 0u);
+#elif defined(OS_MACOSX)
+  EXPECT_GT(dump->platform_private_footprint.internal_bytes, 0u);
+#endif
+}
+
+}  // namespace memory_instrumentation
diff --git a/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_win.cc b/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_win.cc
new file mode 100644
index 0000000..feef20d3f
--- /dev/null
+++ b/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_win.cc
@@ -0,0 +1,23 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.h"
+
+#include "base/process/process_metrics.h"
+
+namespace memory_instrumentation {
+
+void FillOSMemoryDump(base::ProcessId pid, mojom::RawOSMemDump* dump) {
+  // Creating process metrics for child processes in mac or windows requires
+  // additional information like ProcessHandle or port provider.
+  DCHECK_EQ(base::kNullProcessId, pid);
+  auto process_metrics = base::ProcessMetrics::CreateCurrentProcessMetrics();
+
+  size_t private_bytes = 0;
+  process_metrics->GetMemoryBytes(&private_bytes, nullptr);
+  dump->platform_private_footprint.private_bytes = private_bytes;
+  dump->resident_set_kb = process_metrics->GetWorkingSetSize() / 1024;
+}
+
+}  // namespace memory_instrumentation
diff --git a/services/resource_coordinator/public/cpp/memory_instrumentation/process_metrics_memory_dump_provider.cc b/services/resource_coordinator/public/cpp/memory_instrumentation/process_metrics_memory_dump_provider.cc
index d948b7b8..f6c73328 100644
--- a/services/resource_coordinator/public/cpp/memory_instrumentation/process_metrics_memory_dump_provider.cc
+++ b/services/resource_coordinator/public/cpp/memory_instrumentation/process_metrics_memory_dump_provider.cc
@@ -23,6 +23,7 @@
 #include "base/trace_event/process_memory_maps.h"
 #include "base/trace_event/process_memory_totals.h"
 #include "build/build_config.h"
+#include "services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.h"
 
 #if defined(OS_MACOSX)
 #include <libproc.h>
@@ -181,20 +182,6 @@
   return num_valid_regions;
 }
 
-bool GetResidentAndSharedPagesFromStatmFile(int fd,
-                                            uint64_t* resident_pages,
-                                            uint64_t* shared_pages) {
-  lseek(fd, 0, SEEK_SET);
-  char line[kMaxLineSize];
-  int res = read(fd, line, kMaxLineSize - 1);
-  if (res <= 0)
-    return false;
-  line[res] = '\0';
-  int num_scanned =
-      sscanf(line, "%*s %" SCNu64 " %" SCNu64, resident_pages, shared_pages);
-  return num_scanned == 2;
-}
-
 #endif  // defined(OS_LINUX) || defined(OS_ANDROID)
 
 std::unique_ptr<base::ProcessMetrics> CreateProcessMetrics(
@@ -250,6 +237,18 @@
     pmd->set_has_process_mmaps();
   return res;
 }
+
+bool GetResidentPagesFromStatmFile(int fd, uint64_t* resident_pages) {
+  lseek(fd, 0, SEEK_SET);
+  char line[kMaxLineSize];
+  int res = read(fd, line, kMaxLineSize - 1);
+  if (res <= 0)
+    return false;
+  line[res] = '\0';
+  int num_scanned = sscanf(line, "%*s %" SCNu64, resident_pages);
+  return num_scanned == 1;
+}
+
 #endif  // defined(OS_LINUX) || defined(OS_ANDROID)
 
 #if defined(OS_WIN)
@@ -612,6 +611,22 @@
 bool ProcessMetricsMemoryDumpProvider::DumpProcessTotals(
     const base::trace_event::MemoryDumpArgs& args,
     base::trace_event::ProcessMemoryDump* pmd) {
+// On Windows add extra region if necessary:
+#if defined(OS_WIN)
+  if (args.level_of_detail ==
+      base::trace_event::MemoryDumpLevelOfDetail::DETAILED) {
+    uint64_t pss_bytes = 0;
+    bool res = process_metrics_->GetProportionalSetSizeBytes(&pss_bytes);
+    if (res) {
+      base::trace_event::ProcessMemoryMaps::VMRegion region;
+      region.byte_stats_proportional_resident = pss_bytes;
+      pmd->process_mmaps()->AddVMRegion(region);
+      pmd->set_has_process_mmaps();
+    }
+  }
+#endif  // defined(OS_WIN)
+
+// On Mac set a few extra values on process_totals:
 #if defined(OS_MACOSX)
   size_t private_bytes;
   size_t shared_bytes;
@@ -621,66 +636,30 @@
                                         &resident_bytes, &locked_bytes)) {
     return false;
   }
-  uint64_t rss_bytes = resident_bytes;
   pmd->process_totals()->SetExtraFieldInBytes("private_bytes", private_bytes);
   pmd->process_totals()->SetExtraFieldInBytes("shared_bytes", shared_bytes);
   pmd->process_totals()->SetExtraFieldInBytes("locked_bytes", locked_bytes);
-
-  base::trace_event::ProcessMemoryTotals::PlatformPrivateFootprint footprint;
-  base::ProcessMetrics::TaskVMInfo info = process_metrics_->GetTaskVMInfo();
-  footprint.phys_footprint_bytes = info.phys_footprint;
-  footprint.internal_bytes = info.internal;
-  footprint.compressed_bytes = info.compressed;
-
-  pmd->process_totals()->SetPlatformPrivateFootprint(footprint);
-#else
-  uint64_t rss_bytes = process_metrics_->GetWorkingSetSize();
 #endif  // defined(OS_MACOSX)
-  if (rss_bytes_for_testing)
+
+  mojom::RawOSMemDumpPtr dump = GetOSMemoryDump(process_);
+
+  uint64_t rss_bytes = dump->resident_set_kb * 1024;
+  if (rss_bytes_for_testing) {
     rss_bytes = rss_bytes_for_testing;
-
-  // rss_bytes will be 0 if the process ended while dumping.
-  if (!rss_bytes)
-    return false;
-
-  uint64_t peak_rss_bytes = 0;
-
-#if defined(OS_LINUX) || defined(OS_ANDROID)
-  base::trace_event::ProcessMemoryTotals::PlatformPrivateFootprint footprint;
-
-  base::ScopedFD autoclose;
-  int statm_fd = fast_polling_statm_fd_.get();
-  if (statm_fd == -1) {
-    autoclose = OpenStatm();
-    statm_fd = autoclose.get();
   }
-  if (statm_fd == -1)
-    return false;
-  const static size_t page_size = base::GetPageSize();
-  uint64_t resident_pages;
-  uint64_t shared_pages;
-  bool success = GetResidentAndSharedPagesFromStatmFile(
-      statm_fd, &resident_pages, &shared_pages);
-  if (!success)
-    return false;
 
-  footprint.rss_anon_bytes = (resident_pages - shared_pages) * page_size;
-  footprint.vm_swap_bytes = process_metrics_->GetVmSwapBytes();
-  pmd->process_totals()->SetPlatformPrivateFootprint(footprint);
-#endif  // defined(OS_LINUX) || defined(OS_ANDROID)
-
-#if defined(OS_WIN)
-  {
-    size_t private_bytes;
-    base::trace_event::ProcessMemoryTotals::PlatformPrivateFootprint footprint;
-    process_metrics_->GetMemoryBytes(&private_bytes, nullptr);
-    footprint.private_bytes = private_bytes;
-    pmd->process_totals()->SetPlatformPrivateFootprint(footprint);
+  // resident set size will be 0 if the process ended while dumping.
+  if (rss_bytes == 0u) {
+    return false;
   }
-#endif
 
-#if !defined(OS_IOS)
-  peak_rss_bytes = process_metrics_->GetPeakWorkingSetSize();
+  pmd->process_totals()->set_resident_set_bytes(rss_bytes);
+  pmd->process_totals()->SetPlatformPrivateFootprint(
+      dump->platform_private_footprint);
+  pmd->set_has_process_totals();
+  pmd->process_totals()->set_peak_resident_set_bytes(GetPeakResidentSetBytes());
+
+// On Linux and Android reset rss peak if necessary
 #if defined(OS_LINUX) || defined(OS_ANDROID)
   if (is_rss_peak_resettable_) {
     std::string clear_refs_file =
@@ -698,30 +677,20 @@
     }
     close(clear_refs_fd);
   }
-#elif defined(OS_WIN)
-  if (args.level_of_detail ==
-      base::trace_event::MemoryDumpLevelOfDetail::DETAILED) {
-    uint64_t pss_bytes = 0;
-    bool res = process_metrics_->GetProportionalSetSizeBytes(&pss_bytes);
-    if (res) {
-      base::trace_event::ProcessMemoryMaps::VMRegion region;
-      region.byte_stats_proportional_resident = pss_bytes;
-      pmd->process_mmaps()->AddVMRegion(region);
-      pmd->set_has_process_mmaps();
-    }
-  }
-
 #endif
-#endif  // !defined(OS_IOS)
-
-  pmd->process_totals()->set_resident_set_bytes(rss_bytes);
-  pmd->set_has_process_totals();
-  pmd->process_totals()->set_peak_resident_set_bytes(peak_rss_bytes);
 
   // Returns true even if other metrics failed, since rss is reported.
   return true;
 }
 
+uint64_t ProcessMetricsMemoryDumpProvider::GetPeakResidentSetBytes() {
+#if defined(OS_IOS)
+  return 0u;
+#else
+  return process_metrics_->GetPeakWorkingSetSize();
+#endif
+}
+
 #if defined(OS_LINUX) || defined(OS_ANDROID)
 base::ScopedFD ProcessMetricsMemoryDumpProvider::OpenStatm() {
   std::string name =
@@ -750,9 +719,7 @@
   }
 
   uint64_t resident_pages = 0;
-  uint64_t ignored_shared_pages = 0;
-  if (!GetResidentAndSharedPagesFromStatmFile(statm_fd, &resident_pages,
-                                              &ignored_shared_pages))
+  if (!GetResidentPagesFromStatmFile(statm_fd, &resident_pages))
     return;
 
   static size_t page_size = base::GetPageSize();
diff --git a/services/resource_coordinator/public/cpp/memory_instrumentation/process_metrics_memory_dump_provider.h b/services/resource_coordinator/public/cpp/memory_instrumentation/process_metrics_memory_dump_provider.h
index 1489845..c0de92f 100644
--- a/services/resource_coordinator/public/cpp/memory_instrumentation/process_metrics_memory_dump_provider.h
+++ b/services/resource_coordinator/public/cpp/memory_instrumentation/process_metrics_memory_dump_provider.h
@@ -13,6 +13,7 @@
 #include "base/trace_event/memory_dump_provider.h"
 #include "build/build_config.h"
 #include "services/resource_coordinator/public/cpp/resource_coordinator_export.h"
+#include "services/resource_coordinator/public/interfaces/memory_instrumentation/memory_instrumentation.mojom.h"
 
 namespace base {
 class ProcessMetrics;
@@ -66,6 +67,7 @@
                          base::trace_event::ProcessMemoryDump* pmd);
   bool DumpProcessMemoryMaps(const base::trace_event::MemoryDumpArgs& args,
                              base::trace_event::ProcessMemoryDump* pmd);
+  uint64_t GetPeakResidentSetBytes();
 
   static uint64_t rss_bytes_for_testing;
   static FactoryFunction factory_for_testing;
diff --git a/services/resource_coordinator/public/cpp/memory_instrumentation/process_metrics_memory_dump_provider_unittest.cc b/services/resource_coordinator/public/cpp/memory_instrumentation/process_metrics_memory_dump_provider_unittest.cc
index a9c32497..b997757 100644
--- a/services/resource_coordinator/public/cpp/memory_instrumentation/process_metrics_memory_dump_provider_unittest.cc
+++ b/services/resource_coordinator/public/cpp/memory_instrumentation/process_metrics_memory_dump_provider_unittest.cc
@@ -17,6 +17,7 @@
 #include "base/trace_event/process_memory_maps.h"
 #include "base/trace_event/process_memory_totals.h"
 #include "base/trace_event/trace_event_argument.h"
+#include "services/resource_coordinator/public/interfaces/memory_instrumentation/memory_instrumentation.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 #if defined(OS_MACOSX)
@@ -411,4 +412,5 @@
 }
 
 #endif  // defined(OS_MACOSX)
+
 }  // namespace memory_instrumentation
diff --git a/services/resource_coordinator/public/cpp/tracing/chrome_trace_event_agent.cc b/services/resource_coordinator/public/cpp/tracing/chrome_trace_event_agent.cc
index 706cc4b..fa5ff37 100644
--- a/services/resource_coordinator/public/cpp/tracing/chrome_trace_event_agent.cc
+++ b/services/resource_coordinator/public/cpp/tracing/chrome_trace_event_agent.cc
@@ -30,9 +30,7 @@
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(!g_chrome_trace_event_agent);
   g_chrome_trace_event_agent = this;
-  // agent_registry can be null in tests. The constructor is private and cannot
-  // be directly used in non-test scenarios. GetOrCreateInstance makes sure that
-  // the constructor is called with a non-null agent_registry.
+  // agent_registry can be null in tests.
   if (!agent_registry)
     return;
 
@@ -55,10 +53,8 @@
 }
 
 void ChromeTraceEventAgent::StartTracing(const std::string& config,
-                                         mojom::RecorderPtr recorder,
                                          const StartTracingCallback& callback) {
   DCHECK(!recorder_);
-  recorder_ = std::move(recorder);
   if (!base::trace_event::TraceLog::GetInstance()->IsEnabled()) {
     base::trace_event::TraceLog::GetInstance()->SetEnabled(
         base::trace_event::TraceConfig(config),
@@ -67,10 +63,10 @@
   callback.Run();
 }
 
-void ChromeTraceEventAgent::StopAndFlush() {
+void ChromeTraceEventAgent::StopAndFlush(mojom::RecorderPtr recorder) {
+  DCHECK(!recorder_);
+  recorder_ = std::move(recorder);
   base::trace_event::TraceLog::GetInstance()->SetDisabled();
-  if (!recorder_)
-    return;
   for (const auto& generator : metadata_generator_functions_) {
     auto metadata = generator.Run();
     if (metadata)
diff --git a/services/resource_coordinator/public/cpp/tracing/chrome_trace_event_agent.h b/services/resource_coordinator/public/cpp/tracing/chrome_trace_event_agent.h
index 8e8fe475..f8213186 100644
--- a/services/resource_coordinator/public/cpp/tracing/chrome_trace_event_agent.h
+++ b/services/resource_coordinator/public/cpp/tracing/chrome_trace_event_agent.h
@@ -27,20 +27,20 @@
 
   static ChromeTraceEventAgent* GetInstance();
 
+  explicit ChromeTraceEventAgent(mojom::AgentRegistryPtr agent_registry);
+
   void AddMetadataGeneratorFunction(MetadataGeneratorFunction generator);
 
  private:
   friend std::default_delete<ChromeTraceEventAgent>;  // For Testing
   friend class ChromeTraceEventAgentTest;             // For Testing
 
-  explicit ChromeTraceEventAgent(mojom::AgentRegistryPtr agent_registry);
   ~ChromeTraceEventAgent() override;
 
   // mojom::Agent
   void StartTracing(const std::string& config,
-                    mojom::RecorderPtr recorder,
                     const StartTracingCallback& callback) override;
-  void StopAndFlush() override;
+  void StopAndFlush(mojom::RecorderPtr recorder) override;
   void RequestClockSyncMarker(
       const std::string& sync_id,
       const RequestClockSyncMarkerCallback& callback) override;
diff --git a/services/resource_coordinator/public/cpp/tracing/chrome_trace_event_agent_unittest.cc b/services/resource_coordinator/public/cpp/tracing/chrome_trace_event_agent_unittest.cc
index b7ca96ec..bb71454 100644
--- a/services/resource_coordinator/public/cpp/tracing/chrome_trace_event_agent_unittest.cc
+++ b/services/resource_coordinator/public/cpp/tracing/chrome_trace_event_agent_unittest.cc
@@ -89,17 +89,19 @@
     message_loop_.reset();
   }
 
-  void StartTracing(const std::string& categories, base::Closure quit_closure) {
+  void StartTracing(const std::string& categories) {
+    agent_->StartTracing(
+        base::trace_event::TraceConfig(categories, "").ToString(),
+        base::BindRepeating([] {}));
+  }
+
+  void StopAndFlush(base::Closure quit_closure) {
     mojom::RecorderPtr recorder_ptr;
     recorder_.reset(new MockRecorder(MakeRequest(&recorder_ptr)));
     recorder_->set_quit_closure(quit_closure);
-    agent_->StartTracing(
-        base::trace_event::TraceConfig(categories, "").ToString(),
-        std::move(recorder_ptr), base::BindRepeating([] {}));
+    agent_->StopAndFlush(std::move(recorder_ptr));
   }
 
-  void StopAndFlush() { agent_->StopAndFlush(); }
-
   void AddMetadataGeneratorFunction(
       ChromeTraceEventAgent::MetadataGeneratorFunction generator) {
     agent_->AddMetadataGeneratorFunction(generator);
@@ -130,19 +132,19 @@
 TEST_F(ChromeTraceEventAgentTest, StartTracing) {
   EXPECT_FALSE(base::trace_event::TraceLog::GetInstance()->IsEnabled());
   base::RunLoop run_loop;
-  StartTracing("*", run_loop.QuitClosure());
+  StartTracing("*");
   EXPECT_TRUE(base::trace_event::TraceLog::GetInstance()->IsEnabled());
-  StopAndFlush();
+  StopAndFlush(run_loop.QuitClosure());
   run_loop.Run();
 }
 
 TEST_F(ChromeTraceEventAgentTest, StopAndFlushEvents) {
   EXPECT_FALSE(base::trace_event::TraceLog::GetInstance()->IsEnabled());
   base::RunLoop run_loop;
-  StartTracing(kTestCategory, run_loop.QuitClosure());
+  StartTracing(kTestCategory);
   TRACE_EVENT_INSTANT0(kTestCategory, "event1", TRACE_EVENT_SCOPE_THREAD);
   TRACE_EVENT_INSTANT0(kTestCategory, "event2", TRACE_EVENT_SCOPE_THREAD);
-  StopAndFlush();
+  StopAndFlush(run_loop.QuitClosure());
   run_loop.Run();
 
   auto* mock_recorder = recorder();
@@ -167,8 +169,8 @@
     metadata_dict->SetString(kTestMetadataKey, "test metadata");
     return metadata_dict;
   }));
-  StartTracing(kTestCategory, run_loop.QuitClosure());
-  StopAndFlush();
+  StartTracing(kTestCategory);
+  StopAndFlush(run_loop.QuitClosure());
   run_loop.Run();
 
   auto* mock_recorder = recorder();
diff --git a/services/resource_coordinator/public/interfaces/BUILD.gn b/services/resource_coordinator/public/interfaces/BUILD.gn
index a11ab082..33587e1 100644
--- a/services/resource_coordinator/public/interfaces/BUILD.gn
+++ b/services/resource_coordinator/public/interfaces/BUILD.gn
@@ -16,6 +16,7 @@
     "service_callbacks.mojom",
     "service_constants.mojom",
     "tracing/tracing.mojom",
+    "tracing/tracing_constants.mojom",
   ]
 
   public_deps = [
diff --git a/services/resource_coordinator/public/interfaces/tracing/tracing.mojom b/services/resource_coordinator/public/interfaces/tracing/tracing.mojom
index a102cca..f56022b 100644
--- a/services/resource_coordinator/public/interfaces/tracing/tracing.mojom
+++ b/services/resource_coordinator/public/interfaces/tracing/tracing.mojom
@@ -31,12 +31,12 @@
 };
 
 // When the tracing service calls |StopAndFlush| on an agent, the agent begins
-// serializing data into the recorder that was given in the |StartTracing| call.
-// When finished, the agent should close the recorder connection to signal the
-// tracing service that no more data will be sent.
+// serializing data into the given recorder.  When finished, the agent should
+// close the recorder connection to signal the tracing service that no more data
+// will be sent.
 interface Agent {
-  StartTracing(string config, Recorder recorder) => ();
-  StopAndFlush();
+  StartTracing(string config) => ();
+  StopAndFlush(Recorder recorder);
   RequestClockSyncMarker(string sync_id) => (
       mojo.common.mojom.TimeTicks issue_ts,
       mojo.common.mojom.TimeTicks issue_end_ts);
@@ -57,10 +57,13 @@
 // from all registered agents. At any given time, there should be at most one
 // connected controller.
 interface Coordinator {
-  StartTracing(handle<data_pipe_producer> stream, string config);
-  StopAndFlush();
+  // The return value is false if tracing is already enabled with a different
+  // config. Otherwise, true is returned as soon as the service receives acks
+  // from all existing agents and agents that connect during |StartTracing|. 
+  StartTracing(string config) => (bool success);
+  StopAndFlush(handle<data_pipe_producer> stream);
   IsTracing() => (bool is_tracing);
   RequestBufferUsage() => (bool success, float percent_full,
                            uint32 approximate_count);
-  GetCategories() => (string categories);
+  GetCategories() => (bool success, string categories);
 };
diff --git a/services/resource_coordinator/public/interfaces/tracing/tracing_constants.mojom b/services/resource_coordinator/public/interfaces/tracing/tracing_constants.mojom
new file mode 100644
index 0000000..dc04c8c
--- /dev/null
+++ b/services/resource_coordinator/public/interfaces/tracing/tracing_constants.mojom
@@ -0,0 +1,7 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module tracing.mojom;
+
+const uint32 kStopTracingRetryTimeMilliseconds = 100;
diff --git a/services/resource_coordinator/tracing/BUILD.gn b/services/resource_coordinator/tracing/BUILD.gn
index e578dfcd..a4182cc5 100644
--- a/services/resource_coordinator/tracing/BUILD.gn
+++ b/services/resource_coordinator/tracing/BUILD.gn
@@ -6,6 +6,8 @@
   sources = [
     "agent_registry.cc",
     "agent_registry.h",
+    "coordinator.cc",
+    "coordinator.h",
     "recorder.cc",
     "recorder.h",
   ]
diff --git a/services/resource_coordinator/tracing/agent_registry.cc b/services/resource_coordinator/tracing/agent_registry.cc
index d87a055..b7a2712 100644
--- a/services/resource_coordinator/tracing/agent_registry.cc
+++ b/services/resource_coordinator/tracing/agent_registry.cc
@@ -37,25 +37,30 @@
 
 AgentRegistry::AgentEntry::~AgentEntry() = default;
 
-void AgentRegistry::AgentEntry::SetDisconnectClosure(
+void AgentRegistry::AgentEntry::AddDisconnectClosure(
+    const void* closure_name,
     base::OnceClosure closure) {
-  DCHECK(closure_.is_null());
-  closure_ = std::move(closure);
+  DCHECK_EQ(0u, closures_.count(closure_name));
+  closures_[closure_name] = std::move(closure);
 }
 
-bool AgentRegistry::AgentEntry::RemoveDisconnectClosure() {
-  bool closure_was_set = !closure_.is_null();
-  closure_.Reset();
-  return closure_was_set;
+bool AgentRegistry::AgentEntry::RemoveDisconnectClosure(
+    const void* closure_name) {
+  return closures_.erase(closure_name) > 0;
+}
+
+bool AgentRegistry::AgentEntry::HasDisconnectClosure(const void* closure_name) {
+  return closures_.count(closure_name) > 0;
 }
 
 void AgentRegistry::AgentEntry::OnConnectionError() {
-  // Run the disconnect closure if it is set. We should mark |closure_| as
-  // movable so that the version of |Run| that takes an rvalue reference is
+  // Run disconnect closures if there is any. We should mark |key_value.second|
+  // as movable so that the version of |Run| that takes an rvalue reference is
   // selected not the version that takes a const reference. The former is for
   // once callbacks and the latter is for repeating callbacks.
-  if (!closure_.is_null())
-    std::move(closure_).Run();
+  for (auto& key_value : closures_) {
+    std::move(key_value.second).Run();
+  }
   agent_registry_->UnregisterAgent(id_);
 }
 
@@ -95,6 +100,15 @@
   agent_initialization_callback_.Reset();
 }
 
+bool AgentRegistry::HasDisconnectClosure(const void* closure_name) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  for (const auto& key_value : agents_) {
+    if (key_value.second->HasDisconnectClosure(closure_name))
+      return true;
+  }
+  return false;
+}
+
 void AgentRegistry::RegisterAgent(mojom::AgentPtr agent,
                                   const std::string& label,
                                   mojom::TraceDataType type,
diff --git a/services/resource_coordinator/tracing/agent_registry.h b/services/resource_coordinator/tracing/agent_registry.h
index 6841978c..970bd8d 100644
--- a/services/resource_coordinator/tracing/agent_registry.h
+++ b/services/resource_coordinator/tracing/agent_registry.h
@@ -30,11 +30,13 @@
                bool supports_explicit_clock_sync);
     ~AgentEntry();
 
-    // Currently, at most one callback when the tracing agent is disconnected is
-    // enough. We can generalize this later if several parts of the service need
-    // to get notified when an agent disconnects.
-    void SetDisconnectClosure(base::OnceClosure closure);
-    bool RemoveDisconnectClosure();
+    void AddDisconnectClosure(const void* closure_name,
+                              base::OnceClosure closure);
+    bool RemoveDisconnectClosure(const void* closure_name);
+    bool HasDisconnectClosure(const void* closure_name);
+    size_t num_disconnect_closures_for_testing() const {
+      return closures_.size();
+    }
 
     mojom::Agent* agent() const { return agent_.get(); }
     const std::string& label() const { return label_; }
@@ -52,7 +54,7 @@
     const std::string label_;
     const mojom::TraceDataType type_;
     const bool supports_explicit_clock_sync_;
-    base::OnceClosure closure_;
+    std::map<const void*, base::OnceClosure> closures_;
 
     DISALLOW_COPY_AND_ASSIGN(AgentEntry);
   };
@@ -71,6 +73,7 @@
   void SetAgentInitializationCallback(
       const AgentInitializationCallback& callback);
   void RemoveAgentInitializationCallback();
+  bool HasDisconnectClosure(const void* closure_name);
 
   template <typename FunctionType>
   void ForAllAgents(FunctionType function) {
@@ -83,6 +86,7 @@
  private:
   friend std::default_delete<AgentRegistry>;
   friend class AgentRegistryTest;  // For testing.
+  friend class CoordinatorTest;    // For testing.
 
   ~AgentRegistry() override;
 
diff --git a/services/resource_coordinator/tracing/agent_registry_unittest.cc b/services/resource_coordinator/tracing/agent_registry_unittest.cc
index c356e680..3493ee2 100644
--- a/services/resource_coordinator/tracing/agent_registry_unittest.cc
+++ b/services/resource_coordinator/tracing/agent_registry_unittest.cc
@@ -10,38 +10,12 @@
 
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
-#include "mojo/public/cpp/bindings/binding.h"
 #include "services/resource_coordinator/public/interfaces/tracing/tracing.mojom.h"
+#include "services/resource_coordinator/tracing/test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace tracing {
 
-class MockAgent : public mojom::Agent {
- public:
-  MockAgent() : binding_(this) {}
-
-  mojom::AgentPtr CreateAgentPtr() {
-    mojom::AgentPtr agent;
-    binding_.Bind(mojo::MakeRequest(&agent));
-    return agent;
-  }
-
- private:
-  // mojom::Agent
-  void StartTracing(const std::string& config,
-                    mojom::RecorderPtr recorder,
-                    const StartTracingCallback& callback) override {}
-  void StopAndFlush() override {}
-  void RequestClockSyncMarker(
-      const std::string& sync_id,
-      const RequestClockSyncMarkerCallback& callback) override {}
-  void RequestBufferStatus(
-      const RequestBufferStatusCallback& callback) override {}
-  void GetCategories(const GetCategoriesCallback& callback) override {}
-
-  mojo::Binding<mojom::Agent> binding_;
-};
-
 class AgentRegistryTest : public testing::Test {
  public:
   void SetUp() override {
diff --git a/services/resource_coordinator/tracing/coordinator.cc b/services/resource_coordinator/tracing/coordinator.cc
new file mode 100644
index 0000000..625cd80
--- /dev/null
+++ b/services/resource_coordinator/tracing/coordinator.cc
@@ -0,0 +1,352 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/resource_coordinator/tracing/coordinator.h"
+
+#include <algorithm>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback_forward.h"
+#include "base/callback_helpers.h"
+#include "base/json/json_writer.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/task_scheduler/task_traits.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/trace_event/trace_config.h"
+#include "mojo/common/data_pipe_utils.h"
+#include "services/resource_coordinator/public/interfaces/tracing/tracing.mojom.h"
+#include "services/resource_coordinator/public/interfaces/tracing/tracing_constants.mojom.h"
+#include "services/resource_coordinator/tracing/agent_registry.h"
+#include "services/resource_coordinator/tracing/recorder.h"
+#include "services/service_manager/public/cpp/bind_source_info.h"
+
+namespace {
+
+const char kMetadataTraceLabel[] = "metadata";
+
+const char kStartTracingClosureName[] = "StartTracingClosure";
+const char kRequestBufferUsageClosureName[] = "RequestBufferUsageClosure";
+const char kGetCategoriesClosureName[] = "GetCategoriesClosure";
+
+tracing::Coordinator* g_coordinator = nullptr;
+
+}  // namespace
+
+namespace tracing {
+
+// static
+Coordinator* Coordinator::GetInstance() {
+  DCHECK(g_coordinator);
+  return g_coordinator;
+}
+
+Coordinator::Coordinator()
+    : binding_(this),
+      task_runner_(base::ThreadTaskRunnerHandle::Get()),
+      agent_registry_(AgentRegistry::GetInstance()) {
+  DCHECK(!g_coordinator);
+  DCHECK(agent_registry_);
+  g_coordinator = this;
+  constexpr base::TaskTraits traits = {base::MayBlock(),
+                                       base::WithBaseSyncPrimitives(),
+                                       base::TaskPriority::BACKGROUND};
+  background_task_runner_ = base::CreateSequencedTaskRunnerWithTraits(traits);
+}
+
+Coordinator::~Coordinator() {
+  g_coordinator = nullptr;
+}
+
+void Coordinator::BindCoordinatorRequest(
+    const service_manager::BindSourceInfo& source_info,
+    mojom::CoordinatorRequest request) {
+  binding_.Bind(std::move(request));
+}
+
+void Coordinator::StartTracing(const std::string& config,
+                               const StartTracingCallback& callback) {
+  if (is_tracing_) {
+    // Cannot change the config while tracing is enabled.
+    callback.Run(config == config_);
+    return;
+  }
+
+  is_tracing_ = true;
+  config_ = config;
+  agent_registry_->SetAgentInitializationCallback(base::BindRepeating(
+      &Coordinator::SendStartTracingToAgent, base::Unretained(this)));
+  if (!agent_registry_->HasDisconnectClosure(&kStartTracingClosureName)) {
+    callback.Run(true);
+    return;
+  }
+  start_tracing_callback_ = callback;
+}
+
+void Coordinator::SendStartTracingToAgent(
+    AgentRegistry::AgentEntry* agent_entry) {
+  agent_entry->AddDisconnectClosure(
+      &kStartTracingClosureName,
+      base::BindOnce(&Coordinator::OnTracingStarted, base::Unretained(this),
+                     base::Unretained(agent_entry)));
+  agent_entry->agent()->StartTracing(
+      config_, base::BindRepeating(&Coordinator::OnTracingStarted,
+                                   base::Unretained(this),
+                                   base::Unretained(agent_entry)));
+}
+
+void Coordinator::OnTracingStarted(AgentRegistry::AgentEntry* agent_entry) {
+  bool removed =
+      agent_entry->RemoveDisconnectClosure(&kStartTracingClosureName);
+  DCHECK(removed);
+
+  if (!agent_registry_->HasDisconnectClosure(&kStartTracingClosureName) &&
+      !start_tracing_callback_.is_null()) {
+    base::ResetAndReturn(&start_tracing_callback_).Run(true);
+  }
+}
+
+void Coordinator::StopAndFlush(mojo::ScopedDataPipeProducerHandle stream) {
+  DCHECK(is_tracing_);
+  DCHECK(!stream_.is_valid());
+  DCHECK(stream.is_valid());
+
+  // Do not send |StartTracing| to agents that connect from now on.
+  agent_registry_->RemoveAgentInitializationCallback();
+  stream_ = std::move(stream);
+  StopAndFlushInternal();
+}
+
+void Coordinator::StopAndFlushInternal() {
+  if (agent_registry_->HasDisconnectClosure(&kStartTracingClosureName)) {
+    // We received a |StopAndFlush| command before receiving |StartTracing| acks
+    // from all agents. Let's retry after a delay.
+    task_runner_->PostDelayedTask(
+        FROM_HERE,
+        base::BindRepeating(&Coordinator::StopAndFlushInternal,
+                            base::Unretained(this)),
+        base::TimeDelta::FromMilliseconds(
+            mojom::kStopTracingRetryTimeMilliseconds));
+    return;
+  }
+
+  stream_header_written_ = false;
+  streaming_label_.clear();
+
+  agent_registry_->ForAllAgents([this](AgentRegistry::AgentEntry* agent_entry) {
+    bool data_is_array = agent_entry->type() == mojom::TraceDataType::ARRAY;
+    mojom::RecorderPtr ptr;
+    recorders_[agent_entry->label()].insert(base::MakeUnique<Recorder>(
+        MakeRequest(&ptr), data_is_array,
+        base::BindRepeating(&Coordinator::OnRecorderDataChange,
+                            base::Unretained(this), agent_entry->label()),
+        background_task_runner_));
+    DCHECK(data_is_array || recorders_[agent_entry->label()].size() == 1);
+    agent_entry->agent()->StopAndFlush(std::move(ptr));
+  });
+
+  if (recorders_.empty())
+    OnFlushDone();
+}
+
+void Coordinator::OnRecorderDataChange(const std::string& label) {
+  DCHECK(background_task_runner_->RunsTasksOnCurrentThread());
+
+  // Bail out if we are in the middle of writing events for another label to the
+  // stream, since we do not want to interleave chunks for different fields. For
+  // example, we do not want to mix |traceEvent| chunks with |battor| chunks.
+  //
+  // If we receive a |battor| chunk from an agent while writing |traceEvent|
+  // chunks to the stream, we wait until all agents that send |traceEvent|
+  // chunks are done, and then, we start writing |battor| chunks.
+  if (!streaming_label_.empty() && streaming_label_ != label)
+    return;
+
+  while (streaming_label_.empty() || !StreamEventsForCurrentLabel()) {
+    // We are not waiting for data from any particular label now. So, we look at
+    // the recorders that have some data available and select the next label to
+    // stream.
+    streaming_label_.clear();
+    bool all_finished = true;
+    for (const auto& key_value : recorders_) {
+      for (const auto& recorder : key_value.second) {
+        all_finished &= !recorder->is_recording();
+        if (!recorder->data().empty()) {
+          streaming_label_ = key_value.first;
+          json_field_name_written_ = false;
+          break;
+        }
+      }
+      if (!streaming_label_.empty())
+        break;
+    }
+
+    if (streaming_label_.empty()) {
+      // No recorder has any data for us, right now.
+      if (all_finished) {
+        StreamMetadata();
+        if (stream_header_written_)
+          mojo::common::BlockingCopyFromString("}", stream_);
+        // Recorder connections should be closed on their binding thread.
+        task_runner_->PostTask(
+            FROM_HERE,
+            base::BindOnce(&Coordinator::OnFlushDone, base::Unretained(this)));
+      }
+      return;
+    }
+  }
+}
+
+bool Coordinator::StreamEventsForCurrentLabel() {
+  DCHECK(background_task_runner_->RunsTasksOnCurrentThread());
+  bool waiting_for_agents = false;
+  bool data_is_array = (*recorders_[streaming_label_].begin())->data_is_array();
+  for (const auto& recorder : recorders_[streaming_label_]) {
+    waiting_for_agents |= recorder->is_recording();
+    if (recorder->data().empty())
+      continue;
+    if (json_field_name_written_) {
+      if (data_is_array)
+        mojo::common::BlockingCopyFromString(",", stream_);
+    } else {
+      std::string prefix = stream_header_written_ ? ",\"" : "{\"";
+      prefix += streaming_label_ + "\":" + (data_is_array ? "[" : "\"");
+      mojo::common::BlockingCopyFromString(prefix, stream_);
+      json_field_name_written_ = true;
+      stream_header_written_ = true;
+    }
+    mojo::common::BlockingCopyFromString(recorder->data(), stream_);
+    recorder->clear_data();
+  }
+  if (!waiting_for_agents) {
+    if (json_field_name_written_)
+      mojo::common::BlockingCopyFromString(data_is_array ? "]" : "\"", stream_);
+  }
+  return waiting_for_agents;
+}
+
+void Coordinator::StreamMetadata() {
+  DCHECK(background_task_runner_->RunsTasksOnCurrentThread());
+
+  base::DictionaryValue metadata;
+  for (const auto& key_value : recorders_) {
+    for (const auto& recorder : key_value.second) {
+      metadata.MergeDictionary(&(recorder->metadata()));
+    }
+  }
+
+  std::string metadataJSON;
+  if (!metadata.empty() && base::JSONWriter::Write(metadata, &metadataJSON)) {
+    std::string prefix = stream_header_written_ ? ",\"" : "{\"";
+    mojo::common::BlockingCopyFromString(
+        prefix + std::string(kMetadataTraceLabel) + "\":" + metadataJSON,
+        stream_);
+    stream_header_written_ = true;
+  }
+}
+
+void Coordinator::OnFlushDone() {
+  recorders_.clear();
+  stream_.reset();
+  is_tracing_ = false;
+}
+
+void Coordinator::IsTracing(const IsTracingCallback& callback) {
+  callback.Run(is_tracing_);
+}
+
+void Coordinator::RequestBufferUsage(
+    const RequestBufferUsageCallback& callback) {
+  if (!request_buffer_usage_callback_.is_null()) {
+    callback.Run(false, 0, 0);
+    return;
+  }
+
+  maximum_trace_buffer_usage_ = 0;
+  approximate_event_count_ = 0;
+  request_buffer_usage_callback_ = callback;
+  agent_registry_->ForAllAgents([this](AgentRegistry::AgentEntry* agent_entry) {
+    agent_entry->AddDisconnectClosure(
+        &kRequestBufferUsageClosureName,
+        base::BindOnce(&Coordinator::OnRequestBufferStatusResponse,
+                       base::Unretained(this), base::Unretained(agent_entry),
+                       0 /* capacity */, 0 /* count */));
+    agent_entry->agent()->RequestBufferStatus(base::BindRepeating(
+        &Coordinator::OnRequestBufferStatusResponse, base::Unretained(this),
+        base::Unretained(agent_entry)));
+  });
+}
+
+void Coordinator::OnRequestBufferStatusResponse(
+    AgentRegistry::AgentEntry* agent_entry,
+    uint32_t capacity,
+    uint32_t count) {
+  bool removed =
+      agent_entry->RemoveDisconnectClosure(&kRequestBufferUsageClosureName);
+  DCHECK(removed);
+
+  if (capacity > 0) {
+    float percent_full =
+        static_cast<float>(static_cast<double>(count) / capacity);
+    maximum_trace_buffer_usage_ =
+        std::max(maximum_trace_buffer_usage_, percent_full);
+    approximate_event_count_ += count;
+  }
+
+  if (!agent_registry_->HasDisconnectClosure(&kRequestBufferUsageClosureName)) {
+    base::ResetAndReturn(&request_buffer_usage_callback_)
+        .Run(true, maximum_trace_buffer_usage_, approximate_event_count_);
+  }
+}
+
+void Coordinator::GetCategories(const GetCategoriesCallback& callback) {
+  if (is_tracing_) {
+    callback.Run(false, "");
+  }
+
+  DCHECK(get_categories_callback_.is_null());
+  is_tracing_ = true;
+  category_set_.clear();
+  get_categories_callback_ = callback;
+  agent_registry_->ForAllAgents([this](AgentRegistry::AgentEntry* agent_entry) {
+    agent_entry->AddDisconnectClosure(
+        &kGetCategoriesClosureName,
+        base::BindOnce(&Coordinator::OnGetCategoriesResponse,
+                       base::Unretained(this), base::Unretained(agent_entry),
+                       ""));
+    agent_entry->agent()->GetCategories(base::BindRepeating(
+        &Coordinator::OnGetCategoriesResponse, base::Unretained(this),
+        base::Unretained(agent_entry)));
+  });
+}
+
+void Coordinator::OnGetCategoriesResponse(
+    AgentRegistry::AgentEntry* agent_entry,
+    const std::string& categories) {
+  bool removed =
+      agent_entry->RemoveDisconnectClosure(&kGetCategoriesClosureName);
+  DCHECK(removed);
+
+  std::vector<std::string> split = base::SplitString(
+      categories, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+  for (const auto& category : split) {
+    category_set_.insert(category);
+  }
+
+  if (!agent_registry_->HasDisconnectClosure(&kGetCategoriesClosureName)) {
+    std::vector<std::string> category_vector(category_set_.begin(),
+                                             category_set_.end());
+    base::ResetAndReturn(&get_categories_callback_)
+        .Run(true, base::JoinString(category_vector, ","));
+    is_tracing_ = false;
+  }
+}
+
+}  // namespace tracing
diff --git a/services/resource_coordinator/tracing/coordinator.h b/services/resource_coordinator/tracing/coordinator.h
new file mode 100644
index 0000000..758e79d
--- /dev/null
+++ b/services/resource_coordinator/tracing/coordinator.h
@@ -0,0 +1,103 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SERVICES_RESOURCE_COORDINATOR_TRACING_COORDINATOR_H_
+#define SERVICES_RESOURCE_COORDINATOR_TRACING_COORDINATOR_H_
+
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+
+#include "base/memory/ref_counted.h"
+#include "base/sequenced_task_runner.h"
+#include "base/single_thread_task_runner.h"
+#include "mojo/public/cpp/system/data_pipe.h"
+#include "services/resource_coordinator/public/interfaces/tracing/tracing.mojom.h"
+#include "services/resource_coordinator/tracing/agent_registry.h"
+#include "services/resource_coordinator/tracing/recorder.h"
+#include "services/service_manager/public/cpp/bind_source_info.h"
+
+namespace tracing {
+
+// Note that this implementation of mojom::Coordinator assumes that agents
+// either respond to messages that expect a response or disconnect. Mojo
+// verifies this to some extend by DCHECKing if the callback is deleted by the
+// agent before being run. However, the agent should not store the callback and
+// never run it.
+//
+// If we see that the above-mentioned assumption does not hold in some cases, we
+// should guard against it using timeouts.
+class Coordinator : public mojom::Coordinator {
+ public:
+  static Coordinator* GetInstance();
+
+  Coordinator();
+
+  void BindCoordinatorRequest(
+      const service_manager::BindSourceInfo& source_info,
+      mojom::CoordinatorRequest request);
+
+ private:
+  friend std::default_delete<Coordinator>;
+  friend class CoordinatorTest;  // For testing.
+
+  ~Coordinator() override;
+
+  // mojom::Coordinator
+  void StartTracing(const std::string& config,
+                    const StartTracingCallback& callback) override;
+  void StopAndFlush(mojo::ScopedDataPipeProducerHandle stream) override;
+  void IsTracing(const IsTracingCallback& callback) override;
+  void RequestBufferUsage(const RequestBufferUsageCallback& callback) override;
+  void GetCategories(const GetCategoriesCallback& callback) override;
+
+  // Internal methods for collecting events from agents.
+  void SendStartTracingToAgent(AgentRegistry::AgentEntry* agent_entry);
+  void OnTracingStarted(AgentRegistry::AgentEntry* agent_entry);
+  void StopAndFlushInternal();
+  void OnRecorderDataChange(const std::string& label);
+  bool StreamEventsForCurrentLabel();
+  void StreamMetadata();
+  void OnFlushDone();
+
+  void OnRequestBufferStatusResponse(AgentRegistry::AgentEntry* agent_entry,
+                                     uint32_t capacity,
+                                     uint32_t count);
+
+  void OnGetCategoriesResponse(AgentRegistry::AgentEntry* agent_entry,
+                               const std::string& categories);
+
+  mojo::Binding<mojom::Coordinator> binding_;
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+  scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
+  AgentRegistry* agent_registry_;
+  std::string config_;
+  std::map<std::string, std::set<std::unique_ptr<Recorder>>> recorders_;
+  bool is_tracing_ = false;
+
+  // The stream to which trace events from different agents should be
+  // serialized, eventually. This is set when tracing is stopped.
+  mojo::ScopedDataPipeProducerHandle stream_;
+  // If |streaming_label_| is not empty, it shows the label for which we are
+  // writing chunks to the output stream.
+  std::string streaming_label_;
+  bool stream_header_written_ = false;
+  bool json_field_name_written_ = false;
+  StartTracingCallback start_tracing_callback_;
+
+  // For computing trace buffer usage.
+  float maximum_trace_buffer_usage_ = 0;
+  uint32_t approximate_event_count_ = 0;
+  RequestBufferUsageCallback request_buffer_usage_callback_;
+
+  // For getting categories.
+  std::set<std::string> category_set_;
+  GetCategoriesCallback get_categories_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(Coordinator);
+};
+
+}  // namespace tracing
+#endif  // SERVICES_RESOURCE_COORDINATOR_TRACING_COORDINATOR_H_
diff --git a/services/resource_coordinator/tracing/coordinator_unittest.cc b/services/resource_coordinator/tracing/coordinator_unittest.cc
new file mode 100644
index 0000000..cf53430
--- /dev/null
+++ b/services/resource_coordinator/tracing/coordinator_unittest.cc
@@ -0,0 +1,374 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/resource_coordinator/tracing/coordinator.h"
+
+#include <algorithm>
+#include <memory>
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/strings/string_split.h"
+#include "base/test/scoped_task_environment.h"
+#include "mojo/common/data_pipe_drainer.h"
+#include "mojo/public/cpp/system/data_pipe.h"
+#include "services/resource_coordinator/public/interfaces/tracing/tracing.mojom.h"
+#include "services/resource_coordinator/tracing/test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace tracing {
+
+class CoordinatorTest : public testing::Test,
+                        public mojo::common::DataPipeDrainer::Client {
+ public:
+  // testing::Test
+  void SetUp() override {
+    agent_registry_.reset(new AgentRegistry());
+    coordinator_.reset(new Coordinator());
+    output_ = "";
+  }
+
+  // testing::Test
+  void TearDown() override {
+    agents_.clear();
+    coordinator_.reset();
+    agent_registry_.reset();
+  }
+
+  // mojo::common::DataPipeDrainer::Client
+  void OnDataAvailable(const void* data, size_t num_bytes) override {
+    output_.append(static_cast<const char*>(data), num_bytes);
+  }
+
+  // mojo::common::DataPipeDrainer::Client
+  void OnDataComplete() override { base::ResetAndReturn(&quit_closure_).Run(); }
+
+  MockAgent* AddArrayAgent() {
+    auto agent = base::MakeUnique<MockAgent>();
+    agent_registry_->RegisterAgent(agent->CreateAgentPtr(), "traceEvents",
+                                   mojom::TraceDataType::ARRAY, false);
+    agents_.push_back(std::move(agent));
+    return agents_.back().get();
+  }
+
+  MockAgent* AddStringAgent() {
+    auto agent = base::MakeUnique<MockAgent>();
+    agent_registry_->RegisterAgent(agent->CreateAgentPtr(), "battor",
+                                   mojom::TraceDataType::STRING, false);
+    agents_.push_back(std::move(agent));
+    return agents_.back().get();
+  }
+
+  void StartTracing(std::string config,
+                    bool expected_response,
+                    bool stop_and_flush) {
+    base::RepeatingClosure closure;
+    if (stop_and_flush) {
+      closure = base::BindRepeating(&CoordinatorTest::StopAndFlush,
+                                    base::Unretained(this));
+    }
+
+    coordinator_->StartTracing(
+        config,
+        base::BindRepeating(
+            [](bool expected, base::RepeatingClosure closure, bool actual) {
+              EXPECT_EQ(expected, actual);
+              if (!closure.is_null())
+                closure.Run();
+            },
+            expected_response, closure));
+  }
+
+  void StartTracing(std::string config, bool expected_response) {
+    StartTracing(config, expected_response, false);
+  }
+
+  void StopAndFlush() {
+    mojo::DataPipe data_pipe;
+    coordinator_->StopAndFlush(std::move(data_pipe.producer_handle));
+    drainer_.reset(new mojo::common::DataPipeDrainer(
+        this, std::move(data_pipe.consumer_handle)));
+  }
+
+  void IsTracing(bool expected_response) {
+    coordinator_->IsTracing(base::BindRepeating(
+        [](bool expected, bool actual) { EXPECT_EQ(expected, actual); },
+        expected_response));
+  }
+
+  void RequestBufferUsage(float expected_usage, uint32_t expected_count) {
+    coordinator_->RequestBufferUsage(base::BindRepeating(
+        [](float expected_usage, uint32_t expected_count, bool success,
+           float usage, uint32_t count) {
+          EXPECT_TRUE(success);
+          EXPECT_EQ(expected_usage, usage);
+          EXPECT_EQ(expected_count, count);
+        },
+        expected_usage, expected_count));
+  }
+
+  void CheckDisconnectClosures(size_t num_agents) {
+    // Verify that all disconnect closures are cleared up. This means that, for
+    // each agent, either the tracing service is notified that the agent is
+    // disconnected or the agent has answered to all requests.
+    size_t count = 0;
+    agent_registry_->ForAllAgents([&count](AgentRegistry::AgentEntry* entry) {
+      count++;
+      EXPECT_EQ(0u, entry->num_disconnect_closures_for_testing());
+    });
+    EXPECT_EQ(num_agents, count);
+  }
+
+  void GetCategories(bool expected_success,
+                     std::set<std::string> expected_categories) {
+    coordinator_->GetCategories(base::BindRepeating(
+        [](bool expected_success, std::set<std::string> expected_categories,
+           bool success, const std::string& categories) {
+          EXPECT_EQ(expected_success, success);
+          if (!success)
+            return;
+          std::vector<std::string> category_vector = base::SplitString(
+              categories, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+          EXPECT_EQ(expected_categories.size(), category_vector.size());
+          for (const auto& expected_category : expected_categories) {
+            EXPECT_EQ(1, std::count(category_vector.begin(),
+                                    category_vector.end(), expected_category));
+          }
+        },
+        expected_success, expected_categories));
+  }
+
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+  std::unique_ptr<AgentRegistry> agent_registry_;
+  std::unique_ptr<Coordinator> coordinator_;
+  std::vector<std::unique_ptr<MockAgent>> agents_;
+  std::unique_ptr<mojo::common::DataPipeDrainer> drainer_;
+  base::RepeatingClosure quit_closure_;
+  std::string output_;
+};
+
+TEST_F(CoordinatorTest, StartTracingSimple) {
+  base::RunLoop run_loop;
+  auto* agent = AddArrayAgent();
+  StartTracing("*", true);
+  run_loop.RunUntilIdle();
+
+  // The agent should have received exactly one call from the coordinator.
+  EXPECT_EQ(1u, agent->call_stat().size());
+  EXPECT_EQ("StartTracing", agent->call_stat()[0]);
+}
+
+TEST_F(CoordinatorTest, StartTracingTwoAgents) {
+  base::RunLoop run_loop;
+  auto* agent1 = AddArrayAgent();
+  StartTracing("*", true);
+  auto* agent2 = AddStringAgent();
+  run_loop.RunUntilIdle();
+
+  // Each agent should have received exactly one call from the coordinatr.
+  EXPECT_EQ(1u, agent1->call_stat().size());
+  EXPECT_EQ("StartTracing", agent1->call_stat()[0]);
+  EXPECT_EQ(1u, agent2->call_stat().size());
+  EXPECT_EQ("StartTracing", agent2->call_stat()[0]);
+}
+
+TEST_F(CoordinatorTest, StartTracingWithDifferentConfigs) {
+  base::RunLoop run_loop;
+  auto* agent = AddArrayAgent();
+  StartTracing("config 1", true);
+  // The 2nd |StartTracing| should return false.
+  StartTracing("config 2", false);
+  run_loop.RunUntilIdle();
+
+  // The agent should have received exactly one call from the coordinator
+  // because the 2nd |StartTracing| was aborted.
+  EXPECT_EQ(1u, agent->call_stat().size());
+  EXPECT_EQ("StartTracing", agent->call_stat()[0]);
+}
+
+TEST_F(CoordinatorTest, StartTracingWithSameConfigs) {
+  base::RunLoop run_loop;
+  auto* agent = AddArrayAgent();
+  StartTracing("config", true);
+  // The 2nd |StartTracing| should return true when we are not trying to change
+  // the config.
+  StartTracing("config", true);
+  run_loop.RunUntilIdle();
+
+  // The agent should have received exactly one call from the coordinator
+  // because the 2nd |StartTracing| was a no-op.
+  EXPECT_EQ(1u, agent->call_stat().size());
+  EXPECT_EQ("StartTracing", agent->call_stat()[0]);
+}
+
+TEST_F(CoordinatorTest, StopAndFlushTwoArrayAgents) {
+  base::RunLoop run_loop;
+  quit_closure_ = run_loop.QuitClosure();
+
+  auto* agent1 = AddArrayAgent();
+  agent1->data_.push_back("e1");
+  agent1->data_.push_back("e2");
+
+  auto* agent2 = AddArrayAgent();
+  agent2->data_.push_back("e3");
+  agent2->data_.push_back("e4");
+
+  StartTracing("config", true, true);
+  if (!quit_closure_.is_null())
+    run_loop.Run();
+
+  // |output_| should be of the form {"traceEvents":[ei,ej,ek,el]}, where
+  // ei,ej,ek,el is a permutation of e1,e2,e3,e4 such that e1 is before e2 and
+  // e3 is before e4 since e1 and 2 come from the same agent and their order
+  // should be preserved and, similarly, the order of e3 and e4 should be
+  // preserved, too.
+  EXPECT_TRUE(output_ == "{\"traceEvents\":[e1,e2,e3,e4]}" ||
+              output_ == "{\"traceEvents\":[e1,e3,e2,e4]}" ||
+              output_ == "{\"traceEvents\":[e1,e3,e4,e2]}" ||
+              output_ == "{\"traceEvents\":[e3,e1,e2,e4]}" ||
+              output_ == "{\"traceEvents\":[e3,e1,e4,e2]}" ||
+              output_ == "{\"traceEvents\":[e3,e4,e1,e2]}");
+
+  // Each agent should have received exactly two calls.
+  EXPECT_EQ(2u, agent1->call_stat().size());
+  EXPECT_EQ("StartTracing", agent1->call_stat()[0]);
+  EXPECT_EQ("StopAndFlush", agent1->call_stat()[1]);
+
+  EXPECT_EQ(2u, agent2->call_stat().size());
+  EXPECT_EQ("StartTracing", agent2->call_stat()[0]);
+  EXPECT_EQ("StopAndFlush", agent2->call_stat()[1]);
+}
+
+TEST_F(CoordinatorTest, StopAndFlushDifferentTypeAgents) {
+  base::RunLoop run_loop;
+  quit_closure_ = run_loop.QuitClosure();
+
+  auto* agent1 = AddArrayAgent();
+  agent1->data_.push_back("e1");
+  agent1->data_.push_back("e2");
+
+  auto* agent2 = AddStringAgent();
+  agent2->data_.push_back("e3");
+  agent2->data_.push_back("e4");
+
+  StartTracing("config", true, true);
+  if (!quit_closure_.is_null())
+    run_loop.Run();
+
+  EXPECT_TRUE(output_ == "{\"traceEvents\":[e1,e2],\"battor\":\"e3e4\"}" ||
+              output_ == "{\"battor\":\"e3e4\",\"traceEvents\":[e1,e2]}");
+
+  // Each agent should have received exactly two calls.
+  EXPECT_EQ(2u, agent1->call_stat().size());
+  EXPECT_EQ("StartTracing", agent1->call_stat()[0]);
+  EXPECT_EQ("StopAndFlush", agent1->call_stat()[1]);
+
+  EXPECT_EQ(2u, agent2->call_stat().size());
+  EXPECT_EQ("StartTracing", agent2->call_stat()[0]);
+  EXPECT_EQ("StopAndFlush", agent2->call_stat()[1]);
+}
+
+TEST_F(CoordinatorTest, StopAndFlushWithMetadata) {
+  base::RunLoop run_loop;
+  quit_closure_ = run_loop.QuitClosure();
+
+  auto* agent = AddArrayAgent();
+  agent->data_.push_back("event");
+  agent->metadata_.SetString("key", "value");
+
+  StartTracing("config", true, true);
+  if (!quit_closure_.is_null())
+    run_loop.Run();
+
+  // Metadata is written at after trace data.
+  EXPECT_EQ("{\"traceEvents\":[event],\"metadata\":{\"key\":\"value\"}}",
+            output_);
+  EXPECT_EQ(2u, agent->call_stat().size());
+  EXPECT_EQ("StartTracing", agent->call_stat()[0]);
+  EXPECT_EQ("StopAndFlush", agent->call_stat()[1]);
+}
+
+TEST_F(CoordinatorTest, IsTracing) {
+  base::RunLoop run_loop;
+  StartTracing("config", true);
+  IsTracing(true);
+  run_loop.RunUntilIdle();
+}
+
+TEST_F(CoordinatorTest, IsNotTracing) {
+  base::RunLoop run_loop;
+  IsTracing(false);
+  run_loop.RunUntilIdle();
+}
+
+TEST_F(CoordinatorTest, RequestBufferUsage) {
+  auto* agent1 = AddArrayAgent();
+  agent1->trace_log_status_.event_capacity = 4;
+  agent1->trace_log_status_.event_count = 1;
+  RequestBufferUsage(0.25, 1);
+  base::RunLoop().RunUntilIdle();
+  CheckDisconnectClosures(1);
+
+  auto* agent2 = AddArrayAgent();
+  agent2->trace_log_status_.event_capacity = 8;
+  agent2->trace_log_status_.event_count = 1;
+  // The buffer usage of |agent2| is less than the buffer usage of |agent1| and
+  // so the total buffer usage, i.e 0.25, does not change. But, the approximage
+  // count will be increased from 1 to 2.
+  RequestBufferUsage(0.25, 2);
+  base::RunLoop().RunUntilIdle();
+  CheckDisconnectClosures(2);
+
+  base::RunLoop run_loop3;
+  auto* agent3 = AddArrayAgent();
+  agent3->trace_log_status_.event_capacity = 8;
+  agent3->trace_log_status_.event_count = 4;
+  // |agent3| has the worst buffer usage of 0.5.
+  RequestBufferUsage(0.5, 6);
+  base::RunLoop().RunUntilIdle();
+  CheckDisconnectClosures(3);
+
+  // At the end |agent1| receveis 3 calls, |agent2| receives 2 calls, and
+  // |agent3| receives 1 call.
+  EXPECT_EQ(3u, agent1->call_stat().size());
+  EXPECT_EQ(2u, agent2->call_stat().size());
+  EXPECT_EQ(1u, agent3->call_stat().size());
+}
+
+TEST_F(CoordinatorTest, GetCategoriesFail) {
+  base::RunLoop run_loop;
+  StartTracing("config", true);
+  std::set<std::string> expected_categories;
+  GetCategories(false, expected_categories);
+  run_loop.RunUntilIdle();
+}
+
+TEST_F(CoordinatorTest, GetCategoriesSimple) {
+  base::RunLoop run_loop;
+  auto* agent = AddArrayAgent();
+  agent->categories_ = "cat2,cat1";
+  std::set<std::string> expected_categories;
+  expected_categories.insert("cat1");
+  expected_categories.insert("cat2");
+  GetCategories(true, expected_categories);
+  run_loop.RunUntilIdle();
+}
+
+TEST_F(CoordinatorTest, GetCategoriesFromTwoAgents) {
+  base::RunLoop run_loop;
+  auto* agent1 = AddArrayAgent();
+  agent1->categories_ = "cat2,cat1";
+  auto* agent2 = AddArrayAgent();
+  agent2->categories_ = "cat3,cat2";
+  std::set<std::string> expected_categories;
+  expected_categories.insert("cat1");
+  expected_categories.insert("cat2");
+  expected_categories.insert("cat3");
+  GetCategories(true, expected_categories);
+  run_loop.RunUntilIdle();
+}
+
+}  // namespace tracing
diff --git a/services/resource_coordinator/tracing/recorder.cc b/services/resource_coordinator/tracing/recorder.cc
index 7997cb1..2701449 100644
--- a/services/resource_coordinator/tracing/recorder.cc
+++ b/services/resource_coordinator/tracing/recorder.cc
@@ -20,10 +20,29 @@
       data_is_array_(data_is_array),
       on_data_change_callback_(on_data_change_callback),
       background_task_runner_(background_task_runner),
-      binding_(this, std::move(request)),
-      weak_factory_(this) {
+      binding_(this, std::move(request)) {
+  // A recorder should be deleted only if |is_recording_| is false to ensure
+  // that:
+  //
+  // 1- |OnConnectionError| is already executed and so using Unretained(this) is
+  // safe here.
+  //
+  // 2- The task possibly posted by |OnConnectionError| is already executed and
+  // so using Unretained(this) is safe in that PostTask.
+  //
+  // 3- Since the connection is closed, the tasks posted by |AddChunk| are
+  // already executed and so using Unretained(this) is safe in the PostTask in
+  // |AddChunk|.
+  //
+  // We cannot use a weak pointer factory here since the weak pointers should be
+  // dereferenced on the same SequencedTaskRunner. We could use two weak pointer
+  // factories but that increases the complexity of the code since each factory
+  // should be created on the correct thread and we should deal with cases that
+  // one of the factories is not created, yet.
+  //
+  // The tracing coordinator deletes a recorder when |is_recording_| is false.
   binding_.set_connection_error_handler(base::BindRepeating(
-      &Recorder::OnConnectionError, weak_factory_.GetWeakPtr()));
+      &Recorder::OnConnectionError, base::Unretained(this)));
 }
 
 Recorder::~Recorder() = default;
@@ -34,7 +53,7 @@
   if (!background_task_runner_->RunsTasksInCurrentSequence()) {
     background_task_runner_->PostTask(
         FROM_HERE, base::BindRepeating(&Recorder::AddChunk,
-                                       weak_factory_.GetWeakPtr(), chunk));
+                                       base::Unretained(this), chunk));
     return;
   }
   if (data_is_array_ && !data_.empty())
@@ -48,8 +67,14 @@
 }
 
 void Recorder::OnConnectionError() {
+  if (!background_task_runner_->RunsTasksOnCurrentThread()) {
+    background_task_runner_->PostTask(
+        FROM_HERE, base::BindRepeating(&Recorder::OnConnectionError,
+                                       base::Unretained(this)));
+    return;
+  }
   is_recording_ = false;
-  background_task_runner_->PostTask(FROM_HERE, on_data_change_callback_);
+  on_data_change_callback_.Run();
 }
 
 }  // namespace tracing
diff --git a/services/resource_coordinator/tracing/recorder.h b/services/resource_coordinator/tracing/recorder.h
index 1530c6c..78fee95e7 100644
--- a/services/resource_coordinator/tracing/recorder.h
+++ b/services/resource_coordinator/tracing/recorder.h
@@ -9,7 +9,6 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
 #include "base/sequenced_task_runner.h"
 #include "base/values.h"
 #include "mojo/public/cpp/bindings/binding.h"
@@ -76,7 +75,6 @@
   // final stream is done on a background thread.
   scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
   mojo::Binding<mojom::Recorder> binding_;
-  base::WeakPtrFactory<Recorder> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(Recorder);
 };
diff --git a/services/resource_coordinator/tracing/test_util.cc b/services/resource_coordinator/tracing/test_util.cc
new file mode 100644
index 0000000..28ffbb3
--- /dev/null
+++ b/services/resource_coordinator/tracing/test_util.cc
@@ -0,0 +1,55 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/resource_coordinator/tracing/test_util.h"
+
+#include <string>
+
+#include "base/memory/ptr_util.h"
+#include "services/resource_coordinator/public/interfaces/tracing/tracing.mojom.h"
+
+namespace tracing {
+
+MockAgent::MockAgent() : binding_(this) {}
+
+MockAgent::~MockAgent() = default;
+
+mojom::AgentPtr MockAgent::CreateAgentPtr() {
+  mojom::AgentPtr agent_proxy;
+  binding_.Bind(mojo::MakeRequest(&agent_proxy));
+  return agent_proxy;
+}
+
+void MockAgent::StartTracing(const std::string& config,
+                             const StartTracingCallback& cb) {
+  call_stat_.push_back("StartTracing");
+  cb.Run();
+}
+
+void MockAgent::StopAndFlush(mojom::RecorderPtr recorder) {
+  call_stat_.push_back("StopAndFlush");
+  if (!metadata_.empty())
+    recorder->AddMetadata(base::MakeUnique<base::DictionaryValue>(metadata_));
+  for (const auto& chunk : data_) {
+    recorder->AddChunk(chunk);
+  }
+}
+
+void MockAgent::RequestClockSyncMarker(
+    const std::string& sync_id,
+    const RequestClockSyncMarkerCallback& cb) {
+  call_stat_.push_back("RequestClockSyncMarker");
+}
+
+void MockAgent::GetCategories(const GetCategoriesCallback& cb) {
+  call_stat_.push_back("GetCategories");
+  cb.Run(categories_);
+}
+
+void MockAgent::RequestBufferStatus(const RequestBufferStatusCallback& cb) {
+  call_stat_.push_back("RequestBufferStatus");
+  cb.Run(trace_log_status_.event_capacity, trace_log_status_.event_count);
+}
+
+}  // namespace tracing
diff --git a/services/resource_coordinator/tracing/test_util.h b/services/resource_coordinator/tracing/test_util.h
new file mode 100644
index 0000000..9f92afd0
--- /dev/null
+++ b/services/resource_coordinator/tracing/test_util.h
@@ -0,0 +1,50 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SERVICES_RESOURCE_COORDINATOR_TRACING_TEST_UTIL_H_
+#define SERVICES_RESOURCE_COORDINATOR_TRACING_TEST_UTIL_H_
+
+#include <string>
+#include <vector>
+
+#include "base/trace_event/trace_log.h"
+#include "base/values.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "services/resource_coordinator/public/interfaces/tracing/tracing.mojom.h"
+
+namespace tracing {
+
+class MockAgent : public mojom::Agent {
+ public:
+  MockAgent();
+  ~MockAgent() override;
+
+  mojom::AgentPtr CreateAgentPtr();
+
+  std::vector<std::string> call_stat() const { return call_stat_; }
+
+  // Set these variables to configure the agent.
+  std::vector<std::string> data_;
+  base::DictionaryValue metadata_;
+  std::string categories_;
+  base::trace_event::TraceLogStatus trace_log_status_;
+
+ private:
+  // mojom::Agent
+  void StartTracing(const std::string& config,
+                    const StartTracingCallback& cb) override;
+  void StopAndFlush(mojom::RecorderPtr recorder) override;
+  void RequestClockSyncMarker(
+      const std::string& sync_id,
+      const RequestClockSyncMarkerCallback& cb) override;
+  void GetCategories(const GetCategoriesCallback& cb) override;
+  void RequestBufferStatus(const RequestBufferStatusCallback& cb) override;
+
+  mojo::Binding<mojom::Agent> binding_;
+  std::vector<std::string> call_stat_;
+};
+
+}  // namespace tracing
+
+#endif  // SERVICES_RESOURCE_COORDINATOR_TRACING_TEST_UTIL_H_
diff --git a/services/shape_detection/barcode_detection_impl_mac.h b/services/shape_detection/barcode_detection_impl_mac.h
index 6a5609a..062a68b 100644
--- a/services/shape_detection/barcode_detection_impl_mac.h
+++ b/services/shape_detection/barcode_detection_impl_mac.h
@@ -20,8 +20,8 @@
   ~BarcodeDetectionImplMac() override;
 
   void Detect(const SkBitmap& bitmap,
-              const shape_detection::mojom::BarcodeDetection::DetectCallback&
-                  callback) override;
+              shape_detection::mojom::BarcodeDetection::DetectCallback callback)
+      override;
 
  private:
   base::scoped_nsobject<CIDetector> detector_;
diff --git a/services/shape_detection/barcode_detection_impl_mac.mm b/services/shape_detection/barcode_detection_impl_mac.mm
index 23569cd..a3df01e2 100644
--- a/services/shape_detection/barcode_detection_impl_mac.mm
+++ b/services/shape_detection/barcode_detection_impl_mac.mm
@@ -17,16 +17,9 @@
 
 namespace {
 
-void RunCallbackWithBarcodes(
-    const shape_detection::mojom::BarcodeDetection::DetectCallback& callback,
-    std::vector<shape_detection::mojom::BarcodeDetectionResultPtr> results) {
-  callback.Run(std::move(results));
-}
-
 void RunCallbackWithNoBarcodes(
     shape_detection::mojom::BarcodeDetection::DetectCallback callback) {
-  callback.Run(
-      std::vector<shape_detection::mojom::BarcodeDetectionResultPtr>());
+  std::move(callback).Run({});
 }
 
 }  // anonymous namespace
@@ -52,10 +45,9 @@
 BarcodeDetectionImplMac::~BarcodeDetectionImplMac() {}
 
 void BarcodeDetectionImplMac::Detect(const SkBitmap& bitmap,
-                                     const DetectCallback& callback) {
+                                     DetectCallback callback) {
   media::ScopedResultCallback<DetectCallback> scoped_callback(
-      base::Bind(&RunCallbackWithBarcodes, callback),
-      base::Bind(&RunCallbackWithNoBarcodes));
+      std::move(callback), base::Bind(&RunCallbackWithNoBarcodes));
 
   base::scoped_nsobject<CIImage> ci_image = CreateCIImageFromSkBitmap(bitmap);
   if (!ci_image)
diff --git a/services/shape_detection/face_detection_impl_mac.h b/services/shape_detection/face_detection_impl_mac.h
index 459ffa7..51d6ac2 100644
--- a/services/shape_detection/face_detection_impl_mac.h
+++ b/services/shape_detection/face_detection_impl_mac.h
@@ -19,9 +19,9 @@
       shape_detection::mojom::FaceDetectorOptionsPtr options);
   ~FaceDetectionImplMac() override;
 
-  void Detect(const SkBitmap& bitmap,
-              const shape_detection::mojom::FaceDetection::DetectCallback&
-                  callback) override;
+  void Detect(
+      const SkBitmap& bitmap,
+      shape_detection::mojom::FaceDetection::DetectCallback callback) override;
 
  private:
   base::scoped_nsobject<CIDetector> detector_;
diff --git a/services/shape_detection/face_detection_impl_mac.mm b/services/shape_detection/face_detection_impl_mac.mm
index d7547e0..29648c3 100644
--- a/services/shape_detection/face_detection_impl_mac.mm
+++ b/services/shape_detection/face_detection_impl_mac.mm
@@ -14,15 +14,9 @@
 
 namespace {
 
-void RunCallbackWithFaces(
-    const shape_detection::mojom::FaceDetection::DetectCallback& callback,
-    std::vector<shape_detection::mojom::FaceDetectionResultPtr> results) {
-  callback.Run(std::move(results));
-}
-
 void RunCallbackWithNoFaces(
     shape_detection::mojom::FaceDetection::DetectCallback callback) {
-  callback.Run(std::vector<shape_detection::mojom::FaceDetectionResultPtr>());
+  std::move(callback).Run({});
 }
 
 }  // anonymous namespace
@@ -48,10 +42,9 @@
 FaceDetectionImplMac::~FaceDetectionImplMac() {}
 
 void FaceDetectionImplMac::Detect(const SkBitmap& bitmap,
-                                  const DetectCallback& callback) {
+                                  DetectCallback callback) {
   media::ScopedResultCallback<DetectCallback> scoped_callback(
-      base::Bind(&RunCallbackWithFaces, callback),
-      base::Bind(&RunCallbackWithNoFaces));
+      std::move(callback), base::Bind(&RunCallbackWithNoFaces));
 
   base::scoped_nsobject<CIImage> ci_image = CreateCIImageFromSkBitmap(bitmap);
   if (!ci_image)
diff --git a/services/shape_detection/public/interfaces/BUILD.gn b/services/shape_detection/public/interfaces/BUILD.gn
index 432da86..44522415 100644
--- a/services/shape_detection/public/interfaces/BUILD.gn
+++ b/services/shape_detection/public/interfaces/BUILD.gn
@@ -17,10 +17,4 @@
     "//skia/public/interfaces",
     "//ui/gfx/geometry/mojo",
   ]
-
-  # TODO(crbug.com/714018): Convert the implementation to use OnceCallback.
-  use_once_callback = false
-
-  # TODO(crbug.com/699569): Convert to use the new JS bindings.
-  use_new_js_bindings = false
 }
diff --git a/services/shape_detection/text_detection_impl_mac.h b/services/shape_detection/text_detection_impl_mac.h
index 738d8da..64563c67 100644
--- a/services/shape_detection/text_detection_impl_mac.h
+++ b/services/shape_detection/text_detection_impl_mac.h
@@ -18,7 +18,7 @@
   ~TextDetectionImplMac() override;
 
   void Detect(const SkBitmap& bitmap,
-              const mojom::TextDetection::DetectCallback& callback) override;
+              mojom::TextDetection::DetectCallback callback) override;
 
  private:
   base::scoped_nsobject<CIDetector> detector_;
diff --git a/services/shape_detection/text_detection_impl_mac.mm b/services/shape_detection/text_detection_impl_mac.mm
index 779c341..f645cce 100644
--- a/services/shape_detection/text_detection_impl_mac.mm
+++ b/services/shape_detection/text_detection_impl_mac.mm
@@ -17,14 +17,8 @@
 
 namespace {
 
-void RunCallbackWithResults(
-    const mojom::TextDetection::DetectCallback& callback,
-    std::vector<mojom::TextDetectionResultPtr> results) {
-  callback.Run(std::move(results));
-}
-
 void RunCallbackWithNoResults(mojom::TextDetection::DetectCallback callback) {
-  callback.Run(std::vector<mojom::TextDetectionResultPtr>());
+  std::move(callback).Run({});
 }
 
 }  // anonymous namespace
@@ -50,11 +44,10 @@
 TextDetectionImplMac::~TextDetectionImplMac() {}
 
 void TextDetectionImplMac::Detect(const SkBitmap& bitmap,
-                                  const DetectCallback& callback) {
+                                  DetectCallback callback) {
   DCHECK(base::mac::IsAtLeastOS10_11());
   media::ScopedResultCallback<DetectCallback> scoped_callback(
-      base::Bind(&RunCallbackWithResults, callback),
-      base::Bind(&RunCallbackWithNoResults));
+      std::move(callback), base::Bind(&RunCallbackWithNoResults));
 
   base::scoped_nsobject<CIImage> ci_image = CreateCIImageFromSkBitmap(bitmap);
   if (!ci_image)
diff --git a/skia/public/interfaces/BUILD.gn b/skia/public/interfaces/BUILD.gn
index 5dc61b7..3b935a2 100644
--- a/skia/public/interfaces/BUILD.gn
+++ b/skia/public/interfaces/BUILD.gn
@@ -9,9 +9,6 @@
     "bitmap.mojom",
     "image_filter.mojom",
   ]
-
-  # TODO(crbug.com/699569): Convert to use the new JS bindings.
-  use_new_js_bindings = false
 }
 
 mojom("test_interfaces") {
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 9bd8bab..df6a60f 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -290,6 +290,44 @@
         "test": "chrome_public_test_vr_apk"
       },
       {
+        "args": [
+          "--shared-prefs-file=../../chrome/android/shared_preference_files/test/vr_cardboard_skipdon_setupcomplete.json",
+          "--replace-system-package=com.google.vr.vrcore,../../third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk"
+        ],
+        "name": "chrome_public_test_vr_apk-marlin-cardboard-nougat",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_os": "NMF26U",
+              "device_type": "marlin"
+            }
+          ],
+          "hard_timeout": 960
+        },
+        "test": "chrome_public_test_vr_apk"
+      },
+      {
+        "args": [
+          "--shared-prefs-file=../../chrome/android/shared_preference_files/test/vr_ddview_skipdon_setupcomplete.json",
+          "--replace-system-package=com.google.vr.vrcore,../../third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk"
+        ],
+        "name": "chrome_public_test_vr_apk-marlin-ddview-nougat",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "device_os": "NMF26U",
+              "device_type": "marlin"
+            }
+          ],
+          "hard_timeout": 960
+        },
+        "test": "chrome_public_test_vr_apk"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": false
         },
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json
index 563e05f..35f89e28 100644
--- a/testing/buildbot/chromium.gpu.fyi.json
+++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -10740,6 +10740,22 @@
             }
           ]
         },
+        "test": "audio_unittests",
+        "use_xvfb": false
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Ubuntu"
+            }
+          ]
+        },
         "test": "gles2_conform_test",
         "use_xvfb": false
       },
@@ -10911,6 +10927,22 @@
             }
           ]
         },
+        "test": "audio_unittests",
+        "use_xvfb": false
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0a2e",
+              "os": "Mac-10.12"
+            }
+          ]
+        },
         "test": "gles2_conform_test",
         "use_xvfb": false
       },
@@ -11037,6 +11069,23 @@
             }
           ]
         },
+        "test": "audio_unittests",
+        "use_xvfb": false
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac"
+            }
+          ]
+        },
         "test": "gles2_conform_test",
         "use_xvfb": false
       },
@@ -11165,6 +11214,22 @@
             }
           ]
         },
+        "test": "audio_unittests",
+        "use_xvfb": false
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0a2e",
+              "os": "Mac-10.12"
+            }
+          ]
+        },
         "test": "gles2_conform_test",
         "use_xvfb": false
       },
@@ -11291,6 +11356,23 @@
             }
           ]
         },
+        "test": "audio_unittests",
+        "use_xvfb": false
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:0fe9",
+              "hidpi": "1",
+              "os": "Mac"
+            }
+          ]
+        },
         "test": "gles2_conform_test",
         "use_xvfb": false
       },
@@ -11421,6 +11503,23 @@
             }
           ]
         },
+        "test": "audio_unittests",
+        "use_xvfb": false
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac"
+            }
+          ]
+        },
         "test": "gles2_conform_test",
         "use_xvfb": false
       },
@@ -11551,6 +11650,23 @@
             }
           ]
         },
+        "test": "audio_unittests",
+        "use_xvfb": false
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:0fe9",
+              "hidpi": "1",
+              "os": "Mac"
+            }
+          ]
+        },
         "test": "gles2_conform_test",
         "use_xvfb": false
       },
@@ -11703,6 +11819,22 @@
       },
       {
         "args": [
+          "--use-gpu-in-tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6613",
+              "os": "Windows-2008ServerR2-SP1"
+            }
+          ]
+        },
+        "test": "audio_unittests",
+        "use_xvfb": false
+      },
+      {
+        "args": [
           "--use-gpu-in-tests",
           "--use-angle=d3d9"
         ],
@@ -11946,6 +12078,22 @@
       },
       {
         "args": [
+          "--use-gpu-in-tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-2008ServerR2-SP1"
+            }
+          ]
+        },
+        "test": "audio_unittests",
+        "use_xvfb": false
+      },
+      {
+        "args": [
           "--use-gpu-in-tests",
           "--use-angle=d3d9"
         ],
diff --git a/testing/buildbot/chromium.perf.fyi.json b/testing/buildbot/chromium.perf.fyi.json
index bbcb21ac..9c17388d 100644
--- a/testing/buildbot/chromium.perf.fyi.json
+++ b/testing/buildbot/chromium.perf.fyi.json
@@ -3810,6 +3810,65 @@
       },
       {
         "args": [
+          "tab_switching.typical_25",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-chromium"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "tab_switching.typical_25",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build249-m4--device1",
+              "os": "Android",
+              "pool": "Chrome-perf-fyi"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "tab_switching.typical_25",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=reference",
+          "--output-trace-tag=_ref"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "tab_switching.typical_25.reference",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build249-m4--device1",
+              "os": "Android",
+              "pool": "Chrome-perf-fyi"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": true,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
           "thread_times.key_hit_test_cases",
           "-v",
           "--upload-results",
@@ -4754,6 +4813,31 @@
       }
     ]
   },
+  "Mojo Linux Perf": {
+    "isolated_scripts": [
+      {
+        "args": [
+          "loading.desktop.network_service",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=release"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "loading.desktop.network_service",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      }
+    ]
+  },
   "Win 10 4 Core Low-End Perf Tests": {
     "isolated_scripts": [
       {
diff --git a/testing/buildbot/chromium.perf.json b/testing/buildbot/chromium.perf.json
index 37055978..0949bf09 100644
--- a/testing/buildbot/chromium.perf.json
+++ b/testing/buildbot/chromium.perf.json
@@ -3855,6 +3855,65 @@
       },
       {
         "args": [
+          "tab_switching.typical_25",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-chromium"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "tab_switching.typical_25",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build48-b1--device1",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "tab_switching.typical_25",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=reference",
+          "--output-trace-tag=_ref"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "tab_switching.typical_25.reference",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build48-b1--device1",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": true,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
           "thread_times.key_hit_test_cases",
           "-v",
           "--upload-results",
@@ -8648,6 +8707,65 @@
       },
       {
         "args": [
+          "tab_switching.typical_25",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-chromium"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "tab_switching.typical_25",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build75-b1--device1",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "tab_switching.typical_25",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=reference",
+          "--output-trace-tag=_ref"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "tab_switching.typical_25.reference",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build75-b1--device1",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": true,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
           "thread_times.key_hit_test_cases",
           "-v",
           "--upload-results",
@@ -11566,6 +11684,36 @@
       },
       {
         "args": [
+          "tab_switching.typical_25",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedder-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "tab_switching.typical_25",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build166-b1--device3",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
           "thread_times.key_hit_test_cases",
           "-v",
           "--upload-results",
@@ -15895,6 +16043,65 @@
       },
       {
         "args": [
+          "tab_switching.typical_25",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-chromium"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "tab_switching.typical_25",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build45-b1--device1",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "tab_switching.typical_25",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=reference",
+          "--output-trace-tag=_ref"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "tab_switching.typical_25.reference",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build45-b1--device1",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": true,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
           "thread_times.key_hit_test_cases",
           "-v",
           "--upload-results",
@@ -18813,6 +19020,36 @@
       },
       {
         "args": [
+          "tab_switching.typical_25",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-webview",
+          "--webview-embedder-apk=../../out/Release/apks/SystemWebViewShell.apk"
+        ],
+        "isolate_name": "telemetry_perf_webview_tests",
+        "name": "tab_switching.typical_25",
+        "override_compile_targets": [
+          "telemetry_perf_webview_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build114-b1--device3",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
           "thread_times.key_hit_test_cases",
           "-v",
           "--upload-results",
@@ -23142,6 +23379,65 @@
       },
       {
         "args": [
+          "tab_switching.typical_25",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-chromium"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "tab_switching.typical_25",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build49-b1--device1",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "tab_switching.typical_25",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=reference",
+          "--output-trace-tag=_ref"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "tab_switching.typical_25.reference",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build49-b1--device1",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": true,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
           "thread_times.key_hit_test_cases",
           "-v",
           "--upload-results",
@@ -27935,6 +28231,65 @@
       },
       {
         "args": [
+          "tab_switching.typical_25",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=android-chromium"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "tab_switching.typical_25",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build18-b1--device7",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
+          "tab_switching.typical_25",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=reference",
+          "--output-trace-tag=_ref"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "tab_switching.typical_25.reference",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "android_devices": "1",
+              "id": "build18-b1--device7",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": true,
+          "io_timeout": 3600
+        }
+      },
+      {
+        "args": [
           "thread_times.key_hit_test_cases",
           "-v",
           "--upload-results",
diff --git a/testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter b/testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter
index 8285dec5..19fa3da 100644
--- a/testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter
+++ b/testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter
@@ -92,6 +92,7 @@
 -PaymentAppBrowserTest.PaymentAppInvocation
 -PaymentAppBrowserTest.PaymentAppOpenWindowFailed
 -PlzNavigateNavigationHandleImplBrowserTest.ErrorPageNetworkError
+-PowerMonitorTest.TestGpuProcess
 -PowerMonitorTest.TestRendererProcess
 -PowerMonitorTest.TestUtilityProcess
 -PreviewsStateResourceDispatcherHostBrowserTest.ShouldEnableLoFiModeNavigateBackThenForward
diff --git a/testing/libfuzzer/fuzzers/BUILD.gn b/testing/libfuzzer/fuzzers/BUILD.gn
index d0c0b6e..4fa3fca 100644
--- a/testing/libfuzzer/fuzzers/BUILD.gn
+++ b/testing/libfuzzer/fuzzers/BUILD.gn
@@ -462,12 +462,15 @@
   ]
 }
 
-# TODO(yawano): Add compress test case
-fuzzer_test("minizip_uncompress_fuzzer") {
-  sources = [
-    "minizip_uncompress_fuzzer.cc",
-  ]
-  deps = [
-    "//third_party/minizip:minizip",
-  ]
+# third_party/minizip/src is checked out only on unix platform by gclient.
+if (is_linux) {
+  # TODO(yawano): Add compress test case
+  fuzzer_test("minizip_uncompress_fuzzer") {
+    sources = [
+      "minizip_uncompress_fuzzer.cc",
+    ]
+    deps = [
+      "//third_party/minizip:minizip",
+    ]
+  }
 }
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 83c4156a..efa8894 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -147,13 +147,9 @@
             ],
             "experiments": [
                 {
-                    "name": "LastUsedDate_IncreaseDropdownItemHeight_Experiment",
-                    "params": {
-                        "dropdown_item_height": "56"
-                    },
+                    "name": "BankName_Experiment",
                     "enable_features": [
-                        "AutofillCreditCardLastUsedDateDisplay",
-                        "AutofillCreditCardPopupLayout"
+                        "AutofillCreditCardBankNameDisplay"
                     ]
                 }
             ]
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
index 339e5ac..928eb277 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
@@ -230,7 +230,6 @@
 crbug.com/591099 animations/3d/state-at-end-event-transform.html [ Failure Pass ]
 crbug.com/591099 animations/animation-css-rule-types.html [ Failure ]
 crbug.com/591099 animations/animation-events-create.html [ Failure ]
-crbug.com/591099 animations/prefixed/animation-inherit-initial-unprefixed.html [ Failure ]
 crbug.com/591099 animations/animations-parsing.html [ Timeout ]
 crbug.com/591099 animations/animations-responsive-to-color-change.html [ Crash ]
 crbug.com/591099 animations/clear-svg-animation-effects.html [ Crash ]
@@ -329,13 +328,14 @@
 crbug.com/591099 animations/interpolation/webkit-text-stroke-color-interpolation.html [ Crash ]
 crbug.com/591099 animations/interpolation/webkit-transform-interpolation.html [ Crash ]
 crbug.com/591099 animations/interpolation/webkit-transform-origin-interpolation.html [ Crash ]
-crbug.com/591099 animations/prefixed/keyframes-cssom-prefixed-02.html [ Failure ]
-crbug.com/591099 animations/prefixed/keyframes-cssom-unprefixed-02.html [ Failure ]
 crbug.com/591099 animations/keyframes-rule.html [ Failure ]
 crbug.com/591099 animations/lazy-detached-animation-stop.html [ Failure ]
 crbug.com/591099 animations/negative-delay-events.html [ Failure ]
 crbug.com/591099 animations/play-state-initially-paused-start-event.html [ Failure ]
 crbug.com/591099 animations/play-state.html [ Failure ]
+crbug.com/591099 animations/prefixed/animation-inherit-initial-unprefixed.html [ Failure ]
+crbug.com/591099 animations/prefixed/keyframes-cssom-prefixed-02.html [ Failure ]
+crbug.com/591099 animations/prefixed/keyframes-cssom-unprefixed-02.html [ Failure ]
 crbug.com/591099 animations/responsive/d-responsive.html [ Crash ]
 crbug.com/591099 animations/responsive/line-height-responsive.html [ Pass Timeout ]
 crbug.com/591099 animations/rotate-transform-equivalent.html [ Failure ]
@@ -718,6 +718,7 @@
 crbug.com/591099 compositing/overflow/clip-descendents.html [ Failure ]
 crbug.com/591099 compositing/overflow/clip-parent-reset.html [ Failure ]
 crbug.com/591099 compositing/overflow/clipping-ancestor-with-accelerated-scrolling-ancestor.html [ Failure ]
+crbug.com/591099 compositing/overflow/composited-layer-under-border-radius-under-composited-layer.html [ Failure ]
 crbug.com/591099 compositing/overflow/composited-nested-sticky-left.html [ Failure ]
 crbug.com/591099 compositing/overflow/composited-scrolling-paint-phases.html [ Failure ]
 crbug.com/591099 compositing/overflow/content-gains-scrollbars.html [ Failure ]
@@ -4105,7 +4106,7 @@
 crbug.com/591099 external/wpt/2dcontext/drawing-paths-to-the-canvas/drawFocusIfNeeded_003.html [ Crash ]
 crbug.com/591099 external/wpt/2dcontext/drawing-paths-to-the-canvas/drawFocusIfNeeded_004.html [ Crash ]
 crbug.com/591099 external/wpt/2dcontext/drawing-paths-to-the-canvas/drawFocusIfNeeded_005.html [ Crash ]
-crbug.com/591099 external/wpt/2dcontext/fill-and-stroke-styles/2d.pattern.image.incomplete.emptysrc.html [ Crash ]
+crbug.com/591099 external/wpt/2dcontext/fill-and-stroke-styles/2d.pattern.image.incomplete.emptysrc.html [ Crash Pass ]
 crbug.com/591099 external/wpt/2dcontext/fill-and-stroke-styles/2d.pattern.image.incomplete.removedsrc.html [ Crash ]
 crbug.com/591099 external/wpt/2dcontext/hit-regions/hitregions-members-exist.html [ Crash ]
 crbug.com/591099 external/wpt/2dcontext/line-styles/setLineDash.html [ Crash ]
@@ -4137,6 +4138,8 @@
 crbug.com/591099 external/wpt/XMLHttpRequest/send-authentication-prompt-2-manual.htm [ Crash Failure ]
 crbug.com/591099 external/wpt/XMLHttpRequest/xmlhttprequest-sync-block-defer-scripts.html [ Crash ]
 crbug.com/591099 external/wpt/XMLHttpRequest/xmlhttprequest-sync-not-hang-scriptloader.html [ Crash ]
+crbug.com/591099 external/wpt/clear-site-data/navigation-insecure.html [ Crash ]
+crbug.com/591099 external/wpt/clear-site-data/navigation.https.html [ Crash ]
 crbug.com/591099 external/wpt/content-security-policy/child-src/child-src-about-blank-allowed-by-default.sub.html [ Crash ]
 crbug.com/591099 external/wpt/content-security-policy/child-src/child-src-about-blank-allowed-by-scheme.sub.html [ Crash ]
 crbug.com/591099 external/wpt/content-security-policy/child-src/child-src-allowed.sub.html [ Crash ]
@@ -4183,6 +4186,39 @@
 crbug.com/591099 external/wpt/content-security-policy/svg/svg-policy-resource-doc-includes.html [ Crash ]
 crbug.com/591099 external/wpt/content-security-policy/svg/svg-policy-with-resource.html [ Crash ]
 crbug.com/591099 external/wpt/cors/remote-origin.htm [ Crash ]
+crbug.com/591099 external/wpt/css-paint-api/geometry-background-image-001.html [ Failure Pass ]
+crbug.com/591099 external/wpt/css-paint-api/geometry-background-image-002.html [ Failure Pass ]
+crbug.com/591099 external/wpt/css-paint-api/geometry-background-image-003.html [ Failure Pass ]
+crbug.com/591099 external/wpt/css-paint-api/geometry-background-image-tiled-001.html [ Failure Pass ]
+crbug.com/591099 external/wpt/css-paint-api/geometry-background-image-tiled-002.html [ Failure Pass ]
+crbug.com/591099 external/wpt/css-paint-api/geometry-background-image-tiled-003.html [ Failure Pass ]
+crbug.com/591099 external/wpt/css-paint-api/geometry-border-image-001.html [ Failure Pass ]
+crbug.com/591099 external/wpt/css-paint-api/geometry-border-image-002.html [ Failure Pass ]
+crbug.com/591099 external/wpt/css-paint-api/geometry-border-image-003.html [ Failure Pass ]
+crbug.com/591099 external/wpt/css-paint-api/geometry-border-image-004.html [ Failure Pass ]
+crbug.com/591099 external/wpt/css-paint-api/geometry-border-image-005.html [ Failure Pass ]
+crbug.com/591099 external/wpt/css-paint-api/paint2d-zoom.html [ Failure Pass ]
+crbug.com/591099 external/wpt/css-paint-api/parse-input-arguments-001.html [ Failure Pass ]
+crbug.com/591099 external/wpt/css-paint-api/parse-input-arguments-002.html [ Failure Pass ]
+crbug.com/591099 external/wpt/css-paint-api/parse-input-arguments-003.html [ Failure Pass ]
+crbug.com/591099 external/wpt/css-paint-api/parse-input-arguments-004.html [ Failure Pass ]
+crbug.com/591099 external/wpt/css-paint-api/parse-input-arguments-005.html [ Failure Pass ]
+crbug.com/591099 external/wpt/css-paint-api/parse-input-arguments-006.html [ Failure Pass ]
+crbug.com/591099 external/wpt/css-paint-api/parse-input-arguments-007.html [ Failure Pass ]
+crbug.com/591099 external/wpt/css-paint-api/parse-input-arguments-008.html [ Failure Pass ]
+crbug.com/591099 external/wpt/css-paint-api/parse-input-arguments-009.html [ Failure Pass ]
+crbug.com/591099 external/wpt/css-paint-api/parse-input-arguments-010.html [ Failure Pass ]
+crbug.com/591099 external/wpt/css-paint-api/parse-input-arguments-011.html [ Failure Pass ]
+crbug.com/591099 external/wpt/css-paint-api/parse-input-arguments-012.html [ Failure Pass ]
+crbug.com/591099 external/wpt/css-paint-api/parse-input-arguments-013.html [ Failure Pass ]
+crbug.com/591099 external/wpt/css-paint-api/parse-input-arguments-014.html [ Failure Pass ]
+crbug.com/591099 external/wpt/css-paint-api/parse-input-arguments-015.html [ Failure Pass ]
+crbug.com/591099 external/wpt/css-paint-api/parse-input-arguments-016.html [ Failure Pass ]
+crbug.com/591099 external/wpt/css-paint-api/parse-input-arguments-017.html [ Failure Pass ]
+crbug.com/591099 external/wpt/css-paint-api/registered-properties-in-custom-paint.html [ Failure Pass ]
+crbug.com/591099 external/wpt/css-paint-api/style-background-image.html [ Failure Pass ]
+crbug.com/591099 external/wpt/css-paint-api/style-before-pseudo.html [ Failure Pass ]
+crbug.com/591099 external/wpt/css-paint-api/style-first-letter-pseudo.html [ Failure Pass ]
 crbug.com/591099 external/wpt/css/CSS2/abspos/abspos-containing-block-initial-001.xht [ Failure ]
 crbug.com/591099 external/wpt/css/CSS2/abspos/abspos-containing-block-initial-009a.xht [ Failure ]
 crbug.com/591099 external/wpt/css/CSS2/floats-clear/float-replaced-width-002.xht [ Failure ]
@@ -5223,39 +5259,6 @@
 crbug.com/591099 external/wpt/cssom-view/scrollingElement.html [ Crash ]
 crbug.com/591099 external/wpt/cssom-view/ttwf-js-cssomview-getclientrects-length.html [ Crash ]
 crbug.com/591099 external/wpt/cssom/serialize-values.html [ Pass Timeout ]
-crbug.com/591099 external/wpt/css-paint-api/geometry-background-image-tiled-001.html [ Failure ]
-crbug.com/591099 external/wpt/css-paint-api/geometry-background-image-tiled-002.html [ Failure ]
-crbug.com/591099 external/wpt/css-paint-api/geometry-background-image-tiled-003.html [ Failure ]
-crbug.com/591099 external/wpt/css-paint-api/geometry-background-image-001.html [ Failure ]
-crbug.com/591099 external/wpt/css-paint-api/geometry-background-image-002.html [ Failure ]
-crbug.com/591099 external/wpt/css-paint-api/geometry-background-image-003.html [ Failure ]
-crbug.com/591099 external/wpt/css-paint-api/geometry-border-image-001.html [ Failure ]
-crbug.com/591099 external/wpt/css-paint-api/geometry-border-image-002.html [ Failure ]
-crbug.com/591099 external/wpt/css-paint-api/geometry-border-image-003.html [ Failure ]
-crbug.com/591099 external/wpt/css-paint-api/geometry-border-image-004.html [ Failure ]
-crbug.com/591099 external/wpt/css-paint-api/geometry-border-image-005.html [ Failure ]
-crbug.com/591099 external/wpt/css-paint-api/paint2d-zoom.html [ Failure Pass ]
-crbug.com/591099 external/wpt/css-paint-api/parse-input-arguments-001.html [ Failure ]
-crbug.com/591099 external/wpt/css-paint-api/parse-input-arguments-002.html [ Failure ]
-crbug.com/591099 external/wpt/css-paint-api/parse-input-arguments-003.html [ Failure ]
-crbug.com/591099 external/wpt/css-paint-api/parse-input-arguments-004.html [ Failure ]
-crbug.com/591099 external/wpt/css-paint-api/parse-input-arguments-005.html [ Failure ]
-crbug.com/591099 external/wpt/css-paint-api/parse-input-arguments-006.html [ Failure ]
-crbug.com/591099 external/wpt/css-paint-api/parse-input-arguments-007.html [ Failure ]
-crbug.com/591099 external/wpt/css-paint-api/parse-input-arguments-008.html [ Failure ]
-crbug.com/591099 external/wpt/css-paint-api/parse-input-arguments-009.html [ Failure ]
-crbug.com/591099 external/wpt/css-paint-api/parse-input-arguments-010.html [ Failure ]
-crbug.com/591099 external/wpt/css-paint-api/parse-input-arguments-011.html [ Failure ]
-crbug.com/591099 external/wpt/css-paint-api/parse-input-arguments-012.html [ Failure ]
-crbug.com/591099 external/wpt/css-paint-api/parse-input-arguments-013.html [ Failure ]
-crbug.com/591099 external/wpt/css-paint-api/parse-input-arguments-014.html [ Failure ]
-crbug.com/591099 external/wpt/css-paint-api/parse-input-arguments-015.html [ Failure ]
-crbug.com/591099 external/wpt/css-paint-api/parse-input-arguments-016.html [ Failure ]
-crbug.com/591099 external/wpt/css-paint-api/parse-input-arguments-017.html [ Failure ]
-crbug.com/591099 external/wpt/css-paint-api/registered-properties-in-custom-paint.html [ Failure ]
-crbug.com/591099 external/wpt/css-paint-api/style-background-image.html [ Failure ]
-crbug.com/591099 external/wpt/css-paint-api/style-before-pseudo.html [ Failure ]
-crbug.com/591099 external/wpt/css-paint-api/style-first-letter-pseudo.html [ Failure ]
 crbug.com/591099 external/wpt/custom-elements/custom-element-reaction-queue.html [ Crash ]
 crbug.com/591099 external/wpt/custom-elements/custom-element-registry/per-global.html [ Crash ]
 crbug.com/591099 external/wpt/custom-elements/htmlconstructor/newtarget.html [ Crash ]
@@ -5337,7 +5340,75 @@
 crbug.com/591099 external/wpt/editing/run/superscript.html [ Pass Timeout ]
 crbug.com/591099 external/wpt/editing/run/underline.html [ Pass Timeout ]
 crbug.com/591099 external/wpt/encoding/api-invalid-label.html [ Pass Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-japanese/shift_jis/sjis-decode-csshiftjis.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-japanese/shift_jis/sjis-decode-ms932.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-japanese/shift_jis/sjis-decode-ms_kanji.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-japanese/shift_jis/sjis-decode-shift-jis.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-japanese/shift_jis/sjis-decode-sjis.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-japanese/shift_jis/sjis-decode-windows-31j.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-japanese/shift_jis/sjis-decode-x-sjis.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-japanese/shift_jis/sjis-decode.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-csshiftjis.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-errors-han.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-errors-hangul.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-errors-misc.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-ms932.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-ms_kanji.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-shift-jis.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-sjis.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-windows-31j.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form-x-sjis.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-japanese/shift_jis/sjis-encode-form.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-japanese/shift_jis/sjis-encode-href-errors-han.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-japanese/shift_jis/sjis-encode-href-errors-hangul.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-japanese/shift_jis/sjis-encode-href-errors-misc.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-japanese/shift_jis/sjis-encode-href.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-korean/euc-kr/euckr-decode-cseuckr.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-korean/euc-kr/euckr-decode-csksc56011987.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-korean/euc-kr/euckr-decode-iso-ir-149.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-korean/euc-kr/euckr-decode-korean.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-korean/euc-kr/euckr-decode-ks_c_5601-1987.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-korean/euc-kr/euckr-decode-ks_c_5601-1989.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-korean/euc-kr/euckr-decode-ksc5601.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-korean/euc-kr/euckr-decode-ksc_5601.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-korean/euc-kr/euckr-decode-windows-949.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-korean/euc-kr/euckr-decode.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-cseuckr.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-csksc56011987.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-errors-han.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-errors-misc.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-iso-ir-149.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-korean.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-ks_c_5601-1987.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-ks_c_5601-1989.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-ksc5601.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-ksc_5601.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-windows-949.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-korean/euc-kr/euckr-encode-form.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-korean/euc-kr/euckr-encode-href-errors-han.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-korean/euc-kr/euckr-encode-href-errors-misc.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-korean/euc-kr/euckr-encode-href.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-tchinese/big5/big5-decode-big5-hkscs.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-tchinese/big5/big5-decode-cn-big5.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-tchinese/big5/big5-decode-csbig5.html [ Timeout ]
 crbug.com/591099 external/wpt/encoding/legacy-mb-tchinese/big5/big5-decode-extra.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-tchinese/big5/big5-decode-x-x-big5.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-tchinese/big5/big5-decode.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-tchinese/big5/big5-encode-form-big5-hkscs.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-tchinese/big5/big5-encode-form-cn-big5.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-tchinese/big5/big5-encode-form-csbig5.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-tchinese/big5/big5-encode-form-errors-extBa.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-tchinese/big5/big5-encode-form-errors-extBb.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-tchinese/big5/big5-encode-form-errors-han.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-tchinese/big5/big5-encode-form-errors-hangul.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-tchinese/big5/big5-encode-form-errors-misc.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-tchinese/big5/big5-encode-form-errors-pua.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-tchinese/big5/big5-encode-form-x-x-big5.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-tchinese/big5/big5-encode-form.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-tchinese/big5/big5-encode-href-errors-han.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-tchinese/big5/big5-encode-href-errors-hangul.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-tchinese/big5/big5-encode-href-errors-misc.html [ Timeout ]
+crbug.com/591099 external/wpt/encoding/legacy-mb-tchinese/big5/big5-encode-href.html [ Timeout ]
 crbug.com/591099 external/wpt/encoding/textdecoder-fatal-single-byte.html [ Timeout ]
 crbug.com/591099 external/wpt/eventsource/eventsource-onmessage-realm.htm [ Crash ]
 crbug.com/591099 external/wpt/fetch/api/request/multi-globals/url-parsing.html [ Crash ]
@@ -10649,6 +10720,7 @@
 crbug.com/591099 fast/forms/image-disconnected-during-parse.html [ Failure ]
 crbug.com/591099 fast/forms/image/002.html [ Crash Failure ]
 crbug.com/591099 fast/forms/image/005.html [ Failure ]
+crbug.com/591099 fast/forms/image/fallback-reattach-crash.html [ Crash ]
 crbug.com/591099 fast/forms/image/image-error-event-modifies-type-crash.html [ Crash Failure ]
 crbug.com/591099 fast/forms/image/image-setrangetext.html [ Crash Failure ]
 crbug.com/591099 fast/forms/image/input-align-image.html [ Failure ]
@@ -14625,7 +14697,7 @@
 crbug.com/591099 http/tests/inspector-protocol/network-data-length.html [ Failure Timeout ]
 crbug.com/591099 http/tests/inspector-protocol/network-fetch-content-with-error-status-code.html [ Failure Timeout ]
 crbug.com/591099 http/tests/inspector-protocol/network/disable-interception-midway.html [ Failure Timeout ]
-crbug.com/591099 http/tests/inspector-protocol/network/interception-auth-cancel.html [ Failure ]
+crbug.com/591099 http/tests/inspector-protocol/network/interception-auth-cancel.html [ Failure Timeout ]
 crbug.com/591099 http/tests/inspector-protocol/network/interception-auth-provide-credentials.html [ Failure Timeout ]
 crbug.com/591099 http/tests/inspector-protocol/network/navigation-interception.html [ Failure ]
 crbug.com/591099 http/tests/inspector-protocol/network/redirect-interception-blocked.html [ Failure ]
@@ -16651,7 +16723,7 @@
 crbug.com/591099 inspector/elements/reveal-whitespace-text-node.html [ Crash ]
 crbug.com/591099 inspector/elements/selected-element-changes-execution-context.html [ Crash ]
 crbug.com/591099 inspector/elements/shadow/breadcrumb-shadow-roots.html [ Crash ]
-crbug.com/591099 inspector/elements/shadow/create-shadow-root.html [ Crash ]
+crbug.com/591099 inspector/elements/shadow/create-shadow-root.html [ Crash Timeout ]
 crbug.com/591099 inspector/elements/shadow/elements-panel-shadow-selection-on-refresh-1.html [ Crash ]
 crbug.com/591099 inspector/elements/shadow/elements-panel-shadow-selection-on-refresh-2.html [ Crash ]
 crbug.com/591099 inspector/elements/shadow/elements-panel-shadow-selection-on-refresh-3.html [ Crash ]
@@ -16778,7 +16850,7 @@
 crbug.com/591099 inspector/elements/styles/undo-after-cancelled-editing.html [ Crash ]
 crbug.com/591099 inspector/elements/styles/undo-change-property.html [ Crash ]
 crbug.com/591099 inspector/elements/styles/undo-property-toggle.html [ Crash ]
-crbug.com/591099 inspector/elements/styles/undo-set-selector-text.html [ Crash ]
+crbug.com/591099 inspector/elements/styles/undo-set-selector-text.html [ Crash Timeout ]
 crbug.com/591099 inspector/elements/styles/up-down-numerics-and-colors.html [ Crash ]
 crbug.com/591099 inspector/elements/styles/updates-during-dom-traversal.html [ Crash ]
 crbug.com/591099 inspector/elements/styles/updates-throttled.html [ Crash ]
@@ -16791,7 +16863,7 @@
 crbug.com/591099 inspector/file-system-project.html [ Failure ]
 crbug.com/591099 inspector/filtered-item-selection-dialog-rendering.html [ Failure ]
 crbug.com/591099 inspector/geolocation-emulation-tests.html [ Failure ]
-crbug.com/591099 inspector/help/release-note-unit.html [ Failure ]
+crbug.com/591099 inspector/help/release-note-unit.html [ Crash Failure ]
 crbug.com/591099 inspector/help/release-note.html [ Crash ]
 crbug.com/591099 inspector/import-open-inspector.html [ Failure ]
 crbug.com/591099 inspector/initial-modules-load.html [ Failure ]
@@ -16934,7 +17006,7 @@
 crbug.com/591099 inspector/sources/debugger-breakpoints/dynamic-scripts-breakpoints.html [ Crash Failure ]
 crbug.com/591099 inspector/sources/debugger-breakpoints/event-listener-breakpoints-after-suspension.html [ Failure ]
 crbug.com/591099 inspector/sources/debugger-breakpoints/event-listener-breakpoints-script-first-stmt.html [ Crash Failure ]
-crbug.com/591099 inspector/sources/debugger-breakpoints/event-listener-breakpoints-xhr.html [ Failure ]
+crbug.com/591099 inspector/sources/debugger-breakpoints/event-listener-breakpoints-xhr.html [ Crash Failure ]
 crbug.com/591099 inspector/sources/debugger-breakpoints/event-listener-breakpoints.html [ Failure ]
 crbug.com/591099 inspector/sources/debugger-breakpoints/nodejs-set-breakpoint.html [ Failure ]
 crbug.com/591099 inspector/sources/debugger-breakpoints/possible-breakpoints.html [ Crash Failure ]
@@ -18629,7 +18701,7 @@
 crbug.com/591099 storage/indexeddb/events.html [ Failure ]
 crbug.com/591099 storage/indexeddb/exception-in-event-aborts.html [ Failure ]
 crbug.com/591099 storage/indexeddb/exceptions.html [ Timeout ]
-crbug.com/591099 storage/indexeddb/factory-cmp.html [ Timeout ]
+crbug.com/591099 storage/indexeddb/factory-cmp.html [ Failure Timeout ]
 crbug.com/591099 storage/indexeddb/factory-deletedatabase.html [ Failure ]
 crbug.com/591099 storage/indexeddb/get-keyrange.html [ Failure ]
 crbug.com/591099 storage/indexeddb/index-basics-workers.html [ Failure ]
@@ -20262,6 +20334,7 @@
 crbug.com/591099 vibration/vibration-exceptions.html [ Failure ]
 crbug.com/591099 vibration/vibration-iframe.html [ Timeout ]
 crbug.com/591099 vibration/vibration-patterns.html [ Failure ]
+crbug.com/591099 virtual/android/fast/rootscroller/set-root-scroller.html [ Failure ]
 crbug.com/591099 virtual/android/fullscreen/anonymous-block-merge-crash.html [ Crash ]
 crbug.com/591099 virtual/android/fullscreen/compositor-touch-hit-rects-fullscreen-video-controls.html [ Failure ]
 crbug.com/591099 virtual/android/fullscreen/exit-full-screen-iframe.html [ Crash Failure ]
@@ -20285,7 +20358,6 @@
 crbug.com/591099 virtual/android/fullscreen/video-controls-timeline.html [ Failure ]
 crbug.com/591099 virtual/android/fullscreen/video-fail-to-enter-full-screen.html [ Failure ]
 crbug.com/591099 virtual/android/media/mediadocument/media-document-with-download-button.html [ Failure ]
-crbug.com/591099 virtual/android/fast/rootscroller/set-root-scroller.html [ Failure ]
 crbug.com/591099 virtual/display_list_2d_canvas/fast/canvas/2d.composite.globalAlpha.fillPath.html [ Crash ]
 crbug.com/591099 virtual/display_list_2d_canvas/fast/canvas/2d.fillText.gradient.html [ Crash ]
 crbug.com/591099 virtual/display_list_2d_canvas/fast/canvas/2d.text.draw.fill.maxWidth.gradient.html [ Crash ]
@@ -21216,6 +21288,7 @@
 crbug.com/591099 virtual/prefer_compositing_to_lcd_text/compositing/overflow/clip-descendents.html [ Failure ]
 crbug.com/591099 virtual/prefer_compositing_to_lcd_text/compositing/overflow/clip-parent-reset.html [ Failure ]
 crbug.com/591099 virtual/prefer_compositing_to_lcd_text/compositing/overflow/clipping-ancestor-with-accelerated-scrolling-ancestor.html [ Failure ]
+crbug.com/591099 virtual/prefer_compositing_to_lcd_text/compositing/overflow/composited-layer-under-border-radius-under-composited-layer.html [ Failure ]
 crbug.com/591099 virtual/prefer_compositing_to_lcd_text/compositing/overflow/composited-nested-sticky-left.html [ Failure ]
 crbug.com/591099 virtual/prefer_compositing_to_lcd_text/compositing/overflow/composited-scrolling-paint-phases.html [ Failure ]
 crbug.com/591099 virtual/prefer_compositing_to_lcd_text/compositing/overflow/content-gains-scrollbars.html [ Failure ]
@@ -21400,7 +21473,7 @@
 crbug.com/591099 virtual/service-worker-navigation-preload-disabled/webexposed/global-interface-listing.html [ Timeout ]
 crbug.com/591099 virtual/service-worker-navigation-preload-disabled/webexposed/nonstable-css-properties.html [ Failure ]
 crbug.com/591099 virtual/service-worker-navigation-preload-disabled/webexposed/permissions-attribute.html [ Failure ]
-crbug.com/591099 virtual/service-worker-script-streaming/external/wpt/service-workers/service-worker/ServiceWorkerGlobalScope/extendable-message-event.https.html [ Crash ]
+crbug.com/591099 virtual/service-worker-script-streaming/external/wpt/service-workers/service-worker/ServiceWorkerGlobalScope/extendable-message-event.https.html [ Crash Timeout ]
 crbug.com/591099 virtual/service-worker-script-streaming/external/wpt/service-workers/service-worker/ServiceWorkerGlobalScope/unregister.https.html [ Crash ]
 crbug.com/591099 virtual/service-worker-script-streaming/external/wpt/service-workers/service-worker/ServiceWorkerGlobalScope/update.https.html [ Crash ]
 crbug.com/591099 virtual/service-worker-script-streaming/external/wpt/service-workers/service-worker/activation.https.html [ Crash ]
@@ -21533,7 +21606,6 @@
 crbug.com/591099 virtual/threaded/animations/3d/state-at-end-event-transform.html [ Failure Pass ]
 crbug.com/591099 virtual/threaded/animations/animation-css-rule-types.html [ Failure ]
 crbug.com/591099 virtual/threaded/animations/animation-events-create.html [ Failure ]
-crbug.com/591099 virtual/threaded/animations/prefixed/animation-inherit-initial-unprefixed.html [ Failure ]
 crbug.com/591099 virtual/threaded/animations/animations-parsing.html [ Timeout ]
 crbug.com/591099 virtual/threaded/animations/animations-responsive-to-color-change.html [ Crash ]
 crbug.com/591099 virtual/threaded/animations/clear-svg-animation-effects.html [ Crash ]
@@ -21634,13 +21706,14 @@
 crbug.com/591099 virtual/threaded/animations/interpolation/webkit-text-stroke-color-interpolation.html [ Crash ]
 crbug.com/591099 virtual/threaded/animations/interpolation/webkit-transform-interpolation.html [ Crash ]
 crbug.com/591099 virtual/threaded/animations/interpolation/webkit-transform-origin-interpolation.html [ Crash ]
-crbug.com/591099 virtual/threaded/animations/prefixed/keyframes-cssom-prefixed-02.html [ Failure ]
-crbug.com/591099 virtual/threaded/animations/prefixed/keyframes-cssom-unprefixed-02.html [ Failure ]
 crbug.com/591099 virtual/threaded/animations/keyframes-rule.html [ Failure ]
 crbug.com/591099 virtual/threaded/animations/lazy-detached-animation-stop.html [ Failure ]
 crbug.com/591099 virtual/threaded/animations/negative-delay-events.html [ Failure ]
 crbug.com/591099 virtual/threaded/animations/play-state-initially-paused-start-event.html [ Failure ]
 crbug.com/591099 virtual/threaded/animations/play-state.html [ Failure ]
+crbug.com/591099 virtual/threaded/animations/prefixed/animation-inherit-initial-unprefixed.html [ Failure ]
+crbug.com/591099 virtual/threaded/animations/prefixed/keyframes-cssom-prefixed-02.html [ Failure ]
+crbug.com/591099 virtual/threaded/animations/prefixed/keyframes-cssom-unprefixed-02.html [ Failure ]
 crbug.com/591099 virtual/threaded/animations/responsive/d-responsive.html [ Crash ]
 crbug.com/591099 virtual/threaded/animations/responsive/line-height-responsive.html [ Pass Timeout ]
 crbug.com/591099 virtual/threaded/animations/rotate-transform-equivalent.html [ Failure Timeout ]
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService b/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService
index 47bee8c..83e8515 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService
@@ -262,6 +262,7 @@
 Bug(none) external/wpt/beacon/headers/header-referrer-strict-origin-when-cross-origin.https.html [ Failure Timeout ]
 Bug(none) external/wpt/beacon/headers/header-referrer-strict-origin.https.html [ Failure Timeout ]
 Bug(none) external/wpt/beacon/headers/header-referrer-unsafe-url.https.html [ Failure Timeout ]
+Bug(none) external/wpt/clear-site-data/navigation.https.html [ Timeout ]
 Bug(none) external/wpt/content-security-policy/child-src/child-src-about-blank-allowed-by-default.sub.html [ Failure Timeout ]
 Bug(none) external/wpt/content-security-policy/child-src/child-src-about-blank-allowed-by-scheme.sub.html [ Failure Timeout ]
 Bug(none) external/wpt/content-security-policy/child-src/child-src-allowed.sub.html [ Failure Timeout ]
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 224f3344..6e6368b 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -827,6 +827,8 @@
 # Mac does not support fractional scale factor.
 crbug.com/567837 [ Mac ] virtual/scalefactor150/fast/hidpi/static [ Skip ]
 
+crbug.com/739409 [ Linux ] fast/imagecapture/MediaStreamTrack-getConstraints.html [ Pass Crash ]
+
 # TODO(oshima): Move the event scaling code to eventSender and remove this.
 crbug.com/567837 virtual/scalefactor200/fast/hidpi/static/mousewheel-scroll-amount.html [ Skip ]
 crbug.com/567837 virtual/scalefactor200/fast/hidpi/static/gesture-scroll-amount.html [ Skip ]
@@ -866,6 +868,7 @@
 # Mark as Failure until the development is done.
 crbug.com/739091 fast/forms/validation-bubble-appearance-edge.html [ Failure ]
 crbug.com/739091 fast/forms/validation-bubble-appearance-iframe.html [ Failure ]
+crbug.com/739091 fast/forms/validation-bubble-appearance-rtl-ui.html [ Failure ]
 
 crbug.com/543110 [ Mac ] fast/text/international/text-shaping-arabic.html [ Failure ]
 
diff --git a/third_party/WebKit/LayoutTests/accessibility/aom-relation-list-properties.html b/third_party/WebKit/LayoutTests/accessibility/aom-relation-list-properties.html
new file mode 100644
index 0000000..5bfb6e0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/accessibility/aom-relation-list-properties.html
@@ -0,0 +1,239 @@
+<!DOCTYPE HTML>
+<script src="../resources/gc.js"></script>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+
+<!--
+
+Accessibility Object Model
+Explainer: https://github.com/WICG/aom/blob/master/explainer.md
+Spec: https://wicg.github.io/aom/spec/
+
+-->
+
+<script>
+test(function(t) {
+    assert_true(internals.runtimeFlags.accessibilityObjectModelEnabled);
+}, "Make sure that Accessibility Object Model is enabled");
+</script>
+
+<input id="input" aria-labeledby="l1 l2">
+<input id="input2" aria-labelledby="l1 l3">
+<label id="l1">L1</label>
+<label id="l2">L2</label>
+<label id="l3">L3</label>
+<label id="l4">L4</label>
+
+<script>
+test(function(t) {
+    var input = document.getElementById("input");
+    var axInput = accessibilityController.accessibleElementById("input");
+    assert_equals(axInput.name, "L1 L2");
+
+    var input2 = document.getElementById("input2");
+    var axInput2 = accessibilityController.accessibleElementById("input2");
+    assert_equals(axInput2.name, "L1 L3");
+
+    var l3 = document.getElementById("l3");
+    var l4 = document.getElementById("l4");
+
+    input.accessibleNode.labeledBy = new AccessibleNodeList();
+    input.accessibleNode.labeledBy.add(l3.accessibleNode);
+
+    input2.accessibleNode.labeledBy = new AccessibleNodeList();
+    input2.accessibleNode.labeledBy.add(l3.accessibleNode);
+    input2.accessibleNode.labeledBy.add(l4.accessibleNode);
+
+    assert_equals(input2.accessibleNode.labeledBy.length, 2);
+    assert_equals(input2.accessibleNode.labeledBy[0], l3.accessibleNode);
+    assert_equals(input2.accessibleNode.labeledBy[1], l4.accessibleNode);
+    assert_equals(input2.accessibleNode.labeledBy[2], undefined);
+
+    assert_equals(axInput.name, "L3");
+    assert_equals(axInput2.name, "L3 L4");
+
+    input.accessibleNode.labeledBy = null;
+    assert_equals(axInput.name, "L1 L2");
+
+    input2.accessibleNode.labeledBy.remove(l3.accessibleNode);
+    assert_equals(axInput2.name, "L4");
+
+    input2.accessibleNode.labeledBy[0] = l2.accessibleNode;
+    assert_equals(axInput2.name, "L2");
+    input2.accessibleNode.labeledBy[2] = l4.accessibleNode;
+    assert_equals(axInput2.name, "L2 L4");
+}, "AccessibleNode.labeledBy");
+</script>
+
+<input id="input3" aria-describedby="l1 l2">
+
+<script>
+test(function(t) {
+    var input = document.getElementById("input3");
+    var axInput = accessibilityController.accessibleElementById("input3");
+    assert_equals(axInput.description, "L1 L2");
+
+    var l3 = document.getElementById("l3");
+    var l4 = document.getElementById("l4");
+
+    input.accessibleNode.describedBy = new AccessibleNodeList();
+    input.accessibleNode.describedBy.add(l3.accessibleNode);
+
+    input.accessibleNode.describedBy = new AccessibleNodeList();
+    input.accessibleNode.describedBy.add(l3.accessibleNode);
+    input.accessibleNode.describedBy.add(l4.accessibleNode);
+
+    assert_equals(input.accessibleNode.describedBy.length, 2);
+    assert_equals(input.accessibleNode.describedBy[0], l3.accessibleNode);
+    assert_equals(input.accessibleNode.describedBy[1], l4.accessibleNode);
+    assert_equals(input.accessibleNode.describedBy[2], undefined);
+
+    assert_equals(axInput.description, "L3 L4");
+
+    input.accessibleNode.describedBy.remove(l3.accessibleNode);
+    assert_equals(axInput.description, "L4");
+
+    input.accessibleNode.describedBy[0] = l2.accessibleNode;
+    assert_equals(axInput.description, "L2");
+    input.accessibleNode.describedBy[2] = l4.accessibleNode;
+    assert_equals(axInput.description, "L2 L4");
+
+    input.accessibleNode.describedBy = null;
+    assert_equals(axInput.description, "L1 L2");
+}, "AccessibleNode.describedBy");
+</script>
+
+<ul id="tablist_1" role="tablist">
+<li id="tab_1" role="tab" aria-controls="panel_1 panel_2"></li>
+</ul>
+
+<div id="panel_1" role="tabpanel">Panel 1</div>
+<div id="panel_2" role="tabpanel">Panel 2</div>
+<div id="panel_3" role="tabpanel">Panel 3</div>
+<div id="panel_4" role="tabpanel">Panel 4</div>
+
+<p id="description"></p>
+<div id="console"></div>
+
+<script>
+test(function(t) {
+    var axTab1 = accessibilityController.accessibleElementById("tablist_1").childAtIndex(0);
+    var axPanel1 = accessibilityController.accessibleElementById("panel_1");
+    var axPanel2 = accessibilityController.accessibleElementById("panel_2");
+    var axPanel3 = accessibilityController.accessibleElementById("panel_3");
+    var axPanel4 = accessibilityController.accessibleElementById("panel_4");
+
+    assert_true(axTab1.ariaControlsElementAtIndex(0).isEqual(axPanel1));
+    assert_true(axTab1.ariaControlsElementAtIndex(1).isEqual(axPanel2));
+
+    var tab1 = document.getElementById("tablist_1").firstElementChild;
+    var panel3 = document.getElementById("panel_3");
+    var panel4 = document.getElementById("panel_4");
+    tab1.accessibleNode.controls = new AccessibleNodeList();
+    tab1.accessibleNode.controls[0] = panel3.accessibleNode;
+
+    assert_true(axTab1.ariaControlsElementAtIndex(0).isEqual(axPanel3));
+    assert_equals(axTab1.ariaControlsElementAtIndex(1), undefined);
+
+    tab1.accessibleNode.controls[1] = panel4.accessibleNode;
+
+    assert_true(axTab1.ariaControlsElementAtIndex(0).isEqual(axPanel3));
+    assert_true(axTab1.ariaControlsElementAtIndex(1).isEqual(axPanel4));
+
+    tab1.accessibleNode.controls = null;
+
+    assert_true(axTab1.ariaControlsElementAtIndex(0).isEqual(axPanel1));
+    assert_true(axTab1.ariaControlsElementAtIndex(1).isEqual(axPanel2));
+}, "AccessibleNode.controls");
+</script>
+
+<h1 id="item1" aria-flowto="item2 item3"></h1>
+
+<div id="item2">Content 2</div>
+<div id="item3">Content 3</div>
+<div id="item4">Content 4</div>
+
+<p id="description"></p>
+<div id="console"></div>
+
+<script>
+test(function(t) {
+    var axItem1 = accessibilityController.accessibleElementById("item1");
+    var axItem2 = accessibilityController.accessibleElementById("item2");
+    var axItem3 = accessibilityController.accessibleElementById("item3");
+    var axItem4 = accessibilityController.accessibleElementById("item4");
+
+    assert_true(axItem1.ariaFlowToElementAtIndex(0).isEqual(axItem2));
+    assert_true(axItem1.ariaFlowToElementAtIndex(1).isEqual(axItem3));
+
+    var item1 = document.getElementById("item1");
+    var item2 = document.getElementById("item2");
+    var item3 = document.getElementById("item3");
+    var item4 = document.getElementById("item4");
+
+    item1.accessibleNode.flowTo = new AccessibleNodeList();
+    item1.accessibleNode.flowTo.add(item4.accessibleNode);
+
+    assert_true(axItem1.ariaFlowToElementAtIndex(0).isEqual(axItem4));
+    assert_equals(axItem1.ariaFlowToElementAtIndex(1), undefined);
+
+    item1.accessibleNode.flowTo.add(item2.accessibleNode);
+
+    assert_true(axItem1.ariaFlowToElementAtIndex(1).isEqual(axItem2));
+
+    item1.accessibleNode.flowTo = null;
+
+    assert_true(axItem1.ariaFlowToElementAtIndex(0).isEqual(axItem2));
+    assert_true(axItem1.ariaFlowToElementAtIndex(1).isEqual(axItem3));
+}, "AccessibleNode.flowTo");
+</script>
+
+<div class="container">
+    <ul id="list1" role="listbox" aria-owns="listitem3">
+        <li role="option">One</li>
+        <li role="option">Two</li>
+    </ul>
+    <ul role="listbox" id="list2">
+        <li role="option" id="listitem3">Three</li>
+        <li role="option" id="listitem4">Four</li>
+    </ul>
+</div>
+
+<script>
+test(function(t)
+{
+    var axList1 = accessibilityController.accessibleElementById("list1");
+    assert_equals(axList1.role, "AXRole: AXListBox");
+    assert_equals(axList1.childrenCount, 3);
+
+    var axListitem = axList1.childAtIndex(0);
+    assert_equals(axList1.childAtIndex(0).name, "One");
+    assert_equals(axList1.childAtIndex(1).name, "Two");
+    assert_equals(axList1.childAtIndex(2).name, "Three");
+
+    var axList2 = accessibilityController.accessibleElementById("list2");
+    assert_equals(axList2.role, "AXRole: AXListBox");
+    assert_equals(axList2.childrenCount, 1);
+    assert_equals(axList2.childAtIndex(0).name, "Four");
+
+    var list1 = document.getElementById("list1");
+    var item4 = document.getElementById("item4");
+
+    list1.accessibleNode.owns = new AccessibleNodeList();
+    assert_equals(axList1.childrenCount, 2);
+/**
+
+
+    list1.accessibleNode.owns[0] = item4.accessibleNode;
+    assert_equals(axList1.childrenCount, 3);
+    assert_equals(axList1.childAtIndex(0).name, "One");
+    assert_equals(axList1.childAtIndex(1).name, "Two");
+assert_equals(axList1.childAtIndex(2).name, "Four");
+**/
+}, "AccessibleNode.owns");
+</script>
+
+<script>
+if (window.testRunner)
+    document.body.className = "hideAllContainers";
+</script>
diff --git a/third_party/WebKit/LayoutTests/accessibility/input-mixed.html b/third_party/WebKit/LayoutTests/accessibility/input-mixed.html
index 68e2f77..b62b689 100644
--- a/third_party/WebKit/LayoutTests/accessibility/input-mixed.html
+++ b/third_party/WebKit/LayoutTests/accessibility/input-mixed.html
@@ -54,9 +54,9 @@
 
   test(function(t) {
     var ax = axElementById("element3");
-    assert_equals(ax.checked, "mixed");
+    assert_equals(ax.checked, "false");
   }, "A native radio that in a group with nothing checked" +
-      " must have the mixed state");
+      " must appear unchecked, not mixed");
 
   test(function(t) {
     var ax = axElementById("element4");
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/respect-clip-for-non-composited-scrollers-when-prefering-compositing-over-lcd-text-expected.html b/third_party/WebKit/LayoutTests/compositing/overflow/respect-clip-for-non-composited-scrollers-when-prefering-compositing-over-lcd-text-expected.html
deleted file mode 100644
index b4f933b5..0000000
--- a/third_party/WebKit/LayoutTests/compositing/overflow/respect-clip-for-non-composited-scrollers-when-prefering-compositing-over-lcd-text-expected.html
+++ /dev/null
@@ -1,33 +0,0 @@
-<!DOCTYPE HTML>
-<style>
-#scroller {
-  width: 100px;
-  height: 100px;
-  border: 1px solid black;
-  overflow: scroll;
-  border-radius: 1px;
-}
-
-#scrolled {
-  width: 60px;
-  height: 300px;
-  border: 1px solid black;
-  background: papayawhip;
-}
-
-#composited {
-  width: 10px;
-  height: 10px;
-  will-change: transform;
-  background: green;
-  border: 2px solid orange;
-}
-</style>
-<div id="scroller">
-  <div id="scrolled"></div>
-  <div id="composited"></div>
-</div>
-<script>
-if (window.internals)
-  internals.settings.setPreferCompositingToLCDTextEnabled(false);
-</script>
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/respect-clip-for-non-composited-scrollers-when-prefering-compositing-over-lcd-text-expected.png b/third_party/WebKit/LayoutTests/compositing/overflow/respect-clip-for-non-composited-scrollers-when-prefering-compositing-over-lcd-text-expected.png
new file mode 100644
index 0000000..1626ec0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/compositing/overflow/respect-clip-for-non-composited-scrollers-when-prefering-compositing-over-lcd-text-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/respect-clip-for-non-composited-scrollers-when-prefering-compositing-over-lcd-text-expected.txt b/third_party/WebKit/LayoutTests/compositing/overflow/respect-clip-for-non-composited-scrollers-when-prefering-compositing-over-lcd-text-expected.txt
new file mode 100644
index 0000000..591a94a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/compositing/overflow/respect-clip-for-non-composited-scrollers-when-prefering-compositing-over-lcd-text-expected.txt
@@ -0,0 +1,10 @@
+layer at (0,0) size 800x600
+  LayoutView at (0,0) size 800x600
+layer at (0,0) size 800x118
+  LayoutBlockFlow {HTML} at (0,0) size 800x118
+    LayoutBlockFlow {BODY} at (8,8) size 784x102
+layer at (8,8) size 102x102 clip at (9,9) size 85x85 scrollHeight 316
+  LayoutBlockFlow {DIV} at (0,0) size 102x102 [border: (1px solid #000000)]
+    LayoutBlockFlow {DIV} at (1,1) size 62x302 [bgcolor=#FFEFD5] [border: (1px solid #000000)]
+layer at (9,311) size 14x14 backgroundClip at (9,9) size 85x85 clip at (9,9) size 85x85
+  LayoutBlockFlow {DIV} at (1,303) size 14x14 [bgcolor=#008000] [border: (2px solid #FFA500)]
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/scroller-with-border-radius-expected.txt b/third_party/WebKit/LayoutTests/compositing/overflow/scroller-with-border-radius-expected.txt
index decd541..e5ce051 100644
--- a/third_party/WebKit/LayoutTests/compositing/overflow/scroller-with-border-radius-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/overflow/scroller-with-border-radius-expected.txt
@@ -1,3 +1,3 @@
 No border radius (should be using composited scrolling): Pass.
-Has border radius (should not be using composited scrolling): Pass.
+Has border radius (should also be using composited scrolling): Pass.
 
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/scroller-with-border-radius.html b/third_party/WebKit/LayoutTests/compositing/overflow/scroller-with-border-radius.html
index 5729ca08..de4fa85c 100644
--- a/third_party/WebKit/LayoutTests/compositing/overflow/scroller-with-border-radius.html
+++ b/third_party/WebKit/LayoutTests/compositing/overflow/scroller-with-border-radius.html
@@ -49,11 +49,11 @@
     document.getElementById("scroller").style.borderRadius = '5px';
     requestAnimationFrame(function() {
         if (window.internals) {
-            result += "Has border radius (should not be using composited scrolling): ";
+            result += "Has border radius (should also be using composited scrolling): ";
             if (!isUsingCompositedScrolling(JSON.parse(window.internals.layerTreeAsText(document))))
-                result += "Pass.\n";
+                result += "Fail.\n";
             else
-                result += "Fail.\n"
+                result += "Pass.\n"
         }
 
         if (window.testRunner) {
diff --git a/third_party/WebKit/LayoutTests/external/wpt/performance-timeline/resources/worker-with-performance-observer.js b/third_party/WebKit/LayoutTests/external/wpt/performance-timeline/resources/worker-with-performance-observer.js
new file mode 100644
index 0000000..a72fe81
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/performance-timeline/resources/worker-with-performance-observer.js
@@ -0,0 +1,6 @@
+try {
+  new PerformanceObserver(() => true);
+  postMessage("SUCCESS");
+} catch (ex) {
+  postMessage("FAILURE");
+}
diff --git a/third_party/WebKit/LayoutTests/external/wpt/performance-timeline/worker-with-performance-observer-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/performance-timeline/worker-with-performance-observer-expected.txt
new file mode 100644
index 0000000..b319839c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/performance-timeline/worker-with-performance-observer-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Worker: Test Performance Observer inside a worker. assert_equals: expected "SUCCESS" but got "FAILURE"
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/performance-timeline/worker-with-performance-observer.html b/third_party/WebKit/LayoutTests/external/wpt/performance-timeline/worker-with-performance-observer.html
new file mode 100644
index 0000000..fc92bc9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/performance-timeline/worker-with-performance-observer.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script>
+async_test(function(t) {
+  const worker = new Worker("resources/worker-with-performance-observer.js");
+  worker.onmessage = function(event) {
+    t.step(() => assert_equals(event.data, 'SUCCESS'));
+    t.done();
+  }
+}, 'Worker: Test Performance Observer inside a worker.');
+</script>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/chooser_service.mojom.js b/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/chooser_service.mojom.js
index f125fac..30bba4b 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/chooser_service.mojom.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/chooser_service.mojom.js
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+
 'use strict';
 
 (function() {
@@ -18,21 +19,19 @@
   var associatedBindings = mojo;
   var codec = mojo.internal;
   var validator = mojo.internal;
+
+  var exports = mojo.internal.exposeNamespace('device.mojom');
   var device$ =
       mojo.internal.exposeNamespace('device.mojom');
   if (mojo.config.autoLoadMojomDeps) {
     mojo.internal.loadMojomIfNecessary(
-        'device/usb/public/interfaces/device.mojom',
-        new URL('device.mojom.js',
-                document.currentScript.src).href);
+        'device/usb/public/interfaces/device.mojom', 'device.mojom.js');
   }
   var device_manager$ =
       mojo.internal.exposeNamespace('device.mojom');
   if (mojo.config.autoLoadMojomDeps) {
     mojo.internal.loadMojomIfNecessary(
-        'device/usb/public/interfaces/device_manager.mojom',
-        new URL('device_manager.mojom.js',
-                document.currentScript.src).href);
+        'device/usb/public/interfaces/device_manager.mojom', 'device_manager.mojom.js');
   }
 
 
@@ -274,7 +273,6 @@
   };
   UsbChooserServiceStub.prototype.validator = validateUsbChooserServiceRequest;
   UsbChooserServiceProxy.prototype.validator = validateUsbChooserServiceResponse;
-  var exports = mojo.internal.exposeNamespace("device.mojom");
   exports.UsbChooserService = UsbChooserService;
   exports.UsbChooserServicePtr = UsbChooserServicePtr;
   exports.UsbChooserServiceAssociatedPtr = UsbChooserServiceAssociatedPtr;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/device.mojom.js b/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/device.mojom.js
index 5e618ec..ce5660cd 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/device.mojom.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/device.mojom.js
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+
 'use strict';
 
 (function() {
@@ -19,6 +20,14 @@
   var codec = mojo.internal;
   var validator = mojo.internal;
 
+  var exports = mojo.internal.exposeNamespace('device.mojom');
+  var string16$ =
+      mojo.internal.exposeNamespace('mojo.common.mojom');
+  if (mojo.config.autoLoadMojomDeps) {
+    mojo.internal.loadMojomIfNecessary(
+        'mojo/common/string16.mojom', '../../../../mojo/common/string16.mojom.js');
+  }
+
 
   var UsbOpenDeviceError = {};
   UsbOpenDeviceError.OK = 0;
@@ -290,7 +299,7 @@
 
     
     // validate UsbAlternateInterfaceInfo.interfaceName
-    err = messageValidator.validateStringPointer(offset + codec.kStructHeaderSize + 8, true)
+    err = messageValidator.validateStructPointer(offset + codec.kStructHeaderSize + 8, string16$.String16, true);
     if (err !== validator.validationError.NONE)
         return err;
 
@@ -319,7 +328,7 @@
     decoder.skip(1);
     decoder.skip(1);
     decoder.skip(1);
-    val.interfaceName = decoder.decodeStruct(codec.NullableString);
+    val.interfaceName = decoder.decodeStructPointer(string16$.String16);
     val.endpoints = decoder.decodeArrayPointer(new codec.PointerTo(UsbEndpointInfo));
     return val;
   };
@@ -336,7 +345,7 @@
     encoder.skip(1);
     encoder.skip(1);
     encoder.skip(1);
-    encoder.encodeStruct(codec.NullableString, val.interfaceName);
+    encoder.encodeStructPointer(string16$.String16, val.interfaceName);
     encoder.encodeArrayPointer(new codec.PointerTo(UsbEndpointInfo), val.endpoints);
   };
   function UsbInterfaceInfo(values) {
@@ -448,7 +457,7 @@
 
     
     // validate UsbConfigurationInfo.configurationName
-    err = messageValidator.validateStringPointer(offset + codec.kStructHeaderSize + 8, true)
+    err = messageValidator.validateStructPointer(offset + codec.kStructHeaderSize + 8, string16$.String16, true);
     if (err !== validator.validationError.NONE)
         return err;
 
@@ -477,7 +486,7 @@
     decoder.skip(1);
     decoder.skip(1);
     decoder.skip(1);
-    val.configurationName = decoder.decodeStruct(codec.NullableString);
+    val.configurationName = decoder.decodeStructPointer(string16$.String16);
     val.interfaces = decoder.decodeArrayPointer(new codec.PointerTo(UsbInterfaceInfo));
     return val;
   };
@@ -494,7 +503,7 @@
     encoder.skip(1);
     encoder.skip(1);
     encoder.skip(1);
-    encoder.encodeStruct(codec.NullableString, val.configurationName);
+    encoder.encodeStructPointer(string16$.String16, val.configurationName);
     encoder.encodeArrayPointer(new codec.PointerTo(UsbInterfaceInfo), val.interfaces);
   };
   function UsbDeviceInfo(values) {
@@ -563,21 +572,21 @@
 
     
     // validate UsbDeviceInfo.manufacturerName
-    err = messageValidator.validateStringPointer(offset + codec.kStructHeaderSize + 24, true)
+    err = messageValidator.validateStructPointer(offset + codec.kStructHeaderSize + 24, string16$.String16, true);
     if (err !== validator.validationError.NONE)
         return err;
 
 
     
     // validate UsbDeviceInfo.productName
-    err = messageValidator.validateStringPointer(offset + codec.kStructHeaderSize + 32, true)
+    err = messageValidator.validateStructPointer(offset + codec.kStructHeaderSize + 32, string16$.String16, true);
     if (err !== validator.validationError.NONE)
         return err;
 
 
     
     // validate UsbDeviceInfo.serialNumber
-    err = messageValidator.validateStringPointer(offset + codec.kStructHeaderSize + 40, true)
+    err = messageValidator.validateStructPointer(offset + codec.kStructHeaderSize + 40, string16$.String16, true);
     if (err !== validator.validationError.NONE)
         return err;
 
@@ -614,9 +623,9 @@
     val.activeConfiguration = decoder.decodeStruct(codec.Uint8);
     decoder.skip(1);
     decoder.skip(1);
-    val.manufacturerName = decoder.decodeStruct(codec.NullableString);
-    val.productName = decoder.decodeStruct(codec.NullableString);
-    val.serialNumber = decoder.decodeStruct(codec.NullableString);
+    val.manufacturerName = decoder.decodeStructPointer(string16$.String16);
+    val.productName = decoder.decodeStructPointer(string16$.String16);
+    val.serialNumber = decoder.decodeStructPointer(string16$.String16);
     val.configurations = decoder.decodeArrayPointer(new codec.PointerTo(UsbConfigurationInfo));
     return val;
   };
@@ -640,9 +649,9 @@
     encoder.encodeStruct(codec.Uint8, val.activeConfiguration);
     encoder.skip(1);
     encoder.skip(1);
-    encoder.encodeStruct(codec.NullableString, val.manufacturerName);
-    encoder.encodeStruct(codec.NullableString, val.productName);
-    encoder.encodeStruct(codec.NullableString, val.serialNumber);
+    encoder.encodeStructPointer(string16$.String16, val.manufacturerName);
+    encoder.encodeStructPointer(string16$.String16, val.productName);
+    encoder.encodeStructPointer(string16$.String16, val.serialNumber);
     encoder.encodeArrayPointer(new codec.PointerTo(UsbConfigurationInfo), val.configurations);
   };
   function UsbControlTransferParams(values) {
@@ -3432,7 +3441,6 @@
   };
   UsbDeviceStub.prototype.validator = validateUsbDeviceRequest;
   UsbDeviceProxy.prototype.validator = validateUsbDeviceResponse;
-  var exports = mojo.internal.exposeNamespace("device.mojom");
   exports.UsbOpenDeviceError = UsbOpenDeviceError;
   exports.UsbTransferDirection = UsbTransferDirection;
   exports.UsbControlTransferType = UsbControlTransferType;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/device_manager.mojom.js b/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/device_manager.mojom.js
index 5a03393..99ebd85 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/device_manager.mojom.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/device_manager.mojom.js
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+
 'use strict';
 
 (function() {
@@ -18,13 +19,19 @@
   var associatedBindings = mojo;
   var codec = mojo.internal;
   var validator = mojo.internal;
+
+  var exports = mojo.internal.exposeNamespace('device.mojom');
   var device$ =
       mojo.internal.exposeNamespace('device.mojom');
   if (mojo.config.autoLoadMojomDeps) {
     mojo.internal.loadMojomIfNecessary(
-        'device/usb/public/interfaces/device.mojom',
-        new URL('device.mojom.js',
-                document.currentScript.src).href);
+        'device/usb/public/interfaces/device.mojom', 'device.mojom.js');
+  }
+  var string16$ =
+      mojo.internal.exposeNamespace('mojo.common.mojom');
+  if (mojo.config.autoLoadMojomDeps) {
+    mojo.internal.loadMojomIfNecessary(
+        'mojo/common/string16.mojom', '../../../../mojo/common/string16.mojom.js');
   }
 
 
@@ -81,7 +88,7 @@
 
     
     // validate UsbDeviceFilter.serialNumber
-    err = messageValidator.validateStringPointer(offset + codec.kStructHeaderSize + 8, true)
+    err = messageValidator.validateStructPointer(offset + codec.kStructHeaderSize + 8, string16$.String16, true);
     if (err !== validator.validationError.NONE)
         return err;
 
@@ -106,7 +113,7 @@
     val.productId = decoder.decodeStruct(codec.Uint16);
     val.subclassCode = decoder.decodeStruct(codec.Uint8);
     val.protocolCode = decoder.decodeStruct(codec.Uint8);
-    val.serialNumber = decoder.decodeStruct(codec.NullableString);
+    val.serialNumber = decoder.decodeStructPointer(string16$.String16);
     return val;
   };
 
@@ -126,7 +133,7 @@
     encoder.encodeStruct(codec.Uint16, val.productId);
     encoder.encodeStruct(codec.Uint8, val.subclassCode);
     encoder.encodeStruct(codec.Uint8, val.protocolCode);
-    encoder.encodeStruct(codec.NullableString, val.serialNumber);
+    encoder.encodeStructPointer(string16$.String16, val.serialNumber);
   };
   function UsbEnumerationOptions(values) {
     this.initDefaults_();
@@ -838,7 +845,6 @@
   };
   UsbDeviceManagerClientStub.prototype.validator = validateUsbDeviceManagerClientRequest;
   UsbDeviceManagerClientProxy.prototype.validator = null;
-  var exports = mojo.internal.exposeNamespace("device.mojom");
   exports.UsbDeviceFilter = UsbDeviceFilter;
   exports.UsbEnumerationOptions = UsbEnumerationOptions;
   exports.UsbDeviceManager = UsbDeviceManager;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/mojo_bindings.js b/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/mojo_bindings.js
index af8d859..67d6a88 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/mojo_bindings.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/mojo_bindings.js
@@ -34,18 +34,25 @@
   // you merge bar.mojom.js and foo.mojom.js into a single file.
   //
   // Performance tip: Avoid loading the same mojom.js file multiple times.
-  // Assume that |autoLoadMojomDeps| is set to true:
-  // <!-- No duplicate loading; recommended. -->
+  // Assume that |autoLoadMojomDeps| is set to true,
+  //
+  // <!--
+  // (This comment tag is necessary on IOS to avoid interpreting the closing
+  // script tags in the example.)
+  //
+  // No duplicate loading; recommended:
   // <script src="http://example.org/scripts/b/c/foo.mojom.js"></script>
   //
-  // <!-- No duplicate loading, although unnecessary. -->
+  // No duplicate loading, although unnecessary:
   // <script src="http://example.org/scripts/b/d/bar.mojom.js"></script>
   // <script src="http://example.org/scripts/b/c/foo.mojom.js"></script>
   //
-  // <!-- Load bar.mojom.js twice; should be avoided. -->
+  // Load bar.mojom.js twice; should be avoided:
   // <script src="http://example.org/scripts/b/c/foo.mojom.js"></script>
   // <script src="http://example.org/scripts/b/d/bar.mojom.js"></script>
-  autoLoadMojomDeps: false
+  //
+  // -->
+  autoLoadMojomDeps: true
 };
 
 (function() {
@@ -92,14 +99,22 @@
     mojomRegistry.set(id, LoadState.LOADED);
   }
 
-  function loadMojomIfNecessary(id, url) {
+  function loadMojomIfNecessary(id, relativePath) {
     if (mojomRegistry.has(id)) {
       return;
     }
 
+    if (internal.global.document === undefined) {
+      throw new Error(
+          'Mojom dependency autoloading is not implemented in workers. ' +
+          'Please see config variable mojo.config.autoLoadMojomDeps for more ' +
+          'details.');
+    }
+
     markMojomPendingLoad(id);
+    var url = new URL(relativePath, document.currentScript.src).href;
     internal.global.document.write('<script type="text/javascript" src="' +
-                                   url + '"></script>');
+                                   url + '"><' + '/script>');
   }
 
   internal.exposeNamespace = exposeNamespace;
@@ -3957,6 +3972,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+
 'use strict';
 
 (function() {
@@ -3974,6 +3990,8 @@
   var codec = mojo.internal;
   var validator = mojo.internal;
 
+  var exports = mojo.internal.exposeNamespace('mojo.interfaceControl2');
+
 
   var kRunMessageId = 0xFFFFFFFF;
   var kRunOrClosePipeMessageId = 0xFFFFFFFE;
@@ -4750,7 +4768,6 @@
     };
   
   RunOrClosePipeInput.encodedSize = 16;
-  var exports = mojo.internal.exposeNamespace("mojo.interfaceControl2");
   exports.kRunMessageId = kRunMessageId;
   exports.kRunOrClosePipeMessageId = kRunOrClosePipeMessageId;
   exports.RunMessageParams = RunMessageParams;
@@ -4767,6 +4784,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+
 'use strict';
 
 (function() {
@@ -4784,6 +4802,8 @@
   var codec = mojo.internal;
   var validator = mojo.internal;
 
+  var exports = mojo.internal.exposeNamespace('mojo.pipeControl2');
+
 
   var kRunOrClosePipeMessageId = 0xFFFFFFFE;
 
@@ -5101,10 +5121,9 @@
     };
   
   RunOrClosePipeInput.encodedSize = 16;
-  var exports = mojo.internal.exposeNamespace("mojo.pipeControl2");
   exports.kRunOrClosePipeMessageId = kRunOrClosePipeMessageId;
   exports.RunOrClosePipeMessageParams = RunOrClosePipeMessageParams;
   exports.DisconnectReason = DisconnectReason;
   exports.PeerAssociatedEndpointClosedEvent = PeerAssociatedEndpointClosedEvent;
   exports.RunOrClosePipeInput = RunOrClosePipeInput;
-})();
+})();
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/string16.mojom.js b/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/string16.mojom.js
new file mode 100644
index 0000000..058956b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/string16.mojom.js
@@ -0,0 +1,159 @@
+// 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.
+
+
+'use strict';
+if ((typeof mojo !== 'undefined')  && mojo.internal && mojo.config) {
+
+(function() {
+  var mojomId = 'mojo/common/string16.mojom';
+  if (mojo.internal.isMojomLoaded(mojomId)) {
+    console.warn('The following mojom is loaded multiple times: ' + mojomId);
+    return;
+  }
+  mojo.internal.markMojomLoaded(mojomId);
+
+  // TODO(yzshen): Define these aliases to minimize the differences between the
+  // old/new modes. Remove them when the old mode goes away.
+  var bindings = mojo;
+  var associatedBindings = mojo;
+  var codec = mojo.internal;
+  var validator = mojo.internal;
+
+  var exports = mojo.internal.exposeNamespace('mojo.common.mojom');
+
+
+
+  function String16(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  String16.prototype.initDefaults_ = function() {
+    this.data = null;
+  };
+  String16.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  String16.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 16}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    
+    // validate String16.data
+    err = messageValidator.validateArrayPointer(offset + codec.kStructHeaderSize + 0, 2, codec.Uint16, false, [0], 0);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  String16.encodedSize = codec.kStructHeaderSize + 8;
+
+  String16.decode = function(decoder) {
+    var packed;
+    var val = new String16();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.data = decoder.decodeArrayPointer(codec.Uint16);
+    return val;
+  };
+
+  String16.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(String16.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeArrayPointer(codec.Uint16, val.data);
+  };
+  exports.String16 = String16;
+})();
+}
+
+if ((typeof mojo === 'undefined') || !mojo.internal || !mojo.config) {
+
+define("mojo/common/string16.mojom", [
+    "mojo/public/js/associated_bindings",
+    "mojo/public/js/bindings",
+    "mojo/public/js/codec",
+    "mojo/public/js/core",
+    "mojo/public/js/validator",
+], function(associatedBindings, bindings, codec, core, validator) {
+  var exports = {};
+
+  function String16(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  String16.prototype.initDefaults_ = function() {
+    this.data = null;
+  };
+  String16.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  String16.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 16}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    
+    // validate String16.data
+    err = messageValidator.validateArrayPointer(offset + codec.kStructHeaderSize + 0, 2, codec.Uint16, false, [0], 0);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  String16.encodedSize = codec.kStructHeaderSize + 8;
+
+  String16.decode = function(decoder) {
+    var packed;
+    var val = new String16();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.data = decoder.decodeArrayPointer(codec.Uint16);
+    return val;
+  };
+
+  String16.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(String16.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeArrayPointer(codec.Uint16, val.data);
+  };
+  exports.String16 = String16;
+
+  return exports;
+});
+}
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/webusb-test.js b/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/webusb-test.js
index 97eac81..d700538b 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/webusb-test.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/webusb-test.js
@@ -19,6 +19,21 @@
   chooserCrossFrameProxy: null,
 };
 
+// Converts an ECMAScript String object to an instance of
+// mojo.common.mojom.String16.
+function mojoString16ToString(string16) {
+  return String.fromCharCode.apply(null, string16.data);
+}
+
+// Converts an instance of mojo.common.mojom.String16 to an ECMAScript String.
+function stringToMojoString16(string) {
+  let array = new Array(string.length);
+  for (var i = 0; i < string.length; ++i) {
+    array[i] = string.charCodeAt(i);
+  }
+  return { data: array }
+}
+
 function fakeDeviceInitToDeviceInfo(guid, init) {
   let deviceInfo = {
     guid: guid + "",
@@ -33,16 +48,16 @@
     deviceVersionMajor: init.deviceVersionMajor,
     deviceVersionMinor: init.deviceVersionMinor,
     deviceVersionSubminor: init.deviceVersionSubminor,
-    manufacturerName: init.manufacturerName,
-    productName: init.productName,
-    serialNumber: init.serialNumber,
+    manufacturerName: stringToMojoString16(init.manufacturerName),
+    productName: stringToMojoString16(init.productName),
+    serialNumber: stringToMojoString16(init.serialNumber),
     activeConfiguration: init.activeConfigurationValue,
     configurations: []
   };
   init.configurations.forEach(config => {
     var configInfo = {
       configurationValue: config.configurationValue,
-      configurationName: config.configurationName,
+      configurationName: stringToMojoString16(config.configurationName),
       interfaces: []
     };
     config.interfaces.forEach(iface => {
@@ -56,7 +71,7 @@
           classCode: alternate.interfaceClass,
           subclassCode: alternate.interfaceSubclass,
           protocolCode: alternate.interfaceProtocol,
-          interfaceName: alternate.interfaceName,
+          interfaceName: stringToMojoString16(alternate.interfaceName),
           endpoints: []
         };
         alternate.endpoints.forEach(endpoint => {
@@ -115,7 +130,7 @@
   if (input.hasProtocolCode)
     output.protocolCode = input.protocolCode;
   if (input.serialNumber)
-    output.serialNumber = input.serialNumber;
+    output.serialNumber = mojoString16ToString(input.serialNumber);
   return output;
 }
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webusb/resources/usb-helpers.js b/third_party/WebKit/LayoutTests/external/wpt/webusb/resources/usb-helpers.js
index 452c04f..989e8f1f 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webusb/resources/usb-helpers.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/webusb/resources/usb-helpers.js
@@ -18,6 +18,7 @@
   let chain = Promise.resolve();
   [
     '/resources/chromium/mojo_bindings.js',
+    '/resources/chromium/string16.mojom.js',
     '/resources/chromium/device.mojom.js',
     '/resources/chromium/device_manager.mojom.js',
     '/resources/chromium/chooser_service.mojom.js',
diff --git a/third_party/WebKit/LayoutTests/fast/forms/validation-bubble-appearance-edge-expected.png b/third_party/WebKit/LayoutTests/fast/forms/validation-bubble-appearance-edge-expected.png
index ec414a90..b1251809 100644
--- a/third_party/WebKit/LayoutTests/fast/forms/validation-bubble-appearance-edge-expected.png
+++ b/third_party/WebKit/LayoutTests/fast/forms/validation-bubble-appearance-edge-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/fast/forms/validation-bubble-appearance-edge-expected.txt b/third_party/WebKit/LayoutTests/fast/forms/validation-bubble-appearance-edge-expected.txt
index 36877905..69e3b0b 100644
--- a/third_party/WebKit/LayoutTests/fast/forms/validation-bubble-appearance-edge-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/forms/validation-bubble-appearance-edge-expected.txt
@@ -1,15 +1,3 @@
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x50
-  LayoutBlockFlow {HTML} at (0,0) size 800x50
-    LayoutBlockFlow {BODY} at (8,16) size 784x18
-      LayoutBlockFlow {P} at (0,0) size 784x18
-        LayoutText {#text} at (0,0) size 377x18
-          text run at (0,0) width 377: "Check if a validation bubble is shown at a correct position."
-layer at (661,573) size 131x19 clip at (663,575) size 127x15
-  LayoutTextControl (positioned) {INPUT} at (661,573) size 131x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
-layer at (664,576) size 125x13
-  LayoutBlockFlow {DIV} at (3,3) size 125x13
-    LayoutText {#text} at (0,0) size 19x13
-      text run at (0,0) width 19: "abc"
-caret: position 0 of child 0 {#text} of child 0 {DIV} of {#document-fragment} of child 3 {INPUT} of body
+Check if a validation bubble is shown at a correct position.
+
+
diff --git a/third_party/WebKit/LayoutTests/fast/forms/validation-bubble-appearance-edge.html b/third_party/WebKit/LayoutTests/fast/forms/validation-bubble-appearance-edge.html
index 3411897..b86c1ce 100644
--- a/third_party/WebKit/LayoutTests/fast/forms/validation-bubble-appearance-edge.html
+++ b/third_party/WebKit/LayoutTests/fast/forms/validation-bubble-appearance-edge.html
@@ -3,6 +3,11 @@
 <p>Check if a validation bubble is shown at a correct position.</p>
 <input pattern="\d{4}" title="Please specify four digits." value="abc" style="position:absolute; right:8px; bottom: 8px;">
 <script>
+if (window.testRunner) {
+  // Layout tree dump doesn't matter.
+  testRunner.dumpAsTextWithPixelResults();
+}
+
 document.querySelector('input').reportValidity();
 </script>
 <body>
diff --git a/third_party/WebKit/LayoutTests/fast/forms/validation-bubble-appearance-iframe-expected.png b/third_party/WebKit/LayoutTests/fast/forms/validation-bubble-appearance-iframe-expected.png
index 80755b9..4aedf2ad 100644
--- a/third_party/WebKit/LayoutTests/fast/forms/validation-bubble-appearance-iframe-expected.png
+++ b/third_party/WebKit/LayoutTests/fast/forms/validation-bubble-appearance-iframe-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/fast/forms/validation-bubble-appearance-iframe-expected.txt b/third_party/WebKit/LayoutTests/fast/forms/validation-bubble-appearance-iframe-expected.txt
index 4946765..50e0f46 100644
--- a/third_party/WebKit/LayoutTests/fast/forms/validation-bubble-appearance-iframe-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/forms/validation-bubble-appearance-iframe-expected.txt
@@ -1,27 +1,3 @@
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x216
-  LayoutBlockFlow {HTML} at (0,0) size 800x216
-    LayoutBlockFlow {BODY} at (8,16) size 784x192
-      LayoutBlockFlow {P} at (0,0) size 784x18
-        LayoutText {#text} at (0,0) size 406x18
-          text run at (0,0) width 406: "Check if a validation bubble is shown over IFRAME boundary."
-      LayoutBlockFlow (anonymous) at (0,34) size 784x158
-        LayoutText {#text} at (0,0) size 0x0
-layer at (8,50) size 304x154
-  LayoutIFrame {IFRAME} at (0,0) size 304x154 [border: (2px inset #EEEEEE)]
-    layer at (0,0) size 300x150
-      LayoutView at (0,0) size 300x150
-    layer at (0,0) size 300x68
-      LayoutBlockFlow {HTML} at (0,0) size 300x68
-        LayoutBlockFlow {BODY} at (8,16) size 284x36
-          LayoutBlockFlow {P} at (0,0) size 284x36
-            LayoutText {#text} at (0,0) size 269x36
-              text run at (0,0) width 269: "Check if a validation bubble is shown at a"
-              text run at (0,18) width 104: "correct position."
-    layer at (161,123) size 131x19 clip at (163,125) size 127x15
-      LayoutTextControl (positioned) {INPUT} at (161,123) size 131x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
-    layer at (164,126) size 125x13
-      LayoutBlockFlow {DIV} at (3,3) size 125x13
-        LayoutText {#text} at (0,0) size 19x13
-          text run at (0,0) width 19: "abc"
+Check if a validation bubble is shown over IFRAME boundary.
+
+
diff --git a/third_party/WebKit/LayoutTests/fast/forms/validation-bubble-appearance-iframe.html b/third_party/WebKit/LayoutTests/fast/forms/validation-bubble-appearance-iframe.html
index a62cdbf..5967a68 100644
--- a/third_party/WebKit/LayoutTests/fast/forms/validation-bubble-appearance-iframe.html
+++ b/third_party/WebKit/LayoutTests/fast/forms/validation-bubble-appearance-iframe.html
@@ -2,4 +2,10 @@
 <body>
 <p>Check if a validation bubble is shown over IFRAME boundary.</p>
 <iframe src="validation-bubble-appearance-edge.html"></iframe>
-<body>
+<script>
+if (window.testRunner) {
+  // Layout tree dump doesn't matter.
+  testRunner.dumpAsTextWithPixelResults();
+}
+</script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/fast/forms/validation-bubble-appearance-rtl-ui-expected.png b/third_party/WebKit/LayoutTests/fast/forms/validation-bubble-appearance-rtl-ui-expected.png
new file mode 100644
index 0000000..0d30cd6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/forms/validation-bubble-appearance-rtl-ui-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/fast/forms/validation-bubble-appearance-rtl-ui-expected.txt b/third_party/WebKit/LayoutTests/fast/forms/validation-bubble-appearance-rtl-ui-expected.txt
new file mode 100644
index 0000000..98cf1f8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/forms/validation-bubble-appearance-rtl-ui-expected.txt
@@ -0,0 +1,3 @@
+Check if a validation bubble is shown at a correct position in RTL UI.
+
+
diff --git a/third_party/WebKit/LayoutTests/fast/forms/validation-bubble-appearance-rtl-ui.html b/third_party/WebKit/LayoutTests/fast/forms/validation-bubble-appearance-rtl-ui.html
new file mode 100644
index 0000000..53e1057
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/forms/validation-bubble-appearance-rtl-ui.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<body>
+<p>Check if a validation bubble is shown at a correct position in RTL UI.</p>
+<input pattern="\d{4}" title="Please specify four digits." value="abc" style="margin-left: 30%;">
+<script>
+if (window.testRunner) {
+  // Layout tree dump doesn't matter.
+  testRunner.dumpAsTextWithPixelResults();
+}
+
+internals.setUserPreferredLanguages(['ar']);
+document.querySelector('input').reportValidity();
+</script>
+<body>
diff --git a/third_party/WebKit/LayoutTests/fast/history/scroll-restoration/scroll-restoration-same-doc.html b/third_party/WebKit/LayoutTests/fast/history/scroll-restoration/scroll-restoration-same-doc.html
new file mode 100644
index 0000000..d59e3db
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/history/scroll-restoration/scroll-restoration-same-doc.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+
+<style>
+  #bigdiv {
+    width: 200px;
+    height: 1100px;
+  }
+</style>
+
+<div id="bigdiv">
+</div>
+
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+
+<script>
+  'use strict';
+  const bigdiv = document.getElementById("bigdiv");
+
+  async_test((t) => {
+    // 1. scroll to bottom.
+    window.scrollBy(0, 100);
+    assert_equals(window.scrollY, 100);
+
+    // 2. remove bigdiv and push state.
+    history.pushState(1, null, "page2.html");
+    document.body.removeChild(bigdiv);
+    assert_equals(window.scrollY, 0);
+
+    window.onpopstate = () => {
+      document.body.appendChild(bigdiv);
+      setTimeout(() => {
+        t.step(() => {
+          assert_equals(window.scrollY, 100);
+        });
+        t.done();
+      }, 0);
+    };
+
+    // 3. navigation back. page should restore scroll position to 100, see below.
+    history.back();
+  }, "load-in-same-doc-and-change-DOM-onpopstate");
+</script>
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/compositing/overflow/respect-clip-for-non-composited-scrollers-when-prefering-compositing-over-lcd-text-expected.png b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/compositing/overflow/respect-clip-for-non-composited-scrollers-when-prefering-compositing-over-lcd-text-expected.png
new file mode 100644
index 0000000..281a6bd
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/compositing/overflow/respect-clip-for-non-composited-scrollers-when-prefering-compositing-over-lcd-text-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/http/tests/budget/budget-service-mock.js b/third_party/WebKit/LayoutTests/http/tests/budget/budget-service-mock.js
index 60ceac0..ba8b78f 100644
--- a/third_party/WebKit/LayoutTests/http/tests/budget/budget-service-mock.js
+++ b/third_party/WebKit/LayoutTests/http/tests/budget/budget-service-mock.js
@@ -16,12 +16,16 @@
   const [budgetService, bindings] = mojo.modules;
 
   class BudgetServiceMock {
-    constructor(interfaceProvider) {
-      interfaceProvider.addInterfaceOverrideForTesting(
+    constructor() {
+      // Register process-wide (worker) interface override.
+      mojo.interfaces.addInterfaceOverrideForTesting(
           budgetService.BudgetService.name,
           handle => this.bindingSet_.addBinding(this, handle));
 
-      this.interfaceProvider_ = interfaceProvider;
+      // Register frame interface override.
+      mojo.frameInterfaces.addInterfaceOverrideForTesting(
+          budgetService.BudgetService.name,
+          handle => this.bindingSet_.addBinding(this, handle));
 
       // Values to return for the next getBudget and getCost calls.
       this.cost_ = {};
@@ -88,5 +92,5 @@
       return Promise.resolve({ error_type: this.error_, success: this.success_ });
     }
   }
-  return new BudgetServiceMock(mojo.interfaces);
+  return new BudgetServiceMock();
 });
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/resources/interception-test.js b/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/resources/interception-test.js
index ef14c50..59750ec 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/resources/interception-test.js
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/resources/interception-test.js
@@ -83,14 +83,14 @@
         }
         InspectorTest.log("Network agent enabled");
         InspectorTest.sendCommand(
-            "Network.enableRequestInterception", {"enabled": true},
-            didEnableRequestInterception);
+            "Network.setRequestInterceptionEnabled", {"enabled": true},
+            didSetRequestInterceptionEnabled);
     }
 
-    function didEnableRequestInterception(messageObject)
+    function didSetRequestInterceptionEnabled(messageObject)
     {
         if (messageObject.error) {
-            completeTest("FAIL: Couldn't enable fetch interception" +
+            completeTest("FAIL: Couldn't enable fetch interception " +
                                     messageObject.error.message);
             return;
         }
@@ -256,7 +256,7 @@
 InspectorTest.disableRequestInterception = function(event) {
     var id = canonicalId(event.params.interceptionId);
     log(id, "----- disableRequestInterception -----");
-    InspectorTest.sendCommand("Network.enableRequestInterception", {
+    InspectorTest.sendCommand("Network.setRequestInterceptionEnabled", {
         "enabled": false,
     });
 }
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/subresourceIntegrity/clear-integrity-attribute-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/subresourceIntegrity/clear-integrity-attribute-expected.txt
new file mode 100644
index 0000000..98696d4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/security/subresourceIntegrity/clear-integrity-attribute-expected.txt
@@ -0,0 +1,5 @@
+CONSOLE ERROR: Failed to find a valid digest in the 'integrity' attribute for resource 'http://127.0.0.1:8000/security/subresourceIntegrity/resources/clear-integrity-attribute.js' with computed SHA-256 integrity 'yM5ZyzNsyKfaXRY78zSGapeQKtl0oGdpPpYxgwl8XW8='. The resource has been blocked.
+ALERT: FAIL
+This test passes if only one 'FAIL' alert appears.
+
+
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/subresourceIntegrity/clear-integrity-attribute.html b/third_party/WebKit/LayoutTests/http/tests/security/subresourceIntegrity/clear-integrity-attribute.html
new file mode 100644
index 0000000..067c9c5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/security/subresourceIntegrity/clear-integrity-attribute.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<body onload="runTest()">
+  <script>
+    if (window.testRunner) {
+      testRunner.dumpAsText();
+      testRunner.waitUntilDone();
+    }
+    function runTest() {
+      var script = document.createElement('script');
+      script.onload = function() { testRunner.notifyDone(); };
+      script.onerror = function() { testRunner.notifyDone(); };
+      script.setAttribute('integrity', "sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=");
+      script.src = "resources/clear-integrity-attribute.js";
+      document.body.appendChild(script);
+    }
+  </script>
+  <p>
+    This test passes if only one 'FAIL' alert appears.
+  </p>
+  <iframe src="http://127.0.0.1:8000/security/subresourceIntegrity/resources/clear-integrity-attribute.html"></iframe>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/subresourceIntegrity/resources/clear-integrity-attribute.html b/third_party/WebKit/LayoutTests/http/tests/security/subresourceIntegrity/resources/clear-integrity-attribute.html
new file mode 100644
index 0000000..898e7df
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/security/subresourceIntegrity/resources/clear-integrity-attribute.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title></title>
+    <script id="script0" defer src="http://127.0.0.1:8000/security/subresourceIntegrity/resources/clear-integrity-attribute.js" integrity="sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE="></script>
+    <script>
+      document.getElementById('script0').setAttribute('integrity', '');
+    </script>
+  </head>
+  <body>
+  </body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/subresourceIntegrity/resources/clear-integrity-attribute.js b/third_party/WebKit/LayoutTests/http/tests/security/subresourceIntegrity/resources/clear-integrity-attribute.js
new file mode 100644
index 0000000..2462b4b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/security/subresourceIntegrity/resources/clear-integrity-attribute.js
@@ -0,0 +1 @@
+alert('FAIL');
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/subresourceIntegrity/resources/shared-with-xhtml.html b/third_party/WebKit/LayoutTests/http/tests/security/subresourceIntegrity/resources/shared-with-xhtml.html
new file mode 100644
index 0000000..401644b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/security/subresourceIntegrity/resources/shared-with-xhtml.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title></title>
+  <script src="shared-with-xhtml.js" integrity="sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE="></script>
+</head>
+<body>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/subresourceIntegrity/resources/shared-with-xhtml.js b/third_party/WebKit/LayoutTests/http/tests/security/subresourceIntegrity/resources/shared-with-xhtml.js
new file mode 100644
index 0000000..2462b4b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/security/subresourceIntegrity/resources/shared-with-xhtml.js
@@ -0,0 +1 @@
+alert('FAIL');
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/subresourceIntegrity/resources/shared-with-xhtml.xhtml b/third_party/WebKit/LayoutTests/http/tests/security/subresourceIntegrity/resources/shared-with-xhtml.xhtml
new file mode 100644
index 0000000..403a6a0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/security/subresourceIntegrity/resources/shared-with-xhtml.xhtml
@@ -0,0 +1,11 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html lang="EN" dir="ltr" xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <meta http-equiv="content-type" content="text/xml; charset=utf-8" />
+    <title></title>
+    <script src="http://127.0.0.1:8000/security/subresourceIntegrity/resources/shared-with-xhtml.js" integrity="sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE="></script>
+  </head>
+  <body>
+  </body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/subresourceIntegrity/shared-with-xhtml-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/subresourceIntegrity/shared-with-xhtml-expected.txt
new file mode 100644
index 0000000..b0d4a300
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/security/subresourceIntegrity/shared-with-xhtml-expected.txt
@@ -0,0 +1,5 @@
+CONSOLE ERROR: Failed to find a valid digest in the 'integrity' attribute for resource 'http://127.0.0.1:8000/security/subresourceIntegrity/resources/shared-with-xhtml.js' with computed SHA-256 integrity 'yM5ZyzNsyKfaXRY78zSGapeQKtl0oGdpPpYxgwl8XW8='. The resource has been blocked.
+ALERT: FAIL
+This test passes if only one 'FAIL' alert appears.
+
+  
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/subresourceIntegrity/shared-with-xhtml.html b/third_party/WebKit/LayoutTests/http/tests/security/subresourceIntegrity/shared-with-xhtml.html
new file mode 100644
index 0000000..46aab8d0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/security/subresourceIntegrity/shared-with-xhtml.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<body>
+  <script>
+    if (window.testRunner)
+      testRunner.dumpAsText();
+  </script>
+  <p>
+    This test passes if only one 'FAIL' alert appears.
+  </p>
+  <iframe src="resources/shared-with-xhtml.xhtml"></iframe>
+  <iframe src="resources/shared-with-xhtml.html"></iframe>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/platform/linux/compositing/overflow/nested-border-radius-clipping-expected.png b/third_party/WebKit/LayoutTests/platform/linux/compositing/overflow/nested-border-radius-clipping-expected.png
index 75b3fc74..018367d 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/compositing/overflow/nested-border-radius-clipping-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/compositing/overflow/nested-border-radius-clipping-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/virtual/disable-spinvalidation/compositing/overflow/nested-border-radius-clipping-expected.png b/third_party/WebKit/LayoutTests/platform/linux/virtual/disable-spinvalidation/compositing/overflow/nested-border-radius-clipping-expected.png
new file mode 100644
index 0000000..018367d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/linux/virtual/disable-spinvalidation/compositing/overflow/nested-border-radius-clipping-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/virtual/prefer_compositing_to_lcd_text/compositing/overflow/nested-border-radius-clipping-expected.png b/third_party/WebKit/LayoutTests/platform/linux/virtual/prefer_compositing_to_lcd_text/compositing/overflow/nested-border-radius-clipping-expected.png
index 75b3fc74..018367d 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/virtual/prefer_compositing_to_lcd_text/compositing/overflow/nested-border-radius-clipping-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/virtual/prefer_compositing_to_lcd_text/compositing/overflow/nested-border-radius-clipping-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/scrollbars/border-box-rect-clips-scrollbars-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/scrollbars/border-box-rect-clips-scrollbars-expected.png
index fedb6411..cb4dce0 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/scrollbars/border-box-rect-clips-scrollbars-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/scrollbars/border-box-rect-clips-scrollbars-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/virtual/prefer_compositing_to_lcd_text/scrollbars/border-box-rect-clips-scrollbars-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/virtual/prefer_compositing_to_lcd_text/scrollbars/border-box-rect-clips-scrollbars-expected.png
index 942a7e3..87fab43 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/virtual/prefer_compositing_to_lcd_text/scrollbars/border-box-rect-clips-scrollbars-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/virtual/prefer_compositing_to_lcd_text/scrollbars/border-box-rect-clips-scrollbars-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/virtual/rootlayerscrolls/scrollbars/border-box-rect-clips-scrollbars-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/virtual/rootlayerscrolls/scrollbars/border-box-rect-clips-scrollbars-expected.png
index fedb6411..cb4dce0 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/virtual/rootlayerscrolls/scrollbars/border-box-rect-clips-scrollbars-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/virtual/rootlayerscrolls/scrollbars/border-box-rect-clips-scrollbars-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/compositing/overflow/nested-border-radius-clipping-expected.png b/third_party/WebKit/LayoutTests/platform/mac/compositing/overflow/nested-border-radius-clipping-expected.png
index f909d13..8c07beda 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/compositing/overflow/nested-border-radius-clipping-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/compositing/overflow/nested-border-radius-clipping-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/scrollbars/border-box-rect-clips-scrollbars-expected.png b/third_party/WebKit/LayoutTests/platform/mac/scrollbars/border-box-rect-clips-scrollbars-expected.png
index 3f249aa..fa48bc9e 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/scrollbars/border-box-rect-clips-scrollbars-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/scrollbars/border-box-rect-clips-scrollbars-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/disable-spinvalidation/compositing/overflow/nested-border-radius-clipping-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/disable-spinvalidation/compositing/overflow/nested-border-radius-clipping-expected.png
new file mode 100644
index 0000000..8c07beda
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/disable-spinvalidation/compositing/overflow/nested-border-radius-clipping-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/prefer_compositing_to_lcd_text/compositing/overflow/nested-border-radius-clipping-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/prefer_compositing_to_lcd_text/compositing/overflow/nested-border-radius-clipping-expected.png
index f909d13..8c07beda 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/virtual/prefer_compositing_to_lcd_text/compositing/overflow/nested-border-radius-clipping-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/prefer_compositing_to_lcd_text/compositing/overflow/nested-border-radius-clipping-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/prefer_compositing_to_lcd_text/scrollbars/border-box-rect-clips-scrollbars-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/prefer_compositing_to_lcd_text/scrollbars/border-box-rect-clips-scrollbars-expected.png
index 57434748..e83464152 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/virtual/prefer_compositing_to_lcd_text/scrollbars/border-box-rect-clips-scrollbars-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/prefer_compositing_to_lcd_text/scrollbars/border-box-rect-clips-scrollbars-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/rootlayerscrolls/scrollbars/border-box-rect-clips-scrollbars-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/rootlayerscrolls/scrollbars/border-box-rect-clips-scrollbars-expected.png
index 3f249aa..fa48bc9e 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/virtual/rootlayerscrolls/scrollbars/border-box-rect-clips-scrollbars-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/rootlayerscrolls/scrollbars/border-box-rect-clips-scrollbars-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/compositing/overflow/nested-border-radius-clipping-expected.png b/third_party/WebKit/LayoutTests/platform/win/compositing/overflow/nested-border-radius-clipping-expected.png
index 8a697828fc..38f87c2 100644
--- a/third_party/WebKit/LayoutTests/platform/win/compositing/overflow/nested-border-radius-clipping-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/compositing/overflow/nested-border-radius-clipping-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/disable-spinvalidation/compositing/overflow/nested-border-radius-clipping-expected.png b/third_party/WebKit/LayoutTests/platform/win/virtual/disable-spinvalidation/compositing/overflow/nested-border-radius-clipping-expected.png
new file mode 100644
index 0000000..38f87c2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win/virtual/disable-spinvalidation/compositing/overflow/nested-border-radius-clipping-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/prefer_compositing_to_lcd_text/compositing/overflow/nested-border-radius-clipping-expected.png b/third_party/WebKit/LayoutTests/platform/win/virtual/prefer_compositing_to_lcd_text/compositing/overflow/nested-border-radius-clipping-expected.png
index 8a697828fc..38f87c2 100644
--- a/third_party/WebKit/LayoutTests/platform/win/virtual/prefer_compositing_to_lcd_text/compositing/overflow/nested-border-radius-clipping-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/virtual/prefer_compositing_to_lcd_text/compositing/overflow/nested-border-radius-clipping-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/shapedetection/detection-HTMLCanvasElement.html b/third_party/WebKit/LayoutTests/shapedetection/detection-HTMLCanvasElement.html
index 161df617..97bb275f 100644
--- a/third_party/WebKit/LayoutTests/shapedetection/detection-HTMLCanvasElement.html
+++ b/third_party/WebKit/LayoutTests/shapedetection/detection-HTMLCanvasElement.html
@@ -1,7 +1,11 @@
 <!DOCTYPE html>
 <script src="../resources/testharness.js"></script>
 <script src="../resources/testharnessreport.js"></script>
-<script src="../resources/mojo-helpers.js"></script>
+<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
+<script src="file:///gen/services/shape_detection/public/interfaces/barcodedetection.mojom.js"></script>
+<script src="file:///gen/services/shape_detection/public/interfaces/facedetection.mojom.js"></script>
+<script src="file:///gen/services/shape_detection/public/interfaces/facedetection_provider.mojom.js"></script>
+<script src="file:///gen/services/shape_detection/public/interfaces/textdetection.mojom.js"></script>
 <script src="resources/mock-barcodedetection.js"></script>
 <script src="resources/mock-facedetection.js"></script>
 <script src="resources/mock-textdetection.js"></script>
@@ -9,36 +13,23 @@
 
 function detectShapeForCanvas(createDetector,
                               createCanvas,
-                              mockReady,
+                              mock,
                               detectionResultTest) {
   return new Promise(function(resolve, reject) {
       var img = new Image();
       img.onload = function() {
+        var canvas = createCanvas();
+        canvas.getContext("2d").drawImage(img, 0, 0);
 
-      var canvas = createCanvas();
-      canvas.getContext("2d").drawImage(img, 0, 0);
-
-      var theMock = null;
-      mockReady()
-        .then(mock => {
-          theMock = mock;
-          var detector = createDetector();
-          return detector;
-        })
-        .catch(error => {
-          assert_unreached("Error creating Mock Detector: " + error);
-        })
-        .then(detector => {
-          return detector.detect(canvas);
-        })
-        .then(detectionResult => {
-          detectionResultTest(detectionResult, theMock);
-          resolve("Success");
-        })
-        .catch(error => {
-          assert_unreached("Error during detect(canvas): " + error);
-        });
-      }
+        var detector = createDetector();
+        detector.detect(canvas)
+          .then(detectionResult => {
+            detectionResultTest(detectionResult, mock);
+            resolve("Success");
+          }, error => {
+            assert_unreached("Error during detect(canvas): " + error);
+          });
+      };
 
       img.src = "../media/content/greenbox.png";
   });
@@ -46,12 +37,12 @@
 
 var createTestForCanvasElement = function(createDetector,
                                           createCanvas,
-                                          mockReady,
+                                          mock,
                                           detectionResultTest) {
   promise_test(function() {
     return detectShapeForCanvas(createDetector,
                                 createCanvas,
-                                mockReady,
+                                mock,
                                 detectionResultTest)
         .then(function(result) {
             assert_equals(result, "Success", "Detect 'Success'");
@@ -87,42 +78,42 @@
     "Face - detect(HTMLCanvasElement)",
     () => { return new FaceDetector(); },
     () => { return document.createElement("canvas"); },
-    () => { return mockFaceDetectionProviderReady; },
+    mockFaceDetectionProvider,
     FaceDetectorDetectionResultTest
   ],
   [
     "Face - detect(OffscreenCanvas)",
     () => { return new FaceDetector(); },
     () => { return new OffscreenCanvas(300, 150); },
-    () => { return mockFaceDetectionProviderReady; },
+    mockFaceDetectionProvider,
     FaceDetectorDetectionResultTest
   ],
   [
     "Barcode - detect(HTMLCanvasElement)",
     () => { return new BarcodeDetector(); },
     () => { return document.createElement("canvas"); },
-    () => { return mockBarcodeDetectionReady; },
+    mockBarcodeDetection,
     BarcodeDetectorDetectionResultTest
   ],
   [
     "Barcode - detect(OffscreenCanvas)",
     () => { return new BarcodeDetector(); },
     () => { return new OffscreenCanvas(300, 150); },
-    () => { return mockBarcodeDetectionReady; },
+    mockBarcodeDetection,
     BarcodeDetectorDetectionResultTest
   ],
   [
     "Text - detect(HTMLCanvasElement)",
     () => { return new TextDetector(); },
     () => { return document.createElement("canvas"); },
-    () => { return mockTextDetectionReady; },
+    mockTextDetection,
     TextDetectorDetectionResultTest
   ],
   [
     "Text - detect(OffscreenCanvas)",
     () => { return new TextDetector(); },
     () => { return new OffscreenCanvas(300, 150); },
-    () => { return mockTextDetectionReady; },
+    mockTextDetection,
     TextDetectorDetectionResultTest
   ]
 ]);
diff --git a/third_party/WebKit/LayoutTests/shapedetection/detection-HTMLImageElement.html b/third_party/WebKit/LayoutTests/shapedetection/detection-HTMLImageElement.html
index 8dfa455..7822219 100644
--- a/third_party/WebKit/LayoutTests/shapedetection/detection-HTMLImageElement.html
+++ b/third_party/WebKit/LayoutTests/shapedetection/detection-HTMLImageElement.html
@@ -1,7 +1,11 @@
 <!DOCTYPE html>
 <script src="../resources/testharness.js"></script>
 <script src="../resources/testharnessreport.js"></script>
-<script src="../resources/mojo-helpers.js"></script>
+<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
+<script src="file:///gen/services/shape_detection/public/interfaces/barcodedetection.mojom.js"></script>
+<script src="file:///gen/services/shape_detection/public/interfaces/facedetection.mojom.js"></script>
+<script src="file:///gen/services/shape_detection/public/interfaces/facedetection_provider.mojom.js"></script>
+<script src="file:///gen/services/shape_detection/public/interfaces/textdetection.mojom.js"></script>
 <script src="resources/mock-barcodedetection.js"></script>
 <script src="resources/mock-facedetection.js"></script>
 <script src="resources/mock-textdetection.js"></script>
@@ -10,32 +14,18 @@
 </body>
 <script>
 
-var createTestForImageElement = function(createDetector, mockReady,
+var createTestForImageElement = function(createDetector, mock,
                                          detectionResultTest) {
-  async_test(function(t) {
+  promise_test(async function() {
     var img = document.getElementById("img");
 
-    var theMock = null;
-    mockReady()
-      .then(mock => {
-        theMock = mock;
-        var detector = createDetector();
-        return detector;
-      })
-      .catch(error => {
-        assert_unreached("Error creating Mock Detector: " + error);
-      })
-      .then(detector => {
-        return detector.detect(img);
-      })
-      .then(detectionResult => {
-        detectionResultTest(detectionResult, theMock);
-        t.done();
-      })
-      .catch(error => {
-        assert_unreached("Error during detect(img): " + error);
-      });
-
+    var detector = createDetector();
+    try {
+      var detectionResult = await detector.detect(img);
+      detectionResultTest(detectionResult, mock);
+    } catch(error) {
+      assert_unreached("Error during detect(img): " + error);
+    }
   });
 };
 
@@ -69,19 +59,19 @@
   [
     "Face - detect(HTMLImageElement)",
     () => { return new FaceDetector(); },
-    () => { return mockFaceDetectionProviderReady; },
+    mockFaceDetectionProvider,
     FaceDetectorDetectionResultTest
   ],
   [
     "Barcode - detect(HTMLImageElement)",
     () => { return new BarcodeDetector(); },
-    () => { return mockBarcodeDetectionReady; },
+    mockBarcodeDetection,
     BarcodeDetectorDetectionResultTest
   ],
   [
     "Text - detect(HTMLImageElement)",
     () => { return new TextDetector(); },
-    () => { return mockTextDetectionReady; },
+    mockTextDetection,
     TextDetectorDetectionResultTest
   ]
 ]);
diff --git a/third_party/WebKit/LayoutTests/shapedetection/detection-HTMLVideoElement.html b/third_party/WebKit/LayoutTests/shapedetection/detection-HTMLVideoElement.html
index 82dee81..2261ac6 100644
--- a/third_party/WebKit/LayoutTests/shapedetection/detection-HTMLVideoElement.html
+++ b/third_party/WebKit/LayoutTests/shapedetection/detection-HTMLVideoElement.html
@@ -1,13 +1,17 @@
 <!DOCTYPE html>
 <script src="../resources/testharness.js"></script>
 <script src="../resources/testharnessreport.js"></script>
-<script src="../resources/mojo-helpers.js"></script>
+<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
+<script src="file:///gen/services/shape_detection/public/interfaces/barcodedetection.mojom.js"></script>
+<script src="file:///gen/services/shape_detection/public/interfaces/facedetection.mojom.js"></script>
+<script src="file:///gen/services/shape_detection/public/interfaces/facedetection_provider.mojom.js"></script>
+<script src="file:///gen/services/shape_detection/public/interfaces/textdetection.mojom.js"></script>
 <script src="resources/mock-barcodedetection.js"></script>
 <script src="resources/mock-facedetection.js"></script>
 <script src="resources/mock-textdetection.js"></script>
 <script>
 
-var createTestForVideoElement = function(createDetector, mockReady,
+var createTestForVideoElement = function(createDetector, mock,
                                          detectionResultTest) {
   async_test(function(t) {
     var video = document.createElement("video");
@@ -15,27 +19,15 @@
     video.loop = true;
     video.autoplay = true;
     video.onerror = this.unreached_func("<video> error");
-    video.onplay = this.step_func(function() {
-      var theMock = null;
-      mockReady()
-        .then(mock => {
-          theMock = mock;
-          var detector = createDetector();
-          return detector;
-        })
-        .catch(error => {
-          assert_unreached("Error creating Mock Detector: " + error);
-        })
-        .then(detector => {
-          return detector.detect(video);
-        })
-        .then(detectionResult => {
-          detectionResultTest(detectionResult, theMock);
-          t.done();
-        })
-        .catch(error => {
-          assert_unreached("Error during detect(video): " + error);
-        });
+    video.onplay = this.step_func(async function() {
+      var detector = createDetector();
+      try {
+        var detectionResult = await detector.detect(video);
+        detectionResultTest(detectionResult, mock);
+        t.done();
+      } catch (error) {
+        assert_unreached("Error during detect(video): " + error);
+      }
     });
 
     video.load();
@@ -69,19 +61,19 @@
   [
     "Face - detect(HTMLVideoElement)",
     () => { return new FaceDetector(); },
-    () => { return mockFaceDetectionProviderReady; },
+    mockFaceDetectionProvider,
     FaceDetectorDetectionResultTest
   ],
   [
     "Barcode - detect(HTMLVideoElement)",
     () => { return new BarcodeDetector(); },
-    () => { return mockBarcodeDetectionReady; },
+    mockBarcodeDetection,
     BarcodeDetectorDetectionResultTest
   ],
   [
     "Text - detect(HTMLVideoElement)",
     () => { return new TextDetector(); },
-    () => { return mockTextDetectionReady; },
+    mockTextDetection,
     TextDetectorDetectionResultTest
   ]
 ]);
diff --git a/third_party/WebKit/LayoutTests/shapedetection/detection-ImageBitmap.html b/third_party/WebKit/LayoutTests/shapedetection/detection-ImageBitmap.html
index 6a568616..79494d8 100644
--- a/third_party/WebKit/LayoutTests/shapedetection/detection-ImageBitmap.html
+++ b/third_party/WebKit/LayoutTests/shapedetection/detection-ImageBitmap.html
@@ -1,47 +1,32 @@
 <!DOCTYPE html>
 <script src="../resources/testharness.js"></script>
 <script src="../resources/testharnessreport.js"></script>
-<script src="../resources/mojo-helpers.js"></script>
+<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
+<script src="file:///gen/services/shape_detection/public/interfaces/barcodedetection.mojom.js"></script>
+<script src="file:///gen/services/shape_detection/public/interfaces/facedetection.mojom.js"></script>
+<script src="file:///gen/services/shape_detection/public/interfaces/facedetection_provider.mojom.js"></script>
+<script src="file:///gen/services/shape_detection/public/interfaces/textdetection.mojom.js"></script>
 <script src="resources/mock-barcodedetection.js"></script>
 <script src="resources/mock-facedetection.js"></script>
 <script src="resources/mock-textdetection.js"></script>
 <script>
 
-var createTestForImageBitmap = function(createDetector, mockReady,
+var createTestForImageBitmap = function(createDetector, mock,
                                         detectionResultTest) {
   async_test(function(t) {
     var img = new Image();
 
-    img.onload = function() {
-      var theImageBitmap = null;
-      var theMock = null;
+    img.onload = async function() {
+      var imageBitmap = await createImageBitmap(img);
+      var detector = createDetector();
 
-      createImageBitmap(img)
-          .then(imageBitmap => {
-            theImageBitmap = imageBitmap;
-            return mockReady();
-          })
-          .catch(error => {
-            assert_unreached("createImageBitmap() error: " + error);
-          })
-          .then(mock => {
-            theMock = mock;
-            var detector = createDetector();
-            return detector;
-          })
-          .catch(error => {
-            assert_unreached("Error creating Mock Detector: " + error);
-          })
-          .then(detector => {
-            return detector.detect(theImageBitmap);
-          })
-          .then(detectionResult => {
-            detectionResultTest(detectionResult, theMock);
-            t.done();
-          })
-          .catch(error => {
-            assert_unreached("Error during detect(img): " + error);
-          });
+      try {
+        var detectionResult = await detector.detect(imageBitmap);
+        detectionResultTest(detectionResult, mock);
+        t.done();
+      } catch (error) {
+        assert_unreached("Error during detect(img): " + error);
+      }
     }
     img.src = "../media/content/greenbox.png";
   });
@@ -73,19 +58,19 @@
   [
     "Face - detect(ImageBitmap)",
     () => { return new FaceDetector(); },
-    () => { return mockFaceDetectionProviderReady; },
+    mockFaceDetectionProvider,
     FaceDetectorDetectionResultTest
   ],
   [
     "Barcode - detect(ImageBitmap)",
     () => { return new BarcodeDetector(); },
-    () => { return mockBarcodeDetectionReady; },
+    mockBarcodeDetection,
     BarcodeDetectorDetectionResultTest
   ],
   [
     "Text - detect(ImageBitmap)",
     () => { return new TextDetector(); },
-    () => { return mockTextDetectionReady; },
+    mockTextDetection,
     TextDetectorDetectionResultTest
   ]
 ]);
diff --git a/third_party/WebKit/LayoutTests/shapedetection/detection-ImageData.html b/third_party/WebKit/LayoutTests/shapedetection/detection-ImageData.html
index 57a5630..78dad14 100644
--- a/third_party/WebKit/LayoutTests/shapedetection/detection-ImageData.html
+++ b/third_party/WebKit/LayoutTests/shapedetection/detection-ImageData.html
@@ -1,43 +1,35 @@
 <!DOCTYPE html>
 <script src="../resources/testharness.js"></script>
 <script src="../resources/testharnessreport.js"></script>
-<script src="../resources/mojo-helpers.js"></script>
+<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
+<script src="file:///gen/services/shape_detection/public/interfaces/barcodedetection.mojom.js"></script>
+<script src="file:///gen/services/shape_detection/public/interfaces/facedetection.mojom.js"></script>
+<script src="file:///gen/services/shape_detection/public/interfaces/facedetection_provider.mojom.js"></script>
+<script src="file:///gen/services/shape_detection/public/interfaces/textdetection.mojom.js"></script>
 <script src="resources/mock-barcodedetection.js"></script>
 <script src="resources/mock-facedetection.js"></script>
 <script src="resources/mock-textdetection.js"></script>
 <script>
 
-var createTestForImageData = function(createDetector, mockReady,
+var createTestForImageData = function(createDetector, mock,
                                       detectionResultTest) {
-  async_test(function(t) {
+  async_test(async function(t) {
 
     var img = new Image();
-    img.onload = function() {
+    img.onload = async function() {
 
       var canvas = document.createElement("canvas");;
       canvas.getContext("2d").drawImage(img, 0, 0);
 
-      var theMock = null;
-      mockReady()
-        .then(mock => {
-          theMock = mock;
-          var detector = createDetector();
-          return detector;
-        })
-        .catch(error => {
-          assert_unreached("Error creating Mock Detector: " + error);
-        })
-        .then(detector => {
-          return detector.detect(canvas.getContext("2d").getImageData(
-              0, 0, canvas.width, canvas.height));
-        })
-        .then(detectionResult => {
-          detectionResultTest(detectionResult, theMock);
-          t.done();
-        })
-        .catch(error => {
-          assert_unreached("Error during detect(canvas): " + error);
-        });
+      var detector = createDetector();
+      try {
+        var detectionResult = await detector.detect(canvas.getContext("2d")
+            .getImageData(0, 0, canvas.width, canvas.height));
+        detectionResultTest(detectionResult, mock);
+        t.done();
+      } catch (error) {
+        assert_unreached("Error during detect(canvas): " + error);
+      }
     }
 
     img.src = "../media/content/greenbox.png";
@@ -70,19 +62,19 @@
   [
     "Face - detect(ImageData)",
     () => { return new FaceDetector(); },
-    () => { return mockFaceDetectionProviderReady; },
+    mockFaceDetectionProvider,
     FaceDetectorDetectionResultTest
   ],
   [
     "Barcode - detect(ImageData)",
     () => { return new BarcodeDetector(); },
-    () => { return mockBarcodeDetectionReady; },
+    mockBarcodeDetection,
     BarcodeDetectorDetectionResultTest
   ],
   [
     "Text - detect(ImageData)",
     () => { return new TextDetector(); },
-    () => { return mockTextDetectionReady; },
+    mockTextDetection,
     TextDetectorDetectionResultTest
   ]
 ]);
diff --git a/third_party/WebKit/LayoutTests/shapedetection/detection-on-worker.html b/third_party/WebKit/LayoutTests/shapedetection/detection-on-worker.html
index 09438fd..0031ff2 100644
--- a/third_party/WebKit/LayoutTests/shapedetection/detection-on-worker.html
+++ b/third_party/WebKit/LayoutTests/shapedetection/detection-on-worker.html
@@ -1,51 +1,25 @@
 <!DOCTYPE html>
 <script src="../resources/testharness.js"></script>
 <script src="../resources/testharnessreport.js"></script>
-<script src="../resources/mojo-helpers.js"></script>
-<script src="resources/mock-barcodedetection.js"></script>
-<script src="resources/mock-facedetection.js"></script>
-<script src="resources/mock-textdetection.js"></script>
 <script>
 
 // ImageBitmap is of transferable type and can be sent to and tested on worker.
-var createTestForImageBitmap = function(detectorType, mockReady,
-                                        resultSize) {
+var createTestForImageBitmap = function(detectorType, resultSize) {
   async_test(function(t) {
-    var img = new Image();
+    let img = new Image();
 
-    img.onload = function() {
-      var theImageBitmap = null;
-      var theMock = null;
-
-      createImageBitmap(img)
-          .then(imageBitmap => {
-            theImageBitmap = imageBitmap;
-            return mockReady();
-          })
-          .catch(error => {
-            assert_unreached("createImageBitmap() error: " + error);
-          })
-          .then(mock => {
-            theMock = mock;
-            return new Worker("resources/worker.js");
-          })
-          .catch(error => {
-            assert_unreached("Error creating Mock: " + error);
-          })
-          .then(worker => {
-            worker.postMessage({
-              detectorType: detectorType,
-              bitmap: theImageBitmap,
-              expectedLength: resultSize
-            }, [theImageBitmap]);
-            worker.onmessage = function(e) {
-              if(e.data=="PASS")
-                t.done();
-            }
-          })
-          .catch(error => {
-            assert_unreached("Error creating detector: " + error);
-          });
+    img.onload = async function() {
+      let theImageBitmap = await createImageBitmap(img);
+      let worker = new Worker("resources/worker.js");
+      worker.postMessage({
+        detectorType: detectorType,
+        bitmap: theImageBitmap,
+        expectedLength: resultSize
+      }, [theImageBitmap]);
+      worker.onmessage = function(e) {
+        if(e.data=="PASS")
+          t.done();
+      }
     }
     img.src = "../media/content/greenbox.png";
   }, detectorType + "Detector detect(ImageBitmap) on worker");
@@ -57,19 +31,16 @@
   [
     "Face",
     "Face",
-    () => { return mockFaceDetectionProviderReady; },
     3 // Number of faces
   ],
   [
     "Barcode",
     "Barcode",
-    () => { return mockBarcodeDetectionReady; },
     2 // Number of barcodes
   ],
   [
     "Text",
     "Text",
-    () => { return mockTextDetectionReady; },
     2 // Number of text blocks
   ]
 ]);
diff --git a/third_party/WebKit/LayoutTests/shapedetection/detection-options.html b/third_party/WebKit/LayoutTests/shapedetection/detection-options.html
index fb12e70..fa0100d 100644
--- a/third_party/WebKit/LayoutTests/shapedetection/detection-options.html
+++ b/third_party/WebKit/LayoutTests/shapedetection/detection-options.html
@@ -1,37 +1,27 @@
 <!DOCTYPE html>
 <script src="../resources/testharness.js"></script>
 <script src="../resources/testharnessreport.js"></script>
-<script src="../resources/mojo-helpers.js"></script>
+<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
+<script src="file:///gen/services/shape_detection/public/interfaces/facedetection.mojom.js"></script>
+<script src="file:///gen/services/shape_detection/public/interfaces/facedetection_provider.mojom.js"></script>
 <script src="resources/mock-facedetection.js"></script>
 <body>
 <img id="img" src="../media/content/greenbox.png"/>
 </body>
 <script>
-async_test(function(t) {
+promise_test(async function() {
   var img = document.getElementById("img");
-  var theMock = null;
-  mockFaceDetectionProviderReady
-    .then(mock => {
-      theMock = mock;
-      return new FaceDetector();
-    })
-    .catch(error => {
-      assert_unreached("Error creating MockShapeDetection: " + error);
-    })
-    .then(detectorWithDefault => detectorWithDefault.detect(img))
-    .then(t.step_func(faceDetectionResult => {
-        assert_equals(theMock.getMaxDetectedFaces(), 10, "default maxDetectedFaces");
-        assert_equals(theMock.getFastMode(), false, "default maxDetectedFaces");
-        return new FaceDetector({maxDetectedFaces: 7, fastMode: true});
-    }))
-    .then(detectorWithOptions => detectorWithOptions.detect(img))
-    .then(t.step_func(faceDetectionResult => {
-        assert_equals(theMock.getMaxDetectedFaces(), 7, "maxDetectedFaces");
-        assert_equals(theMock.getFastMode(), true, "maxDetectedFaces");
-        t.done();
-    }))
-    .catch(error => {
-      assert_unreached("Error creating detectors: " + error);
-    });
+  var mock = mockFaceDetectionProvider;
+
+  var detectorWithDefault = new FaceDetector();
+  var faceDetectionResult = await detectorWithDefault.detect(img);
+  assert_equals(mock.getMaxDetectedFaces(), 10, "default maxDetectedFaces");
+  assert_equals(mock.getFastMode(), false, "default maxDetectedFaces");
+
+  var detectorWithOptions =
+      new FaceDetector({maxDetectedFaces: 7, fastMode: true});
+  faceDetectionResult = await detectorWithOptions.detect(img);
+  assert_equals(mock.getMaxDetectedFaces(), 7, "maxDetectedFaces");
+  assert_equals(mock.getFastMode(), true, "maxDetectedFaces");
 }, "Test that FaceDetectionOptions are correctly propagated");
 </script>
diff --git a/third_party/WebKit/LayoutTests/shapedetection/detection-security-test.html b/third_party/WebKit/LayoutTests/shapedetection/detection-security-test.html
index 5f5f8de5..ffcaad04 100644
--- a/third_party/WebKit/LayoutTests/shapedetection/detection-security-test.html
+++ b/third_party/WebKit/LayoutTests/shapedetection/detection-security-test.html
@@ -1,29 +1,28 @@
 <!DOCTYPE html>
 <script src=../resources/testharness.js></script>
 <script src=../resources/testharnessreport.js></script>
-<script src="../resources/mojo-helpers.js"></script>
+<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
+<script src="file:///gen/services/shape_detection/public/interfaces/barcodedetection.mojom.js"></script>
+<script src="file:///gen/services/shape_detection/public/interfaces/facedetection.mojom.js"></script>
+<script src="file:///gen/services/shape_detection/public/interfaces/facedetection_provider.mojom.js"></script>
+<script src="file:///gen/services/shape_detection/public/interfaces/textdetection.mojom.js"></script>
+<script src="resources/mock-barcodedetection.js"></script>
 <script src="resources/mock-facedetection.js"></script>
+<script src="resources/mock-textdetection.js"></script>
 <script>
 
 // Returns a Promise that is resolve()d if detect() is rejected. Needs an input
 // |element| (e.g. an HTMLImageElement or HTMLVideoElement) and a |url| to load.
 function detectOnElementAndExpectError(createDetector, element, url) {
   return new Promise(function(resolve, reject) {
-    var tryDetection = function() {
-      var theMock = null;
-      mockFaceDetectionProviderReady
-          .then(mock => {
-            return createDetector();
-          })
-          .then(detector => {
-            return detector.detect(element);
-          })
-          .then(detectionResult => {
-            reject("Promise should have been rejected.");
-          })
-          .catch(error => {
-            resolve(error);
-          });
+    var tryDetection = async function() {
+      var detector = createDetector();
+      try {
+        var detectionResult = await detector.detect(element);
+        reject("Promise should have been rejected.");
+      } catch (error) {
+        resolve(error);
+      }
     };
     element.onload = tryDetection;
     element.onerror = tryDetection;
diff --git a/third_party/WebKit/LayoutTests/shapedetection/detector-same-object.html b/third_party/WebKit/LayoutTests/shapedetection/detector-same-object.html
index 1df6e3f..3fafbb01 100644
--- a/third_party/WebKit/LayoutTests/shapedetection/detector-same-object.html
+++ b/third_party/WebKit/LayoutTests/shapedetection/detector-same-object.html
@@ -1,43 +1,35 @@
 <!DOCTYPE html>
 <script src="../resources/testharness.js"></script>
 <script src="../resources/testharnessreport.js"></script>
-<script src="../resources/mojo-helpers.js"></script>
+<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
+<script src="file:///gen/services/shape_detection/public/interfaces/barcodedetection.mojom.js"></script>
+<script src="file:///gen/services/shape_detection/public/interfaces/facedetection.mojom.js"></script>
+<script src="file:///gen/services/shape_detection/public/interfaces/facedetection_provider.mojom.js"></script>
+<script src="file:///gen/services/shape_detection/public/interfaces/textdetection.mojom.js"></script>
 <script src="resources/mock-barcodedetection.js"></script>
 <script src="resources/mock-facedetection.js"></script>
 <script src="resources/mock-textdetection.js"></script>
 <script>
 
-var createTestForImageData = function(createDetector, mockReady,
+var createTestForImageData = function(createDetector, mock,
                                       detectionResultTest) {
   async_test(function(t) {
 
     var img = new Image();
-    img.onload = function() {
+    img.onload = async function() {
 
       var canvas = document.createElement("canvas");;
       canvas.getContext("2d").drawImage(img, 0, 0);
 
-      var theMock = null;
-      mockReady()
-        .then(mock => {
-          theMock = mock;
-          var detector = createDetector();
-          return detector;
-        })
-        .catch(error => {
-          assert_unreached("Error creating Mock Detector: " + error);
-        })
-        .then(detector => {
-          return detector.detect(canvas.getContext("2d").getImageData(
-              0, 0, canvas.width, canvas.height));
-        })
-        .then(detectionResult => {
-          detectionResultTest(detectionResult);
-          t.done();
-        })
-        .catch(error => {
-          assert_unreached("Error during detect(canvas): " + error);
-        });
+      var detector = createDetector();
+      try {
+        var detectionResult = await detector.detect(canvas.getContext("2d")
+            .getImageData(0, 0, canvas.width, canvas.height));
+        detectionResultTest(detectionResult);
+        t.done();
+      } catch(error) {
+        assert_unreached("Error during detect(canvas): " + error);
+      }
     }
 
     img.src = "../media/content/greenbox.png";
@@ -69,19 +61,19 @@
   [
     "Face - detect(ImageData), [SameObject]",
     () => { return new FaceDetector(); },
-    () => { return mockFaceDetectionProviderReady; },
+    mockFaceDetectionProvider,
     CheckDetectedFaceSameObjects
   ],
   [
     "Barcode - detect(ImageData), [SameObject]",
     () => { return new BarcodeDetector(); },
-    () => { return mockBarcodeDetectionReady; },
+    mockBarcodeDetection,
     CheckDetectedBarcodesSameObjects
   ],
   [
     "Text - detect(ImageData), [SameObject]",
     () => { return new TextDetector(); },
-    () => { return mockTextDetectionReady; },
+    mockTextDetection,
     CheckDetectedTextBlocksSameObjects
   ]
 ]);
diff --git a/third_party/WebKit/LayoutTests/shapedetection/resources/mock-barcodedetection.js b/third_party/WebKit/LayoutTests/shapedetection/resources/mock-barcodedetection.js
index 4f9dd625a..de58094 100644
--- a/third_party/WebKit/LayoutTests/shapedetection/resources/mock-barcodedetection.js
+++ b/third_party/WebKit/LayoutTests/shapedetection/resources/mock-barcodedetection.js
@@ -1,59 +1,49 @@
 "use strict";
 
-let mockBarcodeDetectionReady = define(
-  'mockBarcodeDetection',
-  ['services/shape_detection/public/interfaces/barcodedetection.mojom',
-   'mojo/public/js/bindings',
-   'mojo/public/js/core',
-   'content/public/renderer/frame_interfaces',
-   'content/public/renderer/interfaces',
-  ], (barcodeDetection, bindings, mojo, frameInterfaces, processInterfaces) => {
+class MockBarcodeDetection {
+  constructor() {
+    this.bindingSet_ = new mojo.BindingSet(
+        shapeDetection.mojom.BarcodeDetection);
 
-  class MockBarcodeDetection {
-    constructor() {
-      this.bindingSet_ = new bindings.BindingSet(
-          barcodeDetection.BarcodeDetection);
-
-      frameInterfaces.addInterfaceOverrideForTesting(
-          barcodeDetection.BarcodeDetection.name,
-          handle => this.bindingSet_.addBinding(this, handle));
-      processInterfaces.addInterfaceOverrideForTesting(
-          barcodeDetection.BarcodeDetection.name,
-          handle => this.bindingSet_.addBinding(this, handle));
-    }
-
-    detect(bitmap_data) {
-      let receivedStruct = new Uint8Array(bitmap_data.pixel_data);
-      this.buffer_data_ = new Uint32Array(receivedStruct.buffer);
-      return Promise.resolve({
-        results: [
-          {
-            raw_value : "cats",
-            bounding_box: { x: 1.0, y: 1.0, width: 100.0, height: 100.0 },
-            corner_points: [
-              { x: 1.0, y: 1.0 },
-              { x: 101.0, y: 1.0 },
-              { x: 101.0, y: 101.0 },
-              { x: 1.0, y: 101.0 }
-            ],
-          },
-          {
-            raw_value : "dogs",
-            bounding_box: { x: 2.0, y: 2.0, width: 50.0, height: 50.0 },
-            corner_points: [
-              { x: 2.0, y: 2.0 },
-              { x: 52.0, y: 2.0 },
-              { x: 52.0, y: 52.0 },
-              { x: 2.0, y: 52.0 }
-            ],
-          },
-        ],
-      });
-    }
-
-    getFrameData() {
-      return this.buffer_data_;
-    }
+    this.interceptor_ = new MojoInterfaceInterceptor(
+        shapeDetection.mojom.BarcodeDetection.name);
+    this.interceptor_.oninterfacerequest =
+        e => this.bindingSet_.addBinding(this, e.handle);
+    this.interceptor_.start();
   }
-  return new MockBarcodeDetection();
-});
+
+  detect(bitmap_data) {
+    let receivedStruct = new Uint8Array(bitmap_data.pixel_data);
+    this.buffer_data_ = new Uint32Array(receivedStruct.buffer);
+    return Promise.resolve({
+      results: [
+        {
+          rawValue : "cats",
+          boundingBox: { x: 1.0, y: 1.0, width: 100.0, height: 100.0 },
+          cornerPoints: [
+            { x: 1.0, y: 1.0 },
+            { x: 101.0, y: 1.0 },
+            { x: 101.0, y: 101.0 },
+            { x: 1.0, y: 101.0 }
+          ],
+        },
+        {
+          rawValue : "dogs",
+          boundingBox: { x: 2.0, y: 2.0, width: 50.0, height: 50.0 },
+          cornerPoints: [
+            { x: 2.0, y: 2.0 },
+            { x: 52.0, y: 2.0 },
+            { x: 52.0, y: 52.0 },
+            { x: 2.0, y: 52.0 }
+          ],
+        },
+      ],
+    });
+  }
+
+  getFrameData() {
+    return this.buffer_data_;
+  }
+}
+
+let mockBarcodeDetection = new MockBarcodeDetection();
diff --git a/third_party/WebKit/LayoutTests/shapedetection/resources/mock-facedetection.js b/third_party/WebKit/LayoutTests/shapedetection/resources/mock-facedetection.js
index e587cbf..f5c53e0 100644
--- a/third_party/WebKit/LayoutTests/shapedetection/resources/mock-facedetection.js
+++ b/third_party/WebKit/LayoutTests/shapedetection/resources/mock-facedetection.js
@@ -1,77 +1,65 @@
 "use strict";
 
-let mockFaceDetectionProviderReady = define(
-  'mockFaceDetectionProvider',
-  ['services/shape_detection/public/interfaces/facedetection.mojom',
-   'services/shape_detection/public/interfaces/facedetection_provider.mojom',
-   'mojo/public/js/bindings',
-   'mojo/public/js/core',
-   'content/public/renderer/frame_interfaces',
-   'content/public/renderer/interfaces',
-  ], (faceDetection, faceDetectionProvider, bindings, mojo, frameInterfaces,
-      processInterfaces) => {
+class MockFaceDetectionProvider {
+  constructor() {
+    this.bindingSet_ = new mojo.BindingSet(
+        shapeDetection.mojom.FaceDetectionProvider);
 
-  class MockFaceDetectionProvider {
-    constructor() {
-      this.bindingSet_ = new bindings.BindingSet(
-          faceDetectionProvider.FaceDetectionProvider);
-
-      frameInterfaces.addInterfaceOverrideForTesting(
-          faceDetectionProvider.FaceDetectionProvider.name,
-          handle => this.bindingSet_.addBinding(this, handle));
-      processInterfaces.addInterfaceOverrideForTesting(
-          faceDetectionProvider.FaceDetectionProvider.name,
-          handle => this.bindingSet_.addBinding(this, handle));
-    }
-
-    createFaceDetection(request, options) {
-      this.mockService_ = new MockFaceDetection(request, options);
-    }
-
-    getFrameData() {
-      return this.mockService_.bufferData_;
-    }
-
-    getMaxDetectedFaces() {
-     return this.mockService_.maxDetectedFaces_;
-    }
-
-    getFastMode () {
-      return this.mockService_.fastMode_;
-    }
+    this.interceptor_ = new MojoInterfaceInterceptor(
+        shapeDetection.mojom.FaceDetectionProvider.name);
+    this.interceptor_.oninterfacerequest =
+        e => this.bindingSet_.addBinding(this, e.handle);
+    this.interceptor_.start();
   }
 
-  class MockFaceDetection {
-    constructor(request, options) {
-      this.maxDetectedFaces_ = options.max_detected_faces;
-      this.fastMode_ = options.fast_mode;
-      this.binding_ = new bindings.Binding(faceDetection.FaceDetection, this,
-                                           request);
-    }
-
-    detect(bitmap_data) {
-      let receivedStruct = new Uint8Array(bitmap_data.pixel_data);
-      this.bufferData_ = new Uint32Array(receivedStruct.buffer);
-      return Promise.resolve({
-        results: [
-          {
-            bounding_box: {x: 1.0, y: 1.0, width: 100.0, height: 100.0},
-            landmarks: [{
-              type: faceDetection.LandmarkType.EYE,
-              location: {x: 4.0, y: 5.0}
-            }]
-          },
-          {
-            bounding_box: {x: 2.0, y: 2.0, width: 200.0, height: 200.0},
-            landmarks: []
-          },
-          {
-            bounding_box: {x: 3.0, y: 3.0, width: 300.0, height: 300.0},
-            landmarks: []
-          },
-        ]
-      });
-    }
+  createFaceDetection(request, options) {
+    this.mockService_ = new MockFaceDetection(request, options);
   }
-  return new MockFaceDetectionProvider();
-});
+
+  getFrameData() {
+    return this.mockService_.bufferData_;
+  }
+
+  getMaxDetectedFaces() {
+   return this.mockService_.maxDetectedFaces_;
+  }
+
+  getFastMode () {
+    return this.mockService_.fastMode_;
+  }
+}
+
+class MockFaceDetection {
+  constructor(request, options) {
+    this.maxDetectedFaces_ = options.maxDetectedFaces;
+    this.fastMode_ = options.fastMode;
+    this.binding_ =
+        new mojo.Binding(shapeDetection.mojom.FaceDetection, this, request);
+  }
+
+  detect(bitmap_data) {
+    let receivedStruct = new Uint8Array(bitmap_data.pixelData);
+    this.bufferData_ = new Uint32Array(receivedStruct.buffer);
+    return Promise.resolve({
+      results: [
+        {
+          boundingBox: {x: 1.0, y: 1.0, width: 100.0, height: 100.0},
+          landmarks: [{
+            type: shapeDetection.mojom.LandmarkType.EYE,
+            location: {x: 4.0, y: 5.0}
+          }]
+        },
+        {
+          boundingBox: {x: 2.0, y: 2.0, width: 200.0, height: 200.0},
+          landmarks: []
+        },
+        {
+          boundingBox: {x: 3.0, y: 3.0, width: 300.0, height: 300.0},
+          landmarks: []
+        },
+      ]
+    });
+  }
+}
+
+let mockFaceDetectionProvider = new MockFaceDetectionProvider();
diff --git a/third_party/WebKit/LayoutTests/shapedetection/resources/mock-textdetection.js b/third_party/WebKit/LayoutTests/shapedetection/resources/mock-textdetection.js
index a92054e..7e7c394 100644
--- a/third_party/WebKit/LayoutTests/shapedetection/resources/mock-textdetection.js
+++ b/third_party/WebKit/LayoutTests/shapedetection/resources/mock-textdetection.js
@@ -1,46 +1,37 @@
 "use strict";
 
-let mockTextDetectionReady = define(
-  'mockTextDetection',
-  ['services/shape_detection/public/interfaces/textdetection.mojom',
-   'mojo/public/js/bindings',
-   'mojo/public/js/core',
-   'content/public/renderer/frame_interfaces',
-   'content/public/renderer/interfaces',
-  ], (textDetection, bindings, mojo, frameInterfaces, processInterfaces) => {
+class MockTextDetection {
+  constructor() {
+    this.bindingSet_ =
+        new mojo.BindingSet(shapeDetection.mojom.TextDetection);
 
-  class MockTextDetection {
-    constructor() {
-      this.bindingSet_ = new bindings.BindingSet(textDetection.TextDetection);
-
-      frameInterfaces.addInterfaceOverrideForTesting(
-          textDetection.TextDetection.name,
-          handle => this.bindingSet_.addBinding(this, handle));
-      processInterfaces.addInterfaceOverrideForTesting(
-          textDetection.TextDetection.name,
-          handle => this.bindingSet_.addBinding(this, handle));
-    }
-
-    detect(bitmap_data) {
-      let receivedStruct = new Uint8Array(bitmap_data.pixel_data);
-      this.buffer_data_ = new Uint32Array(receivedStruct.buffer);
-      return Promise.resolve({
-        results: [
-          {
-            raw_value : "cats",
-            bounding_box: { x: 1.0, y: 1.0, width: 100.0, height: 100.0 }
-          },
-          {
-            raw_value : "dogs",
-            bounding_box: { x: 2.0, y: 2.0, width: 50.0, height: 50.0 }
-          },
-        ],
-      });
-    }
-
-    getFrameData() {
-      return this.buffer_data_;
-    }
+    this.interceptor_ = new MojoInterfaceInterceptor(
+        shapeDetection.mojom.TextDetection.name);
+    this.interceptor_.oninterfacerequest =
+        e => this.bindingSet_.addBinding(this, e.handle);
+    this.interceptor_.start();
   }
-  return new MockTextDetection();
-});
+
+  detect(bitmap_data) {
+    let receivedStruct = new Uint8Array(bitmap_data.pixel_data);
+    this.buffer_data_ = new Uint32Array(receivedStruct.buffer);
+    return Promise.resolve({
+      results: [
+        {
+          rawValue : "cats",
+          boundingBox: { x: 1.0, y: 1.0, width: 100.0, height: 100.0 }
+        },
+        {
+          rawValue : "dogs",
+          boundingBox: { x: 2.0, y: 2.0, width: 50.0, height: 50.0 }
+        },
+      ],
+    });
+  }
+
+  getFrameData() {
+    return this.buffer_data_;
+  }
+}
+
+let mockTextDetection = new MockTextDetection();
diff --git a/third_party/WebKit/LayoutTests/shapedetection/resources/worker.js b/third_party/WebKit/LayoutTests/shapedetection/resources/worker.js
index c26b28b..a590a500 100644
--- a/third_party/WebKit/LayoutTests/shapedetection/resources/worker.js
+++ b/third_party/WebKit/LayoutTests/shapedetection/resources/worker.js
@@ -1,21 +1,30 @@
 importScripts("../../resources/testharness.js");
+importScripts("file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js");
+importScripts("file:///gen/skia/public/interfaces/bitmap.mojom.js");
+importScripts("file:///gen/ui/gfx/geometry/mojo/geometry.mojom.js");
+importScripts("file:///gen/services/shape_detection/public/interfaces/barcodedetection.mojom.js");
+importScripts("file:///gen/services/shape_detection/public/interfaces/facedetection.mojom.js");
+importScripts("file:///gen/services/shape_detection/public/interfaces/facedetection_provider.mojom.js");
+importScripts("file:///gen/services/shape_detection/public/interfaces/textdetection.mojom.js");
+importScripts("mock-barcodedetection.js");
+importScripts("mock-facedetection.js");
+importScripts("mock-textdetection.js");
 
-onmessage = function(e) {
-  var detector;
+onmessage = async function(e) {
+  let detector;
   switch (e.data.detectorType) {
     case "Face": detector = new FaceDetector(); break;
     case "Barcode": detector = new BarcodeDetector(); break;
     case "Text": detector = new TextDetector(); break;
   }
 
-  var imageBitmap = e.data.bitmap;
-  detector.detect(imageBitmap)
-      .then(detectionResult => {
-        assert_equals(detectionResult.length, e.data.expectedLength,
-                      "Number of " + e.data.detectorType);
-        postMessage("PASS");
-      })
-      .catch(error => {
-        assert_unreached("Error during detect(img): " + error);
-      });
+  let imageBitmap = e.data.bitmap;
+  try {
+    let detectionResult = await detector.detect(imageBitmap);
+    assert_equals(detectionResult.length, e.data.expectedLength,
+                  "Number of " + e.data.detectorType);
+    postMessage("PASS");
+  } catch (error) {
+    assert_unreached("Error during detect(img): " + error);
+  }
 }
diff --git a/third_party/WebKit/LayoutTests/virtual/service-worker-navigation-preload-disabled/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/virtual/service-worker-navigation-preload-disabled/webexposed/global-interface-listing-expected.txt
index 260dc1d..9aa28ab3 100644
--- a/third_party/WebKit/LayoutTests/virtual/service-worker-navigation-preload-disabled/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/service-worker-navigation-preload-disabled/webexposed/global-interface-listing-expected.txt
@@ -24,21 +24,26 @@
     getter colCount
     getter colIndex
     getter colSpan
+    getter controls
     getter current
+    getter describedBy
     getter details
     getter disabled
     getter errorMessage
     getter expanded
+    getter flowTo
     getter hidden
     getter invalid
     getter keyShortcuts
     getter label
+    getter labeledBy
     getter level
     getter live
     getter modal
     getter multiline
     getter multiselectable
     getter orientation
+    getter owns
     getter placeholder
     getter posInSet
     getter pressed
@@ -66,21 +71,26 @@
     setter colCount
     setter colIndex
     setter colSpan
+    setter controls
     setter current
+    setter describedBy
     setter details
     setter disabled
     setter errorMessage
     setter expanded
+    setter flowTo
     setter hidden
     setter invalid
     setter keyShortcuts
     setter label
+    setter labeledBy
     setter level
     setter live
     setter modal
     setter multiline
     setter multiselectable
     setter orientation
+    setter owns
     setter placeholder
     setter posInSet
     setter pressed
@@ -99,6 +109,15 @@
     setter valueMin
     setter valueNow
     setter valueText
+interface AccessibleNodeList
+    attribute @@toStringTag
+    getter length
+    method @@iterator
+    method add
+    method constructor
+    method item
+    method remove
+    setter length
 interface AmbientLightSensor : Sensor
     attribute @@toStringTag
     getter illuminance
diff --git a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
index 8563df7a..84197b0 100644
--- a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
@@ -24,21 +24,26 @@
     getter colCount
     getter colIndex
     getter colSpan
+    getter controls
     getter current
+    getter describedBy
     getter details
     getter disabled
     getter errorMessage
     getter expanded
+    getter flowTo
     getter hidden
     getter invalid
     getter keyShortcuts
     getter label
+    getter labeledBy
     getter level
     getter live
     getter modal
     getter multiline
     getter multiselectable
     getter orientation
+    getter owns
     getter placeholder
     getter posInSet
     getter pressed
@@ -66,21 +71,26 @@
     setter colCount
     setter colIndex
     setter colSpan
+    setter controls
     setter current
+    setter describedBy
     setter details
     setter disabled
     setter errorMessage
     setter expanded
+    setter flowTo
     setter hidden
     setter invalid
     setter keyShortcuts
     setter label
+    setter labeledBy
     setter level
     setter live
     setter modal
     setter multiline
     setter multiselectable
     setter orientation
+    setter owns
     setter placeholder
     setter posInSet
     setter pressed
@@ -99,6 +109,15 @@
     setter valueMin
     setter valueNow
     setter valueText
+interface AccessibleNodeList
+    attribute @@toStringTag
+    getter length
+    method @@iterator
+    method add
+    method constructor
+    method item
+    method remove
+    setter length
 interface AmbientLightSensor : Sensor
     attribute @@toStringTag
     getter illuminance
diff --git a/third_party/WebKit/Source/bindings/core/v8/ActivityLoggerTest.cpp b/third_party/WebKit/Source/bindings/core/v8/ActivityLoggerTest.cpp
index ef367bc6..bcb39ae 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ActivityLoggerTest.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/ActivityLoggerTest.cpp
@@ -70,7 +70,7 @@
   Vector<String> logged_activities_;
 };
 
-class ActivityLoggerTest : public testing::Test {
+class ActivityLoggerTest : public ::testing::Test {
  protected:
   ActivityLoggerTest() {
     activity_logger_ = new TestActivityLogger();
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScriptStreamerTest.cpp b/third_party/WebKit/Source/bindings/core/v8/ScriptStreamerTest.cpp
index a8176cd..55c122b 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ScriptStreamerTest.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/ScriptStreamerTest.cpp
@@ -34,7 +34,8 @@
                                  ->CurrentThread()
                                  ->Scheduler()
                                  ->LoadingTaskRunner()),
-        settings_(Settings::Create()) {
+        settings_(Settings::Create()),
+        dummy_document_(Document::Create()) {
     resource_ = ScriptResource::CreateForTest(
         KURL(kParsedURLString, "http://www.streaming-test.com/"),
         UTF8Encoding());
@@ -45,6 +46,8 @@
     // the method(s) to return default values.
     EXPECT_CALL(*element, IntegrityAttributeValue())
         .WillRepeatedly(::testing::Return(String()));
+    EXPECT_CALL(*element, GetDocument())
+        .WillRepeatedly(::testing::ReturnRef(*dummy_document_.Get()));
 
     pending_script_ = ClassicPendingScript::Create(element, resource_.Get());
     ScriptStreamer::SetSmallScriptThresholdForTesting(0);
@@ -100,6 +103,8 @@
   // ScriptResource::appendData.
   Persistent<ScriptResource> resource_;
   Persistent<ClassicPendingScript> pending_script_;
+
+  Persistent<Document> dummy_document_;
 };
 
 class TestPendingScriptClient final
diff --git a/third_party/WebKit/Source/bindings/core/v8/custom/V8CSSStyleDeclarationCustom.cpp b/third_party/WebKit/Source/bindings/core/v8/custom/V8CSSStyleDeclarationCustom.cpp
index 90042c7..2a2da6b 100644
--- a/third_party/WebKit/Source/bindings/core/v8/custom/V8CSSStyleDeclarationCustom.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/custom/V8CSSStyleDeclarationCustom.cpp
@@ -49,8 +49,6 @@
 #include "platform/wtf/text/StringBuilder.h"
 #include "platform/wtf/text/StringConcatenate.h"
 
-using namespace WTF;
-
 namespace blink {
 
 // Check for a CSS prefix.
@@ -161,7 +159,7 @@
         property_names.push_back(getJSPropertyName(property_id));
     }
     std::sort(property_names.begin(), property_names.end(),
-              CodePointCompareLessThan);
+              WTF::CodePointCompareLessThan);
     property_names_length = property_names.size();
   }
 
diff --git a/third_party/WebKit/Source/build/scripts/make_css_property_names.py b/third_party/WebKit/Source/build/scripts/make_css_property_names.py
index f560950d..b239019 100755
--- a/third_party/WebKit/Source/build/scripts/make_css_property_names.py
+++ b/third_party/WebKit/Source/build/scripts/make_css_property_names.py
@@ -250,6 +250,7 @@
         gperf_args = [self.gperf_path, '--key-positions=*', '-P', '-n']
         gperf_args.extend(['-m', '50'])  # Pick best of 50 attempts.
         gperf_args.append('-D')  # Allow duplicate hashes -> More compact code.
+        gperf_args.extend(['-Q', 'CSSPropStringPool'])  # Unique var names.
 
         # If gperf isn't in the path we get an OSError. We don't want to use
         # the normal solution of shell=True (as this has to run on many
diff --git a/third_party/WebKit/Source/build/scripts/make_css_value_keywords.py b/third_party/WebKit/Source/build/scripts/make_css_value_keywords.py
index f8b773c..cc51f2d 100755
--- a/third_party/WebKit/Source/build/scripts/make_css_value_keywords.py
+++ b/third_party/WebKit/Source/build/scripts/make_css_value_keywords.py
@@ -174,6 +174,7 @@
         gperf_args = [self.gperf_path, '--key-positions=*', '-P', '-n']
         gperf_args.extend(['-m', '50'])  # Pick best of 50 attempts.
         gperf_args.append('-D')  # Allow duplicate hashes -> More compact code.
+        gperf_args.extend(['-Q', 'CSSValueStringPool'])  # Unique var names.
 
         # If gperf isn't in the path we get an OSError. We don't want to use
         # the normal solution of shell=True (as this has to run on many
diff --git a/third_party/WebKit/Source/build/scripts/templates/ElementFactory.cpp.tmpl b/third_party/WebKit/Source/build/scripts/templates/ElementFactory.cpp.tmpl
index 8327d67..b7b4be5b 100644
--- a/third_party/WebKit/Source/build/scripts/templates/ElementFactory.cpp.tmpl
+++ b/third_party/WebKit/Source/build/scripts/templates/ElementFactory.cpp.tmpl
@@ -22,26 +22,24 @@
 
 namespace blink {
 
-using namespace {{namespace}}Names;
-
-typedef {{namespace}}Element* (*ConstructorFunction)(
+typedef {{namespace}}Element* (*{{namespace}}ConstructorFunction)(
     Document&,
     CreateElementFlags);
 
-typedef HashMap<AtomicString, ConstructorFunction> FunctionMap;
+typedef HashMap<AtomicString, {{namespace}}ConstructorFunction> {{namespace}}FunctionMap;
 
-static FunctionMap* g_constructors = 0;
+static {{namespace}}FunctionMap* g_{{namespace}}_constructors = 0;
 
 {% for tag in tags|sort if not tag.noConstructor %}
-static {{namespace}}Element* {{tag|symbol}}Constructor(
+static {{namespace}}Element* {{namespace}}{{tag|symbol}}Constructor(
     Document& document,
     CreateElementFlags flags) {
   {% if tag.runtimeEnabled %}
   if (!RuntimeEnabledFeatures::{{tag.runtimeEnabled}}Enabled())
-    return {{fallback_interface}}::Create({{tag|symbol}}Tag, document);
+    return {{fallback_interface}}::Create({{namespace}}Names::{{tag|symbol}}Tag, document);
   {% endif %}
   return {{tag.interface}}::Create(
-      {%- if tag.multipleTagNames %}{{tag|symbol}}Tag, {% endif -%}
+      {%- if tag.multipleTagNames %}{{namespace}}Names::{{tag|symbol}}Tag, {% endif -%}
       document
       {%- if tag.constructorNeedsCreatedByParser %}, flags & kCreatedByParser{% endif -%}
   );
@@ -50,30 +48,30 @@
 
 struct Create{{namespace}}FunctionMapData {
   const QualifiedName& tag;
-  ConstructorFunction func;
+  {{namespace}}ConstructorFunction func;
 };
 
 static void create{{namespace}}FunctionMap() {
-  DCHECK(!g_constructors);
-  g_constructors = new FunctionMap;
+  DCHECK(!g_{{namespace}}_constructors);
+  g_{{namespace}}_constructors = new {{namespace}}FunctionMap;
   // Empty array initializer lists are illegal [dcl.init.aggr] and will not
   // compile in MSVC. If tags list is empty, add check to skip this.
   static const Create{{namespace}}FunctionMapData data[] = {
   {% for tag in tags|sort if not tag.noConstructor %}
-    { {{tag|symbol}}Tag, {{tag|symbol}}Constructor },
+    { {{namespace}}Names::{{tag|symbol}}Tag, {{namespace}}{{tag|symbol}}Constructor },
   {% endfor %}
   };
   for (size_t i = 0; i < WTF_ARRAY_LENGTH(data); i++)
-    g_constructors->Set(data[i].tag.LocalName(), data[i].func);
+    g_{{namespace}}_constructors->Set(data[i].tag.LocalName(), data[i].func);
 }
 
 {{namespace}}Element* {{namespace}}ElementFactory::create{{namespace}}Element(
     const AtomicString& localName,
     Document& document,
     CreateElementFlags flags) {
-  if (!g_constructors)
+  if (!g_{{namespace}}_constructors)
     create{{namespace}}FunctionMap();
-  if (ConstructorFunction function = g_constructors->at(localName))
+  if ({{namespace}}ConstructorFunction function = g_{{namespace}}_constructors->at(localName))
     return function(document, flags);
 
   {% if namespace == 'HTML' %}
@@ -93,12 +91,12 @@
   if (document.RegistrationContext() &&
       V0CustomElement::IsValidName(localName)) {
     Element* element = document.RegistrationContext()->CreateCustomTagElement(
-        document, QualifiedName(g_null_atom, localName, {{namespace_prefix}}NamespaceURI));
+        document, QualifiedName(g_null_atom, localName, {{namespace}}Names::{{namespace_prefix}}NamespaceURI));
     SECURITY_DCHECK(element->Is{{namespace}}Element());
     return To{{namespace}}Element(element);
   }
 
-  return {{fallback_interface}}::Create(QualifiedName(g_null_atom, localName, {{namespace_prefix}}NamespaceURI), document);
+  return {{fallback_interface}}::Create(QualifiedName(g_null_atom, localName, {{namespace}}Names::{{namespace_prefix}}NamespaceURI), document);
 }
 
 } // namespace blink
diff --git a/third_party/WebKit/Source/core/core_idl_files.gni b/third_party/WebKit/Source/core/core_idl_files.gni
index ecf084c..c8c5ae7 100644
--- a/third_party/WebKit/Source/core/core_idl_files.gni
+++ b/third_party/WebKit/Source/core/core_idl_files.gni
@@ -84,6 +84,7 @@
                                  "css/cssom/StylePropertyMap.idl",
                                  "css/cssom/StylePropertyMapReadonly.idl",
                                  "dom/AccessibleNode.idl",
+                                 "dom/AccessibleNodeList.idl",
                                  "dom/ArrayBuffer.idl",
                                  "dom/ArrayBufferView.idl",
                                  "dom/Attr.idl",
diff --git a/third_party/WebKit/Source/core/css/FontFace.idl b/third_party/WebKit/Source/core/css/FontFace.idl
index 8d94ec59..ea9368e 100644
--- a/third_party/WebKit/Source/core/css/FontFace.idl
+++ b/third_party/WebKit/Source/core/css/FontFace.idl
@@ -28,7 +28,7 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-// https://dev.w3.org/csswg/css-font-loading/#fontface-interface
+// https://drafts.csswg.org/css-font-loading/#fontface-interface
 
 enum FontFaceLoadStatus {
     "unloaded",
diff --git a/third_party/WebKit/Source/core/css/FontFaceDescriptors.idl b/third_party/WebKit/Source/core/css/FontFaceDescriptors.idl
index 5330445..fa21377 100644
--- a/third_party/WebKit/Source/core/css/FontFaceDescriptors.idl
+++ b/third_party/WebKit/Source/core/css/FontFaceDescriptors.idl
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// https://dev.w3.org/csswg/css-font-loading/#dictdef-fontfacedescriptors
+// https://drafts.csswg.org/css-font-loading/#dictdef-fontfacedescriptors
 
 dictionary FontFaceDescriptors {
     DOMString style = "normal";
diff --git a/third_party/WebKit/Source/core/css/FontFaceSet.idl b/third_party/WebKit/Source/core/css/FontFaceSet.idl
index f636dada..2e8622d 100644
--- a/third_party/WebKit/Source/core/css/FontFaceSet.idl
+++ b/third_party/WebKit/Source/core/css/FontFaceSet.idl
@@ -28,7 +28,7 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-// https://dev.w3.org/csswg/css-font-loading/#FontFaceSet-interface
+// https://drafts.csswg.org/css-font-loading/#FontFaceSet-interface
 
 enum FontFaceSetLoadStatus { "loading", "loaded" };
 
diff --git a/third_party/WebKit/Source/core/css/FontFaceSetLoadEvent.idl b/third_party/WebKit/Source/core/css/FontFaceSetLoadEvent.idl
index f22dd2c..59da6a5 100644
--- a/third_party/WebKit/Source/core/css/FontFaceSetLoadEvent.idl
+++ b/third_party/WebKit/Source/core/css/FontFaceSetLoadEvent.idl
@@ -28,7 +28,7 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-// https://dev.w3.org/csswg/css-font-loading/#fontfacesetloadevent
+// https://drafts.csswg.org/css-font-loading/#fontfacesetloadevent
 
 [
   Constructor(DOMString type, optional FontFaceSetLoadEventInit eventInitDict),
diff --git a/third_party/WebKit/Source/core/css/FontFaceSetLoadEventInit.idl b/third_party/WebKit/Source/core/css/FontFaceSetLoadEventInit.idl
index 288c8005..e47188d 100644
--- a/third_party/WebKit/Source/core/css/FontFaceSetLoadEventInit.idl
+++ b/third_party/WebKit/Source/core/css/FontFaceSetLoadEventInit.idl
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// https://dev.w3.org/csswg/css-font-loading/#fontfacesetloadevent
+// https://drafts.csswg.org/css-font-loading/#fontfacesetloadevent
 
 dictionary FontFaceSetLoadEventInit : EventInit {
     sequence<FontFace> fontfaces = [];
diff --git a/third_party/WebKit/Source/core/dom/AccessibleNode.cpp b/third_party/WebKit/Source/core/dom/AccessibleNode.cpp
index 05f23c0..18a4673 100644
--- a/third_party/WebKit/Source/core/dom/AccessibleNode.cpp
+++ b/third_party/WebKit/Source/core/dom/AccessibleNode.cpp
@@ -5,6 +5,7 @@
 #include "core/dom/AccessibleNode.h"
 
 #include "core/dom/AXObjectCache.h"
+#include "core/dom/AccessibleNodeList.h"
 #include "core/dom/Element.h"
 #include "core/dom/QualifiedName.h"
 #include "core/frame/Settings.h"
@@ -70,6 +71,31 @@
   return g_null_name;
 }
 
+QualifiedName GetCorrespondingARIAAttribute(AOMRelationListProperty property) {
+  switch (property) {
+    case AOMRelationListProperty::kDescribedBy:
+      return aria_describedbyAttr;
+      break;
+    case AOMRelationListProperty::kControls:
+      return aria_controlsAttr;
+      break;
+    case AOMRelationListProperty::kFlowTo:
+      return aria_flowtoAttr;
+      break;
+    case AOMRelationListProperty::kLabeledBy:
+      // Note that there are two allowed spellings of this attribute.
+      // Callers should check both.
+      return aria_labelledbyAttr;
+      break;
+    case AOMRelationListProperty::kOwns:
+      return aria_ownsAttr;
+      break;
+  }
+
+  NOTREACHED();
+  return g_null_name;
+}
+
 QualifiedName GetCorrespondingARIAAttribute(AOMBooleanProperty property) {
   switch (property) {
     case AOMBooleanProperty::kAtomic:
@@ -212,6 +238,43 @@
   return nullptr;
 }
 
+// static
+AccessibleNodeList* AccessibleNode::GetProperty(
+    Element* element,
+    AOMRelationListProperty property) {
+  if (!element)
+    return nullptr;
+
+  if (AccessibleNode* accessible_node = element->ExistingAccessibleNode()) {
+    for (const auto& item : accessible_node->relation_list_properties_) {
+      if (item.first == property && item.second)
+        return item.second;
+    }
+  }
+
+  return nullptr;
+}
+
+// static
+bool AccessibleNode::GetProperty(Element* element,
+                                 AOMRelationListProperty property,
+                                 HeapVector<Member<Element>>& targets) {
+  AccessibleNodeList* node_list = GetProperty(element, property);
+  if (!node_list)
+    return false;
+
+  for (size_t i = 0; i < node_list->length(); ++i) {
+    AccessibleNode* accessible_node = node_list->item(i);
+    if (accessible_node) {
+      Element* element = accessible_node->element();
+      if (element)
+        targets.push_back(element);
+    }
+  }
+
+  return true;
+}
+
 template <typename P, typename T>
 static T FindPropertyValue(P property,
                            bool& is_null,
@@ -324,6 +387,39 @@
 }
 
 // static
+bool AccessibleNode::GetPropertyOrARIAAttribute(
+    Element* element,
+    AOMRelationListProperty property,
+    HeapVector<Member<Element>>& targets) {
+  if (!element)
+    return false;
+
+  if (GetProperty(element, property, targets))
+    return true;
+
+  // Fall back on the equivalent ARIA attribute.
+  QualifiedName attribute = GetCorrespondingARIAAttribute(property);
+  String value = element->FastGetAttribute(attribute).GetString();
+  if (value.IsEmpty() && property == AOMRelationListProperty::kLabeledBy)
+    value = element->FastGetAttribute(aria_labeledbyAttr).GetString();
+  if (value.IsEmpty())
+    return false;
+
+  value.SimplifyWhiteSpace();
+  Vector<String> ids;
+  value.Split(' ', ids);
+  if (ids.IsEmpty())
+    return false;
+
+  TreeScope& scope = element->GetTreeScope();
+  for (const auto& id : ids) {
+    if (Element* id_element = scope.getElementById(AtomicString(id)))
+      targets.push_back(id_element);
+  }
+  return true;
+}
+
+// static
 bool AccessibleNode::GetPropertyOrARIAAttribute(Element* element,
                                                 AOMBooleanProperty property,
                                                 bool& is_null) {
@@ -429,9 +525,17 @@
     shadowed_aria_attributes.insert(GetCorrespondingARIAAttribute(item.first));
   }
   for (auto& item : accessible_node->relation_properties_) {
+    if (!item.second)
+      continue;
     client->AddRelationProperty(item.first, *item.second);
     shadowed_aria_attributes.insert(GetCorrespondingARIAAttribute(item.first));
   }
+  for (auto& item : accessible_node->relation_list_properties_) {
+    if (!item.second)
+      continue;
+    client->AddRelationListProperty(item.first, *item.second);
+    shadowed_aria_attributes.insert(GetCorrespondingARIAAttribute(item.first));
+  }
 }
 
 AccessibleNode* AccessibleNode::activeDescendant() const {
@@ -507,6 +611,15 @@
   NotifyAttributeChanged(aria_colspanAttr);
 }
 
+AccessibleNodeList* AccessibleNode::controls() const {
+  return GetProperty(element_, AOMRelationListProperty::kControls);
+}
+
+void AccessibleNode::setControls(AccessibleNodeList* controls) {
+  SetRelationListProperty(AOMRelationListProperty::kControls, controls);
+  NotifyAttributeChanged(aria_controlsAttr);
+}
+
 AtomicString AccessibleNode::current() const {
   return GetProperty(element_, AOMStringProperty::kCurrent);
 }
@@ -518,6 +631,15 @@
     cache->HandleAttributeChanged(aria_currentAttr, element_);
 }
 
+AccessibleNodeList* AccessibleNode::describedBy() {
+  return GetProperty(element_, AOMRelationListProperty::kDescribedBy);
+}
+
+void AccessibleNode::setDescribedBy(AccessibleNodeList* described_by) {
+  SetRelationListProperty(AOMRelationListProperty::kDescribedBy, described_by);
+  NotifyAttributeChanged(aria_describedbyAttr);
+}
+
 AccessibleNode* AccessibleNode::details() const {
   return GetProperty(element_, AOMRelationProperty::kDetails);
 }
@@ -554,6 +676,15 @@
   NotifyAttributeChanged(aria_expandedAttr);
 }
 
+AccessibleNodeList* AccessibleNode::flowTo() const {
+  return GetProperty(element_, AOMRelationListProperty::kFlowTo);
+}
+
+void AccessibleNode::setFlowTo(AccessibleNodeList* flow_to) {
+  SetRelationListProperty(AOMRelationListProperty::kFlowTo, flow_to);
+  NotifyAttributeChanged(aria_flowtoAttr);
+}
+
 bool AccessibleNode::hidden(bool& is_null) const {
   return GetProperty(element_, AOMBooleanProperty::kHidden, is_null);
 }
@@ -590,6 +721,15 @@
   NotifyAttributeChanged(aria_labelAttr);
 }
 
+AccessibleNodeList* AccessibleNode::labeledBy() {
+  return GetProperty(element_, AOMRelationListProperty::kLabeledBy);
+}
+
+void AccessibleNode::setLabeledBy(AccessibleNodeList* labeled_by) {
+  SetRelationListProperty(AOMRelationListProperty::kLabeledBy, labeled_by);
+  NotifyAttributeChanged(aria_labelledbyAttr);
+}
+
 uint32_t AccessibleNode::level(bool& is_null) const {
   return GetProperty(element_, AOMUIntProperty::kLevel, is_null);
 }
@@ -645,6 +785,15 @@
   NotifyAttributeChanged(aria_orientationAttr);
 }
 
+AccessibleNodeList* AccessibleNode::owns() const {
+  return GetProperty(element_, AOMRelationListProperty::kOwns);
+}
+
+void AccessibleNode::setOwns(AccessibleNodeList* owns) {
+  SetRelationListProperty(AOMRelationListProperty::kOwns, owns);
+  NotifyAttributeChanged(aria_ownsAttr);
+}
+
 AtomicString AccessibleNode::placeholder() const {
   return GetProperty(element_, AOMStringProperty::kPlaceholder);
 }
@@ -831,6 +980,22 @@
   relation_properties_.push_back(std::make_pair(property, value));
 }
 
+void AccessibleNode::SetRelationListProperty(AOMRelationListProperty property,
+                                             AccessibleNodeList* value) {
+  for (auto& item : relation_list_properties_) {
+    if (item.first == property) {
+      if (item.second)
+        item.second->RemoveOwner(property, this);
+      if (value)
+        value->AddOwner(property, this);
+      item.second = value;
+      return;
+    }
+  }
+
+  relation_list_properties_.push_back(std::make_pair(property, value));
+}
+
 template <typename P, typename T>
 static void SetProperty(P property,
                         T value,
@@ -874,6 +1039,10 @@
   SetProperty(property, value, is_null, float_properties_);
 }
 
+void AccessibleNode::OnRelationListChanged(AOMRelationListProperty property) {
+  NotifyAttributeChanged(GetCorrespondingARIAAttribute(property));
+}
+
 void AccessibleNode::NotifyAttributeChanged(
     const blink::QualifiedName& attribute) {
   // TODO(dmazzoni): Make a cleaner API for this rather than pretending
@@ -889,6 +1058,7 @@
 DEFINE_TRACE(AccessibleNode) {
   visitor->Trace(element_);
   visitor->Trace(relation_properties_);
+  visitor->Trace(relation_list_properties_);
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/dom/AccessibleNode.h b/third_party/WebKit/Source/core/dom/AccessibleNode.h
index aacad2d3..d7362df 100644
--- a/third_party/WebKit/Source/core/dom/AccessibleNode.h
+++ b/third_party/WebKit/Source/core/dom/AccessibleNode.h
@@ -15,6 +15,7 @@
 
 namespace blink {
 
+class AccessibleNodeList;
 class AXObjectCache;
 class Element;
 class QualifiedName;
@@ -69,6 +70,14 @@
   kErrorMessage,
 };
 
+enum class AOMRelationListProperty {
+  kDescribedBy,
+  kControls,
+  kFlowTo,
+  kLabeledBy,
+  kOwns,
+};
+
 // All of the properties of AccessibleNode that have a signed integer type.
 // (These all allow the value -1.)
 enum class AOMIntProperty { kColCount, kRowCount, kSetSize };
@@ -87,6 +96,8 @@
   virtual void AddFloatProperty(AOMFloatProperty, float) = 0;
   virtual void AddRelationProperty(AOMRelationProperty,
                                    const AccessibleNode&) = 0;
+  virtual void AddRelationListProperty(AOMRelationListProperty,
+                                       const AccessibleNodeList&) = 0;
 };
 
 // Accessibility Object Model node
@@ -110,6 +121,13 @@
   // Returns the given relation property if the Element has an AccessibleNode.
   static AccessibleNode* GetProperty(Element*, AOMRelationProperty);
 
+  // Returns the given relation list property if the Element has an
+  // AccessibleNode.
+  static AccessibleNodeList* GetProperty(Element*, AOMRelationListProperty);
+  static bool GetProperty(Element*,
+                          AOMRelationListProperty,
+                          HeapVector<Member<Element>>&);
+
   // Returns the value of the given property if the
   // Element has an AccessibleNode. Sets |isNull| if the property and
   // attribute are not present.
@@ -129,6 +147,13 @@
   static AccessibleNode* GetPropertyOrARIAAttribute(Element*,
                                                     AOMRelationProperty);
 
+  // Returns true and provides the the value of the given relation
+  // list property if the Element has an AccessibleNode, or if it has
+  // the equivalent ARIA attribute. Otherwise returns false.
+  static bool GetPropertyOrARIAAttribute(Element*,
+                                         AOMRelationListProperty,
+                                         HeapVector<Member<Element>>&);
+
   // Returns the value of the given property if the
   // Element has an AccessibleNode, otherwise returns the equivalent
   // ARIA attribute. Sets |isNull| if the property and attribute are not
@@ -179,9 +204,15 @@
   uint32_t colSpan(bool& is_null) const;
   void setColSpan(uint32_t, bool is_null);
 
+  AccessibleNodeList* controls() const;
+  void setControls(AccessibleNodeList*);
+
   AtomicString current() const;
   void setCurrent(const AtomicString&);
 
+  AccessibleNodeList* describedBy();
+  void setDescribedBy(AccessibleNodeList*);
+
   AccessibleNode* details() const;
   void setDetails(AccessibleNode*);
 
@@ -194,6 +225,9 @@
   bool expanded(bool& is_null) const;
   void setExpanded(bool, bool is_null);
 
+  AccessibleNodeList* flowTo() const;
+  void setFlowTo(AccessibleNodeList*);
+
   bool hidden(bool& is_null) const;
   void setHidden(bool, bool is_null);
 
@@ -206,6 +240,9 @@
   AtomicString label() const;
   void setLabel(const AtomicString&);
 
+  AccessibleNodeList* labeledBy();
+  void setLabeledBy(AccessibleNodeList*);
+
   uint32_t level(bool& is_null) const;
   void setLevel(uint32_t, bool is_null);
 
@@ -224,6 +261,9 @@
   AtomicString orientation() const;
   void setOrientation(const AtomicString&);
 
+  AccessibleNodeList* owns() const;
+  void setOwns(AccessibleNodeList*);
+
   AtomicString placeholder() const;
   void setPlaceholder(const AtomicString&);
 
@@ -280,9 +320,14 @@
 
   DECLARE_VIRTUAL_TRACE();
 
+ protected:
+  friend class AccessibleNodeList;
+  void OnRelationListChanged(AOMRelationListProperty);
+
  private:
   void SetStringProperty(AOMStringProperty, const AtomicString&);
   void SetRelationProperty(AOMRelationProperty, AccessibleNode*);
+  void SetRelationListProperty(AOMRelationListProperty, AccessibleNodeList*);
   void SetBooleanProperty(AOMBooleanProperty, bool value, bool is_null);
   void SetFloatProperty(AOMFloatProperty, float value, bool is_null);
   void SetUIntProperty(AOMUIntProperty, uint32_t value, bool is_null);
@@ -297,6 +342,8 @@
   Vector<std::pair<AOMUIntProperty, uint32_t>> uint_properties_;
   HeapVector<std::pair<AOMRelationProperty, Member<AccessibleNode>>>
       relation_properties_;
+  HeapVector<std::pair<AOMRelationListProperty, Member<AccessibleNodeList>>>
+      relation_list_properties_;
 
   // This object's owner Element.
   Member<Element> element_;
diff --git a/third_party/WebKit/Source/core/dom/AccessibleNode.idl b/third_party/WebKit/Source/core/dom/AccessibleNode.idl
index cb5ce8f..0c8356c 100644
--- a/third_party/WebKit/Source/core/dom/AccessibleNode.idl
+++ b/third_party/WebKit/Source/core/dom/AccessibleNode.idl
@@ -16,21 +16,26 @@
     attribute long? colCount;
     attribute unsigned long? colIndex;
     attribute unsigned long? colSpan;
+    attribute AccessibleNodeList? controls;
     attribute DOMString? current;
+    attribute AccessibleNodeList? describedBy;
     attribute AccessibleNode? details;
     attribute boolean? disabled;
     attribute AccessibleNode? errorMessage;
     attribute boolean? expanded;
+    attribute AccessibleNodeList? flowTo;
     attribute boolean? hidden;
     attribute DOMString? invalid;
     attribute DOMString? keyShortcuts;
     attribute DOMString? label;
+    attribute AccessibleNodeList? labeledBy;
     attribute unsigned long? level;
     attribute DOMString? live;
     attribute boolean? modal;
     attribute boolean? multiline;
     attribute boolean? multiselectable;
     attribute DOMString? orientation;
+    attribute AccessibleNodeList? owns;
     attribute DOMString? placeholder;
     attribute unsigned long? posInSet;
     attribute DOMString? pressed;
diff --git a/third_party/WebKit/Source/core/dom/AccessibleNodeList.cpp b/third_party/WebKit/Source/core/dom/AccessibleNodeList.cpp
new file mode 100644
index 0000000..e905e928
--- /dev/null
+++ b/third_party/WebKit/Source/core/dom/AccessibleNodeList.cpp
@@ -0,0 +1,112 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/dom/AccessibleNodeList.h"
+
+#include "core/dom/AccessibleNode.h"
+
+namespace blink {
+
+// The spec doesn't give a limit, but there's no reason to allow relations
+// between an arbitrarily large number of other accessible nodes.
+static const unsigned kMaxItems = 65536;
+
+// static
+AccessibleNodeList* AccessibleNodeList::Create(
+    const HeapVector<Member<AccessibleNode>>& nodes) {
+  AccessibleNodeList* result = new AccessibleNodeList();
+  result->nodes_ = nodes;
+  return result;
+}
+
+AccessibleNodeList::AccessibleNodeList() {
+  DCHECK(RuntimeEnabledFeatures::AccessibilityObjectModelEnabled());
+}
+
+AccessibleNodeList::~AccessibleNodeList() {}
+
+void AccessibleNodeList::AddOwner(AOMRelationListProperty property,
+                                  AccessibleNode* node) {
+  owners_.push_back(std::make_pair(property, node));
+}
+
+void AccessibleNodeList::RemoveOwner(AOMRelationListProperty property,
+                                     AccessibleNode* node) {
+  for (size_t i = 0; i < owners_.size(); ++i) {
+    auto& item = owners_[i];
+    if (item.first == property && item.second == node) {
+      owners_.erase(i);
+      return;
+    }
+  }
+}
+
+AccessibleNode* AccessibleNodeList::item(unsigned offset) const {
+  if (offset < nodes_.size())
+    return nodes_[offset];
+  return nullptr;
+}
+
+void AccessibleNodeList::add(AccessibleNode* node, AccessibleNode* before) {
+  if (nodes_.size() == kMaxItems)
+    return;
+
+  unsigned index = nodes_.size();
+  if (before) {
+    for (index = 0; index < nodes_.size(); ++index) {
+      if (nodes_[index] == before)
+        break;
+    }
+    if (index == nodes_.size())
+      return;
+  }
+
+  nodes_.insert(index, node);
+}
+
+void AccessibleNodeList::remove(int index) {
+  if (index >= 0 && index < static_cast<int>(nodes_.size()))
+    nodes_.erase(index);
+}
+
+bool AccessibleNodeList::AnonymousIndexedSetter(unsigned index,
+                                                AccessibleNode* node,
+                                                ExceptionState& state) {
+  if (!node) {
+    remove(index);
+    return true;
+  }
+  if (index >= kMaxItems)
+    return false;
+  if (index >= nodes_.size()) {
+    unsigned old_size = nodes_.size();
+    nodes_.resize(index + 1);
+    for (unsigned i = old_size; i < nodes_.size(); ++i)
+      nodes_[i] = nullptr;
+  }
+  nodes_[index] = node;
+  return true;
+}
+
+unsigned AccessibleNodeList::length() const {
+  return nodes_.size();
+}
+
+void AccessibleNodeList::setLength(unsigned new_length) {
+  if (new_length >= kMaxItems)
+    return;
+  nodes_.resize(new_length);
+}
+
+void AccessibleNodeList::NotifyChanged() {
+  for (auto& owner : owners_)
+    owner.second->OnRelationListChanged(owner.first);
+}
+
+DEFINE_TRACE(AccessibleNodeList) {
+  visitor->Trace(nodes_);
+  visitor->Trace(owners_);
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/core/dom/AccessibleNodeList.h b/third_party/WebKit/Source/core/dom/AccessibleNodeList.h
new file mode 100644
index 0000000..130ede4
--- /dev/null
+++ b/third_party/WebKit/Source/core/dom/AccessibleNodeList.h
@@ -0,0 +1,53 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef AccessibleNodeList_h
+#define AccessibleNodeList_h
+
+#include "core/CoreExport.h"
+#include "platform/bindings/ScriptWrappable.h"
+
+namespace blink {
+
+class AccessibleNode;
+enum class AOMRelationListProperty;
+class ExceptionState;
+
+// Accessibility Object Model node list
+// Explainer: https://github.com/WICG/aom/blob/master/explainer.md
+// Spec: https://wicg.github.io/aom/spec/
+class CORE_EXPORT AccessibleNodeList
+    : public GarbageCollectedFinalized<AccessibleNodeList>,
+      public ScriptWrappable {
+  DEFINE_WRAPPERTYPEINFO();
+
+ public:
+  static AccessibleNodeList* Create(const HeapVector<Member<AccessibleNode>>&);
+
+  AccessibleNodeList();
+  virtual ~AccessibleNodeList();
+
+  void AddOwner(AOMRelationListProperty, AccessibleNode*);
+  void RemoveOwner(AOMRelationListProperty, AccessibleNode*);
+
+  AccessibleNode* item(unsigned offset) const;
+  void add(AccessibleNode*, AccessibleNode* = nullptr);
+  void remove(int index);
+  bool AnonymousIndexedSetter(unsigned, AccessibleNode*, ExceptionState&);
+  unsigned length() const;
+  void setLength(unsigned);
+
+  DECLARE_VIRTUAL_TRACE();
+
+ private:
+  void NotifyChanged();
+
+  HeapVector<std::pair<AOMRelationListProperty, Member<AccessibleNode>>>
+      owners_;
+  HeapVector<Member<AccessibleNode>> nodes_;
+};
+
+}  // namespace blink
+
+#endif  // AccessibleNodeList_h
diff --git a/third_party/WebKit/Source/core/dom/AccessibleNodeList.idl b/third_party/WebKit/Source/core/dom/AccessibleNodeList.idl
new file mode 100644
index 0000000..ec9a2b1
--- /dev/null
+++ b/third_party/WebKit/Source/core/dom/AccessibleNodeList.idl
@@ -0,0 +1,17 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Accessibility Object Model node list
+// Explainer: https://github.com/WICG/aom/blob/master/explainer.md
+// Spec: https://wicg.github.io/aom/spec/
+[
+    Constructor(optional sequence<AccessibleNode> nodes = []),
+    RuntimeEnabled=AccessibilityObjectModel
+] interface AccessibleNodeList {
+  attribute unsigned long length;
+  getter AccessibleNode? item(unsigned long index);
+  [RaisesException] setter void (unsigned long index, AccessibleNode node);
+  void add(AccessibleNode node, optional AccessibleNode? before = null);
+  void remove(long index);
+};
diff --git a/third_party/WebKit/Source/core/dom/BUILD.gn b/third_party/WebKit/Source/core/dom/BUILD.gn
index be72fd34..850f480 100644
--- a/third_party/WebKit/Source/core/dom/BUILD.gn
+++ b/third_party/WebKit/Source/core/dom/BUILD.gn
@@ -14,6 +14,8 @@
     "AXObjectCacheBase.h",
     "AccessibleNode.cpp",
     "AccessibleNode.h",
+    "AccessibleNodeList.cpp",
+    "AccessibleNodeList.h",
     "AncestorList.h",
     "AnimationWorkletProxyClient.cpp",
     "AnimationWorkletProxyClient.h",
diff --git a/third_party/WebKit/Source/core/dom/ClassicPendingScript.cpp b/third_party/WebKit/Source/core/dom/ClassicPendingScript.cpp
index 945f5e6c..39ebbfaf 100644
--- a/third_party/WebKit/Source/core/dom/ClassicPendingScript.cpp
+++ b/third_party/WebKit/Source/core/dom/ClassicPendingScript.cpp
@@ -75,51 +75,6 @@
   AdvanceReadyState(error_occurred ? kErrorOccurred : kReady);
 }
 
-// Returns true if SRI check passed.
-static bool CheckScriptResourceIntegrity(Resource* resource,
-                                         ScriptElementBase* element) {
-  DCHECK_EQ(resource->GetType(), Resource::kScript);
-  ScriptResource* script_resource = ToScriptResource(resource);
-  String integrity_attr = element->IntegrityAttributeValue();
-
-  // It is possible to get back a script resource with integrity metadata
-  // for a request with an empty integrity attribute. In that case, the
-  // integrity check should be skipped, so this check ensures that the
-  // integrity attribute isn't empty in addition to checking if the
-  // resource has empty integrity metadata.
-  if (integrity_attr.IsEmpty() ||
-      script_resource->IntegrityMetadata().IsEmpty())
-    return true;
-
-  switch (script_resource->IntegrityDisposition()) {
-    case ResourceIntegrityDisposition::kPassed:
-      return true;
-
-    case ResourceIntegrityDisposition::kFailed:
-      // TODO(jww): This should probably also generate a console
-      // message identical to the one produced by
-      // CheckSubresourceIntegrity below. See https://crbug.com/585267.
-      return false;
-
-    case ResourceIntegrityDisposition::kNotChecked: {
-      if (!resource->ResourceBuffer())
-        return true;
-
-      bool passed = SubresourceIntegrity::CheckSubresourceIntegrity(
-          script_resource->IntegrityMetadata(), element->GetDocument(),
-          resource->ResourceBuffer()->Data(),
-          resource->ResourceBuffer()->size(), resource->Url(), *resource);
-      script_resource->SetIntegrityDisposition(
-          passed ? ResourceIntegrityDisposition::kPassed
-                 : ResourceIntegrityDisposition::kFailed);
-      return passed;
-    }
-  }
-
-  NOTREACHED();
-  return true;
-}
-
 void ClassicPendingScript::NotifyFinished(Resource* resource) {
   // The following SRI checks need to be here because, unfortunately, fetches
   // are not done purely according to the Fetch spec. In particular,
@@ -144,8 +99,19 @@
   //
   // See https://crbug.com/500701 for more information.
   CheckState();
-  if (GetElement()) {
-    integrity_failure_ = !CheckScriptResourceIntegrity(resource, GetElement());
+  ScriptElementBase* element = GetElement();
+  if (element) {
+    GetResource()->CheckResourceIntegrity(element->GetDocument());
+
+    // It is possible to get back a script resource with integrity metadata
+    // for a request with an empty integrity attribute. In that case, the
+    // integrity check should be skipped, so this check ensures that the
+    // integrity attribute isn't empty in addition to checking if the
+    // resource has empty integrity metadata.
+    if (!element->IntegrityAttributeValue().IsEmpty()) {
+      integrity_failure_ = GetResource()->IntegrityDisposition() !=
+                           ResourceIntegrityDisposition::kPassed;
+    }
   }
 
   // We are now waiting for script streaming to finish.
diff --git a/third_party/WebKit/Source/core/dom/ClientRect.idl b/third_party/WebKit/Source/core/dom/ClientRect.idl
index 27230e8..2e9a66e 100644
--- a/third_party/WebKit/Source/core/dom/ClientRect.idl
+++ b/third_party/WebKit/Source/core/dom/ClientRect.idl
@@ -29,8 +29,8 @@
 
 // It has since been replaced by DOMRect in CSSOM View Module and
 // Geometry Interfaces Module:
-// https://dev.w3.org/csswg/cssom-view/#extension-to-the-element-interface
-// https://dev.w3.org/fxtf/geometry/#DOMRect
+// https://drafts.csswg.org/cssom-view/#extension-to-the-element-interface
+// https://drafts.fxtf.org/geometry/#DOMRect
 
 interface ClientRect {
     readonly attribute float top;
diff --git a/third_party/WebKit/Source/core/dom/ClientRectList.idl b/third_party/WebKit/Source/core/dom/ClientRectList.idl
index 4c3a4db9..31767a6c 100644
--- a/third_party/WebKit/Source/core/dom/ClientRectList.idl
+++ b/third_party/WebKit/Source/core/dom/ClientRectList.idl
@@ -30,7 +30,7 @@
 // It has since been replace by DOMRectList in CSSOM View Module and
 // Geometry Interfaces Module:
 // https://dev.w3.org/csswg/cssom-view/#extension-to-the-element-interface
-// https://dev.w3.org/fxtf/geometry/#DOMRectList
+// https://drafts.fxtf.org/geometry/#DOMRectList
 
 // CSSOM View Module also says: "The DOMRectList interface is at-risk.
 // The authors of this specification await feedback from implementers
diff --git a/third_party/WebKit/Source/core/dom/ScriptLoader.cpp b/third_party/WebKit/Source/core/dom/ScriptLoader.cpp
index 42783a5..b947258 100644
--- a/third_party/WebKit/Source/core/dom/ScriptLoader.cpp
+++ b/third_party/WebKit/Source/core/dom/ScriptLoader.cpp
@@ -910,16 +910,19 @@
   DCHECK(!will_be_parser_executed_);
   DCHECK(async_exec_type_ != ScriptRunner::kNone);
   DCHECK(pending_script_->IsExternalOrModule());
+  DCHECK_EQ(pending_script_->IsExternal(), is_external_script_);
   bool error_occurred = false;
   Script* script = pending_script_->GetSource(NullURL(), error_occurred);
   const bool wasCanceled = pending_script_->WasCanceled();
+  const bool is_external = pending_script_->IsExternal();
   DetachPendingScript();
   if (error_occurred) {
     DispatchErrorEvent();
   } else if (!wasCanceled) {
     switch (ExecuteScript(script)) {
       case ExecuteScriptResult::kShouldFireLoadEvent:
-        DispatchLoadEvent();
+        if (is_external)
+          DispatchLoadEvent();
         break;
       case ExecuteScriptResult::kShouldFireErrorEvent:
         DispatchErrorEvent();
diff --git a/third_party/WebKit/Source/core/editing/markers/DocumentMarkerController.cpp b/third_party/WebKit/Source/core/editing/markers/DocumentMarkerController.cpp
index 7b35780..761cea8 100644
--- a/third_party/WebKit/Source/core/editing/markers/DocumentMarkerController.cpp
+++ b/third_party/WebKit/Source/core/editing/markers/DocumentMarkerController.cpp
@@ -402,14 +402,16 @@
     Node* node,
     DocumentMarker::MarkerTypes marker_types) {
   DocumentMarkerVector result;
+  if (!PossiblyHasMarkers(marker_types))
+    return result;
 
   MarkerLists* markers = markers_.at(node);
   if (!markers)
     return result;
 
-  for (DocumentMarker::MarkerType type : DocumentMarker::AllMarkers()) {
+  for (DocumentMarker::MarkerType type : marker_types) {
     DocumentMarkerList* const list = ListForType(markers, type);
-    if (!list || list->IsEmpty() || !marker_types.Contains(type))
+    if (!list || list->IsEmpty())
       continue;
 
     result.AppendVector(list->GetMarkers());
diff --git a/third_party/WebKit/Source/core/geometry/DOMMatrix.idl b/third_party/WebKit/Source/core/geometry/DOMMatrix.idl
index 530347a..5f3378c 100644
--- a/third_party/WebKit/Source/core/geometry/DOMMatrix.idl
+++ b/third_party/WebKit/Source/core/geometry/DOMMatrix.idl
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// https://dev.w3.org/fxtf/geometry/#DOMMatrix
+// https://drafts.fxtf.org/geometry/#DOMMatrix
 
 [
     Constructor(optional (DOMString or sequence<unrestricted double>) init),
diff --git a/third_party/WebKit/Source/core/geometry/DOMPoint.idl b/third_party/WebKit/Source/core/geometry/DOMPoint.idl
index 25556ca..63f084c 100644
--- a/third_party/WebKit/Source/core/geometry/DOMPoint.idl
+++ b/third_party/WebKit/Source/core/geometry/DOMPoint.idl
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// https://dev.w3.org/fxtf/geometry/#DOMPoint
+// https://drafts.fxtf.org/geometry/#DOMPoint
 
 [
     Constructor(optional unrestricted double x = 0, optional unrestricted double y = 0,
diff --git a/third_party/WebKit/Source/core/geometry/DOMPointInit.idl b/third_party/WebKit/Source/core/geometry/DOMPointInit.idl
index 362f0818..e901792 100644
--- a/third_party/WebKit/Source/core/geometry/DOMPointInit.idl
+++ b/third_party/WebKit/Source/core/geometry/DOMPointInit.idl
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// https://dev.w3.org/fxtf/geometry/#DOMPoint
+// https://drafts.fxtf.org/geometry/#DOMPoint
 
 dictionary DOMPointInit {
     unrestricted double x = 0;
diff --git a/third_party/WebKit/Source/core/geometry/DOMRect.idl b/third_party/WebKit/Source/core/geometry/DOMRect.idl
index 241a82c6..6fa18c68 100644
--- a/third_party/WebKit/Source/core/geometry/DOMRect.idl
+++ b/third_party/WebKit/Source/core/geometry/DOMRect.idl
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// https://dev.w3.org/fxtf/geometry/#DOMRect
+// https://drafts.fxtf.org/geometry/#DOMRect
 
 [
     Constructor(optional unrestricted double x = 0,
diff --git a/third_party/WebKit/Source/core/geometry/DOMRectReadOnly.idl b/third_party/WebKit/Source/core/geometry/DOMRectReadOnly.idl
index 598abea..05854af 100644
--- a/third_party/WebKit/Source/core/geometry/DOMRectReadOnly.idl
+++ b/third_party/WebKit/Source/core/geometry/DOMRectReadOnly.idl
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// https://dev.w3.org/fxtf/geometry/#DOMRect
+// https://drafts.fxtf.org/geometry/#DOMRect
 
 [
     Constructor(optional unrestricted double x = 0, optional unrestricted double y = 0,
diff --git a/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp b/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp
index 444ad89..d7a9a68 100644
--- a/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp
@@ -1455,7 +1455,7 @@
     layer_tree_view =
         frame->GetPage()->GetChromeClient().GetWebLayerTreeView(frame);
     surface_layer_bridge_ =
-        WTF::MakeUnique<CanvasSurfaceLayerBridge>(this, layer_tree_view);
+        WTF::MakeUnique<::blink::SurfaceLayerBridge>(this, layer_tree_view);
     // Creates a placeholder layer first before Surface is created.
     surface_layer_bridge_->CreateSolidColorLayer();
   }
diff --git a/third_party/WebKit/Source/core/html/HTMLCanvasElement.h b/third_party/WebKit/Source/core/html/HTMLCanvasElement.h
index 0221e32..ee473e4 100644
--- a/third_party/WebKit/Source/core/html/HTMLCanvasElement.h
+++ b/third_party/WebKit/Source/core/html/HTMLCanvasElement.h
@@ -44,11 +44,11 @@
 #include "platform/bindings/ScriptWrappableVisitor.h"
 #include "platform/geometry/FloatRect.h"
 #include "platform/geometry/IntSize.h"
-#include "platform/graphics/CanvasSurfaceLayerBridge.h"
 #include "platform/graphics/GraphicsTypes.h"
 #include "platform/graphics/GraphicsTypes3D.h"
 #include "platform/graphics/ImageBufferClient.h"
 #include "platform/graphics/OffscreenCanvasPlaceholder.h"
+#include "platform/graphics/SurfaceLayerBridge.h"
 #include "platform/heap/Handle.h"
 
 #define CanvasDefaultInterpolationQuality kInterpolationLow
@@ -75,16 +75,15 @@
 typedef CanvasRenderingContext2DOrWebGLRenderingContextOrWebGL2RenderingContextOrImageBitmapRenderingContext
     RenderingContext;
 
-class CORE_EXPORT HTMLCanvasElement final
-    : public HTMLElement,
-      public ContextLifecycleObserver,
-      public PageVisibilityObserver,
-      public CanvasImageSource,
-      public CanvasRenderingContextHost,
-      public CanvasSurfaceLayerBridgeObserver,
-      public ImageBufferClient,
-      public ImageBitmapSource,
-      public OffscreenCanvasPlaceholder {
+class CORE_EXPORT HTMLCanvasElement final : public HTMLElement,
+                                            public ContextLifecycleObserver,
+                                            public PageVisibilityObserver,
+                                            public CanvasImageSource,
+                                            public CanvasRenderingContextHost,
+                                            public SurfaceLayerBridgeObserver,
+                                            public ImageBufferClient,
+                                            public ImageBitmapSource,
+                                            public OffscreenCanvasPlaceholder {
   DEFINE_WRAPPERTYPEINFO();
   USING_GARBAGE_COLLECTED_MIXIN(HTMLCanvasElement);
   USING_PRE_FINALIZER(HTMLCanvasElement, Dispose);
@@ -199,7 +198,7 @@
   int SourceWidth() override { return size_.Width(); }
   int SourceHeight() override { return size_.Height(); }
 
-  // CanvasSurfaceLayerBridgeObserver implementation
+  // SurfaceLayerBridgeObserver implementation
   void OnWebLayerReplaced() override;
 
   // ImageBufferClient implementation
@@ -243,7 +242,7 @@
   String GetIdFromControl(const Element*);
 
   // For OffscreenCanvas that controls this html canvas element
-  CanvasSurfaceLayerBridge* SurfaceLayerBridge() const {
+  SurfaceLayerBridge* SurfaceLayerBridge() const {
     return surface_layer_bridge_.get();
   }
   void CreateLayer();
@@ -337,7 +336,7 @@
   mutable RefPtr<Image> copied_image_;
 
   // Used for OffscreenCanvas that controls this HTML canvas element
-  std::unique_ptr<CanvasSurfaceLayerBridge> surface_layer_bridge_;
+  std::unique_ptr<::blink::SurfaceLayerBridge> surface_layer_bridge_;
 
   bool did_notify_listeners_for_current_frame_ = false;
 };
diff --git a/third_party/WebKit/Source/core/html/forms/resources/validation_bubble.css b/third_party/WebKit/Source/core/html/forms/resources/validation_bubble.css
index 0749f6f..53b33d7 100644
--- a/third_party/WebKit/Source/core/html/forms/resources/validation_bubble.css
+++ b/third_party/WebKit/Source/core/html/forms/resources/validation_bubble.css
@@ -29,13 +29,18 @@
   padding: 8px;
 }
 
+#spacer-top {
+  display: block;
+  height: var(--arrow-size);
+}
+
 #outer-arrow-top {
   border-color: transparent transparent var(--bubble-border-color) transparent;
   border-style: solid;
   border-width: 0px var(--arrow-size) var(--arrow-size) var(--arrow-size);
   display: block;
   left: 10px;
-  margin-top: -2px;
+  margin-top: 0px;
   position: absolute;
   width: 0px;
 }
@@ -46,26 +51,31 @@
   border-width: 0px var(--arrow-size) var(--arrow-size) var(--arrow-size);
   display: block;
   left: 10px;
-  margin-bottom: -2px;
-  position: relative;
+  margin-top: 1px;
+  position: absolute;
   width: 0px;
 }
 
-.bottom-arrow #outer-arrow-top, .bottom-arrow #inner-arrow-top {
+.bottom-arrow #outer-arrow-top, .bottom-arrow #inner-arrow-top, .bottom-arrow #spacer-top {
   display: none;
 }
 
-#outer-arrow-bottom, #inner-arrow-bottom {
+#outer-arrow-bottom, #inner-arrow-bottom, #spacer-bottom {
   display: none;
 }
 
+.bottom-arrow #spacer-bottom {
+  display: block;
+  height: var(--arrow-size);
+}
+
 .bottom-arrow #outer-arrow-bottom {
   border-color: var(--bubble-border-color) transparent transparent transparent;
   border-style: solid;
   border-width: var(--arrow-size) var(--arrow-size) 0px var(--arrow-size);
   display: block;
   left: 10px;
-  margin-top: 1px;
+  margin-top: 0px;
   position: absolute;
   width: 0px;
 }
@@ -76,15 +86,15 @@
   border-width: var(--arrow-size) var(--arrow-size) 0px var(--arrow-size);
   display: block;
   left: 10px;
-  margin-top: -2px;
-  position: relative;
+  margin-top: -1px;
+  position: absolute;
   width: 0px;
 }
 
 #icon {
   grid-row: 1 / 3;
   grid-column: 1;
-  margin-right: 8px;
+  -webkit-margin-end: 8px;
 }
 
 #main-message {
diff --git a/third_party/WebKit/Source/core/inspector/browser_protocol.json b/third_party/WebKit/Source/core/inspector/browser_protocol.json
index b051b2da..bbafe297 100644
--- a/third_party/WebKit/Source/core/inspector/browser_protocol.json
+++ b/third_party/WebKit/Source/core/inspector/browser_protocol.json
@@ -1579,7 +1579,7 @@
                 "experimental": true
             },
             {
-                "name": "enableRequestInterception",
+                "name": "setRequestInterceptionEnabled",
                 "parameters": [
                     { "name": "enabled", "type": "boolean", "description": "Whether or not HTTP requests should be intercepted and Network.requestIntercepted events sent." }
                 ],
diff --git a/third_party/WebKit/Source/core/inspector/inspector_protocol_config.json b/third_party/WebKit/Source/core/inspector/inspector_protocol_config.json
index 83dad58..df4582c3 100644
--- a/third_party/WebKit/Source/core/inspector/inspector_protocol_config.json
+++ b/third_party/WebKit/Source/core/inspector/inspector_protocol_config.json
@@ -93,7 +93,7 @@
             {
                 "domain": "Network",
                 "exclude": ["clearBrowserCache", "clearBrowserCookies", "getCookies", "getAllCookies", "deleteCookie", "setCookie", "canEmulateNetworkConditions",
-                            "enableRequestInterception", "continueInterceptedRequest"],
+                            "setRequestInterceptionEnabled", "continueInterceptedRequest"],
                 "async": ["getResponseBody"]
             },
             {
diff --git a/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMappingTest.cpp b/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMappingTest.cpp
index 6a631dc..6e6ac26b 100644
--- a/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMappingTest.cpp
+++ b/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMappingTest.cpp
@@ -965,8 +965,9 @@
       true);
   SetBodyInnerHTML(
       "<div id='container' style='overflow: scroll; width: 300px; height: "
-      "300px; border-radius: 5px; background: white; will-change: transform;'>"
-      "    <div style='background-color: blue; width: 2000px; height: "
+      "300px; background: white; will-change: transform;'>"
+      "    <div style='background-color: blue; clip-path: circle(600px at "
+      "1000px 1000px); width: 2000px; height: "
       "2000px;'></div>"
       "</div>");
 
diff --git a/third_party/WebKit/Source/core/loader/FrameLoader.cpp b/third_party/WebKit/Source/core/loader/FrameLoader.cpp
index dfcbd7e..f724ac4 100644
--- a/third_party/WebKit/Source/core/loader/FrameLoader.cpp
+++ b/third_party/WebKit/Source/core/loader/FrameLoader.cpp
@@ -595,8 +595,10 @@
                                        ? std::move(state_object)
                                        : SerializedScriptValue::NullValue());
 
-  if (history_item)
-    RestoreScrollPositionAndViewStateForLoadType(frame_load_type);
+  if (history_item) {
+    RestoreScrollPositionAndViewStateForLoadType(frame_load_type,
+                                                 kHistorySameDocumentLoad);
+  }
 
   // We need to scroll to the fragment whether or not a hash change occurred,
   // since the user might have scrolled since the previous navigation.
@@ -1093,11 +1095,13 @@
 void FrameLoader::RestoreScrollPositionAndViewState() {
   if (!frame_->GetPage() || !GetDocumentLoader())
     return;
-  RestoreScrollPositionAndViewStateForLoadType(GetDocumentLoader()->LoadType());
+  RestoreScrollPositionAndViewStateForLoadType(GetDocumentLoader()->LoadType(),
+                                               kHistoryDifferentDocumentLoad);
 }
 
 void FrameLoader::RestoreScrollPositionAndViewStateForLoadType(
-    FrameLoadType load_type) {
+    FrameLoadType load_type,
+    HistoryLoadType history_load_type) {
   LocalFrameView* view = frame_->View();
   if (!view || !view->LayoutViewportScrollableArea() ||
       !state_machine_.CommittedFirstRealDocumentLoad() ||
@@ -1115,19 +1119,28 @@
   bool should_restore_scale = history_item->PageScaleFactor();
 
   // This tries to balance:
-  // 1. restoring as soon as possible
-  // 2. not overriding user scroll (TODO(majidvp): also respect user scale)
+  // 1. restoring as soon as possible.
+  // 2. not overriding user scroll (TODO(majidvp): also respect user scale).
   // 3. detecting clamping to avoid repeatedly popping the scroll position down
-  //    as the page height increases
-  // 4. ignore clamp detection if we are not restoring scroll or after load
-  //    completes because that may be because the page will never reach its
-  //    previous height
+  //    as the page height increases.
+  // 4. forcing a layout if necessary to avoid clamping.
+  // 5. ignoring clamp detection if scroll state is not being restored, if load
+  //    is complete, or if the navigation is same-document (as the new page may
+  //    be smaller than the previous page).
   bool can_restore_without_clamping =
       view->LayoutViewportScrollableArea()->ClampScrollOffset(
           history_item->GetScrollOffset()) == history_item->GetScrollOffset();
+
+  bool should_force_clamping =
+      !frame_->IsLoading() || history_load_type == kHistorySameDocumentLoad;
+  // Here |can_restore_without_clamping| is false, but layout might be necessary
+  // to ensure correct content size.
+  if (!can_restore_without_clamping && should_force_clamping)
+    frame_->GetDocument()->UpdateStyleAndLayout();
+
   bool can_restore_without_annoying_user =
       !GetDocumentLoader()->GetInitialScrollState().was_scrolled_by_user &&
-      (can_restore_without_clamping || !frame_->IsLoading() ||
+      (can_restore_without_clamping || should_force_clamping ||
        !should_restore_scroll);
   if (!can_restore_without_annoying_user)
     return;
diff --git a/third_party/WebKit/Source/core/loader/FrameLoader.h b/third_party/WebKit/Source/core/loader/FrameLoader.h
index 9f1d0e37..d8f5b92 100644
--- a/third_party/WebKit/Source/core/loader/FrameLoader.h
+++ b/third_party/WebKit/Source/core/loader/FrameLoader.h
@@ -245,7 +245,8 @@
                           HistoryItem*,
                           ClientRedirectPolicy,
                           Document*);
-  void RestoreScrollPositionAndViewStateForLoadType(FrameLoadType);
+  void RestoreScrollPositionAndViewStateForLoadType(FrameLoadType,
+                                                    HistoryLoadType);
 
   void ScheduleCheckCompleted();
 
diff --git a/third_party/WebKit/Source/core/loader/resource/ScriptResource.cpp b/third_party/WebKit/Source/core/loader/resource/ScriptResource.cpp
index 4fb56ae..5642564 100644
--- a/third_party/WebKit/Source/core/loader/resource/ScriptResource.cpp
+++ b/third_party/WebKit/Source/core/loader/resource/ScriptResource.cpp
@@ -26,6 +26,7 @@
 
 #include "core/loader/resource/ScriptResource.h"
 
+#include "core/frame/SubresourceIntegrity.h"
 #include "platform/SharedBuffer.h"
 #include "platform/instrumentation/tracing/web_memory_allocator_dump.h"
 #include "platform/instrumentation/tracing/web_process_memory_dump.h"
@@ -117,4 +118,32 @@
   return kNotSharableCrossOrigin;
 }
 
+void ScriptResource::CheckResourceIntegrity(Document& document) {
+  // Already checked? Retain existing result.
+  //
+  // TODO(vogelheim): If IntegrityDisposition() is kFailed, this should
+  // probably also generate a console message identical to the one produced
+  // by the CheckSubresourceIntegrity call below. See crbug.com/585267.
+  if (IntegrityDisposition() != ResourceIntegrityDisposition::kNotChecked)
+    return;
+
+  // Loading error occurred? Then result is uncheckable.
+  if (ErrorOccurred())
+    return;
+
+  // No integrity attributes to check? Then we're passing.
+  if (IntegrityMetadata().IsEmpty()) {
+    SetIntegrityDisposition(ResourceIntegrityDisposition::kPassed);
+    return;
+  }
+
+  CHECK(!!ResourceBuffer());
+  bool passed = SubresourceIntegrity::CheckSubresourceIntegrity(
+      IntegrityMetadata(), document, ResourceBuffer()->Data(),
+      ResourceBuffer()->size(), Url(), *this);
+  SetIntegrityDisposition(passed ? ResourceIntegrityDisposition::kPassed
+                                 : ResourceIntegrityDisposition::kFailed);
+  DCHECK_NE(IntegrityDisposition(), ResourceIntegrityDisposition::kNotChecked);
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/loader/resource/ScriptResource.h b/third_party/WebKit/Source/core/loader/resource/ScriptResource.h
index 3fa422f..6309f5e 100644
--- a/third_party/WebKit/Source/core/loader/resource/ScriptResource.h
+++ b/third_party/WebKit/Source/core/loader/resource/ScriptResource.h
@@ -36,6 +36,7 @@
 
 namespace blink {
 
+class Document;
 class FetchParameters;
 class KURL;
 class ResourceFetcher;
@@ -84,6 +85,8 @@
 
   AccessControlStatus CalculateAccessControlStatus() const;
 
+  void CheckResourceIntegrity(Document&);
+
  private:
   class ScriptResourceFactory : public ResourceFactory {
    public:
diff --git a/third_party/WebKit/Source/core/mojo/MojoWatcher.cpp b/third_party/WebKit/Source/core/mojo/MojoWatcher.cpp
index e51fe2e0a..6033a80 100644
--- a/third_party/WebKit/Source/core/mojo/MojoWatcher.cpp
+++ b/third_party/WebKit/Source/core/mojo/MojoWatcher.cpp
@@ -43,9 +43,7 @@
   return watcher;
 }
 
-MojoWatcher::~MojoWatcher() {
-  DCHECK(!handle_.is_valid());
-}
+MojoWatcher::~MojoWatcher() {}
 
 MojoResult MojoWatcher::cancel() {
   if (!watcher_handle_.is_valid())
diff --git a/third_party/WebKit/Source/core/page/Page.cpp b/third_party/WebKit/Source/core/page/Page.cpp
index f5c3a05..7473695 100644
--- a/third_party/WebKit/Source/core/page/Page.cpp
+++ b/third_party/WebKit/Source/core/page/Page.cpp
@@ -304,6 +304,15 @@
   return plugin_data_.Get();
 }
 
+void Page::ResetPluginData() {
+  for (Page* page : AllPages()) {
+    if (page->plugin_data_) {
+      page->plugin_data_->ResetPluginData();
+      page->NotifyPluginsChanged();
+    }
+  }
+}
+
 void Page::SetValidationMessageClient(ValidationMessageClient* client) {
   validation_message_client_ = client;
 }
@@ -567,15 +576,19 @@
       }
       break;
     case SettingsDelegate::kPluginsChange: {
-      HeapVector<Member<PluginsChangedObserver>, 32> observers;
-      CopyToVector(plugins_changed_observers_, observers);
-      for (PluginsChangedObserver* observer : observers)
-        observer->PluginsChanged();
+      NotifyPluginsChanged();
       break;
     }
   }
 }
 
+void Page::NotifyPluginsChanged() const {
+  HeapVector<Member<PluginsChangedObserver>, 32> observers;
+  CopyToVector(plugins_changed_observers_, observers);
+  for (PluginsChangedObserver* observer : observers)
+    observer->PluginsChanged();
+}
+
 void Page::UpdateAcceleratedCompositingSettings() {
   for (Frame* frame = MainFrame(); frame;
        frame = frame->Tree().TraverseNext()) {
diff --git a/third_party/WebKit/Source/core/page/Page.h b/third_party/WebKit/Source/core/page/Page.h
index 2543d5d..cc00c32 100644
--- a/third_party/WebKit/Source/core/page/Page.h
+++ b/third_party/WebKit/Source/core/page/Page.h
@@ -134,9 +134,16 @@
 
   ViewportDescription GetViewportDescription() const;
 
-  static void RefreshPlugins();
+  // Returns the plugin data associated with |main_frame_origin|.
   PluginData* GetPluginData(SecurityOrigin* main_frame_origin);
 
+  // Refreshes the browser-side plugin cache.
+  static void RefreshPlugins();
+
+  // Resets the plugin data for all pages in the renderer process and notifies
+  // PluginsChangedObservers.
+  static void ResetPluginData();
+
   EditorClient& GetEditorClient() const { return *editor_client_; }
   SpellCheckerClient& GetSpellCheckerClient() const {
     return *spell_checker_client_;
@@ -305,6 +312,9 @@
   // ScopedPageSuspender helpers.
   void SetSuspended(bool);
 
+  // Notify |plugins_changed_observers_| that plugins have changed.
+  void NotifyPluginsChanged() const;
+
   Member<PageAnimator> animator_;
   const Member<AutoscrollController> autoscroll_controller_;
   Member<ChromeClient> chrome_client_;
diff --git a/third_party/WebKit/Source/core/page/ValidationMessageOverlayDelegate.cpp b/third_party/WebKit/Source/core/page/ValidationMessageOverlayDelegate.cpp
index 3d2dadb..a18f19f 100644
--- a/third_party/WebKit/Source/core/page/ValidationMessageOverlayDelegate.cpp
+++ b/third_party/WebKit/Source/core/page/ValidationMessageOverlayDelegate.cpp
@@ -12,6 +12,7 @@
 #include "core/page/PagePopupClient.h"
 #include "platform/LayoutTestSupport.h"
 #include "platform/graphics/paint/CullRect.h"
+#include "platform/text/PlatformLocale.h"
 
 namespace blink {
 
@@ -129,7 +130,7 @@
                        SubstituteData(data, "text/html", "UTF-8", KURL(),
                                       kForceSynchronousLoad)));
 
-  Element& container = BubbleContainer();
+  Element& container = GetElementById("container");
   if (LayoutTestSupport::IsRunningLayoutTest())
     container.SetInlineStyleProperty(CSSPropertyTransition, "none");
   // Get the size to decide position later.
@@ -137,8 +138,8 @@
   bubble_size_ = container.VisibleBoundsInVisualViewport().Size();
   // Add one because the content sometimes exceeds the exact width due to
   // rounding errors.
-  container.SetInlineStyleProperty(CSSPropertyMinWidth,
-                                   bubble_size_.Width() + 1,
+  bubble_size_.Expand(1, 0);
+  container.SetInlineStyleProperty(CSSPropertyMinWidth, bubble_size_.Width(),
                                    CSSPrimitiveValue::UnitType::kPixels);
 }
 
@@ -146,11 +147,15 @@
   DCHECK(data);
   PagePopupClient::AddString("<!DOCTYPE html><html><head><style>", data);
   data->Append(Platform::Current()->GetDataResource("validation_bubble.css"));
+  PagePopupClient::AddString("</style></head>", data);
   PagePopupClient::AddString(
-      "</style></head><body>"
+      Locale::DefaultLocale().IsRTL() ? "<body dir=rtl>" : "<body dir=ltr>",
+      data);
+  PagePopupClient::AddString(
       "<div id=container>"
       "<div id=outer-arrow-top></div>"
       "<div id=inner-arrow-top></div>"
+      "<div id=spacer-top></div>"
       "<main id=bubble-body>",
       data);
   data->Append(Platform::Current()->GetDataResource("input_alert.svg"));
@@ -168,16 +173,18 @@
       "</div></main>"
       "<div id=outer-arrow-bottom></div>"
       "<div id=inner-arrow-bottom></div>"
+      "<div id=spacer-bottom></div>"
       "</div></body></html>\n",
       data);
 }
 
-Element& ValidationMessageOverlayDelegate::BubbleContainer() const {
-  Element* container = ToLocalFrame(page_->MainFrame())
-                           ->GetDocument()
-                           ->getElementById("container");
-  DCHECK(container) << "Failed to load the document?";
-  return *container;
+Element& ValidationMessageOverlayDelegate::GetElementById(
+    const AtomicString& id) const {
+  Element* element =
+      ToLocalFrame(page_->MainFrame())->GetDocument()->getElementById(id);
+  DCHECK(element) << "No element with id=" << id
+                  << ". Failed to load the document?";
+  return *element;
 }
 
 void ValidationMessageOverlayDelegate::AdjustBubblePosition(
@@ -196,7 +203,7 @@
   else if (bubble_x + bubble_size_.Width() > view_size.Width())
     bubble_x = view_size.Width() - bubble_size_.Width();
 
-  Element& container = BubbleContainer();
+  Element& container = GetElementById("container");
   container.SetInlineStyleProperty(CSSPropertyLeft, bubble_x,
                                    CSSPrimitiveValue::UnitType::kPixels);
   container.SetInlineStyleProperty(CSSPropertyTop, bubble_y,
@@ -208,7 +215,54 @@
   else
     container.removeAttribute(HTMLNames::classAttr);
 
-  // TODO(tkent): Adjust arrow position.
+  // Should match to --arrow-size in validation_bubble.css.
+  const int kArrowSize = 8;
+  const int kArrowMargin = 10;
+  const int kMinArrowAnchorX = kArrowSize + kArrowMargin;
+  double max_arrow_anchor_x =
+      bubble_size_.Width() - (kArrowSize + kArrowMargin);
+  double arrow_anchor_x;
+  const int kOffsetToAnchorRect = 8;
+  double anchor_rect_center = anchor_rect.X() + anchor_rect.Width() / 2;
+  if (!Locale::DefaultLocale().IsRTL()) {
+    double anchor_rect_left = anchor_rect.X() + kOffsetToAnchorRect;
+    if (anchor_rect_left > anchor_rect_center)
+      anchor_rect_left = anchor_rect_center;
+
+    arrow_anchor_x = kMinArrowAnchorX;
+    if (bubble_x + arrow_anchor_x < anchor_rect_left) {
+      arrow_anchor_x = anchor_rect_left - bubble_x;
+      if (arrow_anchor_x > max_arrow_anchor_x)
+        arrow_anchor_x = max_arrow_anchor_x;
+    }
+  } else {
+    double anchor_rect_right = anchor_rect.MaxX() - kOffsetToAnchorRect;
+    if (anchor_rect_right < anchor_rect_center)
+      anchor_rect_right = anchor_rect_center;
+
+    arrow_anchor_x = max_arrow_anchor_x;
+    if (bubble_x + arrow_anchor_x > anchor_rect_right) {
+      arrow_anchor_x = anchor_rect_right - bubble_x;
+      if (arrow_anchor_x < kMinArrowAnchorX)
+        arrow_anchor_x = kMinArrowAnchorX;
+    }
+  }
+  double arrow_x = arrow_anchor_x - kArrowSize;
+  if (show_bottom_arrow) {
+    GetElementById("outer-arrow-bottom")
+        .SetInlineStyleProperty(CSSPropertyLeft, arrow_x,
+                                CSSPrimitiveValue::UnitType::kPixels);
+    GetElementById("inner-arrow-bottom")
+        .SetInlineStyleProperty(CSSPropertyLeft, arrow_x,
+                                CSSPrimitiveValue::UnitType::kPixels);
+  } else {
+    GetElementById("outer-arrow-top")
+        .SetInlineStyleProperty(CSSPropertyLeft, arrow_x,
+                                CSSPrimitiveValue::UnitType::kPixels);
+    GetElementById("inner-arrow-top")
+        .SetInlineStyleProperty(CSSPropertyLeft, arrow_x,
+                                CSSPrimitiveValue::UnitType::kPixels);
+  }
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/page/ValidationMessageOverlayDelegate.h b/third_party/WebKit/Source/core/page/ValidationMessageOverlayDelegate.h
index cdf13342..998c8a61 100644
--- a/third_party/WebKit/Source/core/page/ValidationMessageOverlayDelegate.h
+++ b/third_party/WebKit/Source/core/page/ValidationMessageOverlayDelegate.h
@@ -49,7 +49,7 @@
   void UpdateFrameViewState(const PageOverlay&, const IntSize& view_size);
   void EnsurePage(const PageOverlay&, const IntSize& view_size);
   void WriteDocument(SharedBuffer*);
-  Element& BubbleContainer() const;
+  Element& GetElementById(const AtomicString&) const;
   void AdjustBubblePosition(const IntSize& view_size);
 
   // An internal Page and a ChromeClient for it.
diff --git a/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinatorTest.cpp b/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinatorTest.cpp
index e4c63b0..0fc0961 100644
--- a/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinatorTest.cpp
+++ b/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinatorTest.cpp
@@ -1122,11 +1122,6 @@
       MainThreadScrollingReason::kBackgroundNotOpaqueInRectAndLCDText);
 }
 
-TEST_P(NonCompositedMainThreadScrollingReasonTest, BorderRadiusTest) {
-  TestNonCompositedReasons("border-radius",
-                           MainThreadScrollingReason::kHasBorderRadius);
-}
-
 TEST_P(NonCompositedMainThreadScrollingReasonTest, ClipTest) {
   TestNonCompositedReasons("clip",
                            MainThreadScrollingReason::kHasClipRelatedProperty);
@@ -1182,9 +1177,8 @@
 }
 
 TEST_P(NonCompositedMainThreadScrollingReasonTest, LCDTextEnabledTest) {
-  TestNonCompositedReasons("transparent border-radius",
-                           MainThreadScrollingReason::kHasOpacityAndLCDText |
-                               MainThreadScrollingReason::kHasBorderRadius);
+  TestNonCompositedReasons("transparent",
+                           MainThreadScrollingReason::kHasOpacityAndLCDText);
 }
 
 TEST_P(NonCompositedMainThreadScrollingReasonTest, BoxShadowTest) {
@@ -1249,8 +1243,7 @@
       ToLayoutBoxModelObject(container2->GetLayoutObject())
           ->GetScrollableArea();
   ASSERT_TRUE(scrollable_area2);
-  EXPECT_TRUE(scrollable_area2->GetNonCompositedMainThreadScrollingReasons() &
-              MainThreadScrollingReason::kHasBorderRadius);
+  ASSERT_TRUE(scrollable_area2->UsesCompositedScrolling());
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerClipper.cpp b/third_party/WebKit/Source/core/paint/PaintLayerClipper.cpp
index 238cf7c9d..316cf55 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerClipper.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintLayerClipper.cpp
@@ -500,8 +500,15 @@
   } else {
     const auto* ancestor_overflow_clip = ancestor_properties->OverflowClip();
     if (ancestor_overflow_clip) {
-      DCHECK_EQ(destination_property_tree_state.Clip(),
-                ancestor_overflow_clip->Parent());
+      if (const auto* ancestor_rounded_clip =
+              ancestor_properties->InnerBorderRadiusClip()) {
+        DCHECK_EQ(destination_property_tree_state.Clip(),
+                  ancestor_rounded_clip->Parent());
+        DCHECK_EQ(ancestor_rounded_clip, ancestor_overflow_clip->Parent());
+      } else {
+        DCHECK_EQ(destination_property_tree_state.Clip(),
+                  ancestor_overflow_clip->Parent());
+      }
       destination_property_tree_state.SetClip(ancestor_overflow_clip);
     }
   }
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp b/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp
index 04fc15e..ab812bf 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp
@@ -1967,11 +1967,6 @@
   // layer->layoutObject().style()->hasBorderDecoration() (missing background
   // behind dashed borders). Resolve this case, or not, and update this check
   // with the results.
-  if (layer->GetLayoutObject().Style()->HasBorderRadius()) {
-    non_composited_main_thread_scrolling_reasons_ |=
-        MainThreadScrollingReason::kHasBorderRadius;
-    needs_composited_scrolling = false;
-  }
   if (layer->GetLayoutObject().HasClip() ||
       layer->HasDescendantWithClipPath() || layer->HasAncestorWithClipPath()) {
     non_composited_main_thread_scrolling_reasons_ |=
diff --git a/third_party/WebKit/Source/core/xml/XPathGrammar.y b/third_party/WebKit/Source/core/xml/XPathGrammar.y
index 461cd66..6e9c393 100644
--- a/third_party/WebKit/Source/core/xml/XPathGrammar.y
+++ b/third_party/WebKit/Source/core/xml/XPathGrammar.y
@@ -49,9 +49,7 @@
 #define YYDEBUG 0
 #define YYMAXDEPTH 10000
 
-using namespace blink;
-using namespace XPath;
-
+using blink::XPath::Step;
 %}
 
 %pure-parser
@@ -73,7 +71,7 @@
 
 %{
 
-static int xpathyylex(YYSTYPE* yylval) { return Parser::Current()->Lex(yylval); }
+static int xpathyylex(YYSTYPE* yylval) { return blink::XPath::Parser::Current()->Lex(yylval); }
 static void xpathyyerror(void*, const char*) { }
 
 %}
@@ -140,7 +138,7 @@
 AbsoluteLocationPath:
     '/'
     {
-        $$ = new LocationPath;
+        $$ = new blink::XPath::LocationPath;
     }
     |
     '/' RelativeLocationPath
@@ -158,7 +156,7 @@
 RelativeLocationPath:
     Step
     {
-        $$ = new LocationPath;
+        $$ = new blink::XPath::LocationPath;
         $$->AppendStep($1);
     }
     |
@@ -274,13 +272,13 @@
 PredicateList:
     Predicate
     {
-        $$ = new blink::HeapVector<blink::Member<Predicate>>;
-        $$->push_back(new Predicate($1));
+        $$ = new blink::HeapVector<blink::Member<blink::XPath::Predicate>>;
+        $$->push_back(new blink::XPath::Predicate($1));
     }
     |
     PredicateList Predicate
     {
-        $$->push_back(new Predicate($2));
+        $$->push_back(new blink::XPath::Predicate($2));
     }
     ;
 
@@ -313,7 +311,7 @@
 PrimaryExpr:
     VARIABLEREFERENCE
     {
-        $$ = new VariableReference(*$1);
+        $$ = new blink::XPath::VariableReference(*$1);
         parser->DeleteString($1);
     }
     |
@@ -324,13 +322,13 @@
     |
     LITERAL
     {
-        $$ = new StringExpression(*$1);
+        $$ = new blink::XPath::StringExpression(*$1);
         parser->DeleteString($1);
     }
     |
     NUMBER
     {
-        $$ = new Number($1->ToDouble());
+        $$ = new blink::XPath::Number($1->ToDouble());
         parser->DeleteString($1);
     }
     |
@@ -340,7 +338,7 @@
 FunctionCall:
     FUNCTIONNAME '(' ')'
     {
-        $$ = CreateFunction(*$1);
+        $$ = blink::XPath::CreateFunction(*$1);
         if (!$$)
             YYABORT;
         parser->DeleteString($1);
@@ -348,7 +346,7 @@
     |
     FUNCTIONNAME '(' ArgumentList ')'
     {
-        $$ = CreateFunction(*$1, *$3);
+        $$ = blink::XPath::CreateFunction(*$1, *$3);
         if (!$$)
             YYABORT;
         parser->DeleteString($1);
@@ -358,7 +356,7 @@
 ArgumentList:
     Argument
     {
-        $$ = new blink::HeapVector<blink::Member<Expression>>;
+        $$ = new blink::HeapVector<blink::Member<blink::XPath::Expression>>;
         $$->push_back($1);
     }
     |
@@ -377,7 +375,7 @@
     |
     UnionExpr '|' PathExpr
     {
-        $$ = new Union;
+        $$ = new blink::XPath::Union;
         $$->AddSubExpression($1);
         $$->AddSubExpression($3);
     }
@@ -419,7 +417,7 @@
     |
     OrExpr OR AndExpr
     {
-        $$ = new LogicalOp(LogicalOp::kOP_Or, $1, $3);
+        $$ = new blink::XPath::LogicalOp(blink::XPath::LogicalOp::kOP_Or, $1, $3);
     }
     ;
 
@@ -428,7 +426,7 @@
     |
     AndExpr AND EqualityExpr
     {
-        $$ = new LogicalOp(LogicalOp::kOP_And, $1, $3);
+        $$ = new blink::XPath::LogicalOp(blink::XPath::LogicalOp::kOP_And, $1, $3);
     }
     ;
 
@@ -437,7 +435,7 @@
     |
     EqualityExpr EQOP RelationalExpr
     {
-        $$ = new EqTestOp($2, $1, $3);
+        $$ = new blink::XPath::EqTestOp($2, $1, $3);
     }
     ;
 
@@ -446,7 +444,7 @@
     |
     RelationalExpr RELOP AdditiveExpr
     {
-        $$ = new EqTestOp($2, $1, $3);
+        $$ = new blink::XPath::EqTestOp($2, $1, $3);
     }
     ;
 
@@ -455,12 +453,12 @@
     |
     AdditiveExpr PLUS MultiplicativeExpr
     {
-        $$ = new NumericOp(NumericOp::kOP_Add, $1, $3);
+        $$ = new blink::XPath::NumericOp(blink::XPath::NumericOp::kOP_Add, $1, $3);
     }
     |
     AdditiveExpr MINUS MultiplicativeExpr
     {
-        $$ = new NumericOp(NumericOp::kOP_Sub, $1, $3);
+        $$ = new blink::XPath::NumericOp(blink::XPath::NumericOp::kOP_Sub, $1, $3);
     }
     ;
 
@@ -469,7 +467,7 @@
     |
     MultiplicativeExpr MULOP UnaryExpr
     {
-        $$ = new NumericOp($2, $1, $3);
+        $$ = new blink::XPath::NumericOp($2, $1, $3);
     }
     ;
 
@@ -478,7 +476,7 @@
     |
     MINUS UnaryExpr
     {
-        $$ = new Negative;
+        $$ = new blink::XPath::Negative;
         $$->AddSubExpression($2);
     }
     ;
diff --git a/third_party/WebKit/Source/core/xml/parser/XMLDocumentParser.cpp b/third_party/WebKit/Source/core/xml/parser/XMLDocumentParser.cpp
index a07e29b..448d3ce 100644
--- a/third_party/WebKit/Source/core/xml/parser/XMLDocumentParser.cpp
+++ b/third_party/WebKit/Source/core/xml/parser/XMLDocumentParser.cpp
@@ -436,6 +436,8 @@
 void XMLDocumentParser::NotifyFinished(Resource* unused_resource) {
   DCHECK_EQ(unused_resource, pending_script_);
 
+  pending_script_->CheckResourceIntegrity(*GetDocument());
+
   ScriptSourceCode source_code(pending_script_.Get());
   bool error_occurred = pending_script_->ErrorOccurred();
   bool was_canceled = pending_script_->WasCanceled();
diff --git a/third_party/WebKit/Source/devtools/front_end/console/ConsoleView.js b/third_party/WebKit/Source/devtools/front_end/console/ConsoleView.js
index b4d7591..b41f100b 100644
--- a/third_party/WebKit/Source/devtools/front_end/console/ConsoleView.js
+++ b/third_party/WebKit/Source/devtools/front_end/console/ConsoleView.js
@@ -1337,6 +1337,7 @@
   handleAction(context, actionId) {
     switch (actionId) {
       case 'console.show':
+        InspectorFrontendHost.bringToFront();
         Common.console.show();
         return true;
       case 'console.clear':
diff --git a/third_party/WebKit/Source/devtools/front_end/main/Main.js b/third_party/WebKit/Source/devtools/front_end/main/Main.js
index 33288ac9..5e5968a 100644
--- a/third_party/WebKit/Source/devtools/front_end/main/Main.js
+++ b/third_party/WebKit/Source/devtools/front_end/main/Main.js
@@ -311,8 +311,10 @@
   }
 
   _registerForwardedShortcuts() {
-    /** @const */ var forwardedActions =
-        ['main.toggle-dock', 'debugger.toggle-breakpoints-active', 'debugger.toggle-pause', 'commandMenu.show'];
+    /** @const */ var forwardedActions = [
+      'main.toggle-dock', 'debugger.toggle-breakpoints-active', 'debugger.toggle-pause', 'commandMenu.show',
+      'console.show'
+    ];
     var actionKeys =
         UI.shortcutRegistry.keysForActions(forwardedActions).map(UI.KeyboardShortcut.keyCodeAndModifiersFromKey);
     InspectorFrontendHost.setWhitelistedShortcuts(JSON.stringify(actionKeys));
diff --git a/third_party/WebKit/Source/devtools/front_end/network/NetworkDataGridNode.js b/third_party/WebKit/Source/devtools/front_end/network/NetworkDataGridNode.js
index cc6679d..ca8ee94 100644
--- a/third_party/WebKit/Source/devtools/front_end/network/NetworkDataGridNode.js
+++ b/third_party/WebKit/Source/devtools/front_end/network/NetworkDataGridNode.js
@@ -681,7 +681,7 @@
    * @return {boolean}
    */
   isNavigationRequest() {
-    var pageLoad = NetworkLog.networkLog.pageLoadForRequest(this._request);
+    var pageLoad = NetworkLog.PageLoad.forRequest(this._request);
     return pageLoad ? pageLoad.mainRequest === this._request : false;
   }
 
@@ -920,7 +920,8 @@
     var request = this._request;
     var initiator = NetworkLog.networkLog.initiatorInfoForRequest(request);
 
-    if (request.timing && request.timing.pushStart)
+    var timing = request.timing;
+    if (timing && timing.pushStart)
       cell.appendChild(createTextNode(Common.UIString('Push / ')));
     switch (initiator.type) {
       case SDK.NetworkRequest.InitiatorType.Parser:
diff --git a/third_party/WebKit/Source/devtools/front_end/network/NetworkPanel.js b/third_party/WebKit/Source/devtools/front_end/network/NetworkPanel.js
index 423f4c97..343b688 100644
--- a/third_party/WebKit/Source/devtools/front_end/network/NetworkPanel.js
+++ b/third_party/WebKit/Source/devtools/front_end/network/NetworkPanel.js
@@ -52,7 +52,7 @@
     this._filterBar.show(this.element);
     this.setDefaultFocusedChild(this._filterBar);
 
-    this._filmStripPlaceholderElement = this.element.createChild('div');
+    this._filmStripPlaceholderElement = this.element.createChild('div', 'network-film-strip-placeholder');
 
     // Create top overview component.
     this._overviewPane = new PerfUI.TimelineOverviewPane('network');
diff --git a/third_party/WebKit/Source/devtools/front_end/network/RequestTimingView.js b/third_party/WebKit/Source/devtools/front_end/network/RequestTimingView.js
index 4a76e3b..9654057f 100644
--- a/third_party/WebKit/Source/devtools/front_end/network/RequestTimingView.js
+++ b/third_party/WebKit/Source/devtools/front_end/network/RequestTimingView.js
@@ -305,7 +305,8 @@
     }
 
     /**
-     * param {string} title
+     * @param {string} title
+     * @return {!Element}
      */
     function createHeader(title) {
       var dataHeader = tableElement.createChild('tr', 'network-timing-table-header');
diff --git a/third_party/WebKit/Source/devtools/front_end/network/networkPanel.css b/third_party/WebKit/Source/devtools/front_end/network/networkPanel.css
index 80f149b2..e103b2f 100644
--- a/third_party/WebKit/Source/devtools/front_end/network/networkPanel.css
+++ b/third_party/WebKit/Source/devtools/front_end/network/networkPanel.css
@@ -120,6 +120,10 @@
     flex: none !important;
 }
 
+.network-film-strip-placeholder {
+    flex-shrink: 0;
+}
+
 .network-blocked-urls {
     border-top: 1px solid #dadada;
     flex: 104px 0 0;
diff --git a/third_party/WebKit/Source/devtools/front_end/network_log/HAREntry.js b/third_party/WebKit/Source/devtools/front_end/network_log/HAREntry.js
index 371f2d8..8fbc832 100644
--- a/third_party/WebKit/Source/devtools/front_end/network_log/HAREntry.js
+++ b/third_party/WebKit/Source/devtools/front_end/network_log/HAREntry.js
@@ -73,7 +73,7 @@
 
     if (this._request.connectionId !== '0')
       entry.connection = this._request.connectionId;
-    var page = NetworkLog.networkLog.pageLoadForRequest(this._request);
+    var page = NetworkLog.PageLoad.forRequest(this._request);
     if (page)
       entry.pageref = 'page_' + page.id;
     return entry;
@@ -305,7 +305,7 @@
     var pages = [];
     for (var i = 0; i < this._requests.length; ++i) {
       var request = this._requests[i];
-      var page = NetworkLog.networkLog.pageLoadForRequest(request);
+      var page = NetworkLog.PageLoad.forRequest(request);
       if (!page || seenIdentifiers[page.id])
         continue;
       seenIdentifiers[page.id] = true;
diff --git a/third_party/WebKit/Source/devtools/front_end/network_log/NetworkLog.js b/third_party/WebKit/Source/devtools/front_end/network_log/NetworkLog.js
index e473e57..7d1df3b 100644
--- a/third_party/WebKit/Source/devtools/front_end/network_log/NetworkLog.js
+++ b/third_party/WebKit/Source/devtools/front_end/network_log/NetworkLog.js
@@ -265,14 +265,6 @@
     return request[NetworkLog.NetworkLog._initiatorDataSymbol].request;
   }
 
-  /**
-   * @param {!SDK.NetworkRequest} request
-   * @return {?NetworkLog.PageLoad}
-   */
-  pageLoadForRequest(request) {
-    return request[NetworkLog.NetworkLog._pageLoadForRequestSymbol] || null;
-  }
-
   _willReloadPage() {
     if (!Common.moduleSetting('network_log.preserve-log').get())
       this.reset();
@@ -314,7 +306,7 @@
       oldRequestsSet.delete(request);
       this._requests.push(request);
       this._requestsSet.add(request);
-      request[NetworkLog.NetworkLog._pageLoadForRequestSymbol] = currentPageLoad;
+      currentPageLoad.bindRequest(request);
       this.dispatchEventToListeners(NetworkLog.NetworkLog.Events.RequestAdded, request);
     }
 
@@ -340,7 +332,7 @@
     var manager = SDK.NetworkManager.forRequest(request);
     var pageLoad = manager ? this._pageLoadForManager.get(manager) : null;
     if (pageLoad)
-      request[NetworkLog.NetworkLog._pageLoadForRequestSymbol] = pageLoad;
+      pageLoad.bindRequest(request);
     this.dispatchEventToListeners(NetworkLog.NetworkLog.Events.RequestAdded, request);
   }
 
@@ -410,9 +402,25 @@
     this.contentLoadTime;
     this.mainRequest = mainRequest;
   }
+
+  /**
+   * @param {!SDK.NetworkRequest} request
+   * @return {?NetworkLog.PageLoad}
+   */
+  static forRequest(request) {
+    return request[NetworkLog.PageLoad._pageLoadForRequestSymbol] || null;
+  }
+
+  /**
+   * @param {!SDK.NetworkRequest} request
+   */
+  bindRequest(request) {
+    request[NetworkLog.PageLoad._pageLoadForRequestSymbol] = this;
+  }
 };
 
 NetworkLog.PageLoad._lastIdentifier = 0;
+NetworkLog.PageLoad._pageLoadForRequestSymbol = Symbol('PageLoadForRequest');
 
 /** @typedef {!{initiators: !Set<!SDK.NetworkRequest>, initiated: !Set<!SDK.NetworkRequest>}} */
 NetworkLog.NetworkLog.InitiatorGraph;
@@ -427,7 +435,6 @@
 NetworkLog.NetworkLog._InitiatorInfo;
 
 NetworkLog.NetworkLog._initiatorDataSymbol = Symbol('InitiatorData');
-NetworkLog.NetworkLog._pageLoadForRequestSymbol = Symbol('PageLoadForRequest');
 NetworkLog.NetworkLog._events = Symbol('NetworkLog.NetworkLog.events');
 
 /** @type {!NetworkLog.NetworkLog} */
diff --git a/third_party/WebKit/Source/devtools/front_end/quick_open/CommandMenu.js b/third_party/WebKit/Source/devtools/front_end/quick_open/CommandMenu.js
index 90b98ea..fe03534 100644
--- a/third_party/WebKit/Source/devtools/front_end/quick_open/CommandMenu.js
+++ b/third_party/WebKit/Source/devtools/front_end/quick_open/CommandMenu.js
@@ -312,6 +312,7 @@
    * @return {boolean}
    */
   handleAction(context, actionId) {
+    InspectorFrontendHost.bringToFront();
     QuickOpen.QuickOpen.show('>');
     return true;
   }
diff --git a/third_party/WebKit/Source/devtools/front_end/resources/ClearStorageView.js b/third_party/WebKit/Source/devtools/front_end/resources/ClearStorageView.js
index b91730f1..3b9dca2 100644
--- a/third_party/WebKit/Source/devtools/front_end/resources/ClearStorageView.js
+++ b/third_party/WebKit/Source/devtools/front_end/resources/ClearStorageView.js
@@ -36,9 +36,11 @@
 
     var quota = this._reportView.appendSection(Common.UIString('Usage'));
     this._quotaRow = quota.appendRow();
+    this._quotaUsage = null;
     this._pieChart = new PerfUI.PieChart(110, Number.bytesToString, true);
-    this._pieChartLegend = createElementWithClass('div', 'legend');
+    this._pieChartLegend = createElement('div');
     var usageBreakdownRow = quota.appendRow();
+    usageBreakdownRow.classList.add('usage-breakdown-row');
     usageBreakdownRow.appendChild(this._pieChart.element);
     usageBreakdownRow.appendChild(this._pieChartLegend);
 
@@ -196,25 +198,24 @@
     this._quotaRow.textContent = Common.UIString(
         '%s used out of %s storage quota', Number.bytesToString(response.usage), Number.bytesToString(response.quota));
 
-    this._resetPieChart(response.usage);
-    var colorIndex = 0;
-    for (var usageForType of response.usageBreakdown) {
-      if (!usageForType.usage)
-        continue;
-      if (colorIndex === this._pieColors.length)
-        colorIndex = 0;
-      this._appendLegendRow(
-          this._getStorageTypeName(usageForType.storageType), usageForType.usage, this._pieColors[colorIndex++]);
+    if (!this._quotaUsage || this._quotaUsage !== response.usage) {
+      this._quotaUsage = response.usage;
+      this._resetPieChart(response.usage);
+      var colorIndex = 0;
+      for (var usageForType of response.usageBreakdown) {
+        if (!usageForType.usage)
+          continue;
+        if (colorIndex === this._pieColors.length)
+          colorIndex = 0;
+        this._appendLegendRow(
+            this._getStorageTypeName(usageForType.storageType), usageForType.usage, this._pieColors[colorIndex++]);
+      }
     }
 
     this._usageUpdatedForTest(response.usage, response.quota, response.usageBreakdown);
     this.update();
   }
 
-  _formatPieChartBytes(value) {
-    return Number.bytesToString(value);
-  }
-
   /**
    * @param {number} total
    */
diff --git a/third_party/WebKit/Source/devtools/front_end/resources/clearStorageView.css b/third_party/WebKit/Source/devtools/front_end/resources/clearStorageView.css
index a2ba5f4..ae8e397 100644
--- a/third_party/WebKit/Source/devtools/front_end/resources/clearStorageView.css
+++ b/third_party/WebKit/Source/devtools/front_end/resources/clearStorageView.css
@@ -23,6 +23,10 @@
     display: inline;
 }
 
+.usage-breakdown-row {
+    min-width: fit-content;
+}
+
 .usage-breakdown-legend-title {
     display: inline-block;
 }
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/NetworkRequest.js b/third_party/WebKit/Source/devtools/front_end/sdk/NetworkRequest.js
index 771f0819..a5661f15 100644
--- a/third_party/WebKit/Source/devtools/front_end/sdk/NetworkRequest.js
+++ b/third_party/WebKit/Source/devtools/front_end/sdk/NetworkRequest.js
@@ -463,18 +463,22 @@
   }
 
   /**
-   * @param {!Protocol.Network.ResourceTiming|undefined} x
+   * @param {!Protocol.Network.ResourceTiming|undefined} timingInfo
    */
-  set timing(x) {
-    if (x && !this._fromMemoryCache) {
-      // Take startTime and responseReceivedTime from timing data for better accuracy.
-      // Timing's requestTime is a baseline in seconds, rest of the numbers there are ticks in millis.
-      this._startTime = x.requestTime;
-      this._responseReceivedTime = x.requestTime + x.receiveHeadersEnd / 1000.0;
+  set timing(timingInfo) {
+    if (!timingInfo || this._fromMemoryCache)
+      return;
+    // Take startTime and responseReceivedTime from timing data for better accuracy.
+    // Timing's requestTime is a baseline in seconds, rest of the numbers there are ticks in millis.
+    this._startTime = timingInfo.requestTime;
+    var headersReceivedTime = timingInfo.requestTime + timingInfo.receiveHeadersEnd / 1000.0;
+    if ((this._responseReceivedTime || -1) < 0 || this._responseReceivedTime > headersReceivedTime)
+      this._responseReceivedTime = headersReceivedTime;
+    if (this._startTime > this._responseReceivedTime)
+      this._responseReceivedTime = this._startTime;
 
-      this._timing = x;
-      this.dispatchEventToListeners(SDK.NetworkRequest.Events.TimingChanged, this);
-    }
+    this._timing = timingInfo;
+    this.dispatchEventToListeners(SDK.NetworkRequest.Events.TimingChanged, this);
   }
 
   /**
@@ -668,10 +672,24 @@
   /**
    * @return {string}
    */
+  _filteredProtocolName() {
+    var protocol = this.protocol.toLowerCase();
+    if (protocol === 'h2')
+      return 'http/2.0';
+    return protocol.replace(/^http\/2(\.0)?\+/, 'http/2.0+');
+  }
+
+  /**
+   * @return {string}
+   */
   requestHttpVersion() {
     var headersText = this.requestHeadersText();
-    if (!headersText)
-      return this.requestHeaderValue('version') || this.requestHeaderValue(':version') || 'unknown';
+    if (!headersText) {
+      var version = this.requestHeaderValue('version') || this.requestHeaderValue(':version');
+      if (version)
+        return version;
+      return this._filteredProtocolName();
+    }
     var firstLine = headersText.split(/\r\n/)[0];
     var match = firstLine.match(/(HTTP\/\d+\.\d+)$/);
     return match ? match[1] : 'HTTP/0.9';
@@ -818,8 +836,12 @@
    */
   responseHttpVersion() {
     var headersText = this._responseHeadersText;
-    if (!headersText)
-      return this.responseHeaderValue('version') || this.responseHeaderValue(':version') || 'unknown';
+    if (!headersText) {
+      var version = this.responseHeaderValue('version') || this.responseHeaderValue(':version');
+      if (version)
+        return version;
+      return this._filteredProtocolName();
+    }
     var firstLine = headersText.split(/\r\n/)[0];
     var match = firstLine.match(/^(HTTP\/\d+\.\d+)/);
     return match ? match[1] : 'HTTP/0.9';
diff --git a/third_party/WebKit/Source/modules/accessibility/AXLayoutObject.cpp b/third_party/WebKit/Source/modules/accessibility/AXLayoutObject.cpp
index 60a4a89e..a0d2f7a 100644
--- a/third_party/WebKit/Source/modules/accessibility/AXLayoutObject.cpp
+++ b/third_party/WebKit/Source/modules/accessibility/AXLayoutObject.cpp
@@ -1264,16 +1264,18 @@
 //
 
 void AXLayoutObject::AriaOwnsElements(AXObjectVector& owns) const {
-  AccessibilityChildrenFromAttribute(aria_ownsAttr, owns);
+  AccessibilityChildrenFromAOMProperty(AOMRelationListProperty::kOwns, owns);
 }
 
 void AXLayoutObject::AriaDescribedbyElements(
     AXObjectVector& describedby) const {
-  AccessibilityChildrenFromAttribute(aria_describedbyAttr, describedby);
+  AccessibilityChildrenFromAOMProperty(AOMRelationListProperty::kDescribedBy,
+                                       describedby);
 }
 
 void AXLayoutObject::AriaLabelledbyElements(AXObjectVector& labelledby) const {
-  AccessibilityChildrenFromAttribute(aria_labelledbyAttr, labelledby);
+  AccessibilityChildrenFromAOMProperty(AOMRelationListProperty::kLabeledBy,
+                                       labelledby);
 }
 
 bool AXLayoutObject::AriaHasPopup() const {
@@ -2257,7 +2259,9 @@
     return false;
 
   HeapVector<Member<Element>> elements;
-  ElementsFromAttribute(elements, aria_controlsAttr);
+  if (!HasAOMPropertyOrARIAAttribute(AOMRelationListProperty::kControls,
+                                     elements))
+    return false;
 
   for (const auto& element : elements) {
     AXObject* tab_panel = AxObjectCache().GetOrCreate(element);
diff --git a/third_party/WebKit/Source/modules/accessibility/AXNodeObject.cpp b/third_party/WebKit/Source/modules/accessibility/AXNodeObject.cpp
index 5976ccf4..b806d32 100644
--- a/third_party/WebKit/Source/modules/accessibility/AXNodeObject.cpp
+++ b/third_party/WebKit/Source/modules/accessibility/AXNodeObject.cpp
@@ -30,6 +30,7 @@
 
 #include "core/InputTypeNames.h"
 #include "core/dom/AccessibleNode.h"
+#include "core/dom/AccessibleNodeList.h"
 #include "core/dom/Element.h"
 #include "core/dom/FlatTreeTraversal.h"
 #include "core/dom/NodeTraversal.h"
@@ -268,6 +269,34 @@
       sparse_attribute_client_.AddObjectAttribute(attribute, *target_obj);
   }
 
+  void AddRelationListProperty(AOMRelationListProperty property,
+                               const AccessibleNodeList& relations) override {
+    AXObjectVectorAttribute attribute;
+    switch (property) {
+      case AOMRelationListProperty::kControls:
+        attribute = AXObjectVectorAttribute::kAriaControls;
+        break;
+      case AOMRelationListProperty::kFlowTo:
+        attribute = AXObjectVectorAttribute::kAriaFlowTo;
+        break;
+      default:
+        return;
+    }
+
+    HeapVector<Member<AXObject>> objects;
+    for (size_t i = 0; i < relations.length(); ++i) {
+      AccessibleNode* accessible_node = relations.item(i);
+      if (accessible_node) {
+        Element* element = accessible_node->element();
+        AXObject* ax_element = ax_object_cache_->GetOrCreate(element);
+        if (ax_element && !ax_element->AccessibilityIsIgnored())
+          objects.push_back(ax_element);
+      }
+    }
+
+    sparse_attribute_client_.AddObjectVectorAttribute(attribute, objects);
+  }
+
  private:
   Persistent<AXObjectCacheImpl> ax_object_cache_;
   AXSparseAttributeClient& sparse_attribute_client_;
@@ -773,18 +802,20 @@
   return kUnknownRole;
 }
 
-void AXNodeObject::AccessibilityChildrenFromAttribute(
-    QualifiedName attr,
+void AXNodeObject::AccessibilityChildrenFromAOMProperty(
+    AOMRelationListProperty property,
     AXObject::AXObjectVector& children) const {
   HeapVector<Member<Element>> elements;
-  ElementsFromAttribute(elements, attr);
+  if (!HasAOMPropertyOrARIAAttribute(property, elements))
+    return;
 
   AXObjectCacheImpl& cache = AxObjectCache();
   for (const auto& element : elements) {
     if (AXObject* child = cache.GetOrCreate(element)) {
       // Only aria-labelledby and aria-describedby can target hidden elements.
-      if (child->AccessibilityIsIgnored() && attr != aria_labelledbyAttr &&
-          attr != aria_labeledbyAttr && attr != aria_describedbyAttr) {
+      if (child->AccessibilityIsIgnored() &&
+          property != AOMRelationListProperty::kLabeledBy &&
+          property != AOMRelationListProperty::kDescribedBy) {
         continue;
       }
       children.push_back(child);
@@ -792,6 +823,36 @@
   }
 }
 
+bool AXNodeObject::IsMultiline() const {
+  Node* node = this->GetNode();
+  if (!node)
+    return false;
+
+  const AccessibilityRole role = RoleValue();
+  const bool is_edit_box = role == kSearchBoxRole || role == kTextFieldRole;
+  if (!IsEditable() && !is_edit_box)
+    return false;  // Doesn't support multiline.
+
+  // Supports aria-multiline, so check for attribute.
+  bool is_multiline = false;
+  if (HasAOMPropertyOrARIAAttribute(AOMBooleanProperty::kMultiline,
+                                    is_multiline)) {
+    return is_multiline;
+  }
+
+  // Default for <textarea> is true.
+  if (isHTMLTextAreaElement(*node))
+    return true;
+
+  // Default for other edit boxes is false, including for ARIA, says CORE-AAM.
+  if (is_edit_box)
+    return false;
+
+  // If root of contenteditable area and no ARIA role of textbox/searchbox used,
+  // default to multiline=true which is what the default behavior is.
+  return HasContentEditableAttributeSet();
+}
+
 // This only returns true if this is the element that actually has the
 // contentEditable attribute set, unlike node->hasEditableStyle() which will
 // also return true if an ancestor is editable.
@@ -805,6 +866,9 @@
          EqualIgnoringASCIICase(content_editable_value, "true");
 }
 
+// TODO(aleventhal) Find a more appropriate name or consider returning false
+// for everything but a searchbox or textfield, as a combobox and spinbox
+// can contain a field but should not be considered edit controls themselves.
 bool AXNodeObject::IsTextControl() const {
   if (HasContentEditableAttributeSet())
     return true;
@@ -2536,14 +2600,30 @@
 
 void AXNodeObject::ComputeAriaOwnsChildren(
     HeapVector<Member<AXObject>>& owned_children) const {
+  Vector<String> id_vector;
+  if (!CanHaveChildren() || IsNativeTextControl() ||
+      HasContentEditableAttributeSet()) {
+    AxObjectCache().UpdateAriaOwns(this, id_vector, owned_children);
+    return;
+  }
+
+  HeapVector<Member<Element>> elements;
+  if (HasAOMProperty(AOMRelationListProperty::kOwns, elements)) {
+    AxObjectCache().UpdateAriaOwns(this, id_vector, owned_children);
+
+    for (const auto& element : elements) {
+      AXObject* ax_element = ax_object_cache_->GetOrCreate(&*element);
+      if (ax_element && !ax_element->AccessibilityIsIgnored())
+        owned_children.push_back(ax_element);
+    }
+
+    return;
+  }
+
   if (!HasAttribute(aria_ownsAttr))
     return;
 
-  Vector<String> id_vector;
-  if (CanHaveChildren() && !IsNativeTextControl() &&
-      !HasContentEditableAttributeSet())
-    TokenVectorFromAttribute(id_vector, aria_ownsAttr);
-
+  TokenVectorFromAttribute(id_vector, aria_ownsAttr);
   AxObjectCache().UpdateAriaOwns(this, id_vector, owned_children);
 }
 
@@ -3068,6 +3148,28 @@
 
   // aria-describedby overrides any other accessible description, from:
   // http://rawgit.com/w3c/aria/master/html-aam/html-aam.html
+  // AOM version.
+  HeapVector<Member<Element>> elements;
+  if (HasAOMProperty(AOMRelationListProperty::kDescribedBy, elements)) {
+    AXObjectSet visited;
+    description = TextFromElements(true, visited, elements, related_objects);
+    if (!description.IsNull()) {
+      if (description_sources) {
+        DescriptionSource& source = description_sources->back();
+        source.type = description_from;
+        source.related_objects = *related_objects;
+        source.text = description;
+        found_description = true;
+      } else {
+        return description;
+      }
+    } else if (description_sources) {
+      description_sources->back().invalid = true;
+    }
+  }
+
+  // aria-describedby overrides any other accessible description, from:
+  // http://rawgit.com/w3c/aria/master/html-aam/html-aam.html
   const AtomicString& aria_describedby = GetAttribute(aria_describedbyAttr);
   if (!aria_describedby.IsNull()) {
     if (description_sources)
diff --git a/third_party/WebKit/Source/modules/accessibility/AXNodeObject.h b/third_party/WebKit/Source/modules/accessibility/AXNodeObject.h
index 5e03781..78ab541 100644
--- a/third_party/WebKit/Source/modules/accessibility/AXNodeObject.h
+++ b/third_party/WebKit/Source/modules/accessibility/AXNodeObject.h
@@ -70,8 +70,8 @@
   String AriaAccessibilityDescription() const;
   String AriaAutoComplete() const;
   AccessibilityRole DetermineAriaRoleAttribute() const;
-  void AccessibilityChildrenFromAttribute(QualifiedName attr,
-                                          AXObject::AXObjectVector&) const;
+  void AccessibilityChildrenFromAOMProperty(AOMRelationListProperty,
+                                            AXObject::AXObjectVector&) const;
 
   bool HasContentEditableAttributeSet() const;
   bool IsTextControl() const override;
@@ -102,6 +102,7 @@
   bool IsAnchor() const final;
   bool IsControl() const override;
   bool IsControllingVideoElement() const;
+  bool IsMultiline() const override;
   bool IsEditable() const override { return IsNativeTextControl(); }
   bool IsEmbeddedObject() const final;
   bool IsFieldset() const final;
diff --git a/third_party/WebKit/Source/modules/accessibility/AXObject.cpp b/third_party/WebKit/Source/modules/accessibility/AXObject.cpp
index 82bc2487..96c42953 100644
--- a/third_party/WebKit/Source/modules/accessibility/AXObject.cpp
+++ b/third_party/WebKit/Source/modules/accessibility/AXObject.cpp
@@ -392,6 +392,25 @@
   return target ? target->element() : nullptr;
 }
 
+bool AXObject::HasAOMProperty(AOMRelationListProperty property,
+                              HeapVector<Member<Element>>& result) const {
+  Element* element = this->GetElement();
+  if (!element)
+    return false;
+
+  return AccessibleNode::GetProperty(element, property, result);
+}
+
+bool AXObject::HasAOMPropertyOrARIAAttribute(
+    AOMRelationListProperty property,
+    HeapVector<Member<Element>>& result) const {
+  Element* element = this->GetElement();
+  if (!element)
+    return false;
+
+  return AccessibleNode::GetPropertyOrARIAAttribute(element, property, result);
+}
+
 bool AXObject::HasAOMPropertyOrARIAAttribute(AOMBooleanProperty property,
                                              bool& result) const {
   Element* element = this->GetElement();
@@ -520,7 +539,12 @@
     if (!node)
       return kCheckedStateUndefined;
 
-    if (IsNativeInputInMixedState(node))
+    // Expose native checkbox mixed state as accessibility mixed state. However,
+    // do not expose native radio mixed state as accessibility mixed state.
+    // This would confuse the JAWS screen reader, which reports a mixed radio as
+    // both checked and partially checked, but a native mixed native radio
+    // button sinply means no radio buttons have been checked in the group yet.
+    if (IsNativeCheckboxInMixedState(node))
       return kCheckedStateMixed;
 
     if (isHTMLInputElement(*node) &&
@@ -532,14 +556,13 @@
   return kCheckedStateFalse;
 }
 
-bool AXObject::IsNativeInputInMixedState(const Node* node) {
+bool AXObject::IsNativeCheckboxInMixedState(const Node* node) {
   if (!isHTMLInputElement(node))
     return false;
 
   const HTMLInputElement* input = toHTMLInputElement(node);
   const auto inputType = input->type();
-  if (inputType != InputTypeNames::checkbox &&
-      inputType != InputTypeNames::radio)
+  if (inputType != InputTypeNames::checkbox)
     return false;
   return input->ShouldAppearIndeterminate();
 }
@@ -977,26 +1000,23 @@
   // Step 2B from: http://www.w3.org/TR/accname-aam-1.1
   // If you change this logic, update AXNodeObject::nameFromLabelElement, too.
   if (!in_aria_labelled_by_traversal && !already_visited) {
-    const QualifiedName& attr =
-        HasAttribute(aria_labeledbyAttr) && !HasAttribute(aria_labelledbyAttr)
-            ? aria_labeledbyAttr
-            : aria_labelledbyAttr;
     name_from = kAXNameFromRelatedElement;
-    if (name_sources) {
-      name_sources->push_back(NameSource(*found_text_alternative, attr));
-      name_sources->back().type = name_from;
-    }
 
-    const AtomicString& aria_labelledby = GetAttribute(attr);
-    if (!aria_labelledby.IsNull()) {
-      if (name_sources)
-        name_sources->back().attribute_value = aria_labelledby;
+    // Check AOM property first.
+    HeapVector<Member<Element>> elements;
+    if (HasAOMProperty(AOMRelationListProperty::kLabeledBy, elements)) {
+      if (name_sources) {
+        name_sources->push_back(
+            NameSource(*found_text_alternative, aria_labelledbyAttr));
+        name_sources->back().type = name_from;
+      }
 
       // Operate on a copy of |visited| so that if |nameSources| is not null,
       // the set of visited objects is preserved unmodified for future
       // calculations.
       AXObjectSet visited_copy = visited;
-      text_alternative = TextFromAriaLabelledby(visited_copy, related_objects);
+      text_alternative =
+          TextFromElements(true, visited_copy, elements, related_objects);
       if (!text_alternative.IsNull()) {
         if (name_sources) {
           NameSource& source = name_sources->back();
@@ -1011,6 +1031,44 @@
       } else if (name_sources) {
         name_sources->back().invalid = true;
       }
+    } else {
+      // Now check ARIA attribute
+      const QualifiedName& attr =
+          HasAttribute(aria_labeledbyAttr) && !HasAttribute(aria_labelledbyAttr)
+              ? aria_labeledbyAttr
+              : aria_labelledbyAttr;
+
+      if (name_sources) {
+        name_sources->push_back(NameSource(*found_text_alternative, attr));
+        name_sources->back().type = name_from;
+      }
+
+      const AtomicString& aria_labelledby = GetAttribute(attr);
+      if (!aria_labelledby.IsNull()) {
+        if (name_sources)
+          name_sources->back().attribute_value = aria_labelledby;
+
+        // Operate on a copy of |visited| so that if |nameSources| is not null,
+        // the set of visited objects is preserved unmodified for future
+        // calculations.
+        AXObjectSet visited_copy = visited;
+        text_alternative =
+            TextFromAriaLabelledby(visited_copy, related_objects);
+        if (!text_alternative.IsNull()) {
+          if (name_sources) {
+            NameSource& source = name_sources->back();
+            source.type = name_from;
+            source.related_objects = *related_objects;
+            source.text = text_alternative;
+            *found_text_alternative = true;
+          } else {
+            *found_text_alternative = true;
+            return text_alternative;
+          }
+        } else if (name_sources) {
+          name_sources->back().invalid = true;
+        }
+      }
     }
   }
 
@@ -1162,24 +1220,6 @@
       return AXDefaultActionVerb::kClick;
   }
 }
-
-bool AXObject::IsMultiline() const {
-  Node* node = this->GetNode();
-  if (!node)
-    return false;
-
-  if (isHTMLTextAreaElement(*node))
-    return true;
-
-  if (HasEditableStyle(*node))
-    return true;
-
-  if (!IsNativeTextControl() && !IsNonNativeTextControl())
-    return false;
-
-  return AOMPropertyOrARIAAttributeIsTrue(AOMBooleanProperty::kMultiline);
-}
-
 bool AXObject::AriaPressedIsPresent() const {
   return !GetAttribute(aria_pressedAttr).IsEmpty();
 }
diff --git a/third_party/WebKit/Source/modules/accessibility/AXObject.h b/third_party/WebKit/Source/modules/accessibility/AXObject.h
index ba79e1e..8e746a0 100644
--- a/third_party/WebKit/Source/modules/accessibility/AXObject.h
+++ b/third_party/WebKit/Source/modules/accessibility/AXObject.h
@@ -45,6 +45,7 @@
 
 namespace blink {
 
+class AccessibleNodeList;
 class AXObject;
 class AXObjectCacheImpl;
 class Element;
@@ -60,6 +61,7 @@
 enum class AOMIntProperty;
 enum class AOMFloatProperty;
 enum class AOMRelationProperty;
+enum class AOMRelationListProperty;
 
 typedef unsigned AXID;
 
@@ -354,6 +356,10 @@
   // or the equivalent ARIA attribute, in that order.
   const AtomicString& GetAOMPropertyOrARIAAttribute(AOMStringProperty) const;
   Element* GetAOMPropertyOrARIAAttribute(AOMRelationProperty) const;
+  bool HasAOMProperty(AOMRelationListProperty,
+                      HeapVector<Member<Element>>& result) const;
+  bool HasAOMPropertyOrARIAAttribute(AOMRelationListProperty,
+                                     HeapVector<Member<Element>>& result) const;
   bool HasAOMPropertyOrARIAAttribute(AOMBooleanProperty, bool& result) const;
   bool AOMPropertyOrARIAAttributeIsTrue(AOMBooleanProperty) const;
   bool AOMPropertyOrARIAAttributeIsFalse(AOMBooleanProperty) const;
@@ -634,7 +640,7 @@
   virtual void AriaLabelledbyElements(AXObjectVector&) const {}
   virtual bool AriaHasPopup() const { return false; }
   virtual bool IsEditable() const { return false; }
-  bool IsMultiline() const;
+  virtual bool IsMultiline() const { return false; }
   virtual bool IsRichlyEditable() const { return false; }
   bool AriaCheckedIsPresent() const;
   bool AriaPressedIsPresent() const;
@@ -807,6 +813,8 @@
   static AccessibilityRole AriaRoleToWebCoreRole(const String&);
   static const AtomicString& RoleName(AccessibilityRole);
   static const AtomicString& InternalRoleName(AccessibilityRole);
+  static void AccessibleNodeListToElementVector(const AccessibleNodeList&,
+                                                HeapVector<Member<Element>>&);
 
  protected:
   AXID id_;
@@ -841,7 +849,6 @@
   String TextFromAriaLabelledby(AXObjectSet& visited,
                                 AXRelatedObjectVector* related_objects) const;
   String TextFromAriaDescribedby(AXRelatedObjectVector* related_objects) const;
-
   virtual const AXObject* InheritsPresentationalRoleFrom() const { return 0; }
 
   bool CanReceiveAccessibilityFocus() const;
@@ -880,7 +887,7 @@
 
  private:
   bool IsCheckable() const;
-  static bool IsNativeInputInMixedState(const Node*);
+  static bool IsNativeCheckboxInMixedState(const Node*);
   static bool IncludesARIAWidgetRole(const String&);
   static bool HasInteractiveARIAAttribute(const Element&);
 
diff --git a/third_party/WebKit/Source/modules/accessibility/AXObjectTest.cpp b/third_party/WebKit/Source/modules/accessibility/AXObjectTest.cpp
index a1ca9b9c..f94bfeb1 100644
--- a/third_party/WebKit/Source/modules/accessibility/AXObjectTest.cpp
+++ b/third_party/WebKit/Source/modules/accessibility/AXObjectTest.cpp
@@ -12,7 +12,7 @@
 
 namespace blink {
 
-class AXObjectTest : public testing::Test {
+class AXObjectTest : public ::testing::Test {
  protected:
   Document& GetDocument() { return page_holder_->GetDocument(); }
 
diff --git a/third_party/WebKit/Source/modules/budget/BudgetService.cpp b/third_party/WebKit/Source/modules/budget/BudgetService.cpp
index 9b48bb3b..dcd83f8b 100644
--- a/third_party/WebKit/Source/modules/budget/BudgetService.cpp
+++ b/third_party/WebKit/Source/modules/budget/BudgetService.cpp
@@ -11,9 +11,9 @@
 #include "core/dom/ExecutionContext.h"
 #include "modules/budget/BudgetState.h"
 #include "platform/bindings/ScriptState.h"
-#include "public/platform/InterfaceProvider.h"
 #include "public/platform/Platform.h"
 #include "public/platform/modules/budget_service/budget_service.mojom-blink.h"
+#include "services/service_manager/public/cpp/interface_provider.h"
 
 namespace blink {
 namespace {
@@ -43,9 +43,9 @@
 
 }  // namespace
 
-BudgetService::BudgetService() {
-  Platform::Current()->GetInterfaceProvider()->GetInterface(
-      mojo::MakeRequest(&service_));
+BudgetService::BudgetService(
+    service_manager::InterfaceProvider* interface_provider) {
+  interface_provider->GetInterface(mojo::MakeRequest(&service_));
 
   // Set a connection error handler, so that if an embedder doesn't
   // implement a BudgetSerice mojo service, the developer will get a
diff --git a/third_party/WebKit/Source/modules/budget/BudgetService.h b/third_party/WebKit/Source/modules/budget/BudgetService.h
index 013a80e0..f366f70 100644
--- a/third_party/WebKit/Source/modules/budget/BudgetService.h
+++ b/third_party/WebKit/Source/modules/budget/BudgetService.h
@@ -9,6 +9,10 @@
 #include "platform/bindings/ScriptWrappable.h"
 #include "public/platform/modules/budget_service/budget_service.mojom-blink.h"
 
+namespace service_manager {
+class InterfaceProvider;
+}
+
 namespace blink {
 
 class ScriptPromise;
@@ -23,7 +27,10 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  static BudgetService* Create() { return new BudgetService(); }
+  static BudgetService* Create(
+      service_manager::InterfaceProvider* interface_provider) {
+    return new BudgetService(interface_provider);
+  }
 
   ~BudgetService();
 
@@ -48,7 +55,7 @@
   // Error handler for use if mojo service doesn't connect.
   void OnConnectionError();
 
-  BudgetService();
+  explicit BudgetService(service_manager::InterfaceProvider*);
 
   // Pointer to the Mojo service which will proxy calls to the browser.
   mojom::blink::BudgetServicePtr service_;
diff --git a/third_party/WebKit/Source/modules/budget/DEPS b/third_party/WebKit/Source/modules/budget/DEPS
new file mode 100644
index 0000000..5dbe03e
--- /dev/null
+++ b/third_party/WebKit/Source/modules/budget/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+services/service_manager/public/cpp",
+]
diff --git a/third_party/WebKit/Source/modules/budget/NavigatorBudget.cpp b/third_party/WebKit/Source/modules/budget/NavigatorBudget.cpp
index dc6eb4e7..98ab268 100644
--- a/third_party/WebKit/Source/modules/budget/NavigatorBudget.cpp
+++ b/third_party/WebKit/Source/modules/budget/NavigatorBudget.cpp
@@ -4,6 +4,8 @@
 
 #include "modules/budget/NavigatorBudget.h"
 
+#include "core/frame/LocalFrame.h"
+#include "core/frame/LocalFrameClient.h"
 #include "core/frame/Navigator.h"
 #include "modules/budget/BudgetService.h"
 
@@ -32,8 +34,13 @@
 }
 
 BudgetService* NavigatorBudget::budget() {
-  if (!budget_)
-    budget_ = BudgetService::Create();
+  if (!budget_) {
+    Navigator* navigator = GetSupplementable();
+    if (navigator->GetFrame()) {
+      budget_ = BudgetService::Create(
+          navigator->GetFrame()->Client()->GetInterfaceProvider());
+    }
+  }
   return budget_.Get();
 }
 
diff --git a/third_party/WebKit/Source/modules/budget/WorkerNavigatorBudget.cpp b/third_party/WebKit/Source/modules/budget/WorkerNavigatorBudget.cpp
index aa39cf3..af68d85e 100644
--- a/third_party/WebKit/Source/modules/budget/WorkerNavigatorBudget.cpp
+++ b/third_party/WebKit/Source/modules/budget/WorkerNavigatorBudget.cpp
@@ -4,7 +4,9 @@
 
 #include "modules/budget/WorkerNavigatorBudget.h"
 
+#include "core/workers/WorkerGlobalScope.h"
 #include "core/workers/WorkerNavigator.h"
+#include "core/workers/WorkerThread.h"
 #include "modules/budget/BudgetService.h"
 
 namespace blink {
@@ -33,16 +35,19 @@
   return *worker_navigator_budget;
 }
 
-BudgetService* WorkerNavigatorBudget::budget() {
-  if (!budget_)
-    budget_ = BudgetService::Create();
+BudgetService* WorkerNavigatorBudget::budget(ExecutionContext* context) {
+  if (!budget_) {
+    WorkerThread* thread = ToWorkerGlobalScope(context)->GetThread();
+    budget_ = BudgetService::Create(&thread->GetInterfaceProvider());
+  }
   return budget_.Get();
 }
 
 // static
 BudgetService* WorkerNavigatorBudget::budget(
+    ExecutionContext* context,
     WorkerNavigator& worker_navigator) {
-  return WorkerNavigatorBudget::From(worker_navigator).budget();
+  return WorkerNavigatorBudget::From(worker_navigator).budget(context);
 }
 
 DEFINE_TRACE(WorkerNavigatorBudget) {
diff --git a/third_party/WebKit/Source/modules/budget/WorkerNavigatorBudget.h b/third_party/WebKit/Source/modules/budget/WorkerNavigatorBudget.h
index ccc12fd5..8f6cf1f 100644
--- a/third_party/WebKit/Source/modules/budget/WorkerNavigatorBudget.h
+++ b/third_party/WebKit/Source/modules/budget/WorkerNavigatorBudget.h
@@ -13,6 +13,7 @@
 namespace blink {
 
 class BudgetService;
+class ExecutionContext;
 class WorkerNavigator;
 
 // This exposes the budget object on the WorkerNavigator partial interface.
@@ -25,8 +26,8 @@
  public:
   static WorkerNavigatorBudget& From(WorkerNavigator&);
 
-  static BudgetService* budget(WorkerNavigator&);
-  BudgetService* budget();
+  static BudgetService* budget(ExecutionContext*, WorkerNavigator&);
+  BudgetService* budget(ExecutionContext*);
 
   DECLARE_VIRTUAL_TRACE();
 
diff --git a/third_party/WebKit/Source/modules/budget/WorkerNavigatorBudget.idl b/third_party/WebKit/Source/modules/budget/WorkerNavigatorBudget.idl
index 9720c97..69d19b3 100644
--- a/third_party/WebKit/Source/modules/budget/WorkerNavigatorBudget.idl
+++ b/third_party/WebKit/Source/modules/budget/WorkerNavigatorBudget.idl
@@ -7,5 +7,5 @@
 [
     RuntimeEnabled=Budget
 ] partial interface WorkerNavigator {
-    [SameObject] readonly attribute BudgetService budget;
+    [SameObject, CallWith=ExecutionContext] readonly attribute BudgetService budget;
 };
diff --git a/third_party/WebKit/Source/modules/canvas/HTMLCanvasElementModule.cpp b/third_party/WebKit/Source/modules/canvas/HTMLCanvasElementModule.cpp
index e619c76..00895460 100644
--- a/third_party/WebKit/Source/modules/canvas/HTMLCanvasElementModule.cpp
+++ b/third_party/WebKit/Source/modules/canvas/HTMLCanvasElementModule.cpp
@@ -65,7 +65,7 @@
   offscreen_canvas->SetPlaceholderCanvasId(canvas_id);
   canvas.RegisterPlaceholder(canvas_id);
 
-  CanvasSurfaceLayerBridge* bridge = canvas.SurfaceLayerBridge();
+  SurfaceLayerBridge* bridge = canvas.SurfaceLayerBridge();
   if (bridge) {
     offscreen_canvas->SetFrameSinkId(bridge->GetFrameSinkId().client_id(),
                                      bridge->GetFrameSinkId().sink_id());
diff --git a/third_party/WebKit/Source/modules/credentialmanager/CredentialsContainerTest.cpp b/third_party/WebKit/Source/modules/credentialmanager/CredentialsContainerTest.cpp
index 91a7b9e..873b344 100644
--- a/third_party/WebKit/Source/modules/credentialmanager/CredentialsContainerTest.cpp
+++ b/third_party/WebKit/Source/modules/credentialmanager/CredentialsContainerTest.cpp
@@ -50,7 +50,7 @@
     // Set up.
     scope.GetDocument().SetSecurityOrigin(
         SecurityOrigin::CreateFromString("https://example.test"));
-    testing::StrictMock<MockCredentialManagerClient> mock_client;
+    ::testing::StrictMock<MockCredentialManagerClient> mock_client;
     ProvideCredentialManagerClientTo(scope.GetPage(),
                                      new CredentialManagerClient(&mock_client));
 
diff --git a/third_party/WebKit/Source/modules/csspaint/PaintWorkletTest.cpp b/third_party/WebKit/Source/modules/csspaint/PaintWorkletTest.cpp
index 9035109..66d5748 100644
--- a/third_party/WebKit/Source/modules/csspaint/PaintWorkletTest.cpp
+++ b/third_party/WebKit/Source/modules/csspaint/PaintWorkletTest.cpp
@@ -18,7 +18,7 @@
 
 namespace blink {
 
-class PaintWorkletTest : public testing::Test {
+class PaintWorkletTest : public ::testing::Test {
  public:
   PaintWorkletTest() : page_(DummyPageHolder::Create()) {}
 
diff --git a/third_party/WebKit/Source/modules/geolocation/Geolocation.idl b/third_party/WebKit/Source/modules/geolocation/Geolocation.idl
index 002276a..3e72608d 100644
--- a/third_party/WebKit/Source/modules/geolocation/Geolocation.idl
+++ b/third_party/WebKit/Source/modules/geolocation/Geolocation.idl
@@ -23,7 +23,7 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-// http://www.w3.org/TR/geolocation-API/#geolocation_interface
+// https://www.w3.org/TR/geolocation-API/#geolocation_interface
 [
     NoInterfaceObject
 ] interface Geolocation {
diff --git a/third_party/WebKit/Source/modules/indexeddb/MockWebIDBDatabase.h b/third_party/WebKit/Source/modules/indexeddb/MockWebIDBDatabase.h
index 971f62e..d5fd60e 100644
--- a/third_party/WebKit/Source/modules/indexeddb/MockWebIDBDatabase.h
+++ b/third_party/WebKit/Source/modules/indexeddb/MockWebIDBDatabase.h
@@ -14,7 +14,7 @@
 
 namespace blink {
 
-class MockWebIDBDatabase : public testing::StrictMock<WebIDBDatabase> {
+class MockWebIDBDatabase : public ::testing::StrictMock<WebIDBDatabase> {
  public:
   virtual ~MockWebIDBDatabase();
 
diff --git a/third_party/WebKit/Source/modules/payments/PaymentRequestDetailsTest.cpp b/third_party/WebKit/Source/modules/payments/PaymentRequestDetailsTest.cpp
index 3f821bc..df8dfef1 100644
--- a/third_party/WebKit/Source/modules/payments/PaymentRequestDetailsTest.cpp
+++ b/third_party/WebKit/Source/modules/payments/PaymentRequestDetailsTest.cpp
@@ -123,7 +123,7 @@
 }
 
 class PaymentRequestDetailsTest
-    : public testing::TestWithParam<DetailsTestCase> {};
+    : public ::testing::TestWithParam<DetailsTestCase> {};
 
 TEST_P(PaymentRequestDetailsTest, ValidatesDetails) {
   V8TestingScope scope;
@@ -145,738 +145,738 @@
 INSTANTIATE_TEST_CASE_P(
     EmptyData,
     PaymentRequestDetailsTest,
-    testing::Values(DetailsTestCase(kPaymentTestDetailTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailTotal,
-                                    kPaymentTestDataLabel,
-                                    kPaymentTestOverwriteValue,
-                                    "",
-                                    false),
-                    DetailsTestCase(kPaymentTestDetailItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailItem,
-                                    kPaymentTestDataLabel,
-                                    kPaymentTestOverwriteValue,
-                                    "",
-                                    false),
-                    DetailsTestCase(kPaymentTestDetailShippingOption,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailShippingOption,
-                                    kPaymentTestDataId,
-                                    kPaymentTestOverwriteValue,
-                                    "",
-                                    false),
-                    DetailsTestCase(kPaymentTestDetailShippingOption,
-                                    kPaymentTestDataLabel,
-                                    kPaymentTestOverwriteValue,
-                                    "",
-                                    false),
-                    DetailsTestCase(kPaymentTestDetailModifierTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailModifierTotal,
-                                    kPaymentTestDataLabel,
-                                    kPaymentTestOverwriteValue,
-                                    "",
-                                    false),
-                    DetailsTestCase(kPaymentTestDetailModifierItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailModifierItem,
-                                    kPaymentTestDataLabel,
-                                    kPaymentTestOverwriteValue,
-                                    "",
-                                    false)));
+    ::testing::Values(DetailsTestCase(kPaymentTestDetailTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailTotal,
+                                      kPaymentTestDataLabel,
+                                      kPaymentTestOverwriteValue,
+                                      "",
+                                      false),
+                      DetailsTestCase(kPaymentTestDetailItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailItem,
+                                      kPaymentTestDataLabel,
+                                      kPaymentTestOverwriteValue,
+                                      "",
+                                      false),
+                      DetailsTestCase(kPaymentTestDetailShippingOption,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailShippingOption,
+                                      kPaymentTestDataId,
+                                      kPaymentTestOverwriteValue,
+                                      "",
+                                      false),
+                      DetailsTestCase(kPaymentTestDetailShippingOption,
+                                      kPaymentTestDataLabel,
+                                      kPaymentTestOverwriteValue,
+                                      "",
+                                      false),
+                      DetailsTestCase(kPaymentTestDetailModifierTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailModifierTotal,
+                                      kPaymentTestDataLabel,
+                                      kPaymentTestOverwriteValue,
+                                      "",
+                                      false),
+                      DetailsTestCase(kPaymentTestDetailModifierItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailModifierItem,
+                                      kPaymentTestDataLabel,
+                                      kPaymentTestOverwriteValue,
+                                      "",
+                                      false)));
 
 INSTANTIATE_TEST_CASE_P(
     ValidCurrencyCodeFormat,
     PaymentRequestDetailsTest,
-    testing::Values(DetailsTestCase(kPaymentTestDetailTotal,
-                                    kPaymentTestDataCurrencyCode,
-                                    kPaymentTestOverwriteValue,
-                                    "USD"),
-                    DetailsTestCase(kPaymentTestDetailItem,
-                                    kPaymentTestDataCurrencyCode,
-                                    kPaymentTestOverwriteValue,
-                                    "USD"),
-                    DetailsTestCase(kPaymentTestDetailShippingOption,
-                                    kPaymentTestDataCurrencyCode,
-                                    kPaymentTestOverwriteValue,
-                                    "USD"),
-                    DetailsTestCase(kPaymentTestDetailModifierTotal,
-                                    kPaymentTestDataCurrencyCode,
-                                    kPaymentTestOverwriteValue,
-                                    "USD"),
-                    DetailsTestCase(kPaymentTestDetailModifierItem,
-                                    kPaymentTestDataCurrencyCode,
-                                    kPaymentTestOverwriteValue,
-                                    "USD")));
+    ::testing::Values(DetailsTestCase(kPaymentTestDetailTotal,
+                                      kPaymentTestDataCurrencyCode,
+                                      kPaymentTestOverwriteValue,
+                                      "USD"),
+                      DetailsTestCase(kPaymentTestDetailItem,
+                                      kPaymentTestDataCurrencyCode,
+                                      kPaymentTestOverwriteValue,
+                                      "USD"),
+                      DetailsTestCase(kPaymentTestDetailShippingOption,
+                                      kPaymentTestDataCurrencyCode,
+                                      kPaymentTestOverwriteValue,
+                                      "USD"),
+                      DetailsTestCase(kPaymentTestDetailModifierTotal,
+                                      kPaymentTestDataCurrencyCode,
+                                      kPaymentTestOverwriteValue,
+                                      "USD"),
+                      DetailsTestCase(kPaymentTestDetailModifierItem,
+                                      kPaymentTestDataCurrencyCode,
+                                      kPaymentTestOverwriteValue,
+                                      "USD")));
 
 INSTANTIATE_TEST_CASE_P(
     ValidCurrencySystem,
     PaymentRequestDetailsTest,
-    testing::Values(DetailsTestCase(kPaymentTestDetailTotal,
-                                    kPaymentTestDataCurrencySystem,
-                                    kPaymentTestOverwriteValue,
-                                    "https://bitcoin.org")));
+    ::testing::Values(DetailsTestCase(kPaymentTestDetailTotal,
+                                      kPaymentTestDataCurrencySystem,
+                                      kPaymentTestOverwriteValue,
+                                      "https://bitcoin.org")));
 
 INSTANTIATE_TEST_CASE_P(
     InvalidCurrencySystem,
     PaymentRequestDetailsTest,
-    testing::Values(DetailsTestCase(kPaymentTestDetailTotal,
-                                    kPaymentTestDataCurrencySystem,
-                                    kPaymentTestOverwriteValue,
-                                    "\\^%\\",
-                                    true,
-                                    kV8TypeError)));
+    ::testing::Values(DetailsTestCase(kPaymentTestDetailTotal,
+                                      kPaymentTestDataCurrencySystem,
+                                      kPaymentTestOverwriteValue,
+                                      "\\^%\\",
+                                      true,
+                                      kV8TypeError)));
 
 INSTANTIATE_TEST_CASE_P(
     ValidValueFormat,
     PaymentRequestDetailsTest,
-    testing::Values(DetailsTestCase(kPaymentTestDetailTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "0"),
-                    DetailsTestCase(kPaymentTestDetailTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "1"),
-                    DetailsTestCase(kPaymentTestDetailTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "10"),
-                    DetailsTestCase(kPaymentTestDetailTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "10.99"),
-                    DetailsTestCase(kPaymentTestDetailTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "01234567890123456789.0123456789"),
-                    DetailsTestCase(kPaymentTestDetailTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "01234567890123456789012345678.9"),
-                    DetailsTestCase(kPaymentTestDetailTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "012345678901234567890123456789"),
-                    DetailsTestCase(kPaymentTestDetailItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "0"),
-                    DetailsTestCase(kPaymentTestDetailItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "-0"),
-                    DetailsTestCase(kPaymentTestDetailItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "1"),
-                    DetailsTestCase(kPaymentTestDetailItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "10"),
-                    DetailsTestCase(kPaymentTestDetailItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "-3"),
-                    DetailsTestCase(kPaymentTestDetailItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "10.99"),
-                    DetailsTestCase(kPaymentTestDetailItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "-3.00"),
-                    DetailsTestCase(kPaymentTestDetailItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "01234567890123456789.0123456789"),
-                    DetailsTestCase(kPaymentTestDetailItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "01234567890123456789012345678.9"),
-                    DetailsTestCase(kPaymentTestDetailItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "012345678901234567890123456789"),
-                    DetailsTestCase(kPaymentTestDetailItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "-01234567890123456789.0123456789"),
-                    DetailsTestCase(kPaymentTestDetailItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "-01234567890123456789012345678.9"),
-                    DetailsTestCase(kPaymentTestDetailItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "-012345678901234567890123456789"),
-                    DetailsTestCase(kPaymentTestDetailShippingOption,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "0"),
-                    DetailsTestCase(kPaymentTestDetailShippingOption,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "-0"),
-                    DetailsTestCase(kPaymentTestDetailShippingOption,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "1"),
-                    DetailsTestCase(kPaymentTestDetailShippingOption,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "10"),
-                    DetailsTestCase(kPaymentTestDetailShippingOption,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "-3"),
-                    DetailsTestCase(kPaymentTestDetailShippingOption,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "10.99"),
-                    DetailsTestCase(kPaymentTestDetailShippingOption,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "-3.00"),
-                    DetailsTestCase(kPaymentTestDetailShippingOption,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "01234567890123456789.0123456789"),
-                    DetailsTestCase(kPaymentTestDetailShippingOption,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "01234567890123456789012345678.9"),
-                    DetailsTestCase(kPaymentTestDetailShippingOption,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "012345678901234567890123456789"),
-                    DetailsTestCase(kPaymentTestDetailShippingOption,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "-01234567890123456789.0123456789"),
-                    DetailsTestCase(kPaymentTestDetailShippingOption,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "-01234567890123456789012345678.9"),
-                    DetailsTestCase(kPaymentTestDetailShippingOption,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "-012345678901234567890123456789")));
+    ::testing::Values(DetailsTestCase(kPaymentTestDetailTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "0"),
+                      DetailsTestCase(kPaymentTestDetailTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "1"),
+                      DetailsTestCase(kPaymentTestDetailTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "10"),
+                      DetailsTestCase(kPaymentTestDetailTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "10.99"),
+                      DetailsTestCase(kPaymentTestDetailTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "01234567890123456789.0123456789"),
+                      DetailsTestCase(kPaymentTestDetailTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "01234567890123456789012345678.9"),
+                      DetailsTestCase(kPaymentTestDetailTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "012345678901234567890123456789"),
+                      DetailsTestCase(kPaymentTestDetailItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "0"),
+                      DetailsTestCase(kPaymentTestDetailItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "-0"),
+                      DetailsTestCase(kPaymentTestDetailItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "1"),
+                      DetailsTestCase(kPaymentTestDetailItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "10"),
+                      DetailsTestCase(kPaymentTestDetailItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "-3"),
+                      DetailsTestCase(kPaymentTestDetailItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "10.99"),
+                      DetailsTestCase(kPaymentTestDetailItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "-3.00"),
+                      DetailsTestCase(kPaymentTestDetailItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "01234567890123456789.0123456789"),
+                      DetailsTestCase(kPaymentTestDetailItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "01234567890123456789012345678.9"),
+                      DetailsTestCase(kPaymentTestDetailItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "012345678901234567890123456789"),
+                      DetailsTestCase(kPaymentTestDetailItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "-01234567890123456789.0123456789"),
+                      DetailsTestCase(kPaymentTestDetailItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "-01234567890123456789012345678.9"),
+                      DetailsTestCase(kPaymentTestDetailItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "-012345678901234567890123456789"),
+                      DetailsTestCase(kPaymentTestDetailShippingOption,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "0"),
+                      DetailsTestCase(kPaymentTestDetailShippingOption,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "-0"),
+                      DetailsTestCase(kPaymentTestDetailShippingOption,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "1"),
+                      DetailsTestCase(kPaymentTestDetailShippingOption,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "10"),
+                      DetailsTestCase(kPaymentTestDetailShippingOption,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "-3"),
+                      DetailsTestCase(kPaymentTestDetailShippingOption,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "10.99"),
+                      DetailsTestCase(kPaymentTestDetailShippingOption,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "-3.00"),
+                      DetailsTestCase(kPaymentTestDetailShippingOption,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "01234567890123456789.0123456789"),
+                      DetailsTestCase(kPaymentTestDetailShippingOption,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "01234567890123456789012345678.9"),
+                      DetailsTestCase(kPaymentTestDetailShippingOption,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "012345678901234567890123456789"),
+                      DetailsTestCase(kPaymentTestDetailShippingOption,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "-01234567890123456789.0123456789"),
+                      DetailsTestCase(kPaymentTestDetailShippingOption,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "-01234567890123456789012345678.9"),
+                      DetailsTestCase(kPaymentTestDetailShippingOption,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "-012345678901234567890123456789")));
 
 INSTANTIATE_TEST_CASE_P(
     ValidValueFormatForModifier,
     PaymentRequestDetailsTest,
-    testing::Values(DetailsTestCase(kPaymentTestDetailModifierTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "0"),
-                    DetailsTestCase(kPaymentTestDetailModifierTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "1"),
-                    DetailsTestCase(kPaymentTestDetailModifierTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "10"),
-                    DetailsTestCase(kPaymentTestDetailModifierTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "10.99"),
-                    DetailsTestCase(kPaymentTestDetailModifierTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "01234567890123456789.0123456789"),
-                    DetailsTestCase(kPaymentTestDetailModifierTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "01234567890123456789012345678.9"),
-                    DetailsTestCase(kPaymentTestDetailModifierTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "012345678901234567890123456789"),
-                    DetailsTestCase(kPaymentTestDetailModifierItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "0"),
-                    DetailsTestCase(kPaymentTestDetailModifierItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "-0"),
-                    DetailsTestCase(kPaymentTestDetailModifierItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "1"),
-                    DetailsTestCase(kPaymentTestDetailModifierItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "10"),
-                    DetailsTestCase(kPaymentTestDetailModifierItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "-3"),
-                    DetailsTestCase(kPaymentTestDetailModifierItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "10.99"),
-                    DetailsTestCase(kPaymentTestDetailModifierItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "-3.00"),
-                    DetailsTestCase(kPaymentTestDetailModifierItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "01234567890123456789.0123456789"),
-                    DetailsTestCase(kPaymentTestDetailModifierItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "01234567890123456789012345678.9"),
-                    DetailsTestCase(kPaymentTestDetailModifierItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "012345678901234567890123456789"),
-                    DetailsTestCase(kPaymentTestDetailModifierItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "-01234567890123456789.0123456789"),
-                    DetailsTestCase(kPaymentTestDetailModifierItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "-01234567890123456789012345678.9"),
-                    DetailsTestCase(kPaymentTestDetailModifierItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "-012345678901234567890123456789")));
+    ::testing::Values(DetailsTestCase(kPaymentTestDetailModifierTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "0"),
+                      DetailsTestCase(kPaymentTestDetailModifierTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "1"),
+                      DetailsTestCase(kPaymentTestDetailModifierTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "10"),
+                      DetailsTestCase(kPaymentTestDetailModifierTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "10.99"),
+                      DetailsTestCase(kPaymentTestDetailModifierTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "01234567890123456789.0123456789"),
+                      DetailsTestCase(kPaymentTestDetailModifierTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "01234567890123456789012345678.9"),
+                      DetailsTestCase(kPaymentTestDetailModifierTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "012345678901234567890123456789"),
+                      DetailsTestCase(kPaymentTestDetailModifierItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "0"),
+                      DetailsTestCase(kPaymentTestDetailModifierItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "-0"),
+                      DetailsTestCase(kPaymentTestDetailModifierItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "1"),
+                      DetailsTestCase(kPaymentTestDetailModifierItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "10"),
+                      DetailsTestCase(kPaymentTestDetailModifierItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "-3"),
+                      DetailsTestCase(kPaymentTestDetailModifierItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "10.99"),
+                      DetailsTestCase(kPaymentTestDetailModifierItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "-3.00"),
+                      DetailsTestCase(kPaymentTestDetailModifierItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "01234567890123456789.0123456789"),
+                      DetailsTestCase(kPaymentTestDetailModifierItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "01234567890123456789012345678.9"),
+                      DetailsTestCase(kPaymentTestDetailModifierItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "012345678901234567890123456789"),
+                      DetailsTestCase(kPaymentTestDetailModifierItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "-01234567890123456789.0123456789"),
+                      DetailsTestCase(kPaymentTestDetailModifierItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "-01234567890123456789012345678.9"),
+                      DetailsTestCase(kPaymentTestDetailModifierItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "-012345678901234567890123456789")));
 
 INSTANTIATE_TEST_CASE_P(
     InvalidValueFormat,
     PaymentRequestDetailsTest,
-    testing::Values(DetailsTestCase(kPaymentTestDetailTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "-0",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "-3",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "-3.00",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "-",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "notdigits",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "ALSONOTDIGITS",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "10.",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    ".99",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "-10.",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "10-",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "1-0",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "1.0.0",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "1/3",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "-01234567890123456789.0123456789",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "-01234567890123456789012345678.9",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "-012345678901234567890123456789",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "-",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "notdigits",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "ALSONOTDIGITS",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "10.",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    ".99",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "-10.",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "10-",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "1-0",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "1.0.0",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "1/3",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailShippingOption,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailShippingOption,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "-",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailShippingOption,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "notdigits",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailShippingOption,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "ALSONOTDIGITS",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailShippingOption,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "10.",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailShippingOption,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    ".99",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailShippingOption,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "-10.",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailShippingOption,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "10-",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailShippingOption,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "1-0",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailShippingOption,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "1.0.0",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailShippingOption,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "1/3",
-                                    true,
-                                    kV8TypeError)));
+    ::testing::Values(DetailsTestCase(kPaymentTestDetailTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "-0",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "-3",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "-3.00",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "-",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "notdigits",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "ALSONOTDIGITS",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "10.",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      ".99",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "-10.",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "10-",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "1-0",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "1.0.0",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "1/3",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "-01234567890123456789.0123456789",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "-01234567890123456789012345678.9",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "-012345678901234567890123456789",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "-",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "notdigits",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "ALSONOTDIGITS",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "10.",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      ".99",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "-10.",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "10-",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "1-0",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "1.0.0",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "1/3",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailShippingOption,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailShippingOption,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "-",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailShippingOption,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "notdigits",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailShippingOption,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "ALSONOTDIGITS",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailShippingOption,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "10.",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailShippingOption,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      ".99",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailShippingOption,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "-10.",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailShippingOption,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "10-",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailShippingOption,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "1-0",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailShippingOption,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "1.0.0",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailShippingOption,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "1/3",
+                                      true,
+                                      kV8TypeError)));
 
 INSTANTIATE_TEST_CASE_P(
     InvalidValueFormatForModifier,
     PaymentRequestDetailsTest,
-    testing::Values(DetailsTestCase(kPaymentTestDetailModifierTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "-0",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailModifierTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "-3",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailModifierTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "-3.00",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailModifierTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailModifierTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "-",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailModifierTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "notdigits",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailModifierTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "ALSONOTDIGITS",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailModifierTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "10.",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailModifierTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    ".99",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailModifierTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "-10.",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailModifierTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "10-",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailModifierTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "1-0",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailModifierTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "1.0.0",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailModifierTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "1/3",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailModifierTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "-01234567890123456789.0123456789",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailModifierTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "-01234567890123456789012345678.9",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailModifierTotal,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "-012345678901234567890123456789",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailModifierItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailModifierItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "-",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailModifierItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "notdigits",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailModifierItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "ALSONOTDIGITS",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailModifierItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "10.",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailModifierItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    ".99",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailModifierItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "-10.",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailModifierItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "10-",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailModifierItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "1-0",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailModifierItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "1.0.0",
-                                    true,
-                                    kV8TypeError),
-                    DetailsTestCase(kPaymentTestDetailModifierItem,
-                                    kPaymentTestDataValue,
-                                    kPaymentTestOverwriteValue,
-                                    "1/3",
-                                    true,
-                                    kV8TypeError)));
+    ::testing::Values(DetailsTestCase(kPaymentTestDetailModifierTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "-0",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailModifierTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "-3",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailModifierTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "-3.00",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailModifierTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailModifierTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "-",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailModifierTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "notdigits",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailModifierTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "ALSONOTDIGITS",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailModifierTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "10.",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailModifierTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      ".99",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailModifierTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "-10.",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailModifierTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "10-",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailModifierTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "1-0",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailModifierTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "1.0.0",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailModifierTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "1/3",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailModifierTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "-01234567890123456789.0123456789",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailModifierTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "-01234567890123456789012345678.9",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailModifierTotal,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "-012345678901234567890123456789",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailModifierItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailModifierItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "-",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailModifierItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "notdigits",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailModifierItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "ALSONOTDIGITS",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailModifierItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "10.",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailModifierItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      ".99",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailModifierItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "-10.",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailModifierItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "10-",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailModifierItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "1-0",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailModifierItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "1.0.0",
+                                      true,
+                                      kV8TypeError),
+                      DetailsTestCase(kPaymentTestDetailModifierItem,
+                                      kPaymentTestDataValue,
+                                      kPaymentTestOverwriteValue,
+                                      "1/3",
+                                      true,
+                                      kV8TypeError)));
 
 }  // namespace
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/payments/PaymentRequestUpdateEventTest.cpp b/third_party/WebKit/Source/modules/payments/PaymentRequestUpdateEventTest.cpp
index 6f6c387..f65812d 100644
--- a/third_party/WebKit/Source/modules/payments/PaymentRequestUpdateEventTest.cpp
+++ b/third_party/WebKit/Source/modules/payments/PaymentRequestUpdateEventTest.cpp
@@ -48,8 +48,8 @@
                     scope.GetExceptionState());
   EXPECT_FALSE(scope.GetExceptionState().HadException());
 
-  EXPECT_CALL(*updater, OnUpdatePaymentDetails(testing::_));
-  EXPECT_CALL(*updater, OnUpdatePaymentDetailsFailure(testing::_)).Times(0);
+  EXPECT_CALL(*updater, OnUpdatePaymentDetails(::testing::_));
+  EXPECT_CALL(*updater, OnUpdatePaymentDetailsFailure(::testing::_)).Times(0);
 
   payment_details->Resolve("foo");
 }
@@ -67,8 +67,8 @@
                     scope.GetExceptionState());
   EXPECT_FALSE(scope.GetExceptionState().HadException());
 
-  EXPECT_CALL(*updater, OnUpdatePaymentDetails(testing::_)).Times(0);
-  EXPECT_CALL(*updater, OnUpdatePaymentDetailsFailure(testing::_));
+  EXPECT_CALL(*updater, OnUpdatePaymentDetails(::testing::_)).Times(0);
+  EXPECT_CALL(*updater, OnUpdatePaymentDetailsFailure(::testing::_));
 
   payment_details->Reject("oops");
 }
diff --git a/third_party/WebKit/Source/modules/payments/PaymentResponseTest.cpp b/third_party/WebKit/Source/modules/payments/PaymentResponseTest.cpp
index 7c34ada9..757b9cb 100644
--- a/third_party/WebKit/Source/modules/payments/PaymentResponseTest.cpp
+++ b/third_party/WebKit/Source/modules/payments/PaymentResponseTest.cpp
@@ -28,8 +28,8 @@
 
  public:
   MockPaymentCompleter() {
-    ON_CALL(*this, Complete(testing::_, testing::_))
-        .WillByDefault(testing::ReturnPointee(&dummy_promise_));
+    ON_CALL(*this, Complete(::testing::_, ::testing::_))
+        .WillByDefault(::testing::ReturnPointee(&dummy_promise_));
   }
 
   ~MockPaymentCompleter() override {}
diff --git a/third_party/WebKit/Source/modules/payments/PaymentTestHelper.cpp b/third_party/WebKit/Source/modules/payments/PaymentTestHelper.cpp
index a2a53ee..f8a788ad 100644
--- a/third_party/WebKit/Source/modules/payments/PaymentTestHelper.cpp
+++ b/third_party/WebKit/Source/modules/payments/PaymentTestHelper.cpp
@@ -213,26 +213,26 @@
 PaymentRequestMockFunctionScope::~PaymentRequestMockFunctionScope() {
   v8::MicrotasksScope::PerformCheckpoint(script_state_->GetIsolate());
   for (MockFunction* mock_function : mock_functions_) {
-    testing::Mock::VerifyAndClearExpectations(mock_function);
+    ::testing::Mock::VerifyAndClearExpectations(mock_function);
   }
 }
 
 v8::Local<v8::Function> PaymentRequestMockFunctionScope::ExpectCall(
     String* captor) {
   mock_functions_.push_back(new MockFunction(script_state_, captor));
-  EXPECT_CALL(*mock_functions_.back(), Call(testing::_));
+  EXPECT_CALL(*mock_functions_.back(), Call(::testing::_));
   return mock_functions_.back()->Bind();
 }
 
 v8::Local<v8::Function> PaymentRequestMockFunctionScope::ExpectCall() {
   mock_functions_.push_back(new MockFunction(script_state_));
-  EXPECT_CALL(*mock_functions_.back(), Call(testing::_));
+  EXPECT_CALL(*mock_functions_.back(), Call(::testing::_));
   return mock_functions_.back()->Bind();
 }
 
 v8::Local<v8::Function> PaymentRequestMockFunctionScope::ExpectNoCall() {
   mock_functions_.push_back(new MockFunction(script_state_));
-  EXPECT_CALL(*mock_functions_.back(), Call(testing::_)).Times(0);
+  EXPECT_CALL(*mock_functions_.back(), Call(::testing::_)).Times(0);
   return mock_functions_.back()->Bind();
 }
 
@@ -245,16 +245,16 @@
 PaymentRequestMockFunctionScope::MockFunction::MockFunction(
     ScriptState* script_state)
     : ScriptFunction(script_state) {
-  ON_CALL(*this, Call(testing::_)).WillByDefault(testing::ReturnArg<0>());
+  ON_CALL(*this, Call(::testing::_)).WillByDefault(::testing::ReturnArg<0>());
 }
 
 PaymentRequestMockFunctionScope::MockFunction::MockFunction(
     ScriptState* script_state,
     String* captor)
     : ScriptFunction(script_state), value_(captor) {
-  ON_CALL(*this, Call(testing::_))
+  ON_CALL(*this, Call(::testing::_))
       .WillByDefault(
-          testing::DoAll(SaveValueIn(value_), testing::ReturnArg<0>()));
+          ::testing::DoAll(SaveValueIn(value_), ::testing::ReturnArg<0>()));
 }
 
 v8::Local<v8::Function> PaymentRequestMockFunctionScope::MockFunction::Bind() {
diff --git a/third_party/WebKit/Source/modules/payments/PaymentsValidatorsTest.cpp b/third_party/WebKit/Source/modules/payments/PaymentsValidatorsTest.cpp
index 6b59fada..6a7e110 100644
--- a/third_party/WebKit/Source/modules/payments/PaymentsValidatorsTest.cpp
+++ b/third_party/WebKit/Source/modules/payments/PaymentsValidatorsTest.cpp
@@ -24,7 +24,7 @@
 };
 
 class PaymentsCurrencyValidatorTest
-    : public testing::TestWithParam<CurrencyCodeTestCase> {};
+    : public ::testing::TestWithParam<CurrencyCodeTestCase> {};
 
 const char* LongString2048() {
   static char long_string[2049];
@@ -59,7 +59,7 @@
 INSTANTIATE_TEST_CASE_P(
     CurrencyCodes,
     PaymentsCurrencyValidatorTest,
-    testing::Values(
+    ::testing::Values(
         // The most common identifiers are three-letter alphabetic codes as
         // defined by [ISO4217] (for example, "USD" for US Dollars).
         // |system| is a URL that indicates the currency system that the
@@ -102,7 +102,8 @@
   return out;
 }
 
-class PaymentsAmountValidatorTest : public testing::TestWithParam<TestCase> {};
+class PaymentsAmountValidatorTest : public ::testing::TestWithParam<TestCase> {
+};
 
 TEST_P(PaymentsAmountValidatorTest, IsValidAmountFormat) {
   String error_message;
@@ -121,34 +122,35 @@
 INSTANTIATE_TEST_CASE_P(
     Amounts,
     PaymentsAmountValidatorTest,
-    testing::Values(TestCase("0", true),
-                    TestCase("-0", true),
-                    TestCase("1", true),
-                    TestCase("10", true),
-                    TestCase("-3", true),
-                    TestCase("10.99", true),
-                    TestCase("-3.00", true),
-                    TestCase("01234567890123456789.0123456789", true),
-                    TestCase("01234567890123456789012345678.9", true),
-                    TestCase("012345678901234567890123456789", true),
-                    TestCase("-01234567890123456789.0123456789", true),
-                    TestCase("-01234567890123456789012345678.9", true),
-                    TestCase("-012345678901234567890123456789", true),
-                    // Invalid amount formats
-                    TestCase("", false),
-                    TestCase("-", false),
-                    TestCase("notdigits", false),
-                    TestCase("ALSONOTDIGITS", false),
-                    TestCase("10.", false),
-                    TestCase(".99", false),
-                    TestCase("-10.", false),
-                    TestCase("-.99", false),
-                    TestCase("10-", false),
-                    TestCase("1-0", false),
-                    TestCase("1.0.0", false),
-                    TestCase("1/3", false)));
+    ::testing::Values(TestCase("0", true),
+                      TestCase("-0", true),
+                      TestCase("1", true),
+                      TestCase("10", true),
+                      TestCase("-3", true),
+                      TestCase("10.99", true),
+                      TestCase("-3.00", true),
+                      TestCase("01234567890123456789.0123456789", true),
+                      TestCase("01234567890123456789012345678.9", true),
+                      TestCase("012345678901234567890123456789", true),
+                      TestCase("-01234567890123456789.0123456789", true),
+                      TestCase("-01234567890123456789012345678.9", true),
+                      TestCase("-012345678901234567890123456789", true),
+                      // Invalid amount formats
+                      TestCase("", false),
+                      TestCase("-", false),
+                      TestCase("notdigits", false),
+                      TestCase("ALSONOTDIGITS", false),
+                      TestCase("10.", false),
+                      TestCase(".99", false),
+                      TestCase("-10.", false),
+                      TestCase("-.99", false),
+                      TestCase("10-", false),
+                      TestCase("1-0", false),
+                      TestCase("1.0.0", false),
+                      TestCase("1/3", false)));
 
-class PaymentsRegionValidatorTest : public testing::TestWithParam<TestCase> {};
+class PaymentsRegionValidatorTest : public ::testing::TestWithParam<TestCase> {
+};
 
 TEST_P(PaymentsRegionValidatorTest, IsValidCountryCodeFormat) {
   String error_message;
@@ -166,16 +168,16 @@
 
 INSTANTIATE_TEST_CASE_P(CountryCodes,
                         PaymentsRegionValidatorTest,
-                        testing::Values(TestCase("US", true),
-                                        // Invalid country code formats
-                                        TestCase("U1", false),
-                                        TestCase("U", false),
-                                        TestCase("us", false),
-                                        TestCase("USA", false),
-                                        TestCase("", false)));
+                        ::testing::Values(TestCase("US", true),
+                                          // Invalid country code formats
+                                          TestCase("U1", false),
+                                          TestCase("U", false),
+                                          TestCase("us", false),
+                                          TestCase("USA", false),
+                                          TestCase("", false)));
 
-class PaymentsLanguageValidatorTest : public testing::TestWithParam<TestCase> {
-};
+class PaymentsLanguageValidatorTest
+    : public ::testing::TestWithParam<TestCase> {};
 
 TEST_P(PaymentsLanguageValidatorTest, IsValidLanguageCodeFormat) {
   String error_message;
@@ -193,17 +195,18 @@
 
 INSTANTIATE_TEST_CASE_P(LanguageCodes,
                         PaymentsLanguageValidatorTest,
-                        testing::Values(TestCase("", true),
-                                        TestCase("en", true),
-                                        TestCase("eng", true),
-                                        // Invalid language code formats
-                                        TestCase("e1", false),
-                                        TestCase("en1", false),
-                                        TestCase("e", false),
-                                        TestCase("engl", false),
-                                        TestCase("EN", false)));
+                        ::testing::Values(TestCase("", true),
+                                          TestCase("en", true),
+                                          TestCase("eng", true),
+                                          // Invalid language code formats
+                                          TestCase("e1", false),
+                                          TestCase("en1", false),
+                                          TestCase("e", false),
+                                          TestCase("engl", false),
+                                          TestCase("EN", false)));
 
-class PaymentsScriptValidatorTest : public testing::TestWithParam<TestCase> {};
+class PaymentsScriptValidatorTest : public ::testing::TestWithParam<TestCase> {
+};
 
 TEST_P(PaymentsScriptValidatorTest, IsValidScriptCodeFormat) {
   String error_message;
@@ -221,15 +224,15 @@
 
 INSTANTIATE_TEST_CASE_P(ScriptCodes,
                         PaymentsScriptValidatorTest,
-                        testing::Values(TestCase("", true),
-                                        TestCase("Latn", true),
-                                        // Invalid script code formats
-                                        TestCase("Lat1", false),
-                                        TestCase("1lat", false),
-                                        TestCase("Latin", false),
-                                        TestCase("Lat", false),
-                                        TestCase("latn", false),
-                                        TestCase("LATN", false)));
+                        ::testing::Values(TestCase("", true),
+                                          TestCase("Latn", true),
+                                          // Invalid script code formats
+                                          TestCase("Lat1", false),
+                                          TestCase("1lat", false),
+                                          TestCase("Latin", false),
+                                          TestCase("Lat", false),
+                                          TestCase("latn", false),
+                                          TestCase("LATN", false)));
 
 struct ShippingAddressTestCase {
   ShippingAddressTestCase(const char* country_code,
@@ -249,7 +252,7 @@
 };
 
 class PaymentsShippingAddressValidatorTest
-    : public testing::TestWithParam<ShippingAddressTestCase> {};
+    : public ::testing::TestWithParam<ShippingAddressTestCase> {};
 
 TEST_P(PaymentsShippingAddressValidatorTest, IsValidShippingAddress) {
   payments::mojom::blink::PaymentAddressPtr address =
@@ -272,7 +275,7 @@
 INSTANTIATE_TEST_CASE_P(
     ShippingAddresses,
     PaymentsShippingAddressValidatorTest,
-    testing::Values(
+    ::testing::Values(
         ShippingAddressTestCase("US", "en", "Latn", true),
         ShippingAddressTestCase("US", "en", "", true),
         ShippingAddressTestCase("US", "", "", true),
diff --git a/third_party/WebKit/Source/modules/permissions/PermissionUtils.cpp b/third_party/WebKit/Source/modules/permissions/PermissionUtils.cpp
index 8510138a..97a18f4 100644
--- a/third_party/WebKit/Source/modules/permissions/PermissionUtils.cpp
+++ b/third_party/WebKit/Source/modules/permissions/PermissionUtils.cpp
@@ -15,7 +15,9 @@
 
 namespace blink {
 
-using mojom::blink::PermissionDescriptor;
+// There are two PermissionDescriptor, one in Mojo bindings and one
+// in v8 bindings so we'll rename one here.
+using MojoPermissionDescriptor = mojom::blink::PermissionDescriptor;
 using mojom::blink::PermissionDescriptorPtr;
 using mojom::blink::PermissionName;
 
@@ -37,7 +39,7 @@
 }
 
 PermissionDescriptorPtr CreatePermissionDescriptor(PermissionName name) {
-  auto descriptor = PermissionDescriptor::New();
+  auto descriptor = MojoPermissionDescriptor::New();
   descriptor->name = name;
   return descriptor;
 }
diff --git a/third_party/WebKit/Source/modules/presentation/PresentationReceiverTest.cpp b/third_party/WebKit/Source/modules/presentation/PresentationReceiverTest.cpp
index 17d4c8cd..3abfda6e 100644
--- a/third_party/WebKit/Source/modules/presentation/PresentationReceiverTest.cpp
+++ b/third_party/WebKit/Source/modules/presentation/PresentationReceiverTest.cpp
@@ -71,7 +71,7 @@
 
   auto event_handler = new StrictMock<MockEventListener>();
   AddConnectionavailableEventListener(event_handler, receiver);
-  EXPECT_CALL(*event_handler, handleEvent(testing::_, testing::_)).Times(0);
+  EXPECT_CALL(*event_handler, handleEvent(::testing::_, ::testing::_)).Times(0);
 
   receiver->connectionList(scope.GetScriptState());
 
@@ -86,7 +86,7 @@
 
   auto event_handler = new StrictMock<MockEventListener>();
   AddConnectionavailableEventListener(event_handler, receiver);
-  EXPECT_CALL(*event_handler, handleEvent(testing::_, testing::_)).Times(0);
+  EXPECT_CALL(*event_handler, handleEvent(::testing::_, ::testing::_)).Times(0);
 
   receiver->connectionList(scope.GetScriptState());
 
@@ -106,7 +106,7 @@
   StrictMock<MockEventListener>* event_handler =
       new StrictMock<MockEventListener>();
   AddConnectionavailableEventListener(event_handler, receiver);
-  EXPECT_CALL(*event_handler, handleEvent(testing::_, testing::_)).Times(1);
+  EXPECT_CALL(*event_handler, handleEvent(::testing::_, ::testing::_)).Times(1);
 
   receiver->connectionList(scope.GetScriptState());
 
@@ -127,7 +127,7 @@
   StrictMock<MockEventListener>* event_handler =
       new StrictMock<MockEventListener>();
   AddConnectionavailableEventListener(event_handler, receiver);
-  EXPECT_CALL(*event_handler, handleEvent(testing::_, testing::_)).Times(0);
+  EXPECT_CALL(*event_handler, handleEvent(::testing::_, ::testing::_)).Times(0);
 
   WebPresentationInfo presentation_info(KURL(NullURL(), "http://example.com"),
                                         "id");
@@ -149,7 +149,7 @@
 
 TEST_F(PresentationReceiverTest, CreateReceiver) {
   MockWebPresentationClient client;
-  EXPECT_CALL(client, SetReceiver(testing::_));
+  EXPECT_CALL(client, SetReceiver(::testing::_));
 
   V8TestingScope scope;
   new PresentationReceiver(&scope.GetFrame(), &client);
diff --git a/third_party/WebKit/Source/modules/webaudio/BaseAudioContextTest.cpp b/third_party/WebKit/Source/modules/webaudio/BaseAudioContextTest.cpp
index b4fc3cf..f0c6c9f 100644
--- a/third_party/WebKit/Source/modules/webaudio/BaseAudioContextTest.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/BaseAudioContextTest.cpp
@@ -733,9 +733,9 @@
 INSTANTIATE_TEST_CASE_P(
     BaseAudioContextAutoplayTest,
     BaseAudioContextAutoplayTest,
-    testing::Values(AutoplayPolicy::Type::kNoUserGestureRequired,
-                    AutoplayPolicy::Type::kUserGestureRequired,
-                    AutoplayPolicy::Type::kUserGestureRequiredForCrossOrigin,
-                    AutoplayPolicy::Type::kDocumentUserActivationRequired));
+    ::testing::Values(AutoplayPolicy::Type::kNoUserGestureRequired,
+                      AutoplayPolicy::Type::kUserGestureRequired,
+                      AutoplayPolicy::Type::kUserGestureRequiredForCrossOrigin,
+                      AutoplayPolicy::Type::kDocumentUserActivationRequired));
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/websockets/DOMWebSocketTest.cpp b/third_party/WebKit/Source/modules/websockets/DOMWebSocketTest.cpp
index 9a28f76..0e1df756 100644
--- a/third_party/WebKit/Source/modules/websockets/DOMWebSocketTest.cpp
+++ b/third_party/WebKit/Source/modules/websockets/DOMWebSocketTest.cpp
@@ -26,23 +26,23 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "v8/include/v8.h"
 
-using testing::_;
-using testing::AnyNumber;
-using testing::InSequence;
-using testing::Ref;
-using testing::Return;
+using ::testing::_;
+using ::testing::AnyNumber;
+using ::testing::InSequence;
+using ::testing::Ref;
+using ::testing::Return;
 
 namespace blink {
 
 namespace {
 
-typedef testing::StrictMock<testing::MockFunction<void(int)>>
+typedef ::testing::StrictMock<::testing::MockFunction<void(int)>>
     Checkpoint;  // NOLINT
 
 class MockWebSocketChannel : public WebSocketChannel {
  public:
   static MockWebSocketChannel* Create() {
-    return new testing::StrictMock<MockWebSocketChannel>();
+    return new ::testing::StrictMock<MockWebSocketChannel>();
   }
 
   ~MockWebSocketChannel() override {}
diff --git a/third_party/WebKit/Source/modules/websockets/DocumentWebSocketChannelTest.cpp b/third_party/WebKit/Source/modules/websockets/DocumentWebSocketChannelTest.cpp
index bdcb50b..d83766d1 100644
--- a/third_party/WebKit/Source/modules/websockets/DocumentWebSocketChannelTest.cpp
+++ b/third_party/WebKit/Source/modules/websockets/DocumentWebSocketChannelTest.cpp
@@ -26,17 +26,17 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using testing::_;
-using testing::InSequence;
-using testing::PrintToString;
-using testing::AnyNumber;
-using testing::SaveArg;
+using ::testing::_;
+using ::testing::InSequence;
+using ::testing::PrintToString;
+using ::testing::AnyNumber;
+using ::testing::SaveArg;
 
 namespace blink {
 
 namespace {
 
-typedef testing::StrictMock<testing::MockFunction<void(int)>> Checkpoint;
+typedef ::testing::StrictMock<::testing::MockFunction<void(int)>> Checkpoint;
 
 class MockWebSocketChannelClient
     : public GarbageCollectedFinalized<MockWebSocketChannelClient>,
@@ -45,7 +45,7 @@
 
  public:
   static MockWebSocketChannelClient* Create() {
-    return new testing::StrictMock<MockWebSocketChannelClient>();
+    return new ::testing::StrictMock<MockWebSocketChannelClient>();
   }
 
   MockWebSocketChannelClient() {}
@@ -72,7 +72,7 @@
 class MockWebSocketHandle : public WebSocketHandle {
  public:
   static MockWebSocketHandle* Create() {
-    return new testing::StrictMock<MockWebSocketHandle>();
+    return new ::testing::StrictMock<MockWebSocketHandle>();
   }
 
   MockWebSocketHandle() {}
@@ -100,7 +100,7 @@
 class MockWebSocketHandshakeThrottle : public WebSocketHandshakeThrottle {
  public:
   static MockWebSocketHandshakeThrottle* Create() {
-    return new testing::StrictMock<MockWebSocketHandshakeThrottle>();
+    return new ::testing::StrictMock<MockWebSocketHandshakeThrottle>();
   }
   MockWebSocketHandshakeThrottle() {}
   ~MockWebSocketHandshakeThrottle() override { Destructor(); }
diff --git a/third_party/WebKit/Source/platform/BUILD.gn b/third_party/WebKit/Source/platform/BUILD.gn
index 79c3117..93a78d7 100644
--- a/third_party/WebKit/Source/platform/BUILD.gn
+++ b/third_party/WebKit/Source/platform/BUILD.gn
@@ -627,6 +627,7 @@
     "exported/WebSpeechSynthesizerClientImpl.h",
     "exported/WebStorageQuotaCallbacks.cpp",
     "exported/WebString.cpp",
+    "exported/WebSurfaceLayerBridge.cpp",
     "exported/WebTextRun.cpp",
     "exported/WebThreadSafeData.cpp",
     "exported/WebURL.cpp",
@@ -835,8 +836,6 @@
     "graphics/CanvasHeuristicParameters.h",
     "graphics/CanvasMetrics.cpp",
     "graphics/CanvasMetrics.h",
-    "graphics/CanvasSurfaceLayerBridge.cpp",
-    "graphics/CanvasSurfaceLayerBridge.h",
     "graphics/Color.cpp",
     "graphics/Color.h",
     "graphics/ColorBehavior.cpp",
@@ -958,6 +957,8 @@
     "graphics/StaticBitmapImage.h",
     "graphics/StrokeData.cpp",
     "graphics/StrokeData.h",
+    "graphics/SurfaceLayerBridge.cpp",
+    "graphics/SurfaceLayerBridge.h",
     "graphics/TextureHolder.h",
     "graphics/TouchAction.h",
     "graphics/UnacceleratedImageBufferSurface.cpp",
diff --git a/third_party/WebKit/Source/platform/Language.cpp b/third_party/WebKit/Source/platform/Language.cpp
index 25cb415..12fff45 100644
--- a/third_party/WebKit/Source/platform/Language.cpp
+++ b/third_party/WebKit/Source/platform/Language.cpp
@@ -25,6 +25,7 @@
 
 #include "platform/Language.h"
 
+#include "platform/text/PlatformLocale.h"
 #include "platform/wtf/text/WTFString.h"
 #include "public/platform/Platform.h"
 
@@ -89,6 +90,7 @@
   canonicalized.ReserveCapacity(override.size());
   for (const auto& lang : override)
     canonicalized.push_back(CanonicalizeLanguageIdentifier(lang));
+  Locale::ResetDefautlLocale();
 }
 
 AtomicString DefaultLanguage() {
diff --git a/third_party/WebKit/Source/platform/LayoutLocale.cpp b/third_party/WebKit/Source/platform/LayoutLocale.cpp
index 309e06b9..65161299 100644
--- a/third_party/WebKit/Source/platform/LayoutLocale.cpp
+++ b/third_party/WebKit/Source/platform/LayoutLocale.cpp
@@ -6,6 +6,7 @@
 
 #include "platform/Language.h"
 #include "platform/fonts/AcceptLanguagesResolver.h"
+#include "platform/fonts/FontGlobalContext.h"
 #include "platform/text/ICUError.h"
 #include "platform/text/LocaleToScriptMapping.h"
 #include "platform/wtf/HashMap.h"
@@ -17,11 +18,6 @@
 
 namespace blink {
 
-const LayoutLocale* LayoutLocale::default_ = nullptr;
-const LayoutLocale* LayoutLocale::system_ = nullptr;
-const LayoutLocale* LayoutLocale::default_for_han_ = nullptr;
-bool LayoutLocale::default_for_han_computed_ = false;
-
 static hb_language_t ToHarfbuzLanguage(const AtomicString& locale) {
   CString locale_as_latin1 = locale.Latin1();
   return hb_language_from_string(locale_as_latin1.data(),
@@ -85,21 +81,21 @@
     const LayoutLocale* content_locale) {
   if (content_locale && content_locale->HasScriptForHan())
     return content_locale;
-  if (!default_for_han_computed_)
-    ComputeLocaleForHan();
-  return default_for_han_;
-}
 
-void LayoutLocale::ComputeLocaleForHan() {
+  if (FontGlobalContext::HasDefaultLocaleForHan())
+    return FontGlobalContext::GetDefaultLocaleForHan();
+
+  const LayoutLocale* default_for_han;
   if (const LayoutLocale* locale = AcceptLanguagesResolver::LocaleForHan())
-    default_for_han_ = locale;
+    default_for_han = locale;
   else if (GetDefault().HasScriptForHan())
-    default_for_han_ = &GetDefault();
+    default_for_han = &GetDefault();
   else if (GetSystem().HasScriptForHan())
-    default_for_han_ = &GetSystem();
+    default_for_han = &GetSystem();
   else
-    default_for_han_ = nullptr;
-  default_for_han_computed_ = true;
+    default_for_han = nullptr;
+  FontGlobalContext::SetDefaultLocaleForHan(default_for_han);
+  return default_for_han;
 }
 
 const char* LayoutLocale::LocaleForHanForSkFontMgr() const {
@@ -116,42 +112,38 @@
       has_script_for_han_(false),
       hyphenation_computed_(false) {}
 
-using LayoutLocaleMap =
-    HashMap<AtomicString, RefPtr<LayoutLocale>, CaseFoldingHash>;
-
-static LayoutLocaleMap& GetLocaleMap() {
-  DEFINE_STATIC_LOCAL(LayoutLocaleMap, locale_map, ());
-  return locale_map;
-}
-
 const LayoutLocale* LayoutLocale::Get(const AtomicString& locale) {
   if (locale.IsNull())
     return nullptr;
 
-  auto result = GetLocaleMap().insert(locale, nullptr);
+  auto result = FontGlobalContext::GetLayoutLocaleMap().insert(locale, nullptr);
   if (result.is_new_entry)
     result.stored_value->value = AdoptRef(new LayoutLocale(locale));
   return result.stored_value->value.Get();
 }
 
 const LayoutLocale& LayoutLocale::GetDefault() {
-  if (default_)
-    return *default_;
+  if (const LayoutLocale* locale = FontGlobalContext::GetDefaultLayoutLocale())
+    return *locale;
 
-  AtomicString locale = DefaultLanguage();
-  default_ = Get(!locale.IsEmpty() ? locale : "en");
-  return *default_;
+  AtomicString language = DefaultLanguage();
+  const LayoutLocale* locale =
+      LayoutLocale::Get(!language.IsEmpty() ? language : "en");
+  FontGlobalContext::SetDefaultLayoutLocale(locale);
+  return *locale;
 }
 
 const LayoutLocale& LayoutLocale::GetSystem() {
-  if (system_)
-    return *system_;
+  if (const LayoutLocale* locale = FontGlobalContext::GetSystemLayoutLocale())
+    return *locale;
 
   // Platforms such as Windows can give more information than the default
   // locale, such as "en-JP" for English speakers in Japan.
   String name = icu::Locale::getDefault().getName();
-  system_ = Get(AtomicString(name.Replace('_', '-')));
-  return *system_;
+  const LayoutLocale* locale =
+      LayoutLocale::Get(AtomicString(name.Replace('_', '-')));
+  FontGlobalContext::SetSystemLayoutLocale(locale);
+  return *locale;
 }
 
 PassRefPtr<LayoutLocale> LayoutLocale::CreateForTesting(
@@ -159,14 +151,6 @@
   return AdoptRef(new LayoutLocale(locale));
 }
 
-void LayoutLocale::ClearForTesting() {
-  default_ = nullptr;
-  system_ = nullptr;
-  default_for_han_ = nullptr;
-  default_for_han_computed_ = false;
-  GetLocaleMap().clear();
-}
-
 Hyphenation* LayoutLocale::GetHyphenation() const {
   if (hyphenation_computed_)
     return hyphenation_.Get();
diff --git a/third_party/WebKit/Source/platform/LayoutLocale.h b/third_party/WebKit/Source/platform/LayoutLocale.h
index a2291cb7..e22bb9d 100644
--- a/third_party/WebKit/Source/platform/LayoutLocale.h
+++ b/third_party/WebKit/Source/platform/LayoutLocale.h
@@ -54,7 +54,6 @@
   UScriptCode GetScriptForHan() const;
   bool HasScriptForHan() const;
   static const LayoutLocale* LocaleForHan(const LayoutLocale*);
-  static void InvalidateLocaleForHan() { default_for_han_computed_ = false; }
   const char* LocaleForHanForSkFontMgr() const;
 
   Hyphenation* GetHyphenation() const;
@@ -62,7 +61,6 @@
   AtomicString LocaleWithBreakKeyword(LineBreakIteratorMode) const;
 
   static PassRefPtr<LayoutLocale> CreateForTesting(const AtomicString&);
-  static void ClearForTesting();
   static void SetHyphenationForTesting(const AtomicString&,
                                        PassRefPtr<Hyphenation>);
 
@@ -70,7 +68,6 @@
   explicit LayoutLocale(const AtomicString&);
 
   void ComputeScriptForHan() const;
-  static void ComputeLocaleForHan();
 
   AtomicString string_;
   mutable CString string_for_sk_font_mgr_;
@@ -84,11 +81,6 @@
 
   mutable unsigned has_script_for_han_ : 1;
   mutable unsigned hyphenation_computed_ : 1;
-
-  static const LayoutLocale* default_;
-  static const LayoutLocale* system_;
-  static const LayoutLocale* default_for_han_;
-  static bool default_for_han_computed_;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/LayoutLocaleTest.cpp b/third_party/WebKit/Source/platform/LayoutLocaleTest.cpp
index 853147a..b2ddf65 100644
--- a/third_party/WebKit/Source/platform/LayoutLocaleTest.cpp
+++ b/third_party/WebKit/Source/platform/LayoutLocaleTest.cpp
@@ -4,12 +4,13 @@
 
 #include "platform/LayoutLocale.h"
 
+#include "platform/fonts/FontGlobalContext.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace blink {
 
 TEST(LayoutLocaleTest, Get) {
-  LayoutLocale::ClearForTesting();
+  FontGlobalContext::ClearForTesting();
 
   EXPECT_EQ(nullptr, LayoutLocale::Get(g_null_atom));
 
@@ -20,7 +21,7 @@
   EXPECT_STRCASEEQ("ja-jp",
                    LayoutLocale::Get("ja-jp")->LocaleString().Ascii().data());
 
-  LayoutLocale::ClearForTesting();
+  FontGlobalContext::ClearForTesting();
 }
 
 TEST(LayoutLocaleTest, GetCaseInsensitive) {
diff --git a/third_party/WebKit/Source/platform/PODArenaTest.cpp b/third_party/WebKit/Source/platform/PODArenaTest.cpp
index 4b45df2..2e4359b 100644
--- a/third_party/WebKit/Source/platform/PODArenaTest.cpp
+++ b/third_party/WebKit/Source/platform/PODArenaTest.cpp
@@ -51,7 +51,7 @@
 
 }  // anonymous namespace
 
-class PODArenaTest : public testing::Test {};
+class PODArenaTest : public ::testing::Test {};
 
 // Make sure the arena can successfully allocate from more than one
 // region.
diff --git a/third_party/WebKit/Source/platform/PODFreeListArenaTest.cpp b/third_party/WebKit/Source/platform/PODFreeListArenaTest.cpp
index b2c9828..f5b2a26 100644
--- a/third_party/WebKit/Source/platform/PODFreeListArenaTest.cpp
+++ b/third_party/WebKit/Source/platform/PODFreeListArenaTest.cpp
@@ -54,7 +54,7 @@
 
 }  // anonymous namespace
 
-class PODFreeListArenaTest : public testing::Test {
+class PODFreeListArenaTest : public ::testing::Test {
  protected:
   int GetFreeListSize(PassRefPtr<PODFreeListArena<TestClass1>> arena) const {
     return arena->GetFreeListSizeForTesting();
diff --git a/third_party/WebKit/Source/platform/TimerTest.cpp b/third_party/WebKit/Source/platform/TimerTest.cpp
index 2b648f74..b3cf96a9 100644
--- a/third_party/WebKit/Source/platform/TimerTest.cpp
+++ b/third_party/WebKit/Source/platform/TimerTest.cpp
@@ -21,12 +21,12 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using testing::ElementsAre;
+using ::testing::ElementsAre;
 
 namespace blink {
 namespace {
 
-class TimerTest : public testing::Test {
+class TimerTest : public ::testing::Test {
  public:
   void SetUp() override {
     run_times_.clear();
diff --git a/third_party/WebKit/Source/platform/WebIconSizesParserTest.cpp b/third_party/WebKit/Source/platform/WebIconSizesParserTest.cpp
index e2eef2c8..74d2535b 100644
--- a/third_party/WebKit/Source/platform/WebIconSizesParserTest.cpp
+++ b/third_party/WebKit/Source/platform/WebIconSizesParserTest.cpp
@@ -11,7 +11,7 @@
 
 namespace blink {
 
-class WebIconSizesParserTest : public testing::Test {};
+class WebIconSizesParserTest : public ::testing::Test {};
 
 TEST(WebIconSizesParserTest, parseSizes) {
   WebString sizes_attribute = "32x33";
diff --git a/third_party/WebKit/Source/platform/bindings/RuntimeCallStatsTest.cpp b/third_party/WebKit/Source/platform/bindings/RuntimeCallStatsTest.cpp
index 08270a6..edbf206 100644
--- a/third_party/WebKit/Source/platform/bindings/RuntimeCallStatsTest.cpp
+++ b/third_party/WebKit/Source/platform/bindings/RuntimeCallStatsTest.cpp
@@ -25,7 +25,7 @@
 
 }  // namespace
 
-class RuntimeCallStatsTest : public testing::Test {
+class RuntimeCallStatsTest : public ::testing::Test {
  public:
   void SetUp() override {
     // Add one millisecond because RuntimeCallTimer uses |start_ticks_| =
diff --git a/third_party/WebKit/Source/platform/blob/BlobBytesProviderTest.cpp b/third_party/WebKit/Source/platform/blob/BlobBytesProviderTest.cpp
index 1a5a7b0..75d53a7 100644
--- a/third_party/WebKit/Source/platform/blob/BlobBytesProviderTest.cpp
+++ b/third_party/WebKit/Source/platform/blob/BlobBytesProviderTest.cpp
@@ -12,7 +12,7 @@
 namespace blink {
 namespace {
 
-class BlobBytesProviderTest : public testing::Test {
+class BlobBytesProviderTest : public ::testing::Test {
  public:
   void SetUp() override {
     test_bytes1_.resize(128);
@@ -78,7 +78,7 @@
 }
 
 class RequestAsFile : public BlobBytesProviderTest,
-                      public testing::WithParamInterface<FileTestData> {
+                      public ::testing::WithParamInterface<FileTestData> {
  public:
   void SetUp() override {
     BlobBytesProviderTest::SetUp();
@@ -209,7 +209,7 @@
 
 INSTANTIATE_TEST_CASE_P(BlobBytesProviderTest,
                         RequestAsFile,
-                        testing::ValuesIn(file_tests));
+                        ::testing::ValuesIn(file_tests));
 
 TEST_F(BlobBytesProviderTest, RequestAsFile_MultipleChunks) {
   auto provider = WTF::MakeUnique<BlobBytesProvider>(test_data1_);
diff --git a/third_party/WebKit/Source/platform/blob/BlobDataTest.cpp b/third_party/WebKit/Source/platform/blob/BlobDataTest.cpp
index 5c37d41..74e6df3 100644
--- a/third_party/WebKit/Source/platform/blob/BlobDataTest.cpp
+++ b/third_party/WebKit/Source/platform/blob/BlobDataTest.cpp
@@ -190,7 +190,7 @@
 
 }  // namespace
 
-class BlobDataHandleTest : public testing::Test {
+class BlobDataHandleTest : public ::testing::Test {
  public:
   BlobDataHandleTest()
       : enable_mojo_blobs_(true), testing_platform_(&mock_blob_registry_) {}
diff --git a/third_party/WebKit/Source/platform/exported/WebSurfaceLayerBridge.cpp b/third_party/WebKit/Source/platform/exported/WebSurfaceLayerBridge.cpp
new file mode 100644
index 0000000..118628e
--- /dev/null
+++ b/third_party/WebKit/Source/platform/exported/WebSurfaceLayerBridge.cpp
@@ -0,0 +1,15 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "public/platform/WebSurfaceLayerBridge.h"
+
+#include "third_party/WebKit/Source/platform/graphics/SurfaceLayerBridge.h"
+
+namespace blink {
+
+WebSurfaceLayerBridge* WebSurfaceLayerBridge::Create() {
+  return new SurfaceLayerBridge(nullptr, nullptr);
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/platform/fonts/AcceptLanguagesResolver.cpp b/third_party/WebKit/Source/platform/fonts/AcceptLanguagesResolver.cpp
index 5ca7a21..031e8f3f 100644
--- a/third_party/WebKit/Source/platform/fonts/AcceptLanguagesResolver.cpp
+++ b/third_party/WebKit/Source/platform/fonts/AcceptLanguagesResolver.cpp
@@ -5,6 +5,7 @@
 #include "platform/fonts/AcceptLanguagesResolver.h"
 
 #include "platform/LayoutLocale.h"
+#include "platform/fonts/FontGlobalContext.h"
 
 namespace blink {
 
@@ -20,7 +21,7 @@
     return;
 
   current_value = accept_languages;
-  LayoutLocale::InvalidateLocaleForHan();
+  FontGlobalContext::InvalidateLocaleForHan();
 }
 
 const LayoutLocale* AcceptLanguagesResolver::LocaleForHan() {
diff --git a/third_party/WebKit/Source/platform/fonts/FontCache.cpp b/third_party/WebKit/Source/platform/fonts/FontCache.cpp
index 2e0a775..2e5ebc8 100644
--- a/third_party/WebKit/Source/platform/fonts/FontCache.cpp
+++ b/third_party/WebKit/Source/platform/fonts/FontCache.cpp
@@ -405,8 +405,10 @@
   font_platform_data_cache_.clear();
   generation_++;
 
-  for (const auto& client : *font_cache_clients_)
-    client->FontCacheInvalidated();
+  if (font_cache_clients_) {
+    for (const auto& client : *font_cache_clients_)
+      client->FontCacheInvalidated();
+  }
 
   Purge(kForcePurge);
 }
diff --git a/third_party/WebKit/Source/platform/fonts/FontGlobalContext.cpp b/third_party/WebKit/Source/platform/fonts/FontGlobalContext.cpp
index 36f39ae..d2981cf 100644
--- a/third_party/WebKit/Source/platform/fonts/FontGlobalContext.cpp
+++ b/third_party/WebKit/Source/platform/fonts/FontGlobalContext.cpp
@@ -19,7 +19,12 @@
   return *font_persistent;
 }
 
-FontGlobalContext::FontGlobalContext() : harfbuzz_font_funcs_(nullptr) {}
+FontGlobalContext::FontGlobalContext()
+    : harfbuzz_font_funcs_(nullptr),
+      default_locale_(nullptr),
+      system_locale_(nullptr),
+      default_locale_for_han_(nullptr),
+      has_default_locale_for_han_(false) {}
 
 void FontGlobalContext::ClearMemory() {
   if (!Get(kDoNotCreate))
@@ -28,4 +33,14 @@
   GetFontCache().Invalidate();
 }
 
+void FontGlobalContext::ClearForTesting() {
+  FontGlobalContext* ctx = Get();
+  ctx->default_locale_ = nullptr;
+  ctx->system_locale_ = nullptr;
+  ctx->default_locale_for_han_ = nullptr;
+  ctx->has_default_locale_for_han_ = false;
+  ctx->layout_locale_map_.clear();
+  ctx->font_cache_.Invalidate();
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/fonts/FontGlobalContext.h b/third_party/WebKit/Source/platform/fonts/FontGlobalContext.h
index 7e9c944..4571cd3 100644
--- a/third_party/WebKit/Source/platform/fonts/FontGlobalContext.h
+++ b/third_party/WebKit/Source/platform/fonts/FontGlobalContext.h
@@ -17,8 +17,12 @@
 
 namespace blink {
 
+class LayoutLocale;
 class FontCache;
 
+using LayoutLocaleMap =
+    HashMap<AtomicString, RefPtr<LayoutLocale>, CaseFoldingHash>;
+
 enum CreateIfNeeded { kDoNotCreate, kCreate };
 
 // FontGlobalContext contains non-thread-safe, thread-specific data used for
@@ -43,9 +47,47 @@
     Get()->harfbuzz_font_funcs_ = funcs;
   }
 
+  static inline LayoutLocaleMap& GetLayoutLocaleMap() {
+    return Get()->layout_locale_map_;
+  }
+
+  static inline const LayoutLocale* GetDefaultLayoutLocale() {
+    return Get()->default_locale_;
+  }
+  static inline void SetDefaultLayoutLocale(const LayoutLocale* locale) {
+    Get()->default_locale_ = locale;
+  }
+  static inline const LayoutLocale* GetSystemLayoutLocale() {
+    return Get()->system_locale_;
+  }
+  static inline void SetSystemLayoutLocale(const LayoutLocale* locale) {
+    Get()->system_locale_ = locale;
+  }
+
+  static inline const LayoutLocale* GetDefaultLocaleForHan() {
+    FontGlobalContext* ctx = Get();
+    DCHECK(ctx->has_default_locale_for_han_);
+    return ctx->default_locale_for_han_;
+  }
+  static inline void SetDefaultLocaleForHan(const LayoutLocale* locale) {
+    FontGlobalContext* ctx = Get();
+    ctx->default_locale_for_han_ = locale;
+    ctx->has_default_locale_for_han_ = true;
+  }
+  static inline void InvalidateLocaleForHan() {
+    FontGlobalContext* ctx = Get();
+    ctx->default_locale_for_han_ = nullptr;
+    ctx->has_default_locale_for_han_ = false;
+  }
+  static inline bool HasDefaultLocaleForHan() {
+    return Get()->has_default_locale_for_han_;
+  }
+
   // Called by MemoryCoordinator to clear memory.
   static void ClearMemory();
 
+  static void ClearForTesting();
+
  private:
   friend class WTF::ThreadSpecific<FontGlobalContext>;
 
@@ -57,6 +99,12 @@
   HarfBuzzFontCache harf_buzz_font_cache_;
 
   hb_font_funcs_t* harfbuzz_font_funcs_;
+
+  LayoutLocaleMap layout_locale_map_;
+  const LayoutLocale* default_locale_;
+  const LayoutLocale* system_locale_;
+  const LayoutLocale* default_locale_for_han_;
+  bool has_default_locale_for_han_;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/fonts/OrientationIteratorTest.cpp b/third_party/WebKit/Source/platform/fonts/OrientationIteratorTest.cpp
index 77e4baf..a792987 100644
--- a/third_party/WebKit/Source/platform/fonts/OrientationIteratorTest.cpp
+++ b/third_party/WebKit/Source/platform/fonts/OrientationIteratorTest.cpp
@@ -23,7 +23,7 @@
       : limit(the_limit), render_orientation(the_render_orientation) {}
 };
 
-class OrientationIteratorTest : public testing::Test {
+class OrientationIteratorTest : public ::testing::Test {
  protected:
   void CheckRuns(const Vector<TestRun>& runs) {
     String text(g_empty_string16_bit);
diff --git a/third_party/WebKit/Source/platform/fonts/ScriptRunIteratorTest.cpp b/third_party/WebKit/Source/platform/fonts/ScriptRunIteratorTest.cpp
index 52631ff..6bb487e 100644
--- a/third_party/WebKit/Source/platform/fonts/ScriptRunIteratorTest.cpp
+++ b/third_party/WebKit/Source/platform/fonts/ScriptRunIteratorTest.cpp
@@ -284,7 +284,7 @@
     kGreek3 + kHan2 + kLatin,
 };
 
-class ScriptRunIteratorTest : public testing::Test {
+class ScriptRunIteratorTest : public ::testing::Test {
  protected:
   void CheckRuns(const Vector<TestRun>& runs) {
     String text(g_empty_string16_bit);
@@ -614,7 +614,7 @@
   CHECK_RUNS({{"100-ാം", USCRIPT_MALAYALAM}});
 }
 
-class ScriptRunIteratorICUDataTest : public testing::Test {
+class ScriptRunIteratorICUDataTest : public ::testing::Test {
  public:
   ScriptRunIteratorICUDataTest()
       : max_extensions_(0), max_extensions_codepoint_(0xffff) {
diff --git a/third_party/WebKit/Source/platform/fonts/SmallCapsIteratorTest.cpp b/third_party/WebKit/Source/platform/fonts/SmallCapsIteratorTest.cpp
index 221c77c..8728265 100644
--- a/third_party/WebKit/Source/platform/fonts/SmallCapsIteratorTest.cpp
+++ b/third_party/WebKit/Source/platform/fonts/SmallCapsIteratorTest.cpp
@@ -23,7 +23,7 @@
       : limit(the_limit), small_caps_behavior(the_small_caps_behavior) {}
 };
 
-class SmallCapsIteratorTest : public testing::Test {
+class SmallCapsIteratorTest : public ::testing::Test {
  protected:
   void CheckRuns(const Vector<TestRun>& runs) {
     String text(g_empty_string16_bit);
diff --git a/third_party/WebKit/Source/platform/fonts/SymbolsIteratorTest.cpp b/third_party/WebKit/Source/platform/fonts/SymbolsIteratorTest.cpp
index 04ff8c8..0e0e377 100644
--- a/third_party/WebKit/Source/platform/fonts/SymbolsIteratorTest.cpp
+++ b/third_party/WebKit/Source/platform/fonts/SymbolsIteratorTest.cpp
@@ -23,7 +23,7 @@
       : limit(the_limit), font_fallback_priority(the_font_fallback_priority) {}
 };
 
-class SymbolsIteratorTest : public testing::Test {
+class SymbolsIteratorTest : public ::testing::Test {
  protected:
   void CheckRuns(const Vector<TestRun>& runs) {
     String text(g_empty_string16_bit);
diff --git a/third_party/WebKit/Source/platform/fonts/shaping/RunSegmenterTest.cpp b/third_party/WebKit/Source/platform/fonts/shaping/RunSegmenterTest.cpp
index 9b00d6c..11a6a12 100644
--- a/third_party/WebKit/Source/platform/fonts/shaping/RunSegmenterTest.cpp
+++ b/third_party/WebKit/Source/platform/fonts/shaping/RunSegmenterTest.cpp
@@ -40,7 +40,7 @@
         font_fallback_priority(the_font_fallback_priority) {}
 };
 
-class RunSegmenterTest : public testing::Test {
+class RunSegmenterTest : public ::testing::Test {
  protected:
   void CheckRuns(const Vector<SegmenterTestRun>& runs,
                  FontOrientation orientation) {
diff --git a/third_party/WebKit/Source/platform/geometry/FloatBoxTestHelpers.cpp b/third_party/WebKit/Source/platform/geometry/FloatBoxTestHelpers.cpp
index 136fbe0..3b1ed38 100644
--- a/third_party/WebKit/Source/platform/geometry/FloatBoxTestHelpers.cpp
+++ b/third_party/WebKit/Source/platform/geometry/FloatBoxTestHelpers.cpp
@@ -54,7 +54,7 @@
   if (!ApproximatelyEqual(m, n)) {
     return ::testing::AssertionFailure()
            << "       Value of:" << n_expr << std::endl
-           << "         Actual:" << testing::PrintToString(n) << std::endl
+           << "         Actual:" << ::testing::PrintToString(n) << std::endl
            << "Expected Approx:" << expr << std::endl
            << "       Which is:" << ::testing::PrintToString(m);
   }
@@ -70,7 +70,7 @@
   if (!ApproximatelyEqual(m, new_m)) {
     return ::testing::AssertionFailure()
            << "        Value of:" << n_expr << std::endl
-           << "          Actual:" << testing::PrintToString(n) << std::endl
+           << "          Actual:" << ::testing::PrintToString(n) << std::endl
            << "Not Contained in:" << expr << std::endl
            << "        Which is:" << ::testing::PrintToString(m);
   }
diff --git a/third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridgeTest.cpp b/third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridgeTest.cpp
index 2ce9e62..161a5e2 100644
--- a/third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridgeTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridgeTest.cpp
@@ -53,12 +53,12 @@
 
 #include <memory>
 
-using testing::AnyNumber;
-using testing::AtLeast;
-using testing::InSequence;
-using testing::Return;
-using testing::Test;
-using testing::_;
+using ::testing::AnyNumber;
+using ::testing::AtLeast;
+using ::testing::InSequence;
+using ::testing::Return;
+using ::testing::Test;
+using ::testing::_;
 
 namespace blink {
 
diff --git a/third_party/WebKit/Source/platform/graphics/ContiguousContainerTest.cpp b/third_party/WebKit/Source/platform/graphics/ContiguousContainerTest.cpp
index d51c1da..c5ddbd3 100644
--- a/third_party/WebKit/Source/platform/graphics/ContiguousContainerTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/ContiguousContainerTest.cpp
@@ -80,9 +80,9 @@
   auto& destructible = list.AllocateAndConstruct<MockDestructible>();
   EXPECT_EQ(&destructible, &list.First());
 
-  testing::MockFunction<void()> separator;
+  ::testing::MockFunction<void()> separator;
   {
-    testing::InSequence s;
+    ::testing::InSequence s;
     EXPECT_CALL(destructible, Destruct());
     EXPECT_CALL(separator, Call());
     EXPECT_CALL(destructible, Destruct()).Times(0);
@@ -97,9 +97,9 @@
   auto& destructible = list.AllocateAndConstruct<MockDestructible>();
   EXPECT_EQ(&destructible, &list.First());
 
-  testing::MockFunction<void()> separator;
+  ::testing::MockFunction<void()> separator;
   {
-    testing::InSequence s;
+    ::testing::InSequence s;
     EXPECT_CALL(destructible, Destruct());
     EXPECT_CALL(separator, Call());
     EXPECT_CALL(destructible, Destruct()).Times(0);
@@ -114,13 +114,13 @@
   // free to use more space if the allocator provides it.
   ContiguousContainer<MockDestructible> list(sizeof(MockDestructible),
                                              1 * sizeof(MockDestructible));
-  testing::MockFunction<void()> separator;
+  ::testing::MockFunction<void()> separator;
 
   // We should be okay to allocate and remove a single one, like before.
   list.AllocateAndConstruct<MockDestructible>();
   EXPECT_EQ(1u, list.size());
   {
-    testing::InSequence s;
+    ::testing::InSequence s;
     EXPECT_CALL(list[0], Destruct());
     EXPECT_CALL(separator, Call());
     EXPECT_CALL(list[0], Destruct()).Times(0);
@@ -129,7 +129,7 @@
   separator.Call();
   EXPECT_EQ(0u, list.size());
 
-  testing::Mock::VerifyAndClearExpectations(&separator);
+  ::testing::Mock::VerifyAndClearExpectations(&separator);
 
   // We should also be okay to allocate and remove multiple.
   list.AllocateAndConstruct<MockDestructible>();
@@ -141,7 +141,7 @@
   EXPECT_EQ(6u, list.size());
   {
     // The last three should be destroyed by removeLast.
-    testing::InSequence s;
+    ::testing::InSequence s;
     EXPECT_CALL(list[5], Destruct());
     EXPECT_CALL(separator, Call());
     EXPECT_CALL(list[5], Destruct()).Times(0);
diff --git a/third_party/WebKit/Source/platform/graphics/GraphicsLayerTest.cpp b/third_party/WebKit/Source/platform/graphics/GraphicsLayerTest.cpp
index 3595d7f3..bb44542 100644
--- a/third_party/WebKit/Source/platform/graphics/GraphicsLayerTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/GraphicsLayerTest.cpp
@@ -53,7 +53,7 @@
 
 namespace blink {
 
-class GraphicsLayerTest : public testing::Test {
+class GraphicsLayerTest : public ::testing::Test {
  public:
   GraphicsLayerTest() {
     clip_layer_ = WTF::WrapUnique(new FakeGraphicsLayer(&client_));
diff --git a/third_party/WebKit/Source/platform/graphics/RecordingImageBufferSurfaceTest.cpp b/third_party/WebKit/Source/platform/graphics/RecordingImageBufferSurfaceTest.cpp
index 3b4b653..af9580e 100644
--- a/third_party/WebKit/Source/platform/graphics/RecordingImageBufferSurfaceTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/RecordingImageBufferSurfaceTest.cpp
@@ -21,7 +21,7 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using testing::Test;
+using ::testing::Test;
 
 namespace blink {
 
diff --git a/third_party/WebKit/Source/platform/graphics/CanvasSurfaceLayerBridge.cpp b/third_party/WebKit/Source/platform/graphics/SurfaceLayerBridge.cpp
similarity index 79%
rename from third_party/WebKit/Source/platform/graphics/CanvasSurfaceLayerBridge.cpp
rename to third_party/WebKit/Source/platform/graphics/SurfaceLayerBridge.cpp
index a19b255..f5c7cc1 100644
--- a/third_party/WebKit/Source/platform/graphics/CanvasSurfaceLayerBridge.cpp
+++ b/third_party/WebKit/Source/platform/graphics/SurfaceLayerBridge.cpp
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "platform/graphics/CanvasSurfaceLayerBridge.h"
+#include "platform/graphics/SurfaceLayerBridge.h"
 
 #include "cc/layers/layer.h"
 #include "cc/layers/solid_color_layer.h"
@@ -25,16 +25,14 @@
 namespace blink {
 
 namespace {
-
-class OffscreenCanvasSurfaceReferenceFactory
+class SequenceSurfaceReferenceFactoryImpl
     : public cc::SequenceSurfaceReferenceFactory {
  public:
-  OffscreenCanvasSurfaceReferenceFactory(
-      base::WeakPtr<CanvasSurfaceLayerBridge> bridge)
+  SequenceSurfaceReferenceFactoryImpl(base::WeakPtr<SurfaceLayerBridge> bridge)
       : bridge_(bridge) {}
 
  private:
-  ~OffscreenCanvasSurfaceReferenceFactory() override = default;
+  ~SequenceSurfaceReferenceFactoryImpl() override = default;
 
   // cc::SequenceSurfaceReferenceFactory implementation:
   void RequireSequence(const cc::SurfaceId& id,
@@ -48,16 +46,15 @@
       bridge_->SatisfyCallback(sequence);
   }
 
-  base::WeakPtr<CanvasSurfaceLayerBridge> bridge_;
+  base::WeakPtr<SurfaceLayerBridge> bridge_;
 
-  DISALLOW_COPY_AND_ASSIGN(OffscreenCanvasSurfaceReferenceFactory);
+  DISALLOW_COPY_AND_ASSIGN(SequenceSurfaceReferenceFactoryImpl);
 };
 
 }  // namespace
 
-CanvasSurfaceLayerBridge::CanvasSurfaceLayerBridge(
-    CanvasSurfaceLayerBridgeObserver* observer,
-    WebLayerTreeView* layer_tree_view)
+SurfaceLayerBridge::SurfaceLayerBridge(SurfaceLayerBridgeObserver* observer,
+                                       WebLayerTreeView* layer_tree_view)
     : weak_factory_(this),
       observer_(observer),
       binding_(this),
@@ -65,7 +62,7 @@
       parent_frame_sink_id_(layer_tree_view ? layer_tree_view->GetFrameSinkId()
                                             : cc::FrameSinkId()) {
   ref_factory_ =
-      new OffscreenCanvasSurfaceReferenceFactory(weak_factory_.GetWeakPtr());
+      new SequenceSurfaceReferenceFactoryImpl(weak_factory_.GetWeakPtr());
 
   DCHECK(!service_.is_bound());
   mojom::blink::OffscreenCanvasProviderPtr provider;
@@ -81,23 +78,32 @@
                                          mojo::MakeRequest(&service_));
 }
 
-CanvasSurfaceLayerBridge::~CanvasSurfaceLayerBridge() {
+SurfaceLayerBridge::~SurfaceLayerBridge() {
   observer_ = nullptr;
   if (web_layer_) {
     GraphicsLayer::UnregisterContentsLayer(web_layer_.get());
   }
 }
 
-void CanvasSurfaceLayerBridge::CreateSolidColorLayer() {
+void SurfaceLayerBridge::SatisfyCallback(const cc::SurfaceSequence& sequence) {
+  service_->Satisfy(sequence);
+}
+
+void SurfaceLayerBridge::RequireCallback(const cc::SurfaceId& surface_id,
+                                         const cc::SurfaceSequence& sequence) {
+  service_->Require(surface_id, sequence);
+}
+
+void SurfaceLayerBridge::CreateSolidColorLayer() {
   cc_layer_ = cc::SolidColorLayer::Create();
   cc_layer_->SetBackgroundColor(SK_ColorTRANSPARENT);
+
   web_layer_ = Platform::Current()->CompositorSupport()->CreateLayerFromCCLayer(
       cc_layer_.get());
   GraphicsLayer::RegisterContentsLayer(web_layer_.get());
 }
 
-void CanvasSurfaceLayerBridge::OnSurfaceCreated(
-    const cc::SurfaceInfo& surface_info) {
+void SurfaceLayerBridge::OnSurfaceCreated(const cc::SurfaceInfo& surface_info) {
   if (!current_surface_id_.is_valid() && surface_info.is_valid()) {
     // First time a SurfaceId is received
     current_surface_id_ = surface_info.id();
@@ -125,19 +131,9 @@
     surface_layer->SetFallbackSurfaceInfo(surface_info);
   }
 
-  observer_->OnWebLayerReplaced();
+  if (observer_)
+    observer_->OnWebLayerReplaced();
   cc_layer_->SetBounds(surface_info.size_in_pixels());
 }
 
-void CanvasSurfaceLayerBridge::SatisfyCallback(
-    const cc::SurfaceSequence& sequence) {
-  service_->Satisfy(sequence);
-}
-
-void CanvasSurfaceLayerBridge::RequireCallback(
-    const cc::SurfaceId& surface_id,
-    const cc::SurfaceSequence& sequence) {
-  service_->Require(surface_id, sequence);
-}
-
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/graphics/CanvasSurfaceLayerBridge.h b/third_party/WebKit/Source/platform/graphics/SurfaceLayerBridge.h
similarity index 66%
rename from third_party/WebKit/Source/platform/graphics/CanvasSurfaceLayerBridge.h
rename to third_party/WebKit/Source/platform/graphics/SurfaceLayerBridge.h
index c82436c..1eb2e3a 100644
--- a/third_party/WebKit/Source/platform/graphics/CanvasSurfaceLayerBridge.h
+++ b/third_party/WebKit/Source/platform/graphics/SurfaceLayerBridge.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CanvasSurfaceLayerBridge_h
-#define CanvasSurfaceLayerBridge_h
+#ifndef SurfaceLayerBridge_h
+#define SurfaceLayerBridge_h
 
 #include <memory>
 #include "base/memory/ref_counted.h"
@@ -12,6 +12,7 @@
 #include "cc/surfaces/surface_reference_factory.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "platform/PlatformExport.h"
+#include "public/platform/WebSurfaceLayerBridge.h"
 #include "public/platform/modules/offscreencanvas/offscreen_canvas_surface.mojom-blink.h"
 
 namespace cc {
@@ -24,41 +25,45 @@
 class WebLayer;
 class WebLayerTreeView;
 
-class PLATFORM_EXPORT CanvasSurfaceLayerBridgeObserver {
+class PLATFORM_EXPORT SurfaceLayerBridgeObserver {
  public:
-  CanvasSurfaceLayerBridgeObserver() {}
-  virtual ~CanvasSurfaceLayerBridgeObserver() {}
+  SurfaceLayerBridgeObserver() {}
+  virtual ~SurfaceLayerBridgeObserver() {}
 
   virtual void OnWebLayerReplaced() = 0;
 };
 
-class PLATFORM_EXPORT CanvasSurfaceLayerBridge
-    : NON_EXPORTED_BASE(
-          public blink::mojom::blink::OffscreenCanvasSurfaceClient) {
+class PLATFORM_EXPORT SurfaceLayerBridge
+    : public NON_EXPORTED_BASE(
+          blink::mojom::blink::OffscreenCanvasSurfaceClient),
+      public WebSurfaceLayerBridge {
  public:
-  explicit CanvasSurfaceLayerBridge(CanvasSurfaceLayerBridgeObserver*,
-                                    WebLayerTreeView*);
-  ~CanvasSurfaceLayerBridge();
+  SurfaceLayerBridge(SurfaceLayerBridgeObserver*, WebLayerTreeView*);
+  virtual ~SurfaceLayerBridge();
+
   void CreateSolidColorLayer();
-  WebLayer* GetWebLayer() const { return web_layer_.get(); }
-  const cc::FrameSinkId& GetFrameSinkId() const { return frame_sink_id_; }
 
   // Implementation of blink::mojom::blink::OffscreenCanvasSurfaceClient
   void OnSurfaceCreated(const cc::SurfaceInfo&) override;
-
   void SatisfyCallback(const cc::SurfaceSequence&);
   void RequireCallback(const cc::SurfaceId&, const cc::SurfaceSequence&);
 
+  // Implementation of WebSurfaceLayerBridge.
+  WebLayer* GetWebLayer() const override { return web_layer_.get(); }
+
+  const cc::FrameSinkId& GetFrameSinkId() const { return frame_sink_id_; }
+
  private:
+  mojom::blink::OffscreenCanvasSurfacePtr service_;
+
   scoped_refptr<cc::Layer> cc_layer_;
   std::unique_ptr<WebLayer> web_layer_;
 
   scoped_refptr<cc::SurfaceReferenceFactory> ref_factory_;
-  base::WeakPtrFactory<CanvasSurfaceLayerBridge> weak_factory_;
+  base::WeakPtrFactory<SurfaceLayerBridge> weak_factory_;
 
-  CanvasSurfaceLayerBridgeObserver* observer_;
+  SurfaceLayerBridgeObserver* observer_;
 
-  mojom::blink::OffscreenCanvasSurfacePtr service_;
   mojo::Binding<blink::mojom::blink::OffscreenCanvasSurfaceClient> binding_;
 
   const cc::FrameSinkId frame_sink_id_;
@@ -68,4 +73,4 @@
 
 }  // namespace blink
 
-#endif  // CanvasSurfaceLayerBridge_h
+#endif  // SurfaceLayerBridge_h
diff --git a/third_party/WebKit/Source/platform/graphics/filters/ImageFilterBuilderTest.cpp b/third_party/WebKit/Source/platform/graphics/filters/ImageFilterBuilderTest.cpp
index 34c6414..ab4cb85 100644
--- a/third_party/WebKit/Source/platform/graphics/filters/ImageFilterBuilderTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/filters/ImageFilterBuilderTest.cpp
@@ -31,7 +31,7 @@
 #include "platform/graphics/filters/SourceGraphic.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using testing::Test;
+using ::testing::Test;
 
 namespace blink {
 
diff --git a/third_party/WebKit/Source/platform/graphics/gpu/DrawingBufferTest.cpp b/third_party/WebKit/Source/platform/graphics/gpu/DrawingBufferTest.cpp
index b5867526..db0a033 100644
--- a/third_party/WebKit/Source/platform/graphics/gpu/DrawingBufferTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/gpu/DrawingBufferTest.cpp
@@ -47,8 +47,8 @@
 #include "public/platform/Platform.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using testing::Test;
-using testing::_;
+using ::testing::Test;
+using ::testing::_;
 
 namespace blink {
 
@@ -410,7 +410,7 @@
         kDisableMultisampling);
     CHECK(drawing_buffer_);
     SetAndSaveRestoreState(true);
-    testing::Mock::VerifyAndClearExpectations(gl_);
+    ::testing::Mock::VerifyAndClearExpectations(gl_);
   }
 
   void TearDown() override {
@@ -438,7 +438,7 @@
                                                      &release_callback));
   EXPECT_EQ(initial_size, gl_->MostRecentlyProducedSize());
   EXPECT_TRUE(texture_mailbox.is_overlay_candidate());
-  testing::Mock::VerifyAndClearExpectations(gl_);
+  ::testing::Mock::VerifyAndClearExpectations(gl_);
   VerifyStateWasRestored();
 
   GLuint image_id2 = gl_->NextImageIdToBeCreated();
@@ -452,7 +452,7 @@
   VerifyStateWasRestored();
   release_callback->Run(gpu::SyncToken(), false /* lostResource */);
   VerifyStateWasRestored();
-  testing::Mock::VerifyAndClearExpectations(gl_);
+  ::testing::Mock::VerifyAndClearExpectations(gl_);
 
   GLuint image_id3 = gl_->NextImageIdToBeCreated();
   EXPECT_CALL(*gl_, BindTexImage2DMock(image_id3)).Times(1);
@@ -462,7 +462,7 @@
                                                      &release_callback));
   EXPECT_EQ(alternate_size, gl_->MostRecentlyProducedSize());
   EXPECT_TRUE(texture_mailbox.is_overlay_candidate());
-  testing::Mock::VerifyAndClearExpectations(gl_);
+  ::testing::Mock::VerifyAndClearExpectations(gl_);
 
   GLuint image_id4 = gl_->NextImageIdToBeCreated();
   EXPECT_CALL(*gl_, BindTexImage2DMock(image_id4)).Times(1);
@@ -475,7 +475,7 @@
   VerifyStateWasRestored();
   release_callback->Run(gpu::SyncToken(), false /* lostResource */);
   VerifyStateWasRestored();
-  testing::Mock::VerifyAndClearExpectations(gl_);
+  ::testing::Mock::VerifyAndClearExpectations(gl_);
 
   GLuint image_id5 = gl_->NextImageIdToBeCreated();
   EXPECT_CALL(*gl_, BindTexImage2DMock(image_id5)).Times(1);
@@ -485,7 +485,7 @@
                                                      &release_callback));
   EXPECT_EQ(initial_size, gl_->MostRecentlyProducedSize());
   EXPECT_TRUE(texture_mailbox.is_overlay_candidate());
-  testing::Mock::VerifyAndClearExpectations(gl_);
+  ::testing::Mock::VerifyAndClearExpectations(gl_);
 
   // Prepare one final mailbox and verify that it's the correct size.
   release_callback->Run(gpu::SyncToken(), false /* lostResource */);
@@ -501,7 +501,7 @@
   EXPECT_CALL(*gl_, DestroyImageMock(image_id4)).Times(1);
   EXPECT_CALL(*gl_, ReleaseTexImage2DMock(image_id4)).Times(1);
   drawing_buffer_->BeginDestruction();
-  testing::Mock::VerifyAndClearExpectations(gl_);
+  ::testing::Mock::VerifyAndClearExpectations(gl_);
 }
 
 TEST_F(DrawingBufferImageChromiumTest, allocationFailure) {
@@ -521,7 +521,7 @@
   EXPECT_TRUE(drawing_buffer_->PrepareTextureMailbox(&texture_mailbox1,
                                                      &release_callback1));
   EXPECT_TRUE(texture_mailbox1.is_overlay_candidate());
-  testing::Mock::VerifyAndClearExpectations(gl_);
+  ::testing::Mock::VerifyAndClearExpectations(gl_);
   VerifyStateWasRestored();
 
   // Force image CHROMIUM creation failure. Request another mailbox. It should
@@ -541,7 +541,7 @@
   EXPECT_TRUE(drawing_buffer_->PrepareTextureMailbox(&texture_mailbox3,
                                                      &release_callback3));
   EXPECT_TRUE(texture_mailbox3.is_overlay_candidate());
-  testing::Mock::VerifyAndClearExpectations(gl_);
+  ::testing::Mock::VerifyAndClearExpectations(gl_);
   VerifyStateWasRestored();
 
   release_callback1->Run(gpu::SyncToken(), false /* lostResource */);
@@ -551,7 +551,7 @@
   EXPECT_CALL(*gl_, DestroyImageMock(_)).Times(3);
   EXPECT_CALL(*gl_, ReleaseTexImage2DMock(_)).Times(3);
   drawing_buffer_->BeginDestruction();
-  testing::Mock::VerifyAndClearExpectations(gl_);
+  ::testing::Mock::VerifyAndClearExpectations(gl_);
 }
 
 class DepthStencilTrackingGLES2Interface
diff --git a/third_party/WebKit/Source/platform/graphics/gpu/SharedGpuContextTest.cpp b/third_party/WebKit/Source/platform/graphics/gpu/SharedGpuContextTest.cpp
index e26157a..39b1ff6 100644
--- a/third_party/WebKit/Source/platform/graphics/gpu/SharedGpuContextTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/gpu/SharedGpuContextTest.cpp
@@ -10,7 +10,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/khronos/GLES2/gl2ext.h"
 
-using testing::Test;
+using ::testing::Test;
 
 namespace blink {
 
diff --git a/third_party/WebKit/Source/platform/graphics/gpu/WebGLImageConversionTest.cpp b/third_party/WebKit/Source/platform/graphics/gpu/WebGLImageConversionTest.cpp
index 128ae15..4a8fb4a0 100644
--- a/third_party/WebKit/Source/platform/graphics/gpu/WebGLImageConversionTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/gpu/WebGLImageConversionTest.cpp
@@ -9,7 +9,7 @@
 
 namespace blink {
 
-class WebGLImageConversionTest : public testing::Test {
+class WebGLImageConversionTest : public ::testing::Test {
  protected:
   void UnpackPixels(const uint16_t* source_data,
                     WebGLImageConversion::DataFormat source_data_format,
diff --git a/third_party/WebKit/Source/platform/graphics/paint/CullRectTest.cpp b/third_party/WebKit/Source/platform/graphics/paint/CullRectTest.cpp
index f2950ba0..a7c508e 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/CullRectTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/CullRectTest.cpp
@@ -10,7 +10,7 @@
 
 namespace blink {
 
-class CullRectTest : public testing::Test {
+class CullRectTest : public ::testing::Test {
  protected:
   IntRect Rect(const CullRect& cull_rect) { return cull_rect.rect_; }
 };
diff --git a/third_party/WebKit/Source/platform/graphics/paint/DrawingDisplayItemTest.cpp b/third_party/WebKit/Source/platform/graphics/paint/DrawingDisplayItemTest.cpp
index b4c9577..c9a9f1a 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/DrawingDisplayItemTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/DrawingDisplayItemTest.cpp
@@ -15,7 +15,7 @@
 namespace blink {
 namespace {
 
-using testing::_;
+using ::testing::_;
 
 class DrawingDisplayItemTest : public ::testing::Test {
  protected:
diff --git a/third_party/WebKit/Source/platform/graphics/paint/PaintControllerTest.cpp b/third_party/WebKit/Source/platform/graphics/paint/PaintControllerTest.cpp
index 4f9939ac..c251780d 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/PaintControllerTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/PaintControllerTest.cpp
@@ -23,7 +23,7 @@
 
 using blink::testing::CreateOpacityOnlyEffect;
 using blink::testing::DefaultPaintChunkProperties;
-using testing::UnorderedElementsAre;
+using ::testing::UnorderedElementsAre;
 
 namespace blink {
 
diff --git a/third_party/WebKit/Source/platform/loader/LinkHeaderTest.cpp b/third_party/WebKit/Source/platform/loader/LinkHeaderTest.cpp
index 7ef6296..fade747 100644
--- a/third_party/WebKit/Source/platform/loader/LinkHeaderTest.cpp
+++ b/third_party/WebKit/Source/platform/loader/LinkHeaderTest.cpp
@@ -175,7 +175,7 @@
 
 INSTANTIATE_TEST_CASE_P(LinkHeaderTest,
                         SingleLinkHeaderTest,
-                        testing::ValuesIn(g_single_test_cases));
+                        ::testing::ValuesIn(g_single_test_cases));
 
 struct DoubleTestCase {
   const char* header_value;
@@ -219,7 +219,7 @@
 
 INSTANTIATE_TEST_CASE_P(LinkHeaderTest,
                         DoubleLinkHeaderTest,
-                        testing::ValuesIn(g_double_test_cases));
+                        ::testing::ValuesIn(g_double_test_cases));
 
 struct CrossOriginTestCase {
   const char* header_value;
@@ -281,7 +281,7 @@
 
 INSTANTIATE_TEST_CASE_P(LinkHeaderTest,
                         CrossOriginLinkHeaderTest,
-                        testing::ValuesIn(g_cross_origin_test_cases));
+                        ::testing::ValuesIn(g_cross_origin_test_cases));
 
 }  // namespace
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/scheduler/base/intrusive_heap_unittest.cc b/third_party/WebKit/Source/platform/scheduler/base/intrusive_heap_unittest.cc
index eb28fbb9..1240940 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/intrusive_heap_unittest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/intrusive_heap_unittest.cc
@@ -278,7 +278,8 @@
     heap.Pop();
   }
 
-  EXPECT_THAT(results, testing::ElementsAre(0, 2, 4, 6, 8, 12, 14, 16, 17, 18));
+  EXPECT_THAT(results,
+              ::testing::ElementsAre(0, 2, 4, 6, 8, 12, 14, 16, 17, 18));
 }
 
 TEST_F(IntrusiveHeapTest, ChangeKeyUpButDoesntMove) {
@@ -297,7 +298,8 @@
     heap.Pop();
   }
 
-  EXPECT_THAT(results, testing::ElementsAre(0, 2, 4, 6, 8, 11, 12, 14, 16, 18));
+  EXPECT_THAT(results,
+              ::testing::ElementsAre(0, 2, 4, 6, 8, 11, 12, 14, 16, 18));
 }
 
 TEST_F(IntrusiveHeapTest, ChangeKeyDown) {
@@ -316,7 +318,8 @@
     heap.Pop();
   }
 
-  EXPECT_THAT(results, testing::ElementsAre(0, 1, 2, 4, 6, 8, 12, 14, 16, 18));
+  EXPECT_THAT(results,
+              ::testing::ElementsAre(0, 1, 2, 4, 6, 8, 12, 14, 16, 18));
 }
 
 TEST_F(IntrusiveHeapTest, ChangeKeyDownButDoesntMove) {
@@ -335,7 +338,8 @@
     heap.Pop();
   }
 
-  EXPECT_THAT(results, testing::ElementsAre(0, 2, 4, 6, 8, 9, 12, 14, 16, 18));
+  EXPECT_THAT(results,
+              ::testing::ElementsAre(0, 2, 4, 6, 8, 9, 12, 14, 16, 18));
 }
 
 TEST_F(IntrusiveHeapTest, ChangeKeyCheckAllFinalPositions) {
diff --git a/third_party/WebKit/Source/platform/scheduler/base/queueing_time_estimator_unittest.cc b/third_party/WebKit/Source/platform/scheduler/base/queueing_time_estimator_unittest.cc
index 1a5fa904..8bff10a 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/queueing_time_estimator_unittest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/queueing_time_estimator_unittest.cc
@@ -11,7 +11,7 @@
 namespace blink {
 namespace scheduler {
 
-using QueueingTimeEstimatorTest = testing::Test;
+using QueueingTimeEstimatorTest = ::testing::Test;
 
 class TestQueueingTimeEstimatorClient : public QueueingTimeEstimator::Client {
  public:
@@ -58,7 +58,7 @@
   estimator.OnTopLevelTaskCompleted(time);
 
   EXPECT_THAT(client.expected_queueing_times(),
-              testing::ElementsAre(base::TimeDelta::FromMilliseconds(300)));
+              ::testing::ElementsAre(base::TimeDelta::FromMilliseconds(300)));
 }
 
 // One 20 second long task, starting 3 seconds into the first window.
@@ -90,11 +90,11 @@
   estimator.OnTopLevelTaskCompleted(time);
 
   EXPECT_THAT(client.expected_queueing_times(),
-              testing::ElementsAre(base::TimeDelta::FromMilliseconds(7600),
-                                   base::TimeDelta::FromMilliseconds(15500),
-                                   base::TimeDelta::FromMilliseconds(10500),
-                                   base::TimeDelta::FromMilliseconds(5500),
-                                   base::TimeDelta::FromMilliseconds(900)));
+              ::testing::ElementsAre(base::TimeDelta::FromMilliseconds(7600),
+                                     base::TimeDelta::FromMilliseconds(15500),
+                                     base::TimeDelta::FromMilliseconds(10500),
+                                     base::TimeDelta::FromMilliseconds(5500),
+                                     base::TimeDelta::FromMilliseconds(900)));
 }
 
 // The main thread is considered unresponsive during a single long task. In this
@@ -253,11 +253,11 @@
   estimator.OnTopLevelTaskCompleted(time);
 
   EXPECT_THAT(client.expected_queueing_times(),
-              testing::ElementsAre(base::TimeDelta::FromMilliseconds(0),
-                                   base::TimeDelta::FromMilliseconds(0),
-                                   base::TimeDelta::FromMilliseconds(0),
-                                   base::TimeDelta::FromMilliseconds(0),
-                                   base::TimeDelta::FromMilliseconds(100)));
+              ::testing::ElementsAre(base::TimeDelta::FromMilliseconds(0),
+                                     base::TimeDelta::FromMilliseconds(0),
+                                     base::TimeDelta::FromMilliseconds(0),
+                                     base::TimeDelta::FromMilliseconds(0),
+                                     base::TimeDelta::FromMilliseconds(100)));
 }
 
 // If a task is too long, we assume it's invalid. Perhaps the user's machine
@@ -290,14 +290,14 @@
   estimator.OnTopLevelTaskCompleted(time);
 
   EXPECT_THAT(client.expected_queueing_times(),
-              testing::ElementsAre(base::TimeDelta::FromMilliseconds(100),
-                                   base::TimeDelta::FromMilliseconds(0),
-                                   base::TimeDelta::FromMilliseconds(0),
-                                   base::TimeDelta::FromMilliseconds(0),
-                                   base::TimeDelta::FromMilliseconds(0),
-                                   base::TimeDelta::FromMilliseconds(0),
-                                   base::TimeDelta::FromMilliseconds(0),
-                                   base::TimeDelta::FromMilliseconds(100)));
+              ::testing::ElementsAre(base::TimeDelta::FromMilliseconds(100),
+                                     base::TimeDelta::FromMilliseconds(0),
+                                     base::TimeDelta::FromMilliseconds(0),
+                                     base::TimeDelta::FromMilliseconds(0),
+                                     base::TimeDelta::FromMilliseconds(0),
+                                     base::TimeDelta::FromMilliseconds(0),
+                                     base::TimeDelta::FromMilliseconds(0),
+                                     base::TimeDelta::FromMilliseconds(100)));
 }
 
 // ^ Instantaneous queuing time
@@ -330,16 +330,16 @@
   estimator.OnTopLevelTaskCompleted(time);
 
   EXPECT_THAT(client.expected_queueing_times(),
-              testing::ElementsAre(base::TimeDelta::FromMilliseconds(900),
-                                   base::TimeDelta::FromMilliseconds(1600),
-                                   base::TimeDelta::FromMilliseconds(2100),
-                                   base::TimeDelta::FromMilliseconds(2400),
-                                   base::TimeDelta::FromMilliseconds(2500),
-                                   base::TimeDelta::FromMilliseconds(1600),
-                                   base::TimeDelta::FromMilliseconds(900),
-                                   base::TimeDelta::FromMilliseconds(400),
-                                   base::TimeDelta::FromMilliseconds(100),
-                                   base::TimeDelta::FromMilliseconds(0)));
+              ::testing::ElementsAre(base::TimeDelta::FromMilliseconds(900),
+                                     base::TimeDelta::FromMilliseconds(1600),
+                                     base::TimeDelta::FromMilliseconds(2100),
+                                     base::TimeDelta::FromMilliseconds(2400),
+                                     base::TimeDelta::FromMilliseconds(2500),
+                                     base::TimeDelta::FromMilliseconds(1600),
+                                     base::TimeDelta::FromMilliseconds(900),
+                                     base::TimeDelta::FromMilliseconds(400),
+                                     base::TimeDelta::FromMilliseconds(100),
+                                     base::TimeDelta::FromMilliseconds(0)));
 }
 
 // ^ Instantaneous queuing time
@@ -388,7 +388,7 @@
       base::TimeDelta::FromMilliseconds(100),
       base::TimeDelta::FromMilliseconds(0)};
   EXPECT_THAT(client.expected_queueing_times(),
-              testing::ElementsAreArray(expected_durations));
+              ::testing::ElementsAreArray(expected_durations));
 }
 
 // ^ Instantaneous queuing time
@@ -445,7 +445,7 @@
       base::TimeDelta::FromMilliseconds(0)};
 
   EXPECT_THAT(client.expected_queueing_times(),
-              testing::ElementsAreArray(expected_durations));
+              ::testing::ElementsAreArray(expected_durations));
 }
 
 }  // namespace scheduler
diff --git a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_perftest.cc b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_perftest.cc
index b2776a15..4c33e1d2 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_perftest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_perftest.cc
@@ -60,7 +60,7 @@
   DISALLOW_COPY_AND_ASSIGN(PerfTestTimeDomain);
 };
 
-class TaskQueueManagerPerfTest : public testing::Test {
+class TaskQueueManagerPerfTest : public ::testing::Test {
  public:
   TaskQueueManagerPerfTest()
       : num_queues_(0),
diff --git a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_unittest.cc b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_unittest.cc
index d21cd7b0..5ab22a8 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_unittest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_unittest.cc
@@ -33,13 +33,13 @@
 #include "platform/scheduler/base/work_queue_sets.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
-using testing::AnyNumber;
-using testing::Contains;
-using testing::ElementsAre;
-using testing::ElementsAreArray;
-using testing::Mock;
-using testing::Not;
-using testing::_;
+using ::testing::AnyNumber;
+using ::testing::Contains;
+using ::testing::ElementsAre;
+using ::testing::ElementsAreArray;
+using ::testing::Mock;
+using ::testing::Not;
+using ::testing::_;
 using blink::scheduler::internal::EnqueueOrder;
 
 namespace blink {
@@ -83,7 +83,7 @@
   ~MessageLoopTaskRunner() override {}
 };
 
-class TaskQueueManagerTest : public testing::Test {
+class TaskQueueManagerTest : public ::testing::Test {
  public:
   TaskQueueManagerTest() {}
   void DeleteTaskQueueManager() { manager_.reset(); }
@@ -1781,7 +1781,7 @@
       .Times(1);
   runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), delay1s);
   runners_[1]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), delay10s);
-  testing::Mock::VerifyAndClearExpectations(&observer);
+  ::testing::Mock::VerifyAndClearExpectations(&observer);
 
   std::unique_ptr<TaskQueue::QueueEnabledVoter> voter0 =
       runners_[0]->CreateQueueEnabledVoter();
diff --git a/third_party/WebKit/Source/platform/scheduler/base/task_queue_selector_unittest.cc b/third_party/WebKit/Source/platform/scheduler/base/task_queue_selector_unittest.cc
index c98d2816..96f1c6c 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/task_queue_selector_unittest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/task_queue_selector_unittest.cc
@@ -19,7 +19,7 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using testing::_;
+using ::testing::_;
 
 namespace blink {
 namespace scheduler {
@@ -44,7 +44,7 @@
   using TaskQueueSelector::enabled_selector_for_test;
 };
 
-class TaskQueueSelectorTest : public testing::Test {
+class TaskQueueSelectorTest : public ::testing::Test {
  public:
   TaskQueueSelectorTest()
       : test_closure_(base::Bind(&TaskQueueSelectorTest::TestFunction)) {}
@@ -163,21 +163,21 @@
 TEST_F(TaskQueueSelectorTest, TestDefaultPriority) {
   size_t queue_order[] = {4, 3, 2, 1, 0};
   PushTasks(queue_order, 5);
-  EXPECT_THAT(PopTasks(), testing::ElementsAre(4, 3, 2, 1, 0));
+  EXPECT_THAT(PopTasks(), ::testing::ElementsAre(4, 3, 2, 1, 0));
 }
 
 TEST_F(TaskQueueSelectorTest, TestHighPriority) {
   size_t queue_order[] = {0, 1, 2, 3, 4};
   PushTasks(queue_order, 5);
   selector_.SetQueuePriority(task_queues_[2].get(), TaskQueue::HIGH_PRIORITY);
-  EXPECT_THAT(PopTasks(), testing::ElementsAre(2, 0, 1, 3, 4));
+  EXPECT_THAT(PopTasks(), ::testing::ElementsAre(2, 0, 1, 3, 4));
 }
 
 TEST_F(TaskQueueSelectorTest, TestLowPriority) {
   size_t queue_order[] = {0, 1, 2, 3, 4};
   PushTasks(queue_order, 5);
   selector_.SetQueuePriority(task_queues_[2].get(), TaskQueue::LOW_PRIORITY);
-  EXPECT_THAT(PopTasks(), testing::ElementsAre(0, 1, 3, 4, 2));
+  EXPECT_THAT(PopTasks(), ::testing::ElementsAre(0, 1, 3, 4, 2));
 }
 
 TEST_F(TaskQueueSelectorTest, TestBestEffortPriority) {
@@ -187,7 +187,7 @@
                              TaskQueue::BEST_EFFORT_PRIORITY);
   selector_.SetQueuePriority(task_queues_[2].get(), TaskQueue::LOW_PRIORITY);
   selector_.SetQueuePriority(task_queues_[3].get(), TaskQueue::HIGH_PRIORITY);
-  EXPECT_THAT(PopTasks(), testing::ElementsAre(3, 1, 4, 2, 0));
+  EXPECT_THAT(PopTasks(), ::testing::ElementsAre(3, 1, 4, 2, 0));
 }
 
 TEST_F(TaskQueueSelectorTest, TestControlPriority) {
@@ -198,7 +198,7 @@
   EXPECT_EQ(TaskQueue::CONTROL_PRIORITY, task_queues_[4]->GetQueuePriority());
   selector_.SetQueuePriority(task_queues_[2].get(), TaskQueue::HIGH_PRIORITY);
   EXPECT_EQ(TaskQueue::HIGH_PRIORITY, task_queues_[2]->GetQueuePriority());
-  EXPECT_THAT(PopTasks(), testing::ElementsAre(4, 2, 0, 1, 3));
+  EXPECT_THAT(PopTasks(), ::testing::ElementsAre(4, 2, 0, 1, 3));
 }
 
 TEST_F(TaskQueueSelectorTest, TestObserverWithEnabledQueue) {
@@ -235,15 +235,15 @@
   // Disabling a queue should not affect its priority.
   EXPECT_EQ(TaskQueue::NORMAL_PRIORITY, task_queues_[2]->GetQueuePriority());
   EXPECT_EQ(TaskQueue::NORMAL_PRIORITY, task_queues_[4]->GetQueuePriority());
-  EXPECT_THAT(PopTasks(), testing::ElementsAre(0, 1, 3));
+  EXPECT_THAT(PopTasks(), ::testing::ElementsAre(0, 1, 3));
 
   EXPECT_CALL(mock_observer, OnTaskQueueEnabled(_)).Times(2);
   EnableQueue(voter2.get());
   selector_.SetQueuePriority(task_queues_[2].get(),
                              TaskQueue::BEST_EFFORT_PRIORITY);
-  EXPECT_THAT(PopTasks(), testing::ElementsAre(2));
+  EXPECT_THAT(PopTasks(), ::testing::ElementsAre(2));
   EnableQueue(voter4.get());
-  EXPECT_THAT(PopTasks(), testing::ElementsAre(4));
+  EXPECT_THAT(PopTasks(), ::testing::ElementsAre(4));
 }
 
 TEST_F(TaskQueueSelectorTest, TestDisableChangePriorityThenEnable) {
@@ -263,7 +263,7 @@
   EnableQueue(voter2.get());
 
   EXPECT_EQ(TaskQueue::HIGH_PRIORITY, task_queues_[2]->GetQueuePriority());
-  EXPECT_THAT(PopTasks(), testing::ElementsAre(2, 0, 1, 3, 4));
+  EXPECT_THAT(PopTasks(), ::testing::ElementsAre(2, 0, 1, 3, 4));
 }
 
 TEST_F(TaskQueueSelectorTest, TestEmptyQueues) {
@@ -288,7 +288,7 @@
   size_t enqueue_order[] = {10, 1, 2, 9, 4};
   size_t queue_order[] = {0, 1, 2, 3, 4};
   PushTasksWithEnqueueOrder(queue_order, enqueue_order, 5);
-  EXPECT_THAT(PopTasks(), testing::ElementsAre(1, 2, 4, 3, 0));
+  EXPECT_THAT(PopTasks(), ::testing::ElementsAre(1, 2, 4, 3, 0));
 }
 
 TEST_F(TaskQueueSelectorTest, TestControlStarvesOthers) {
@@ -513,7 +513,7 @@
   WorkQueue* chosen_work_queue;
   EXPECT_CALL(mock_observer, OnTriedToSelectBlockedWorkQueue(_)).Times(1);
   EXPECT_FALSE(selector.SelectWorkQueueToService(&chosen_work_queue));
-  testing::Mock::VerifyAndClearExpectations(&mock_observer);
+  ::testing::Mock::VerifyAndClearExpectations(&mock_observer);
 
   EXPECT_CALL(mock_observer, OnTaskQueueEnabled(_)).Times(2);
 
@@ -551,7 +551,8 @@
 
 class ChooseOldestWithPriorityTest
     : public TaskQueueSelectorTest,
-      public testing::WithParamInterface<ChooseOldestWithPriorityTestParam> {};
+      public ::testing::WithParamInterface<ChooseOldestWithPriorityTestParam> {
+};
 
 TEST_P(ChooseOldestWithPriorityTest, RoundRobinTest) {
   task_queues_[0]->immediate_work_queue()->Push(
@@ -579,9 +580,10 @@
             GetParam().expected_did_starve_immediate_queue);
 }
 
-INSTANTIATE_TEST_CASE_P(ChooseOldestWithPriorityTest,
-                        ChooseOldestWithPriorityTest,
-                        testing::ValuesIn(kChooseOldestWithPriorityTestCases));
+INSTANTIATE_TEST_CASE_P(
+    ChooseOldestWithPriorityTest,
+    ChooseOldestWithPriorityTest,
+    ::testing::ValuesIn(kChooseOldestWithPriorityTestCases));
 
 }  // namespace internal
 }  // namespace scheduler
diff --git a/third_party/WebKit/Source/platform/scheduler/base/thread_load_tracker_unittest.cc b/third_party/WebKit/Source/platform/scheduler/base/thread_load_tracker_unittest.cc
index aa3a0955..7e8eb0e 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/thread_load_tracker_unittest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/thread_load_tracker_unittest.cc
@@ -4,7 +4,7 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using testing::ElementsAre;
+using ::testing::ElementsAre;
 
 namespace blink {
 namespace scheduler {
diff --git a/third_party/WebKit/Source/platform/scheduler/base/time_domain_unittest.cc b/third_party/WebKit/Source/platform/scheduler/base/time_domain_unittest.cc
index 0107d4d..d77dee24 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/time_domain_unittest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/time_domain_unittest.cc
@@ -15,9 +15,9 @@
 #include "platform/scheduler/base/work_queue.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
-using testing::_;
-using testing::AnyNumber;
-using testing::Mock;
+using ::testing::_;
+using ::testing::AnyNumber;
+using ::testing::Mock;
 
 namespace blink {
 namespace scheduler {
@@ -64,7 +64,7 @@
   DISALLOW_COPY_AND_ASSIGN(MockTimeDomain);
 };
 
-class TimeDomainTest : public testing::Test {
+class TimeDomainTest : public ::testing::Test {
  public:
   void SetUp() final {
     time_domain_ = base::WrapUnique(CreateMockTimeDomain());
@@ -194,7 +194,7 @@
   EXPECT_TRUE(time_domain_->NextScheduledTaskQueue(&next_task_queue));
   EXPECT_EQ(task_queue_.get(), next_task_queue);
 
-  testing::Mock::VerifyAndClearExpectations(time_domain_.get());
+  ::testing::Mock::VerifyAndClearExpectations(time_domain_.get());
 
   EXPECT_CALL(*time_domain_.get(), CancelWakeUpAt(wake_up1)).Times(1);
   EXPECT_CALL(*time_domain_.get(), RequestWakeUpAt(_, wake_up2)).Times(1);
@@ -204,7 +204,7 @@
   EXPECT_TRUE(time_domain_->NextScheduledTaskQueue(&next_task_queue));
   EXPECT_EQ(task_queue2_.get(), next_task_queue);
 
-  testing::Mock::VerifyAndClearExpectations(time_domain_.get());
+  ::testing::Mock::VerifyAndClearExpectations(time_domain_.get());
 
   EXPECT_CALL(*time_domain_.get(), CancelWakeUpAt(wake_up2)).Times(1);
 
diff --git a/third_party/WebKit/Source/platform/scheduler/base/work_queue_sets_unittest.cc b/third_party/WebKit/Source/platform/scheduler/base/work_queue_sets_unittest.cc
index de0b9c4..1beb05c 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/work_queue_sets_unittest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/work_queue_sets_unittest.cc
@@ -16,7 +16,7 @@
 
 namespace internal {
 
-class WorkQueueSetsTest : public testing::Test {
+class WorkQueueSetsTest : public ::testing::Test {
  public:
   void SetUp() override {
     work_queue_sets_.reset(new WorkQueueSets(kNumSets, "test"));
diff --git a/third_party/WebKit/Source/platform/scheduler/base/work_queue_unittest.cc b/third_party/WebKit/Source/platform/scheduler/base/work_queue_unittest.cc
index 59f5581c..4941dbe 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/work_queue_unittest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/work_queue_unittest.cc
@@ -20,7 +20,7 @@
 void NopTask() {}
 }
 
-class WorkQueueTest : public testing::Test {
+class WorkQueueTest : public ::testing::Test {
  public:
   void SetUp() override {
     time_domain_.reset(new RealTimeDomain());
diff --git a/third_party/WebKit/Source/platform/scheduler/child/idle_canceled_delayed_task_sweeper_unittest.cc b/third_party/WebKit/Source/platform/scheduler/child/idle_canceled_delayed_task_sweeper_unittest.cc
index 3473eb7..d106333 100644
--- a/third_party/WebKit/Source/platform/scheduler/child/idle_canceled_delayed_task_sweeper_unittest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/child/idle_canceled_delayed_task_sweeper_unittest.cc
@@ -27,7 +27,7 @@
   base::WeakPtrFactory<TestClass> weak_factory_;
 };
 
-class IdleCanceledDelayedTaskSweeperTest : public testing::Test,
+class IdleCanceledDelayedTaskSweeperTest : public ::testing::Test,
                                            public IdleHelper::Delegate {
  public:
   IdleCanceledDelayedTaskSweeperTest()
diff --git a/third_party/WebKit/Source/platform/scheduler/child/idle_helper_unittest.cc b/third_party/WebKit/Source/platform/scheduler/child/idle_helper_unittest.cc
index c42e6bb..8901a29 100644
--- a/third_party/WebKit/Source/platform/scheduler/child/idle_helper_unittest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/child/idle_helper_unittest.cc
@@ -24,12 +24,12 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using testing::_;
-using testing::AnyNumber;
-using testing::AtLeast;
-using testing::Exactly;
-using testing::Invoke;
-using testing::Return;
+using ::testing::_;
+using ::testing::AnyNumber;
+using ::testing::AtLeast;
+using ::testing::Exactly;
+using ::testing::Invoke;
+using ::testing::Return;
 
 namespace blink {
 namespace scheduler {
@@ -177,7 +177,7 @@
   MOCK_METHOD1(OnPendingTasksChanged, void(bool has_tasks));
 };
 
-class BaseIdleHelperTest : public testing::Test {
+class BaseIdleHelperTest : public ::testing::Test {
  public:
   BaseIdleHelperTest(
       base::MessageLoop* message_loop,
@@ -427,7 +427,8 @@
     EXPECT_CALL(*idle_helper_, OnIdlePeriodEnded()).Times(0);
   }
 
-  void ExpectIdlePeriodStartsAndEnds(const testing::Cardinality& cardinality) {
+  void ExpectIdlePeriodStartsAndEnds(
+      const ::testing::Cardinality& cardinality) {
     EXPECT_CALL(*idle_helper_, OnIdlePeriodStarted()).Times(cardinality);
     EXPECT_CALL(*idle_helper_, OnIdlePeriodEnded()).Times(cardinality);
   }
@@ -524,9 +525,9 @@
       clock_->NowTicks() + base::TimeDelta::FromMilliseconds(10));
   RunUntilIdle();
   // Note we expect task 3 to run last because it's non-nestable.
-  EXPECT_THAT(order, testing::ElementsAre(std::string("1"), std::string("2"),
-                                          std::string("4"), std::string("5"),
-                                          std::string("3")));
+  EXPECT_THAT(order, ::testing::ElementsAre(std::string("1"), std::string("2"),
+                                            std::string("4"), std::string("5"),
+                                            std::string("3")));
 }
 
 TEST_F(IdleHelperTestWithIdlePeriodObserver, TestLongIdlePeriod) {
@@ -619,7 +620,7 @@
   EXPECT_EQ(3, run_count);
   EXPECT_THAT(
       actual_deadlines,
-      testing::ElementsAre(
+      ::testing::ElementsAre(
           clock_before + maximum_idle_period_duration(),
           clock_before + idle_task_runtime + maximum_idle_period_duration(),
           clock_before + (2 * idle_task_runtime) +
@@ -701,7 +702,7 @@
   EXPECT_EQ(2, run_count);
   EXPECT_THAT(
       actual_deadlines,
-      testing::ElementsAre(
+      ::testing::ElementsAre(
           clock_before + maximum_idle_period_duration(),
           clock_before + idle_task_runtime + maximum_idle_period_duration()));
 }
@@ -772,7 +773,7 @@
   EXPECT_EQ(2, run_count);
   EXPECT_THAT(
       actual_deadlines,
-      testing::ElementsAre(
+      ::testing::ElementsAre(
           clock_before + maximum_idle_period_duration(),
           clock_before + idle_task_runtime + maximum_idle_period_duration()));
 
@@ -1126,7 +1127,7 @@
   base::TimeTicks deadline_in_task;
 
   {
-    testing::InSequence dummy;
+    ::testing::InSequence dummy;
     // This will be called once. I.e when the one and only task is posted.
     EXPECT_CALL(*idle_helper_, OnPendingTasksChanged(true)).Times(1);
     // This will be called once. I.e when the one and only task completes.
@@ -1157,7 +1158,7 @@
   base::TimeTicks deadline_in_task;
 
   {
-    testing::InSequence dummy;
+    ::testing::InSequence dummy;
     // This will be called 3 times. I.e when T1 and T2 are posted and when T1
     // completes.
     EXPECT_CALL(*idle_helper_, OnPendingTasksChanged(true)).Times(3);
diff --git a/third_party/WebKit/Source/platform/scheduler/child/scheduler_helper_unittest.cc b/third_party/WebKit/Source/platform/scheduler/child/scheduler_helper_unittest.cc
index 5e6ab991..f3dae923 100644
--- a/third_party/WebKit/Source/platform/scheduler/child/scheduler_helper_unittest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/child/scheduler_helper_unittest.cc
@@ -18,10 +18,10 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using testing::_;
-using testing::AnyNumber;
-using testing::Invoke;
-using testing::Return;
+using ::testing::_;
+using ::testing::AnyNumber;
+using ::testing::Invoke;
+using ::testing::Return;
 
 namespace blink {
 namespace scheduler {
@@ -47,7 +47,7 @@
 
 };  // namespace
 
-class SchedulerHelperTest : public testing::Test {
+class SchedulerHelperTest : public ::testing::Test {
  public:
   SchedulerHelperTest()
       : clock_(new base::SimpleTestTickClock()),
@@ -105,8 +105,8 @@
 
   RunUntilIdle();
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("D1"), std::string("D2"),
-                                   std::string("D3"), std::string("D4")));
+              ::testing::ElementsAre(std::string("D1"), std::string("D2"),
+                                     std::string("D3"), std::string("D4")));
 }
 
 TEST_F(SchedulerHelperTest, TestRentrantTask) {
@@ -118,7 +118,7 @@
                             &count, 5));
   RunUntilIdle();
 
-  EXPECT_THAT(run_order, testing::ElementsAre(0, 1, 2, 3, 4));
+  EXPECT_THAT(run_order, ::testing::ElementsAre(0, 1, 2, 3, 4));
 }
 
 TEST_F(SchedulerHelperTest, IsShutdown) {
diff --git a/third_party/WebKit/Source/platform/scheduler/child/webthread_impl_for_worker_scheduler_unittest.cc b/third_party/WebKit/Source/platform/scheduler/child/webthread_impl_for_worker_scheduler_unittest.cc
index 5781866..877bc196 100644
--- a/third_party/WebKit/Source/platform/scheduler/child/webthread_impl_for_worker_scheduler_unittest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/child/webthread_impl_for_worker_scheduler_unittest.cc
@@ -13,10 +13,10 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using testing::_;
-using testing::AnyOf;
-using testing::ElementsAre;
-using testing::Invoke;
+using ::testing::_;
+using ::testing::AnyOf;
+using ::testing::ElementsAre;
+using ::testing::Invoke;
 
 namespace blink {
 namespace scheduler {
@@ -70,7 +70,7 @@
 
 }  // namespace
 
-class WebThreadImplForWorkerSchedulerTest : public testing::Test {
+class WebThreadImplForWorkerSchedulerTest : public ::testing::Test {
  public:
   WebThreadImplForWorkerSchedulerTest() {}
 
@@ -173,7 +173,8 @@
   // Sometimes we get an internal scheduler task running before or after
   // TestTask as well. This is not a bug, and we need to make sure the test
   // doesn't fail when that happens.
-  EXPECT_THAT(calls, testing::HasSubstr("willProcessTask run didProcessTask"));
+  EXPECT_THAT(calls,
+              ::testing::HasSubstr("willProcessTask run didProcessTask"));
 }
 
 TEST_F(WebThreadImplForWorkerSchedulerTest, TestShutdown) {
diff --git a/third_party/WebKit/Source/platform/scheduler/child/worker_global_scope_scheduler_unittest.cc b/third_party/WebKit/Source/platform/scheduler/child/worker_global_scope_scheduler_unittest.cc
index 2fb7153..2725937 100644
--- a/third_party/WebKit/Source/platform/scheduler/child/worker_global_scope_scheduler_unittest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/child/worker_global_scope_scheduler_unittest.cc
@@ -14,7 +14,7 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using testing::ElementsAreArray;
+using ::testing::ElementsAreArray;
 
 namespace blink {
 namespace scheduler {
@@ -28,7 +28,7 @@
 
 }  // namespace
 
-class WorkerGlobalScopeSchedulerTest : public testing::Test {
+class WorkerGlobalScopeSchedulerTest : public ::testing::Test {
  public:
   WorkerGlobalScopeSchedulerTest()
       : clock_(new base::SimpleTestTickClock()),
@@ -76,7 +76,7 @@
   RunUntilIdle();
   PostTestTask(&run_order, "T3");
   RunUntilIdle();
-  EXPECT_THAT(run_order, testing::ElementsAre("T1", "T2", "T3"));
+  EXPECT_THAT(run_order, ::testing::ElementsAre("T1", "T2", "T3"));
 
   // Tasks should not run after the scheduler is disposed of.
   global_scope_scheduler_->Dispose();
diff --git a/third_party/WebKit/Source/platform/scheduler/child/worker_scheduler_impl_unittest.cc b/third_party/WebKit/Source/platform/scheduler/child/worker_scheduler_impl_unittest.cc
index e95e377..67331cf5 100644
--- a/third_party/WebKit/Source/platform/scheduler/child/worker_scheduler_impl_unittest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/child/worker_scheduler_impl_unittest.cc
@@ -16,7 +16,7 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using testing::ElementsAreArray;
+using ::testing::ElementsAreArray;
 
 namespace blink {
 namespace scheduler {
@@ -90,7 +90,7 @@
   std::vector<std::string>* timeline_;  // NOT OWNED
 };
 
-class WorkerSchedulerImplTest : public testing::Test {
+class WorkerSchedulerImplTest : public ::testing::Test {
  public:
   WorkerSchedulerImplTest()
       : clock_(new base::SimpleTestTickClock()),
@@ -190,8 +190,8 @@
 
   RunUntilIdle();
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("D1"), std::string("D2"),
-                                   std::string("D3"), std::string("D4")));
+              ::testing::ElementsAre(std::string("D1"), std::string("D2"),
+                                     std::string("D3"), std::string("D4")));
 }
 
 TEST_F(WorkerSchedulerImplTest, TestPostIdleTask) {
@@ -201,7 +201,7 @@
   PostTestTasks(&run_order, "I1");
 
   RunUntilIdle();
-  EXPECT_THAT(run_order, testing::ElementsAre(std::string("I1")));
+  EXPECT_THAT(run_order, ::testing::ElementsAre(std::string("I1")));
 }
 
 TEST_F(WorkerSchedulerImplTest, TestPostDefaultAndIdleTasks) {
@@ -212,8 +212,8 @@
 
   RunUntilIdle();
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("D2"), std::string("D3"),
-                                   std::string("D4"), std::string("I1")));
+              ::testing::ElementsAre(std::string("D2"), std::string("D3"),
+                                     std::string("D4"), std::string("I1")));
 }
 
 TEST_F(WorkerSchedulerImplTest, TestPostDefaultDelayedAndIdleTasks) {
@@ -228,9 +228,9 @@
 
   RunUntilIdle();
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("D2"), std::string("D3"),
-                                   std::string("D4"), std::string("I1"),
-                                   std::string("DELAYED")));
+              ::testing::ElementsAre(std::string("D2"), std::string("D3"),
+                                     std::string("D4"), std::string("I1"),
+                                     std::string("DELAYED")));
 }
 
 TEST_F(WorkerSchedulerImplTest, TestIdleTaskWhenIsNotQuiescent) {
@@ -325,8 +325,8 @@
 
   RunUntilIdle();
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("D3"), std::string("I1"),
-                                   std::string("I2")));
+              ::testing::ElementsAre(std::string("D3"), std::string("I1"),
+                                     std::string("I2")));
 }
 
 void PostIdleTask(std::vector<std::string>* timeline,
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/auto_advancing_virtual_time_domain_unittest.cc b/third_party/WebKit/Source/platform/scheduler/renderer/auto_advancing_virtual_time_domain_unittest.cc
index 7351924..b6b847e7 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/auto_advancing_virtual_time_domain_unittest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/auto_advancing_virtual_time_domain_unittest.cc
@@ -17,7 +17,7 @@
 namespace blink {
 namespace scheduler {
 
-class AutoAdvancingVirtualTimeDomainTest : public testing::Test {
+class AutoAdvancingVirtualTimeDomainTest : public ::testing::Test {
  public:
   AutoAdvancingVirtualTimeDomainTest() {}
   ~AutoAdvancingVirtualTimeDomainTest() override {}
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/budget_pool_unittest.cc b/third_party/WebKit/Source/platform/scheduler/renderer/budget_pool_unittest.cc
index b80dc892..5e2c42b 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/budget_pool_unittest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/budget_pool_unittest.cc
@@ -26,7 +26,7 @@
 namespace blink {
 namespace scheduler {
 
-class BudgetPoolTest : public testing::Test {
+class BudgetPoolTest : public ::testing::Test {
  public:
   BudgetPoolTest() {}
   ~BudgetPoolTest() override {}
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/deadline_task_runner_unittest.cc b/third_party/WebKit/Source/platform/scheduler/renderer/deadline_task_runner_unittest.cc
index 635eb19..64c47bd 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/deadline_task_runner_unittest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/deadline_task_runner_unittest.cc
@@ -14,7 +14,7 @@
 namespace blink {
 namespace scheduler {
 
-class DeadlineTaskRunnerTest : public testing::Test {
+class DeadlineTaskRunnerTest : public ::testing::Test {
  public:
   DeadlineTaskRunnerTest() {}
   ~DeadlineTaskRunnerTest() override {}
@@ -45,7 +45,7 @@
   deadline_task_runner_->SetDeadline(FROM_HERE, delay, clock_->NowTicks());
   RunUntilIdle();
 
-  EXPECT_THAT(run_times_, testing::ElementsAre(start_time + delay));
+  EXPECT_THAT(run_times_, ::testing::ElementsAre(start_time + delay));
 };
 
 TEST_F(DeadlineTaskRunnerTest, RunTwice) {
@@ -59,7 +59,7 @@
   deadline_task_runner_->SetDeadline(FROM_HERE, delay2, clock_->NowTicks());
   RunUntilIdle();
 
-  EXPECT_THAT(run_times_, testing::ElementsAre(deadline1, deadline2));
+  EXPECT_THAT(run_times_, ::testing::ElementsAre(deadline1, deadline2));
 };
 
 TEST_F(DeadlineTaskRunnerTest, EarlierDeadlinesTakePrecidence) {
@@ -73,7 +73,7 @@
 
   RunUntilIdle();
 
-  EXPECT_THAT(run_times_, testing::ElementsAre(start_time + delay1));
+  EXPECT_THAT(run_times_, ::testing::ElementsAre(start_time + delay1));
 };
 
 TEST_F(DeadlineTaskRunnerTest, LaterDeadlinesIgnored) {
@@ -85,7 +85,7 @@
 
   RunUntilIdle();
 
-  EXPECT_THAT(run_times_, testing::ElementsAre(start_time + delay100));
+  EXPECT_THAT(run_times_, ::testing::ElementsAre(start_time + delay100));
 };
 
 TEST_F(DeadlineTaskRunnerTest, DeleteDeadlineTaskRunnerAfterPosting) {
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/idle_time_estimator_unittest.cc b/third_party/WebKit/Source/platform/scheduler/renderer/idle_time_estimator_unittest.cc
index 14e62b43..e770040 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/idle_time_estimator_unittest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/idle_time_estimator_unittest.cc
@@ -30,7 +30,7 @@
                           estimation_percentile) {}
 };
 
-class IdleTimeEstimatorTest : public testing::Test {
+class IdleTimeEstimatorTest : public ::testing::Test {
  public:
   IdleTimeEstimatorTest()
       : frame_length_(base::TimeDelta::FromMilliseconds(16)) {}
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/render_widget_signals_unittest.cc b/third_party/WebKit/Source/platform/scheduler/renderer/render_widget_signals_unittest.cc
index 0894948..a07f183 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/render_widget_signals_unittest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/render_widget_signals_unittest.cc
@@ -9,9 +9,9 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using testing::AnyNumber;
-using testing::Mock;
-using testing::_;
+using ::testing::AnyNumber;
+using ::testing::Mock;
+using ::testing::_;
 
 namespace blink {
 namespace scheduler {
@@ -31,7 +31,7 @@
 };
 }
 
-class RenderWidgetSignalsTest : public testing::Test {
+class RenderWidgetSignalsTest : public ::testing::Test {
  public:
   RenderWidgetSignalsTest() {}
   ~RenderWidgetSignalsTest() override {}
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl_unittest.cc b/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl_unittest.cc
index 9ac4a702..556ffba 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl_unittest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl_unittest.cc
@@ -256,7 +256,7 @@
   return os << RendererSchedulerImpl::UseCaseToString(use_case);
 }
 
-class RendererSchedulerImplTest : public testing::Test {
+class RendererSchedulerImplTest : public ::testing::Test {
  public:
   using UseCase = RendererSchedulerImpl::UseCase;
 
@@ -705,16 +705,16 @@
 
   RunUntilIdle();
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("D1"), std::string("D2"),
-                                   std::string("D3"), std::string("D4")));
+              ::testing::ElementsAre(std::string("D1"), std::string("D2"),
+                                     std::string("D3"), std::string("D4")));
 }
 
 TEST_F(RendererSchedulerImplTest, TestPostDefaultAndCompositor) {
   std::vector<std::string> run_order;
   PostTestTasks(&run_order, "D1 C1");
   RunUntilIdle();
-  EXPECT_THAT(run_order, testing::Contains("D1"));
-  EXPECT_THAT(run_order, testing::Contains("C1"));
+  EXPECT_THAT(run_order, ::testing::Contains("D1"));
+  EXPECT_THAT(run_order, ::testing::Contains("C1"));
 }
 
 TEST_F(RendererSchedulerImplTest, TestRentrantTask) {
@@ -726,7 +726,7 @@
                             &count, 5));
   RunUntilIdle();
 
-  EXPECT_THAT(run_order, testing::ElementsAre(0, 1, 2, 3, 4));
+  EXPECT_THAT(run_order, ::testing::ElementsAre(0, 1, 2, 3, 4));
 }
 
 TEST_F(RendererSchedulerImplTest, TestPostIdleTask) {
@@ -854,9 +854,9 @@
   EnableIdleTasks();
   RunUntilIdle();
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("C1"), std::string("C2"),
-                                   std::string("L1"), std::string("D1"),
-                                   std::string("D2"), std::string("I1")));
+              ::testing::ElementsAre(std::string("C1"), std::string("C2"),
+                                     std::string("L1"), std::string("D1"),
+                                     std::string("D2"), std::string("I1")));
   EXPECT_EQ(RendererSchedulerImpl::UseCase::NONE, CurrentUseCase());
 }
 
@@ -869,9 +869,9 @@
   EnableIdleTasks();
   RunUntilIdle();
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("L1"), std::string("D1"),
-                                   std::string("C1"), std::string("D2"),
-                                   std::string("C2"), std::string("I1")));
+              ::testing::ElementsAre(std::string("L1"), std::string("D1"),
+                                     std::string("C1"), std::string("D2"),
+                                     std::string("C2"), std::string("I1")));
   EXPECT_EQ(RendererSchedulerImpl::UseCase::NONE, CurrentUseCase());
 }
 
@@ -885,9 +885,9 @@
   SimulateCompositorGestureStart(TouchEventPolicy::SEND_TOUCH_START);
   RunUntilIdle();
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("L1"), std::string("D1"),
-                                   std::string("D2"), std::string("C1"),
-                                   std::string("C2"), std::string("I1")));
+              ::testing::ElementsAre(std::string("L1"), std::string("D1"),
+                                     std::string("D2"), std::string("C1"),
+                                     std::string("C2"), std::string("I1")));
   EXPECT_EQ(RendererSchedulerImpl::UseCase::COMPOSITOR_GESTURE,
             CurrentUseCase());
 }
@@ -902,9 +902,9 @@
   SimulateMainThreadGestureWithoutScrollUpdates();
   RunUntilIdle();
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("C1"), std::string("C2"),
-                                   std::string("L1"), std::string("D1"),
-                                   std::string("D2"), std::string("I1")));
+              ::testing::ElementsAre(std::string("C1"), std::string("C2"),
+                                     std::string("L1"), std::string("D1"),
+                                     std::string("D2"), std::string("I1")));
   EXPECT_EQ(RendererSchedulerImpl::UseCase::MAIN_THREAD_CUSTOM_INPUT_HANDLING,
             CurrentUseCase());
 }
@@ -919,9 +919,9 @@
   SimulateMainThreadGestureWithoutPreventDefault();
   RunUntilIdle();
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("L1"), std::string("D1"),
-                                   std::string("D2"), std::string("C1"),
-                                   std::string("C2"), std::string("I1")));
+              ::testing::ElementsAre(std::string("L1"), std::string("D1"),
+                                     std::string("D2"), std::string("C1"),
+                                     std::string("C2"), std::string("I1")));
   EXPECT_EQ(RendererSchedulerImpl::UseCase::COMPOSITOR_GESTURE,
             CurrentUseCase());
 }
@@ -952,9 +952,9 @@
 
   RunUntilIdle();
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("C1"), std::string("C2"),
-                                   std::string("L1"), std::string("D1"),
-                                   std::string("D2")));
+              ::testing::ElementsAre(std::string("C1"), std::string("C2"),
+                                     std::string("L1"), std::string("D1"),
+                                     std::string("D2")));
   EXPECT_EQ(RendererSchedulerImpl::UseCase::COMPOSITOR_GESTURE,
             CurrentUseCase());
 }
@@ -968,9 +968,9 @@
   SimulateCompositorGestureStart(TouchEventPolicy::DONT_SEND_TOUCH_START);
   RunUntilIdle();
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("L1"), std::string("D1"),
-                                   std::string("D2"), std::string("C1"),
-                                   std::string("C2"), std::string("I1")));
+              ::testing::ElementsAre(std::string("L1"), std::string("D1"),
+                                     std::string("D2"), std::string("C1"),
+                                     std::string("C2"), std::string("I1")));
   EXPECT_EQ(RendererSchedulerImpl::UseCase::COMPOSITOR_GESTURE,
             CurrentUseCase());
 }
@@ -986,9 +986,9 @@
                                  blink::WebInputEvent::kGestureScrollBegin);
   RunUntilIdle();
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("C1"), std::string("C2"),
-                                   std::string("L1"), std::string("D1"),
-                                   std::string("D2"), std::string("I1")));
+              ::testing::ElementsAre(std::string("C1"), std::string("C2"),
+                                     std::string("L1"), std::string("D1"),
+                                     std::string("D2"), std::string("I1")));
   EXPECT_EQ(RendererSchedulerImpl::UseCase::MAIN_THREAD_CUSTOM_INPUT_HANDLING,
             CurrentUseCase());
   scheduler_->DidHandleInputEventOnMainThread(
@@ -1006,9 +1006,9 @@
                                  blink::WebInputEvent::kGestureScrollBegin);
   RunUntilIdle();
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("C1"), std::string("C2"),
-                                   std::string("L1"), std::string("D1"),
-                                   std::string("D2"), std::string("I1")));
+              ::testing::ElementsAre(std::string("C1"), std::string("C2"),
+                                     std::string("L1"), std::string("D1"),
+                                     std::string("D2"), std::string("I1")));
   EXPECT_EQ(RendererSchedulerImpl::UseCase::MAIN_THREAD_CUSTOM_INPUT_HANDLING,
             CurrentUseCase());
   scheduler_->DidHandleInputEventOnMainThread(
@@ -1033,9 +1033,9 @@
   // Because the main thread is performing custom input handling, we let all
   // tasks run. However compositing tasks are still given priority.
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("C1"), std::string("C2"),
-                                   std::string("L1"), std::string("D1"),
-                                   std::string("D2"), std::string("I1")));
+              ::testing::ElementsAre(std::string("C1"), std::string("C2"),
+                                     std::string("L1"), std::string("D1"),
+                                     std::string("D2"), std::string("I1")));
   EXPECT_EQ(RendererSchedulerImpl::UseCase::MAIN_THREAD_CUSTOM_INPUT_HANDLING,
             CurrentUseCase());
 }
@@ -1058,9 +1058,9 @@
   // Because we are still waiting for the touchstart to be processed,
   // non-essential tasks like loading tasks are blocked.
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("C1"), std::string("C2"),
-                                   std::string("D1"), std::string("D2"),
-                                   std::string("I1")));
+              ::testing::ElementsAre(std::string("C1"), std::string("C2"),
+                                     std::string("D1"), std::string("D2"),
+                                     std::string("I1")));
   EXPECT_EQ(RendererSchedulerImpl::UseCase::TOUCHSTART, CurrentUseCase());
 }
 
@@ -1077,9 +1077,9 @@
   EnableIdleTasks();
   RunUntilIdle();
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("D1"), std::string("D2"),
-                                   std::string("C1"), std::string("C2"),
-                                   std::string("I1")));
+              ::testing::ElementsAre(std::string("D1"), std::string("D2"),
+                                     std::string("C1"), std::string("C2"),
+                                     std::string("I1")));
   EXPECT_EQ(RendererSchedulerImpl::UseCase::COMPOSITOR_GESTURE,
             CurrentUseCase());
 }
@@ -1101,7 +1101,7 @@
   RunUntilIdle();
 
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("C1"), std::string("T1")));
+              ::testing::ElementsAre(std::string("C1"), std::string("T1")));
 }
 
 TEST_F(RendererSchedulerImplTest,
@@ -1121,7 +1121,7 @@
   EXPECT_EQ(RendererSchedulerImpl::UseCase::MAIN_THREAD_GESTURE,
             CurrentUseCase());
 
-  EXPECT_THAT(run_order, testing::ElementsAre(std::string("C1")));
+  EXPECT_THAT(run_order, ::testing::ElementsAre(std::string("C1")));
 }
 
 TEST_F(RendererSchedulerImplTest,
@@ -1142,7 +1142,7 @@
             CurrentUseCase());
 
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("C1"), std::string("T1")));
+              ::testing::ElementsAre(std::string("C1"), std::string("T1")));
 }
 
 TEST_F(RendererSchedulerImplTest,
@@ -1163,7 +1163,7 @@
             CurrentUseCase());
 
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("C1"), std::string("T1")));
+              ::testing::ElementsAre(std::string("C1"), std::string("T1")));
 }
 
 TEST_F(RendererSchedulerImplTest, TestTouchstartPolicy_Compositor) {
@@ -1178,8 +1178,8 @@
   EnableIdleTasks();
   RunUntilIdle();
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("C1"), std::string("C2"),
-                                   std::string("D1"), std::string("D2")));
+              ::testing::ElementsAre(std::string("C1"), std::string("C2"),
+                                     std::string("D1"), std::string("D2")));
 
   // Animation or meta events like TapDown/FlingCancel shouldn't affect the
   // priority.
@@ -1192,7 +1192,7 @@
       FakeInputEvent(blink::WebInputEvent::kGestureTapDown),
       RendererScheduler::InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
   RunUntilIdle();
-  EXPECT_THAT(run_order, testing::ElementsAre());
+  EXPECT_THAT(run_order, ::testing::ElementsAre());
 
   // Action events like ScrollBegin will kick us back into compositor priority,
   // allowing service of the timer, loading and idle queues.
@@ -1203,8 +1203,8 @@
   RunUntilIdle();
 
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("L1"), std::string("T1"),
-                                   std::string("T2")));
+              ::testing::ElementsAre(std::string("L1"), std::string("T1"),
+                                     std::string("T2")));
 }
 
 TEST_F(RendererSchedulerImplTest, TestTouchstartPolicy_MainThread) {
@@ -1222,8 +1222,8 @@
   EnableIdleTasks();
   RunUntilIdle();
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("C1"), std::string("C2"),
-                                   std::string("D1"), std::string("D2")));
+              ::testing::ElementsAre(std::string("C1"), std::string("C2"),
+                                     std::string("D1"), std::string("D2")));
 
   // Meta events like TapDown/FlingCancel shouldn't affect the priority.
   run_order.clear();
@@ -1240,7 +1240,7 @@
       FakeInputEvent(blink::WebInputEvent::kGestureTapDown),
       WebInputEventResult::kHandledSystem);
   RunUntilIdle();
-  EXPECT_THAT(run_order, testing::ElementsAre());
+  EXPECT_THAT(run_order, ::testing::ElementsAre());
 
   // Action events like ScrollBegin will kick us back into compositor priority,
   // allowing service of the timer, loading and idle queues.
@@ -1254,8 +1254,8 @@
   RunUntilIdle();
 
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("L1"), std::string("T1"),
-                                   std::string("T2")));
+              ::testing::ElementsAre(std::string("L1"), std::string("T1"),
+                                     std::string("T2")));
 }
 
 // TODO(alexclarke): Reenable once we've reinstaed the Loading UseCase.
@@ -1272,7 +1272,7 @@
       std::string("D1"), std::string("L1"), std::string("D2"),
       std::string("L2"), std::string("C1"), std::string("T1"),
       std::string("C2"), std::string("T2"), std::string("I1")};
-  EXPECT_THAT(run_order, testing::ElementsAreArray(loading_policy_expected));
+  EXPECT_THAT(run_order, ::testing::ElementsAreArray(loading_policy_expected));
   EXPECT_EQ(RendererSchedulerImpl::UseCase::LOADING, CurrentUseCase());
 
   // Advance 15s and try again, the loading policy should have ended and the
@@ -1288,7 +1288,7 @@
       std::string("D1"), std::string("C1"), std::string("T1"),
       std::string("L1"), std::string("D2"), std::string("C2"),
       std::string("T2"), std::string("L2"), std::string("I1")};
-  EXPECT_THAT(run_order, testing::ElementsAreArray(default_order_expected));
+  EXPECT_THAT(run_order, ::testing::ElementsAreArray(default_order_expected));
   EXPECT_EQ(RendererSchedulerImpl::UseCase::NONE, CurrentUseCase());
 }
 
@@ -1307,9 +1307,9 @@
   // Note compositor tasks are not prioritized.
   EXPECT_EQ(RendererSchedulerImpl::UseCase::NONE, CurrentUseCase());
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("D1"), std::string("C1"),
-                                   std::string("D2"), std::string("C2"),
-                                   std::string("I1")));
+              ::testing::ElementsAre(std::string("D1"), std::string("C1"),
+                                     std::string("D2"), std::string("C2"),
+                                     std::string("I1")));
 }
 
 TEST_F(RendererSchedulerImplTest,
@@ -1327,9 +1327,9 @@
   // Note compositor tasks are not prioritized.
   EXPECT_EQ(RendererSchedulerImpl::UseCase::NONE, CurrentUseCase());
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("D1"), std::string("C1"),
-                                   std::string("D2"), std::string("C2"),
-                                   std::string("I1")));
+              ::testing::ElementsAre(std::string("D1"), std::string("C1"),
+                                     std::string("D2"), std::string("C2"),
+                                     std::string("I1")));
 }
 
 TEST_F(RendererSchedulerImplTest,
@@ -1349,9 +1349,9 @@
   EXPECT_EQ(RendererSchedulerImpl::UseCase::COMPOSITOR_GESTURE,
             CurrentUseCase());
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("D1"), std::string("D2"),
-                                   std::string("C1"), std::string("C2"),
-                                   std::string("I1")));
+              ::testing::ElementsAre(std::string("D1"), std::string("D2"),
+                                     std::string("C1"), std::string("C2"),
+                                     std::string("I1")));
 }
 
 TEST_F(RendererSchedulerImplTest,
@@ -1367,9 +1367,9 @@
   RunUntilIdle();
   // Note compositor tasks are prioritized.
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("C1"), std::string("C2"),
-                                   std::string("D1"), std::string("D2"),
-                                   std::string("I1")));
+              ::testing::ElementsAre(std::string("C1"), std::string("C2"),
+                                     std::string("D1"), std::string("D2"),
+                                     std::string("I1")));
   scheduler_->DidHandleInputEventOnMainThread(
       FakeInputEvent(blink::WebInputEvent::kMouseMove,
                      blink::WebInputEvent::kLeftButtonDown),
@@ -1407,9 +1407,9 @@
 
   // Note compositor tasks are prioritized.
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("C1"), std::string("C2"),
-                                   std::string("D1"), std::string("D2"),
-                                   std::string("I1")));
+              ::testing::ElementsAre(std::string("C1"), std::string("C2"),
+                                     std::string("D1"), std::string("D2"),
+                                     std::string("I1")));
 }
 
 TEST_F(RendererSchedulerImplTest,
@@ -1436,9 +1436,9 @@
 
   // Note compositor tasks are prioritized.
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("C1"), std::string("C2"),
-                                   std::string("D1"), std::string("D2"),
-                                   std::string("I1")));
+              ::testing::ElementsAre(std::string("C1"), std::string("C2"),
+                                     std::string("D1"), std::string("D2"),
+                                     std::string("I1")));
 }
 
 TEST_F(RendererSchedulerImplTest, EventConsumedOnCompositorThread_MouseWheel) {
@@ -1452,9 +1452,9 @@
   RunUntilIdle();
   // Note compositor tasks are not prioritized.
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("D1"), std::string("D2"),
-                                   std::string("C1"), std::string("C2"),
-                                   std::string("I1")));
+              ::testing::ElementsAre(std::string("D1"), std::string("D2"),
+                                     std::string("C1"), std::string("C2"),
+                                     std::string("I1")));
   EXPECT_EQ(RendererSchedulerImpl::UseCase::COMPOSITOR_GESTURE,
             CurrentUseCase());
 }
@@ -1471,9 +1471,9 @@
   RunUntilIdle();
   // Note compositor tasks are prioritized (since they are fast).
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("C1"), std::string("C2"),
-                                   std::string("D1"), std::string("D2"),
-                                   std::string("I1")));
+              ::testing::ElementsAre(std::string("C1"), std::string("C2"),
+                                     std::string("D1"), std::string("D2"),
+                                     std::string("I1")));
   EXPECT_EQ(RendererSchedulerImpl::UseCase::MAIN_THREAD_CUSTOM_INPUT_HANDLING,
             CurrentUseCase());
 }
@@ -1498,9 +1498,9 @@
   RunUntilIdle();
   // Note compositor tasks are prioritized.
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("C1"), std::string("C2"),
-                                   std::string("D1"), std::string("D2"),
-                                   std::string("I1")));
+              ::testing::ElementsAre(std::string("C1"), std::string("C2"),
+                                     std::string("D1"), std::string("D2"),
+                                     std::string("I1")));
   EXPECT_EQ(RendererSchedulerImpl::UseCase::MAIN_THREAD_GESTURE,
             CurrentUseCase());
 }
@@ -1527,9 +1527,9 @@
   RunUntilIdle();
   // Note compositor tasks are not prioritized.
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("D1"), std::string("D2"),
-                                   std::string("C1"), std::string("C2"),
-                                   std::string("I1")));
+              ::testing::ElementsAre(std::string("D1"), std::string("D2"),
+                                     std::string("C1"), std::string("C2"),
+                                     std::string("I1")));
   EXPECT_EQ(RendererSchedulerImpl::UseCase::COMPOSITOR_GESTURE,
             CurrentUseCase());
 }
@@ -1548,9 +1548,9 @@
   RunUntilIdle();
   // Note compositor tasks are not prioritized.
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("D1"), std::string("C1"),
-                                   std::string("D2"), std::string("C2"),
-                                   std::string("I1")));
+              ::testing::ElementsAre(std::string("D1"), std::string("C1"),
+                                     std::string("D2"), std::string("C2"),
+                                     std::string("I1")));
   EXPECT_EQ(RendererSchedulerImpl::UseCase::NONE, CurrentUseCase());
 }
 
@@ -1568,9 +1568,9 @@
   RunUntilIdle();
   // Note compositor tasks are not prioritized.
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("D1"), std::string("C1"),
-                                   std::string("D2"), std::string("C2"),
-                                   std::string("I1")));
+              ::testing::ElementsAre(std::string("D1"), std::string("C1"),
+                                     std::string("D2"), std::string("C2"),
+                                     std::string("I1")));
   EXPECT_EQ(RendererSchedulerImpl::UseCase::NONE, CurrentUseCase());
   // Note compositor tasks are not prioritized.
   scheduler_->DidHandleInputEventOnMainThread(
@@ -1599,8 +1599,8 @@
   // Ensure that the default D1 task gets to run at some point before the final
   // C2 compositor task.
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("C1"), std::string("D1"),
-                                   std::string("C2")));
+              ::testing::ElementsAre(std::string("C1"), std::string("D1"),
+                                     std::string("C2")));
 }
 
 TEST_F(RendererSchedulerImplTest,
@@ -1633,8 +1633,8 @@
       RendererScheduler::InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
   RunUntilIdle();
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("C1"), std::string("C2"),
-                                   std::string("D1"), std::string("D2")));
+              ::testing::ElementsAre(std::string("C1"), std::string("C2"),
+                                     std::string("D1"), std::string("D2")));
 
   run_order.clear();
   clock_->Advance(base::TimeDelta::FromMilliseconds(1000));
@@ -1646,8 +1646,8 @@
   // Touchstart policy mode should have ended now that the clock has advanced.
   RunUntilIdle();
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("L1"), std::string("D1"),
-                                   std::string("D2")));
+              ::testing::ElementsAre(std::string("L1"), std::string("D1"),
+                                     std::string("D2")));
 }
 
 TEST_F(RendererSchedulerImplTest,
@@ -1661,8 +1661,8 @@
       RendererScheduler::InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
   RunUntilIdle();
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("C1"), std::string("C2"),
-                                   std::string("D1"), std::string("D2")));
+              ::testing::ElementsAre(std::string("C1"), std::string("C2"),
+                                     std::string("D1"), std::string("D2")));
 
   // Receiving the first touchmove will not affect scheduler priority.
   run_order.clear();
@@ -1670,7 +1670,7 @@
       FakeInputEvent(blink::WebInputEvent::kTouchMove),
       RendererScheduler::InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
   RunUntilIdle();
-  EXPECT_THAT(run_order, testing::ElementsAre());
+  EXPECT_THAT(run_order, ::testing::ElementsAre());
 
   // Receiving the second touchmove will kick us back into compositor priority.
   run_order.clear();
@@ -1678,7 +1678,7 @@
       FakeInputEvent(blink::WebInputEvent::kTouchMove),
       RendererScheduler::InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
   RunUntilIdle();
-  EXPECT_THAT(run_order, testing::ElementsAre(std::string("L1")));
+  EXPECT_THAT(run_order, ::testing::ElementsAre(std::string("L1")));
 }
 
 TEST_F(RendererSchedulerImplTest, TestIsHighPriorityWorkAnticipated) {
@@ -2044,7 +2044,7 @@
   RunUntilIdle();
   EXPECT_THAT(
       mock_scheduler_->use_cases_,
-      testing::ElementsAre(
+      ::testing::ElementsAre(
           std::string("none"), std::string("compositor_gesture"),
           std::string("compositor_gesture touchstart expected"),
           std::string("none touchstart expected"), std::string("none")));
@@ -2106,9 +2106,9 @@
   EnableIdleTasks();
   RunUntilIdle();
   // Note we expect task 3 to run last because it's non-nestable.
-  EXPECT_THAT(order, testing::ElementsAre(std::string("1"), std::string("2"),
-                                          std::string("4"), std::string("5"),
-                                          std::string("3")));
+  EXPECT_THAT(order, ::testing::ElementsAre(std::string("1"), std::string("2"),
+                                            std::string("4"), std::string("5"),
+                                            std::string("3")));
 }
 
 TEST_F(RendererSchedulerImplTest, TestBeginMainFrameNotExpectedUntil) {
@@ -2213,7 +2213,7 @@
   EXPECT_EQ(3, run_count);
   EXPECT_THAT(
       actual_deadlines,
-      testing::ElementsAre(
+      ::testing::ElementsAre(
           clock_before + maximum_idle_period_duration(),
           clock_before + idle_task_runtime + maximum_idle_period_duration(),
           clock_before + (2 * idle_task_runtime) +
@@ -2351,7 +2351,7 @@
   PostTestTasks(&run_order, "T1 T2");
   RunUntilIdle();
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("T1"), std::string("T2")));
+              ::testing::ElementsAre(std::string("T1"), std::string("T2")));
 }
 
 TEST_F(RendererSchedulerImplTest, SuspendAndResumeTimerQueue) {
@@ -2360,12 +2360,12 @@
 
   scheduler_->SuspendTimerQueue();
   RunUntilIdle();
-  EXPECT_THAT(run_order, testing::ElementsAre());
+  EXPECT_THAT(run_order, ::testing::ElementsAre());
 
   scheduler_->ResumeTimerQueue();
   RunUntilIdle();
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("T1"), std::string("T2")));
+              ::testing::ElementsAre(std::string("T1"), std::string("T2")));
 }
 
 TEST_F(RendererSchedulerImplTest, SuspendAndThrottleTimerQueue) {
@@ -2377,7 +2377,7 @@
   scheduler_->task_queue_throttler()->IncreaseThrottleRefCount(
       static_cast<TaskQueue*>(timer_task_runner_.get()));
   RunUntilIdle();
-  EXPECT_THAT(run_order, testing::ElementsAre());
+  EXPECT_THAT(run_order, ::testing::ElementsAre());
 }
 
 TEST_F(RendererSchedulerImplTest, ThrottleAndSuspendTimerQueue) {
@@ -2389,7 +2389,7 @@
   RunUntilIdle();
   scheduler_->SuspendTimerQueue();
   RunUntilIdle();
-  EXPECT_THAT(run_order, testing::ElementsAre());
+  EXPECT_THAT(run_order, ::testing::ElementsAre());
 }
 
 TEST_F(RendererSchedulerImplTest, MultipleSuspendsNeedMultipleResumes) {
@@ -2400,20 +2400,20 @@
   scheduler_->SuspendTimerQueue();
   scheduler_->SuspendTimerQueue();
   RunUntilIdle();
-  EXPECT_THAT(run_order, testing::ElementsAre());
+  EXPECT_THAT(run_order, ::testing::ElementsAre());
 
   scheduler_->ResumeTimerQueue();
   RunUntilIdle();
-  EXPECT_THAT(run_order, testing::ElementsAre());
+  EXPECT_THAT(run_order, ::testing::ElementsAre());
 
   scheduler_->ResumeTimerQueue();
   RunUntilIdle();
-  EXPECT_THAT(run_order, testing::ElementsAre());
+  EXPECT_THAT(run_order, ::testing::ElementsAre());
 
   scheduler_->ResumeTimerQueue();
   RunUntilIdle();
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("T1"), std::string("T2")));
+              ::testing::ElementsAre(std::string("T1"), std::string("T2")));
 }
 
 TEST_F(RendererSchedulerImplTest, SuspendRenderer) {
@@ -2427,15 +2427,15 @@
   EnableIdleTasks();
   RunUntilIdle();
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("C1"), std::string("D1"),
-                                   std::string("I1")));
+              ::testing::ElementsAre(std::string("C1"), std::string("D1"),
+                                     std::string("I1")));
 
   // The rest queued tasks fire when the tab goes foregrounded.
   run_order.clear();
   scheduler_->SetRendererBackgrounded(false);
   RunUntilIdle();
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("L1"), std::string("T1")));
+              ::testing::ElementsAre(std::string("L1"), std::string("T1")));
 
   run_order.clear();
   PostTestTasks(&run_order, "D2 T2");
@@ -2443,7 +2443,7 @@
   scheduler_->SuspendRenderer();
   RunUntilIdle();
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("D2"), std::string("T2")));
+              ::testing::ElementsAre(std::string("D2"), std::string("T2")));
 }
 
 TEST_F(RendererSchedulerImplTest, ResumeRenderer) {
@@ -2459,15 +2459,15 @@
   EnableIdleTasks();
   RunUntilIdle();
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("C1"), std::string("D1"),
-                                   std::string("I1")));
+              ::testing::ElementsAre(std::string("C1"), std::string("D1"),
+                                     std::string("I1")));
 
   // The rest queued tasks fire when the renderer is resumed.
   run_order.clear();
   scheduler_->ResumeRenderer();
   RunUntilIdle();
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("L1"), std::string("T1")));
+              ::testing::ElementsAre(std::string("L1"), std::string("T1")));
 
   run_order.clear();
   // No crash occurs when the renderer is suspended again, and
@@ -2477,15 +2477,15 @@
   EnableIdleTasks();
   RunUntilIdle();
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("C2"), std::string("D2"),
-                                   std::string("I2")));
+              ::testing::ElementsAre(std::string("C2"), std::string("D2"),
+                                     std::string("I2")));
 
   // The rest queued tasks fire when the renderer is resumed.
   run_order.clear();
   scheduler_->ResumeRenderer();
   RunUntilIdle();
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("L2"), std::string("T2")));
+              ::testing::ElementsAre(std::string("L2"), std::string("T2")));
 
   run_order.clear();
   PostTestTasks(&run_order, "D3 T3");
@@ -2494,7 +2494,7 @@
   scheduler_->SetRendererBackgrounded(false);
   RunUntilIdle();
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("D3"), std::string("T3")));
+              ::testing::ElementsAre(std::string("D3"), std::string("T3")));
 
   run_order.clear();
   PostTestTasks(&run_order, "D4 T4");
@@ -2502,7 +2502,7 @@
   scheduler_->ResumeRenderer();
   RunUntilIdle();
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("D4"), std::string("T4")));
+              ::testing::ElementsAre(std::string("D4"), std::string("T4")));
 }
 
 TEST_F(RendererSchedulerImplTest, UseCaseToString) {
@@ -2539,7 +2539,7 @@
   std::vector<std::string> run_order;
   PostTestTasks(&run_order, "D1 C1");
   RunUntilIdle();
-  EXPECT_THAT(run_order, testing::ElementsAre());
+  EXPECT_THAT(run_order, ::testing::ElementsAre());
 }
 
 TEST_F(RendererSchedulerImplTest, TestRendererBackgroundedTimerSuspension) {
@@ -2556,7 +2556,7 @@
   clock_->SetNowTicks(now);
   RunUntilIdle();
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("T1"), std::string("T2")));
+              ::testing::ElementsAre(std::string("T1"), std::string("T2")));
 
   run_order.clear();
   PostTestTasks(&run_order, "T3");
@@ -2564,7 +2564,7 @@
   now += base::TimeDelta::FromSeconds(1);
   clock_->SetNowTicks(now);
   RunUntilIdle();
-  EXPECT_THAT(run_order, testing::ElementsAre(std::string("T3")));
+  EXPECT_THAT(run_order, ::testing::ElementsAre(std::string("T3")));
 
   // Advance the time until after the scheduled timer queue suspension.
   now = base::TimeTicks() + suspend_timers_when_backgrounded_delay() +
@@ -2579,18 +2579,18 @@
   now += base::TimeDelta::FromSeconds(10);
   clock_->SetNowTicks(now);
   RunUntilIdle();
-  EXPECT_THAT(run_order, testing::ElementsAre());
+  EXPECT_THAT(run_order, ::testing::ElementsAre());
 
   scheduler_->SetRendererBackgrounded(false);
   RunUntilIdle();
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("T4"), std::string("T5")));
+              ::testing::ElementsAre(std::string("T4"), std::string("T5")));
 
   // Subsequent timer tasks should fire as usual.
   run_order.clear();
   PostTestTasks(&run_order, "T6");
   RunUntilIdle();
-  EXPECT_THAT(run_order, testing::ElementsAre(std::string("T6")));
+  EXPECT_THAT(run_order, ::testing::ElementsAre(std::string("T6")));
 }
 
 TEST_F(RendererSchedulerImplTest,
@@ -2609,7 +2609,7 @@
   EXPECT_FALSE(TimerTasksSeemExpensive());
   EXPECT_TRUE(TouchStartExpectedSoon());
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("L1"), std::string("D1")));
+              ::testing::ElementsAre(std::string("L1"), std::string("D1")));
 
   // Emit a BeginMainFrame, and the loading task should get blocked.
   DoMainFrame();
@@ -2623,7 +2623,7 @@
   EXPECT_TRUE(LoadingTasksSeemExpensive());
   EXPECT_FALSE(TimerTasksSeemExpensive());
   EXPECT_TRUE(TouchStartExpectedSoon());
-  EXPECT_THAT(run_order, testing::ElementsAre(std::string("D1")));
+  EXPECT_THAT(run_order, ::testing::ElementsAre(std::string("D1")));
   EXPECT_EQ(v8::PERFORMANCE_RESPONSE, GetRAILMode());
 }
 
@@ -2644,7 +2644,7 @@
   EXPECT_FALSE(TimerTasksSeemExpensive());
   EXPECT_FALSE(TouchStartExpectedSoon());
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("L1"), std::string("D1")));
+              ::testing::ElementsAre(std::string("L1"), std::string("D1")));
   EXPECT_EQ(v8::PERFORMANCE_ANIMATION, GetRAILMode());
 }
 
@@ -2665,7 +2665,7 @@
   EXPECT_FALSE(LoadingTasksSeemExpensive());
   EXPECT_TRUE(TimerTasksSeemExpensive());
   EXPECT_TRUE(TouchStartExpectedSoon());
-  EXPECT_THAT(run_order, testing::ElementsAre(std::string("D1")));
+  EXPECT_THAT(run_order, ::testing::ElementsAre(std::string("D1")));
   EXPECT_EQ(v8::PERFORMANCE_RESPONSE, GetRAILMode());
 }
 
@@ -2701,7 +2701,7 @@
   EXPECT_TRUE(TimerTasksSeemExpensive());
   EXPECT_TRUE(TouchStartExpectedSoon());
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("T1"), std::string("D1")));
+              ::testing::ElementsAre(std::string("T1"), std::string("D1")));
   EXPECT_EQ(v8::PERFORMANCE_ANIMATION, GetRAILMode());
 }
 
@@ -2724,7 +2724,7 @@
   EXPECT_FALSE(LoadingTasksSeemExpensive());
   EXPECT_TRUE(TimerTasksSeemExpensive());
   EXPECT_TRUE(TouchStartExpectedSoon());
-  EXPECT_THAT(run_order, testing::ElementsAre(std::string("D1")));
+  EXPECT_THAT(run_order, ::testing::ElementsAre(std::string("D1")));
   EXPECT_EQ(v8::PERFORMANCE_RESPONSE, GetRAILMode());
 }
 
@@ -2746,7 +2746,7 @@
   EXPECT_FALSE(LoadingTasksSeemExpensive());
   EXPECT_TRUE(TimerTasksSeemExpensive());
   EXPECT_TRUE(TouchStartExpectedSoon());
-  EXPECT_THAT(run_order, testing::ElementsAre(std::string("D1")));
+  EXPECT_THAT(run_order, ::testing::ElementsAre(std::string("D1")));
   EXPECT_EQ(v8::PERFORMANCE_RESPONSE, GetRAILMode());
 }
 
@@ -2765,7 +2765,7 @@
   RunUntilIdle();
 
   // The expensive loading task gets blocked.
-  EXPECT_THAT(run_order, testing::ElementsAre(std::string("D1")));
+  EXPECT_THAT(run_order, ::testing::ElementsAre(std::string("D1")));
   EXPECT_EQ(v8::PERFORMANCE_RESPONSE, GetRAILMode());
 }
 
@@ -2790,7 +2790,7 @@
   EXPECT_TRUE(TouchStartExpectedSoon());
   EXPECT_EQ(1, NavigationTaskExpectedCount());
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("L1"), std::string("D1")));
+              ::testing::ElementsAre(std::string("L1"), std::string("D1")));
 
   // After the nagigation has been cancelled, the expensive loading tasks should
   // get blocked.
@@ -2807,7 +2807,7 @@
   EXPECT_FALSE(TimerTasksSeemExpensive());
   EXPECT_TRUE(TouchStartExpectedSoon());
   EXPECT_EQ(0, NavigationTaskExpectedCount());
-  EXPECT_THAT(run_order, testing::ElementsAre(std::string("D1")));
+  EXPECT_THAT(run_order, ::testing::ElementsAre(std::string("D1")));
   EXPECT_EQ(v8::PERFORMANCE_RESPONSE, GetRAILMode());
 }
 
@@ -2834,7 +2834,7 @@
   EXPECT_TRUE(TouchStartExpectedSoon());
   EXPECT_EQ(2, NavigationTaskExpectedCount());
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("L1"), std::string("D1")));
+              ::testing::ElementsAre(std::string("L1"), std::string("D1")));
 
   run_order.clear();
   scheduler_->RemovePendingNavigation(
@@ -2851,7 +2851,7 @@
   EXPECT_TRUE(TouchStartExpectedSoon());
   EXPECT_EQ(1, NavigationTaskExpectedCount());
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("L1"), std::string("D1")));
+              ::testing::ElementsAre(std::string("L1"), std::string("D1")));
 
   run_order.clear();
   scheduler_->RemovePendingNavigation(
@@ -2867,7 +2867,7 @@
   EXPECT_FALSE(TimerTasksSeemExpensive());
   EXPECT_TRUE(TouchStartExpectedSoon());
   EXPECT_EQ(0, NavigationTaskExpectedCount());
-  EXPECT_THAT(run_order, testing::ElementsAre(std::string("D1")));
+  EXPECT_THAT(run_order, ::testing::ElementsAre(std::string("D1")));
   EXPECT_EQ(v8::PERFORMANCE_RESPONSE, GetRAILMode());
 }
 
@@ -2890,7 +2890,7 @@
   EXPECT_TRUE(LoadingTasksSeemExpensive());
   EXPECT_FALSE(TimerTasksSeemExpensive());
   EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("C1"), std::string("L1")));
+              ::testing::ElementsAre(std::string("C1"), std::string("L1")));
   EXPECT_EQ(v8::PERFORMANCE_ANIMATION, GetRAILMode());
 }
 
@@ -3864,8 +3864,9 @@
   mock_task_runner_->RunUntilTime(base::TimeTicks() +
                                   base::TimeDelta::FromMilliseconds(1100));
 
-  EXPECT_THAT(run_times, testing::ElementsAre(base::TimeTicks() +
-                                              base::TimeDelta::FromSeconds(1)));
+  EXPECT_THAT(run_times,
+              ::testing::ElementsAre(base::TimeTicks() +
+                                     base::TimeDelta::FromSeconds(1)));
   run_times.clear();
 
   timer_task_runner_->PostDelayedTask(
@@ -3878,8 +3879,8 @@
                                   base::TimeDelta::FromMilliseconds(1500));
 
   EXPECT_THAT(run_times,
-              testing::ElementsAre(base::TimeTicks() +
-                                   base::TimeDelta::FromMilliseconds(1300)));
+              ::testing::ElementsAre(base::TimeTicks() +
+                                     base::TimeDelta::FromMilliseconds(1300)));
 }
 
 TEST_F(RendererSchedulerImplTest, UnresponsiveMainThread) {
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/task_cost_estimator_unittest.cc b/third_party/WebKit/Source/platform/scheduler/renderer/task_cost_estimator_unittest.cc
index 478fc98d..bf32311 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/task_cost_estimator_unittest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/task_cost_estimator_unittest.cc
@@ -14,7 +14,7 @@
 namespace blink {
 namespace scheduler {
 
-class TaskCostEstimatorTest : public testing::Test {
+class TaskCostEstimatorTest : public ::testing::Test {
  public:
   TaskCostEstimatorTest() {}
   ~TaskCostEstimatorTest() override {}
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/task_duration_metric_reporter_unittest.cc b/third_party/WebKit/Source/platform/scheduler/renderer/task_duration_metric_reporter_unittest.cc
index 52eba2a6..4e7a63f 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/task_duration_metric_reporter_unittest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/task_duration_metric_reporter_unittest.cc
@@ -16,8 +16,8 @@
 
 namespace {
 
-using testing::_;
-using testing::Mock;
+using ::testing::_;
+using ::testing::Mock;
 
 class FakeHistogram : public base::HistogramBase {
  public:
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/task_queue_throttler_unittest.cc b/third_party/WebKit/Source/platform/scheduler/renderer/task_queue_throttler_unittest.cc
index d4aa08f9..cd39aa3 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/task_queue_throttler_unittest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/task_queue_throttler_unittest.cc
@@ -25,7 +25,7 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using testing::ElementsAre;
+using ::testing::ElementsAre;
 
 namespace {
 bool MessageLoopTaskCounter(size_t* count) {
@@ -85,7 +85,7 @@
 
 }  // namespace
 
-class TaskQueueThrottlerTest : public testing::Test {
+class TaskQueueThrottlerTest : public ::testing::Test {
  public:
   TaskQueueThrottlerTest() {}
   ~TaskQueueThrottlerTest() override {}
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/user_model_unittest.cc b/third_party/WebKit/Source/platform/scheduler/renderer/user_model_unittest.cc
index 4c8592f..bfa2e34 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/user_model_unittest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/user_model_unittest.cc
@@ -11,7 +11,7 @@
 namespace blink {
 namespace scheduler {
 
-class UserModelTest : public testing::Test {
+class UserModelTest : public ::testing::Test {
  public:
   UserModelTest() {}
   ~UserModelTest() override {}
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/web_frame_scheduler_impl_unittest.cc b/third_party/WebKit/Source/platform/scheduler/renderer/web_frame_scheduler_impl_unittest.cc
index b0d55fbd..1769afc 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/web_frame_scheduler_impl_unittest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/web_frame_scheduler_impl_unittest.cc
@@ -22,7 +22,7 @@
 namespace blink {
 namespace scheduler {
 
-class WebFrameSchedulerImplTest : public testing::Test {
+class WebFrameSchedulerImplTest : public ::testing::Test {
  public:
   WebFrameSchedulerImplTest() {}
   ~WebFrameSchedulerImplTest() override {}
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/web_view_scheduler_impl_unittest.cc b/third_party/WebKit/Source/platform/scheduler/renderer/web_view_scheduler_impl_unittest.cc
index 25524888..7ebaf18 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/web_view_scheduler_impl_unittest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/web_view_scheduler_impl_unittest.cc
@@ -23,13 +23,13 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using testing::ElementsAre;
+using ::testing::ElementsAre;
 using VirtualTimePolicy = blink::WebViewScheduler::VirtualTimePolicy;
 
 namespace blink {
 namespace scheduler {
 
-class WebViewSchedulerImplTest : public testing::Test {
+class WebViewSchedulerImplTest : public ::testing::Test {
  public:
   WebViewSchedulerImplTest() {}
   ~WebViewSchedulerImplTest() override {}
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/webthread_impl_for_renderer_scheduler_unittest.cc b/third_party/WebKit/Source/platform/scheduler/renderer/webthread_impl_for_renderer_scheduler_unittest.cc
index f57a222..b09a7fb 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/webthread_impl_for_renderer_scheduler_unittest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/webthread_impl_for_renderer_scheduler_unittest.cc
@@ -39,7 +39,7 @@
 };
 }  // namespace
 
-class WebThreadImplForRendererSchedulerTest : public testing::Test {
+class WebThreadImplForRendererSchedulerTest : public ::testing::Test {
  public:
   WebThreadImplForRendererSchedulerTest() {}
 
@@ -77,7 +77,7 @@
   MockTask task;
 
   {
-    testing::InSequence sequence;
+    ::testing::InSequence sequence;
     EXPECT_CALL(observer, WillProcessTask());
     EXPECT_CALL(task, Run());
     EXPECT_CALL(observer, DidProcessTask());
@@ -96,7 +96,7 @@
 
   SetWorkBatchSizeForTesting(kWorkBatchSize);
   {
-    testing::InSequence sequence;
+    ::testing::InSequence sequence;
     EXPECT_CALL(observer, WillProcessTask());
     EXPECT_CALL(task, Run());
     EXPECT_CALL(observer, DidProcessTask());
@@ -116,7 +116,7 @@
 
   SetWorkBatchSizeForTesting(kWorkBatchSize);
   {
-    testing::InSequence sequence;
+    ::testing::InSequence sequence;
     EXPECT_CALL(observer, WillProcessTask());
     EXPECT_CALL(task1, Run());
     EXPECT_CALL(observer, DidProcessTask());
@@ -143,7 +143,7 @@
 
   SetWorkBatchSizeForTesting(kWorkBatchSize);
   {
-    testing::InSequence sequence;
+    ::testing::InSequence sequence;
     EXPECT_CALL(observer, WillProcessTask());
     EXPECT_CALL(task1, Run());
     EXPECT_CALL(observer, DidProcessTask());
@@ -183,7 +183,7 @@
   thread_->AddTaskObserver(&observer);
 
   {
-    testing::InSequence sequence;
+    ::testing::InSequence sequence;
 
     // One callback for EnterRunLoop.
     EXPECT_CALL(observer, WillProcessTask());
diff --git a/third_party/WebKit/Source/platform/scroll/ScrollAnimatorTest.cpp b/third_party/WebKit/Source/platform/scroll/ScrollAnimatorTest.cpp
index f2134c9..950fb14 100644
--- a/third_party/WebKit/Source/platform/scroll/ScrollAnimatorTest.cpp
+++ b/third_party/WebKit/Source/platform/scroll/ScrollAnimatorTest.cpp
@@ -39,9 +39,9 @@
 
 namespace blink {
 
-using testing::AtLeast;
-using testing::Return;
-using testing::_;
+using ::testing::AtLeast;
+using ::testing::Return;
+using ::testing::_;
 
 static double g_mocked_time = 0.0;
 
diff --git a/third_party/WebKit/Source/platform/scroll/ScrollableAreaTest.cpp b/third_party/WebKit/Source/platform/scroll/ScrollableAreaTest.cpp
index 12e17d7..1c3d244 100644
--- a/third_party/WebKit/Source/platform/scroll/ScrollableAreaTest.cpp
+++ b/third_party/WebKit/Source/platform/scroll/ScrollableAreaTest.cpp
@@ -20,8 +20,8 @@
 
 namespace {
 
-using testing::_;
-using testing::Return;
+using ::testing::_;
+using ::testing::Return;
 
 class ScrollbarThemeWithMockInvalidation : public ScrollbarThemeMock {
  public:
@@ -32,7 +32,7 @@
 
 }  // namespace
 
-using ScrollableAreaTest = testing::Test;
+using ScrollableAreaTest = ::testing::Test;
 
 TEST_F(ScrollableAreaTest, ScrollAnimatorCurrentPositionShouldBeSync) {
   ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
diff --git a/third_party/WebKit/Source/platform/scroll/ScrollbarThemeAuraTest.cpp b/third_party/WebKit/Source/platform/scroll/ScrollbarThemeAuraTest.cpp
index 591da78f..e877aa9 100644
--- a/third_party/WebKit/Source/platform/scroll/ScrollbarThemeAuraTest.cpp
+++ b/third_party/WebKit/Source/platform/scroll/ScrollbarThemeAuraTest.cpp
@@ -9,7 +9,7 @@
 
 namespace blink {
 
-using testing::Return;
+using ::testing::Return;
 
 namespace {
 
@@ -29,7 +29,7 @@
 
 }  // namespace
 
-using ScrollbarThemeAuraTest = testing::Test;
+using ScrollbarThemeAuraTest = ::testing::Test;
 
 TEST_F(ScrollbarThemeAuraTest, ButtonSizeHorizontal) {
   ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
diff --git a/third_party/WebKit/Source/platform/scroll/ScrollbarThemeOverlayTest.cpp b/third_party/WebKit/Source/platform/scroll/ScrollbarThemeOverlayTest.cpp
index 7beab665..af00cb4 100644
--- a/third_party/WebKit/Source/platform/scroll/ScrollbarThemeOverlayTest.cpp
+++ b/third_party/WebKit/Source/platform/scroll/ScrollbarThemeOverlayTest.cpp
@@ -9,10 +9,10 @@
 
 namespace blink {
 
-using testing::NiceMock;
-using testing::Return;
+using ::testing::NiceMock;
+using ::testing::Return;
 
-using ScrollbarThemeOverlayTest = testing::Test;
+using ScrollbarThemeOverlayTest = ::testing::Test;
 
 TEST_F(ScrollbarThemeOverlayTest, PaintInvalidation) {
   ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
diff --git a/third_party/WebKit/Source/platform/testing/CompositorTest.h b/third_party/WebKit/Source/platform/testing/CompositorTest.h
index d0e3008b..0a460fc 100644
--- a/third_party/WebKit/Source/platform/testing/CompositorTest.h
+++ b/third_party/WebKit/Source/platform/testing/CompositorTest.h
@@ -13,7 +13,7 @@
 
 namespace blink {
 
-class CompositorTest : public testing::Test {
+class CompositorTest : public ::testing::Test {
   WTF_MAKE_NONCOPYABLE(CompositorTest);
 
  public:
diff --git a/third_party/WebKit/Source/platform/text/CharacterTest.cpp b/third_party/WebKit/Source/platform/text/CharacterTest.cpp
index 55f07d7..55e4c68 100644
--- a/third_party/WebKit/Source/platform/text/CharacterTest.cpp
+++ b/third_party/WebKit/Source/platform/text/CharacterTest.cpp
@@ -9,18 +9,19 @@
 
 namespace blink {
 
-testing::AssertionResult IsCJKIdeographOrSymbolWithMessage(UChar32 codepoint) {
+::testing::AssertionResult IsCJKIdeographOrSymbolWithMessage(
+    UChar32 codepoint) {
   const size_t kFormatBufferSize = 10;
   char formatted_as_hex[kFormatBufferSize];
   snprintf(formatted_as_hex, kFormatBufferSize, "0x%x", codepoint);
 
   if (Character::IsCJKIdeographOrSymbol(codepoint)) {
-    return testing::AssertionSuccess()
+    return ::testing::AssertionSuccess()
            << "Codepoint " << formatted_as_hex << " is a CJKIdeographOrSymbol.";
   }
 
-  return testing::AssertionFailure() << "Codepoint " << formatted_as_hex
-                                     << " is not a CJKIdeographOrSymbol.";
+  return ::testing::AssertionFailure() << "Codepoint " << formatted_as_hex
+                                       << " is not a CJKIdeographOrSymbol.";
 }
 
 TEST(CharacterTest, HammerEmojiVsCJKIdeographOrSymbol) {
diff --git a/third_party/WebKit/Source/platform/text/HyphenationTest.cpp b/third_party/WebKit/Source/platform/text/HyphenationTest.cpp
index 3f9071a..9c54bac 100644
--- a/third_party/WebKit/Source/platform/text/HyphenationTest.cpp
+++ b/third_party/WebKit/Source/platform/text/HyphenationTest.cpp
@@ -6,6 +6,7 @@
 
 #include "build/build_config.h"
 #include "platform/LayoutLocale.h"
+#include "platform/fonts/FontGlobalContext.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 #if defined(OS_ANDROID)
@@ -31,7 +32,7 @@
   LayoutLocale::SetHyphenationForTesting("en-UK", nullptr);
   EXPECT_EQ(nullptr, LayoutLocale::Get("en-UK")->GetHyphenation());
 
-  LayoutLocale::ClearForTesting();
+  FontGlobalContext::ClearForTesting();
 }
 
 #if defined(OS_ANDROID) || defined(OS_MACOSX)
diff --git a/third_party/WebKit/Source/platform/text/PlatformLocale.cpp b/third_party/WebKit/Source/platform/text/PlatformLocale.cpp
index 4f97a11..e69e325 100644
--- a/third_party/WebKit/Source/platform/text/PlatformLocale.cpp
+++ b/third_party/WebKit/Source/platform/text/PlatformLocale.cpp
@@ -37,6 +37,10 @@
 
 namespace blink {
 
+namespace {
+Locale* g_default_locale;
+}
+
 class DateTimeStringBuilder : private DateTimeFormat::TokenHandler {
   WTF_MAKE_NONCOPYABLE(DateTimeStringBuilder);
 
@@ -176,9 +180,17 @@
 }
 
 Locale& Locale::DefaultLocale() {
-  static Locale* locale = Locale::Create(DefaultLanguage()).release();
   DCHECK(IsMainThread());
-  return *locale;
+  if (!g_default_locale)
+    g_default_locale = Locale::Create(DefaultLanguage()).release();
+  return *g_default_locale;
+}
+
+void Locale::ResetDefautlLocale() {
+  // This is safe because no one owns a Locale object returned by
+  // DefaultLocale().
+  delete g_default_locale;
+  g_default_locale = nullptr;
 }
 
 Locale::~Locale() {}
diff --git a/third_party/WebKit/Source/platform/text/PlatformLocale.h b/third_party/WebKit/Source/platform/text/PlatformLocale.h
index 96ce6e9..8649c51 100644
--- a/third_party/WebKit/Source/platform/text/PlatformLocale.h
+++ b/third_party/WebKit/Source/platform/text/PlatformLocale.h
@@ -42,6 +42,7 @@
  public:
   static std::unique_ptr<Locale> Create(const String& locale_identifier);
   static Locale& DefaultLocale();
+  static void ResetDefautlLocale();
 
   String QueryString(WebLocalizedString::Name);
   String QueryString(WebLocalizedString::Name, const String& parameter);
diff --git a/third_party/WebKit/Source/platform/text/TextBreakIteratorTest.cpp b/third_party/WebKit/Source/platform/text/TextBreakIteratorTest.cpp
index 0a322f0..3d407033 100644
--- a/third_party/WebKit/Source/platform/text/TextBreakIteratorTest.cpp
+++ b/third_party/WebKit/Source/platform/text/TextBreakIteratorTest.cpp
@@ -10,7 +10,7 @@
 
 namespace blink {
 
-class TextBreakIteratorTest : public testing::Test {
+class TextBreakIteratorTest : public ::testing::Test {
  protected:
   void SetTestString(const char* test_string) {
     test_string_ = String::FromUTF8(test_string);
@@ -40,7 +40,7 @@
         break_positions.push_back(i);
     }
     EXPECT_THAT(break_positions,
-                testing::ElementsAreArray(expected_break_positions))
+                ::testing::ElementsAreArray(expected_break_positions))
         << break_iterator.BreakType() << " for " << test_string_;
   }
 
@@ -54,7 +54,7 @@
       break_positions.push_back(i);
     }
     EXPECT_THAT(break_positions,
-                testing::ElementsAreArray(expected_break_positions))
+                ::testing::ElementsAreArray(expected_break_positions))
         << break_iterator.BreakType() << " for " << test_string_;
   }
 
diff --git a/third_party/WebKit/Source/platform/weborigin/OriginAccessEntryTest.cpp b/third_party/WebKit/Source/platform/weborigin/OriginAccessEntryTest.cpp
index ce3d933..b8c10eec 100644
--- a/third_party/WebKit/Source/platform/weborigin/OriginAccessEntryTest.cpp
+++ b/third_party/WebKit/Source/platform/weborigin/OriginAccessEntryTest.cpp
@@ -143,8 +143,8 @@
   platform->SetPublicSuffix("com");
 
   for (const auto& test : inputs) {
-    SCOPED_TRACE(testing::Message() << "Host: " << test.host
-                                    << ", Origin: " << test.origin);
+    SCOPED_TRACE(::testing::Message()
+                 << "Host: " << test.host << ", Origin: " << test.origin);
     RefPtr<SecurityOrigin> origin_to_test =
         SecurityOrigin::CreateFromString(test.origin);
     OriginAccessEntry entry1(test.protocol, test.host,
@@ -202,7 +202,7 @@
     OriginAccessEntry entry1(test.protocol, test.host,
                              OriginAccessEntry::kAllowRegisterableDomains);
 
-    SCOPED_TRACE(testing::Message()
+    SCOPED_TRACE(::testing::Message()
                  << "Host: " << test.host << ", Origin: " << test.origin
                  << ", Domain: " << entry1.Registerable().Utf8().data());
     EXPECT_EQ(test.expected, entry1.MatchesOrigin(*origin_to_test));
@@ -258,7 +258,7 @@
     OriginAccessEntry entry1(test.protocol, test.host,
                              OriginAccessEntry::kAllowRegisterableDomains);
 
-    SCOPED_TRACE(testing::Message()
+    SCOPED_TRACE(::testing::Message()
                  << "Host: " << test.host << ", Origin: " << test.origin
                  << ", Domain: " << entry1.Registerable().Utf8().data());
     EXPECT_EQ(test.expected, entry1.MatchesOrigin(*origin_to_test));
@@ -304,8 +304,8 @@
   platform->SetPublicSuffix("com");
 
   for (const auto& test : inputs) {
-    SCOPED_TRACE(testing::Message() << "Host: " << test.host
-                                    << ", Origin: " << test.origin);
+    SCOPED_TRACE(::testing::Message()
+                 << "Host: " << test.host << ", Origin: " << test.origin);
     RefPtr<SecurityOrigin> origin_to_test =
         SecurityOrigin::CreateFromString(test.origin);
     OriginAccessEntry entry1(test.protocol, test.host,
@@ -335,7 +335,7 @@
   platform->SetPublicSuffix("com");
 
   for (const auto& test : inputs) {
-    SCOPED_TRACE(testing::Message() << "Host: " << test.host);
+    SCOPED_TRACE(::testing::Message() << "Host: " << test.host);
     OriginAccessEntry entry(test.protocol, test.host,
                             OriginAccessEntry::kDisallowSubdomains);
     EXPECT_EQ(test.is_ip_address, entry.HostIsIPAddress()) << test.host;
@@ -363,8 +363,8 @@
   platform->SetPublicSuffix("com");
 
   for (const auto& test : inputs) {
-    SCOPED_TRACE(testing::Message() << "Host: " << test.host
-                                    << ", Origin: " << test.origin);
+    SCOPED_TRACE(::testing::Message()
+                 << "Host: " << test.host << ", Origin: " << test.origin);
     RefPtr<SecurityOrigin> origin_to_test =
         SecurityOrigin::CreateFromString(test.origin);
     OriginAccessEntry entry1(test.protocol, test.host,
diff --git a/third_party/WebKit/Source/platform/weborigin/SecurityOriginTest.cpp b/third_party/WebKit/Source/platform/weborigin/SecurityOriginTest.cpp
index 81b1f2c6..5a0012a 100644
--- a/third_party/WebKit/Source/platform/weborigin/SecurityOriginTest.cpp
+++ b/third_party/WebKit/Source/platform/weborigin/SecurityOriginTest.cpp
@@ -516,7 +516,7 @@
   };
 
   for (const TestCase& test : cases) {
-    SCOPED_TRACE(testing::Message() << "raw host: '" << test.host << "'");
+    SCOPED_TRACE(::testing::Message() << "raw host: '" << test.host << "'");
     String host = String::FromUTF8(test.host);
     bool success = false;
     String canonical_host = SecurityOrigin::CanonicalizeHost(host, &success);
diff --git a/third_party/WebKit/Source/web/WebKit.cpp b/third_party/WebKit/Source/web/WebKit.cpp
index b0df44c..169c52447 100644
--- a/third_party/WebKit/Source/web/WebKit.cpp
+++ b/third_party/WebKit/Source/web/WebKit.cpp
@@ -88,6 +88,7 @@
 void ResetPluginCache(bool reload_pages) {
   DCHECK(!reload_pages);
   Page::RefreshPlugins();
+  Page::ResetPluginData();
 }
 
 void DecommitFreeableMemory() {
diff --git a/third_party/WebKit/Source/web/tests/AccessibilityObjectModelTest.cpp b/third_party/WebKit/Source/web/tests/AccessibilityObjectModelTest.cpp
index 1622da19..ac9a4f6 100644
--- a/third_party/WebKit/Source/web/tests/AccessibilityObjectModelTest.cpp
+++ b/third_party/WebKit/Source/web/tests/AccessibilityObjectModelTest.cpp
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "core/dom/AccessibleNode.h"
+#include "core/dom/AccessibleNodeList.h"
 #include "core/html/HTMLBodyElement.h"
 #include "core/testing/sim/SimRequest.h"
 #include "core/testing/sim/SimTest.h"
@@ -342,6 +343,41 @@
                 ->RoleValue());
 }
 
+TEST_F(AccessibilityObjectModelTest, LabeledBy) {
+  SimRequest main_resource("https://example.com/", "text/html");
+  LoadURL("https://example.com/");
+  main_resource.Complete(
+      "<input id=target aria-labelledby='l1 l2'>"
+      "<label id=l1>Label 1</label>"
+      "<label id=l2>Label 2</label>"
+      "<label id=l3>Label 3</label>");
+
+  auto* target = GetDocument().getElementById("target");
+  auto* l1 = GetDocument().getElementById("l1");
+  auto* l2 = GetDocument().getElementById("l2");
+  auto* l3 = GetDocument().getElementById("l3");
+
+  HeapVector<Member<Element>> labeled_by;
+  ASSERT_TRUE(AccessibleNode::GetPropertyOrARIAAttribute(
+      target, AOMRelationListProperty::kLabeledBy, labeled_by));
+  ASSERT_EQ(2U, labeled_by.size());
+  ASSERT_EQ(l1, labeled_by[0]);
+  ASSERT_EQ(l2, labeled_by[1]);
+
+  AccessibleNodeList* node_list = target->accessibleNode()->labeledBy();
+  ASSERT_EQ(nullptr, node_list);
+
+  node_list = new AccessibleNodeList();
+  node_list->add(l3->accessibleNode());
+  target->accessibleNode()->setLabeledBy(node_list);
+
+  labeled_by.clear();
+  ASSERT_TRUE(AccessibleNode::GetPropertyOrARIAAttribute(
+      target, AOMRelationListProperty::kLabeledBy, labeled_by));
+  ASSERT_EQ(1U, labeled_by.size());
+  ASSERT_EQ(l3, labeled_by[0]);
+}
+
 }  // namespace
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/web/tests/ChromeClientImplTest.cpp b/third_party/WebKit/Source/web/tests/ChromeClientImplTest.cpp
index 801ea595..b9877cb7 100644
--- a/third_party/WebKit/Source/web/tests/ChromeClientImplTest.cpp
+++ b/third_party/WebKit/Source/web/tests/ChromeClientImplTest.cpp
@@ -80,7 +80,7 @@
   FrameTestHelpers::WebViewHelper web_view_helper_;
 };
 
-class CreateWindowTest : public testing::Test {
+class CreateWindowTest : public ::testing::Test {
  protected:
   void SetUp() override {
     web_view_ = helper_.Initialize(nullptr, &web_view_client_);
@@ -161,7 +161,7 @@
   Member<Element> owner_element_;
 };
 
-class PagePopupSuppressionTest : public testing::Test {
+class PagePopupSuppressionTest : public ::testing::Test {
  public:
   PagePopupSuppressionTest() {}
 
diff --git a/third_party/WebKit/Source/web/tests/LocalFrameClientImplTest.cpp b/third_party/WebKit/Source/web/tests/LocalFrameClientImplTest.cpp
index 5f4cb67b..6e1569e 100644
--- a/third_party/WebKit/Source/web/tests/LocalFrameClientImplTest.cpp
+++ b/third_party/WebKit/Source/web/tests/LocalFrameClientImplTest.cpp
@@ -41,9 +41,9 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using testing::_;
-using testing::Mock;
-using testing::Return;
+using ::testing::_;
+using ::testing::Mock;
+using ::testing::Return;
 
 namespace blink {
 namespace {
diff --git a/third_party/WebKit/Source/web/tests/ScrollMetricsTest.cpp b/third_party/WebKit/Source/web/tests/ScrollMetricsTest.cpp
index be96ff5..10ec3a8 100644
--- a/third_party/WebKit/Source/web/tests/ScrollMetricsTest.cpp
+++ b/third_party/WebKit/Source/web/tests/ScrollMetricsTest.cpp
@@ -319,11 +319,10 @@
       " .box { overflow:scroll; width: 100px; height: 100px; }"
       " .translucent { opacity: 0.5; }"
       " .transform { transform: scale(0.8); }"
-      " .with-border-radius { border: 5px solid; border-radius: 5px; }"
       " .spacer { height: 1000px; }"
       " .composited { will-change: transform; }"
       "</style>"
-      "<div id='container' class='container with-border-radius'>"
+      "<div id='container' class='container'>"
       "  <div class='translucent box'>"
       "    <div id='inner' class='composited transform box'>"
       "      <div class='spacer'></div>"
@@ -348,9 +347,8 @@
   EXPECT_WHEEL_BUCKET(kHasOpacityAndLCDText, 1);
   EXPECT_WHEEL_BUCKET(kBackgroundNotOpaqueInRectAndLCDText, 1);
   EXPECT_WHEEL_BUCKET(kIsNotStackingContextAndLCDText, 1);
-  EXPECT_WHEEL_BUCKET(kHasBorderRadius, 1);
   EXPECT_WHEEL_BUCKET(kHasTransformAndLCDText, 0);
-  EXPECT_WHEEL_TOTAL(4);
+  EXPECT_WHEEL_TOTAL(3);
 }
 
 }  // namespace
diff --git a/third_party/WebKit/Source/web/tests/WebFrameTest.cpp b/third_party/WebKit/Source/web/tests/WebFrameTest.cpp
index 7e779df..a33f94d 100644
--- a/third_party/WebKit/Source/web/tests/WebFrameTest.cpp
+++ b/third_party/WebKit/Source/web/tests/WebFrameTest.cpp
@@ -168,9 +168,9 @@
 
 using blink::URLTestHelpers::ToKURL;
 using blink::testing::RunPendingTasks;
-using testing::ElementsAre;
-using testing::Mock;
-using testing::_;
+using ::testing::ElementsAre;
+using ::testing::Mock;
+using ::testing::_;
 
 namespace blink {
 
diff --git a/third_party/WebKit/public/BUILD.gn b/third_party/WebKit/public/BUILD.gn
index a7b56c5..6b9fb39f 100644
--- a/third_party/WebKit/public/BUILD.gn
+++ b/third_party/WebKit/public/BUILD.gn
@@ -338,6 +338,7 @@
     "platform/WebStorageQuotaError.h",
     "platform/WebStorageQuotaType.h",
     "platform/WebString.h",
+    "platform/WebSurfaceLayerBridge.h",
     "platform/WebTextInputInfo.h",
     "platform/WebTextInputMode.h",
     "platform/WebTextInputType.h",
diff --git a/third_party/WebKit/public/platform/WebSurfaceLayerBridge.h b/third_party/WebKit/public/platform/WebSurfaceLayerBridge.h
new file mode 100644
index 0000000..10be3a6
--- /dev/null
+++ b/third_party/WebKit/public/platform/WebSurfaceLayerBridge.h
@@ -0,0 +1,22 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WebSurfaceLayerBridge_h
+#define WebSurfaceLayerBridge_h
+
+#include "WebCommon.h"
+#include "WebLayer.h"
+
+namespace blink {
+
+// Maintains and exposes the SurfaceLayer.
+class BLINK_PLATFORM_EXPORT WebSurfaceLayerBridge {
+ public:
+  static WebSurfaceLayerBridge* Create();
+  virtual WebLayer* GetWebLayer() const = 0;
+};
+
+}  // namespace blink
+
+#endif  // WebSurfaceLayerBridge_h
diff --git a/third_party/WebKit/public/web/WebKit.h b/third_party/WebKit/public/web/WebKit.h
index 95f4d32..d130be3 100644
--- a/third_party/WebKit/public/web/WebKit.h
+++ b/third_party/WebKit/public/web/WebKit.h
@@ -60,8 +60,9 @@
 BLINK_EXPORT void SetFontAntialiasingEnabledForTest(bool);
 BLINK_EXPORT bool FontAntialiasingEnabledForTest();
 
-// Purge the plugin list cache. If |reloadPages| is true, any pages
-// containing plugins will be reloaded after refreshing the plugin list.
+// Purge the plugin list cache. This can cause a web-visible and out-of-spec
+// change to |navigator.plugins| if the plugin list has changed (see
+// https://crbug.com/735854). |reloadPages| is unsupported and must be false.
 BLINK_EXPORT void ResetPluginCache(bool reload_pages = false);
 
 // The embedder should call this periodically in an attempt to balance overall
diff --git a/third_party/khronos/EGL/eglplatform.h b/third_party/khronos/EGL/eglplatform.h
index dfbc52ce..fad491b 100644
--- a/third_party/khronos/EGL/eglplatform.h
+++ b/third_party/khronos/EGL/eglplatform.h
@@ -77,7 +77,7 @@
 typedef HBITMAP EGLNativePixmapType;
 typedef HWND    EGLNativeWindowType;
 
-#elif defined(__WINSCW__) || defined(__SYMBIAN32__)  /* Symbian */
+#elif defined(__WINSCW__) || defined(__SYMBIAN32__) || defined(__Fuchsia__)
 
 typedef int   EGLNativeDisplayType;
 typedef void *EGLNativeWindowType;
diff --git a/third_party/khronos/README.chromium b/third_party/khronos/README.chromium
index 83eaa5a2..e6b667f8 100644
--- a/third_party/khronos/README.chromium
+++ b/third_party/khronos/README.chromium
@@ -31,6 +31,7 @@
 EGL/eglplatform.h
  - Added EGLNative*Type for Mac.
  - Added EGLNative*Type for native linux framebuffers.
+ - Added EGLNative*Type for Fuchsia.
 EGL/eglext.h
  - Added support for EGL_EXT_image_flush_external extension
  - Added support for EGL_ANGLE_stream_producer_d3d_texture_nv12
diff --git a/third_party/libaddressinput/chromium/chrome_storage_impl.cc b/third_party/libaddressinput/chromium/chrome_storage_impl.cc
index 2504eee..8228805b 100644
--- a/third_party/libaddressinput/chromium/chrome_storage_impl.cc
+++ b/third_party/libaddressinput/chromium/chrome_storage_impl.cc
@@ -39,10 +39,8 @@
 void ChromeStorageImpl::OnPrefValueChanged(const std::string& key) {}
 
 void ChromeStorageImpl::OnInitializationCompleted(bool succeeded) {
-  for (std::vector<Request*>::iterator iter = outstanding_requests_.begin();
-       iter != outstanding_requests_.end(); ++iter) {
-    DoGet((*iter)->key, (*iter)->callback);
-  }
+  for (const auto& request : outstanding_requests_)
+    DoGet(request->key, request->callback);
 
   outstanding_requests_.clear();
 }
@@ -50,7 +48,7 @@
 void ChromeStorageImpl::DoGet(const std::string& key,
                               const Storage::Callback& data_ready) {
   if (!backing_store_->IsInitializationComplete()) {
-    outstanding_requests_.push_back(new Request(key, data_ready));
+    outstanding_requests_.push_back(base::MakeUnique<Request>(key, data_ready));
     return;
   }
 
diff --git a/third_party/libaddressinput/chromium/chrome_storage_impl.h b/third_party/libaddressinput/chromium/chrome_storage_impl.h
index 6a08adc5..328784d 100644
--- a/third_party/libaddressinput/chromium/chrome_storage_impl.h
+++ b/third_party/libaddressinput/chromium/chrome_storage_impl.h
@@ -6,10 +6,11 @@
 #define THIRD_PARTY_LIBADDRESSINPUT_CHROMIUM_CHROME_STORAGE_IMPL_H_
 
 #include <list>
+#include <memory>
 #include <string>
+#include <vector>
 
 #include "base/macros.h"
-#include "base/memory/scoped_vector.h"
 #include "base/scoped_observer.h"
 #include "components/prefs/pref_store.h"
 #include "third_party/libaddressinput/src/cpp/include/libaddressinput/storage.h"
@@ -50,7 +51,7 @@
   WriteablePrefStore* backing_store_;  // weak
 
   // Get requests that haven't yet been serviced.
-  ScopedVector<Request> outstanding_requests_;
+  std::vector<std::unique_ptr<Request>> outstanding_requests_;
 
   ScopedObserver<PrefStore, ChromeStorageImpl> scoped_observer_;
 
diff --git a/tools/clang/scripts/package.py b/tools/clang/scripts/package.py
index 2f95c5f..dc7d5e3 100755
--- a/tools/clang/scripts/package.py
+++ b/tools/clang/scripts/package.py
@@ -261,6 +261,10 @@
                  'lib/clang/*/lib/darwin/*asan_iossim*',
                  'lib/clang/*/lib/darwin/*profile_osx*',
                  'lib/clang/*/lib/darwin/*profile_iossim*',
+                 # And the OSX and ios builtin libraries (iossim is lipo'd into
+                 # ios) for the _IsOSVersionAtLeast runtime function.
+                 'lib/clang/*/lib/darwin/*.ios.a',
+                 'lib/clang/*/lib/darwin/*.osx.a',
                  ])
   elif sys.platform.startswith('linux'):
     # Copy the libstdc++.so.6 we linked Clang against so it can run.
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py
index e96e4b55..fb597db 100755
--- a/tools/clang/scripts/update.py
+++ b/tools/clang/scripts/update.py
@@ -34,7 +34,7 @@
   CLANG_REVISION = 'HEAD'
 
 # This is incremented when pushing a new build of Clang at the same revision.
-CLANG_SUB_REVISION=2
+CLANG_SUB_REVISION=3
 
 PACKAGE_VERSION = "%s-%s" % (CLANG_REVISION, CLANG_SUB_REVISION)
 
diff --git a/tools/gn/misc/emacs/gn-mode.el b/tools/gn/misc/emacs/gn-mode.el
index c2cd1bd..931207ba 100644
--- a/tools/gn/misc/emacs/gn-mode.el
+++ b/tools/gn/misc/emacs/gn-mode.el
@@ -61,28 +61,38 @@
   '("true" "false" "if" "else"))
 
 (defvar gn-font-lock-target-declaration-keywords
-  '("action" "action_foreach" "copy" "executable" "group" "loadable_module"
-    "shared_library" "source_set" "static_library"))
+  '("action" "action_foreach" "bundle_data" "copy" "create_bundle" "executable"
+    "group" "loadable_module" "shared_library" "source_set" "static_library"
+    "target"))
 
+;; pool() is handled specially since it's also a variable name
 (defvar gn-font-lock-buildfile-fun-keywords
   '("assert" "config" "declare_args" "defined" "exec_script" "foreach"
-    "get_label_info" "get_path_info" "get_target_outputs" "getenv" "import"
-    "print" "process_file_template" "read_file" "rebase_path"
-    "set_default_toolchain" "set_defaults" "set_sources_assignment_filter"
-    "template" "tool" "toolchain" "toolchain_args" "write_file"))
+    "forward_variables_from" "get_label_info" "get_path_info"
+    "get_target_outputs" "getenv" "import" "not_needed" "print"
+    "process_file_template" "read_file" "rebase_path" "set_default_toolchain"
+    "set_defaults" "set_sources_assignment_filter" "split_list" "template"
+    "tool" "toolchain" "write_file"))
 
 (defvar gn-font-lock-predefined-var-keywords
   '("current_cpu" "current_os" "current_toolchain" "default_toolchain"
-    "host_cpu" "host_os" "python_path" "root_build_dir" "root_gen_dir"
-    "root_out_dir" "target_cpu" "target_gen_dir" "target_os" "target_out_dir"))
+    "host_cpu" "host_os" "invoker" "python_path" "root_build_dir" "root_gen_dir"
+    "root_out_dir" "target_cpu" "target_gen_dir" "target_name" "target_os"
+    "target_out_dir"))
 
 (defvar gn-font-lock-var-keywords
-  '("all_dependent_configs" "allow_circular_includes_from" "args" "asmflags"
-    "cflags" "cflags_c" "cflags_cc" "cflags_objc" "cflags_objcc"
-    "check_includes" "complete_static_lib" "configs" "data" "data_deps"
+  '("all_dependent_configs" "allow_circular_includes_from" "arflags" "args"
+    "asmflags" "assert_no_deps" "bundle_deps_filter" "bundle_executable_dir"
+    "bundle_plugins_dir" "bundle_resources_dir" "bundle_root_dir" "cflags"
+    "cflags_c" "cflags_cc" "cflags_objc" "cflags_objcc" "check_includes"
+    "code_signing_args" "code_signing_outputs" "code_signing_script"
+    "code_signing_sources" "complete_static_lib" "configs" "data" "data_deps"
     "defines" "depfile" "deps" "include_dirs" "inputs" "ldflags" "lib_dirs"
-    "libs" "output_extension" "output_name" "outputs" "public" "public_configs"
-    "public_deps" "script" "sources" "testonly" "visibility"))
+    "libs" "output_dir" "output_extension" "output_name"
+    "output_prefix_override" "outputs" "pool" "precompiled_header"
+    "precompiled_header_type" "precompiled_source" "product_type" "public"
+    "public_configs" "public_deps" "response_file_contents" "script" "sources"
+    "testonly" "visibility" "write_runtime_deps"))
 
 (defconst gn-font-lock-keywords
   `((,(regexp-opt gn-font-lock-reserved-keywords 'words) .
@@ -91,6 +101,9 @@
      font-lock-type-face)
     (,(regexp-opt gn-font-lock-buildfile-fun-keywords 'words) .
      font-lock-function-name-face)
+    ;; pool() as a function
+    ("\\<\\(pool\\)\\s-*("
+     (1 font-lock-function-name-face))
     (,(regexp-opt gn-font-lock-predefined-var-keywords 'words) .
      font-lock-constant-face)
     (,(regexp-opt gn-font-lock-var-keywords 'words) .
diff --git a/tools/gn/xcode_writer.cc b/tools/gn/xcode_writer.cc
index c41e4f1..d029291 100644
--- a/tools/gn/xcode_writer.cc
+++ b/tools/gn/xcode_writer.cc
@@ -38,6 +38,7 @@
 const char kEarlGreyFileNameIdentifier[] = "egtest.mm";
 const char kXCTestFileNameIdentifier[] = "xctest.mm";
 const char kXCTestModuleTargetNamePostfix[] = "_module";
+const char kXCUITestRunnerTargetNamePostfix[] = "_runner";
 
 struct SafeEnvironmentVariableInfo {
   const char* name;
@@ -100,6 +101,13 @@
              "com.apple.product-type.application";
 }
 
+bool IsXCUITestRunnerTarget(const Target* target) {
+  return IsApplicationTarget(target) &&
+         base::EndsWith(target->label().name(),
+                        kXCUITestRunnerTargetNamePostfix,
+                        base::CompareCase::SENSITIVE);
+}
+
 bool IsXCTestModuleTarget(const Target* target) {
   return target->output_type() == Target::CREATE_BUNDLE &&
          target->bundle_data().product_type() ==
@@ -108,6 +116,14 @@
                         base::CompareCase::SENSITIVE);
 }
 
+bool IsXCUITestModuleTarget(const Target* target) {
+  return target->output_type() == Target::CREATE_BUNDLE &&
+         target->bundle_data().product_type() ==
+             "com.apple.product-type.bundle.ui-testing" &&
+         base::EndsWith(target->label().name(), kXCTestModuleTargetNamePostfix,
+                        base::CompareCase::SENSITIVE);
+}
+
 bool IsXCTestFile(const SourceFile& file) {
   return base::EndsWith(file.GetName(), kEarlGreyFileNameIdentifier,
                         base::CompareCase::SENSITIVE) ||
@@ -488,6 +504,18 @@
         if (target->bundle_data().product_type().empty())
           continue;
 
+        // For XCUITest, two CREATE_BUNDLE targets are generated:
+        // ${target_name}_runner and ${target_name}_module, however, Xcode
+        // requires only one target named ${target_name} to run tests.
+        if (IsXCUITestRunnerTarget(target))
+          continue;
+        std::string pbxtarget_name = target->label().name();
+        if (IsXCUITestModuleTarget(target)) {
+          std::string target_name = target->label().name();
+          pbxtarget_name = target_name.substr(
+              0, target_name.rfind(kXCTestModuleTargetNamePostfix));
+        }
+
         // Test files need to be known to Xcode for proper indexing and for
         // discovery of tests function for XCTest, but the compilation is done
         // via ninja and thus must prevent Xcode from linking object files via
@@ -500,13 +528,13 @@
         }
 
         PBXNativeTarget* native_target = main_project->AddNativeTarget(
-            target->label().name(), std::string(),
+            pbxtarget_name, std::string(),
             RebasePath(target->bundle_data()
                            .GetBundleRootDirOutput(target->settings())
                            .value(),
                        build_settings->build_dir()),
             target->bundle_data().product_type(),
-            GetBuildScript(target->label().name(), ninja_extra_args, env.get()),
+            GetBuildScript(pbxtarget_name, ninja_extra_args, env.get()),
             extra_attributes);
         bundle_target_to_pbxtarget.insert(
             std::make_pair(target, native_target));
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 14c92ac..7633e42 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -34,6 +34,10 @@
 </action>
 
 <action name="AboutConflicts">
+  <obsolete>
+    Deprecated since the conflicts page is changing. TODO(pmonette): Update this
+    to refer to the action for the new conflicts page.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <description>Please enter the description of this user action.</description>
 </action>
@@ -17289,6 +17293,10 @@
 </action>
 
 <action name="ViewAboutConflicts">
+  <obsolete>
+    Deprecated since the conflicts page is changing. TODO(pmonette): Update this
+    to refer to the action for the new conflicts page.
+  </obsolete>
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <description>Please enter the description of this user action.</description>
 </action>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index d0fcbaf..b732eff 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -19052,7 +19052,7 @@
   <int value="3" label="Create tables"/>
   <int value="4"
       label="Version check and migration (check
-             History.MigrateFailureToVersion)"/>
+             History.MigrateFailureFromVersion)"/>
   <int value="5" label="Commit"/>
 </enum>
 
@@ -22453,6 +22453,8 @@
   <int value="-1797739460" label="brotli-encoding:disabled"/>
   <int value="-1790227231" label="show-autofill-signatures"/>
   <int value="-1784788154" label="NewRemotePlaybackPipeline:disabled"/>
+  <int value="-1780306716"
+      label="OmniboxSpeculativeServiceWorkerStartOnQueryInput:enabled"/>
   <int value="-1772172557" label="enable-osk-overscroll"/>
   <int value="-1767470652" label="out-of-process-pdf"/>
   <int value="-1751928267" label="disable-icon-ntp"/>
@@ -22676,6 +22678,8 @@
   <int value="-1015006759" label="ImportantSitesInCBD:disabled"/>
   <int value="-998255750" label="ExperimentalKeyboardLockUI:enabled"/>
   <int value="-996673716" label="enable-web-app-frame"/>
+  <int value="-991253797"
+      label="OmniboxSpeculativeServiceWorkerStartOnQueryInput:disabled"/>
   <int value="-980260493" label="NTPSnippets:disabled"/>
   <int value="-979313250" label="enable-google-branded-context-menu"/>
   <int value="-979057409" label="enable-seccomp-filter-sandbox"/>
@@ -37111,6 +37115,18 @@
   <int value="10" label="Can not close extracted file"/>
 </enum>
 
+<enum name="UnsupportedContainers">
+  <int value="0" label="3GP"/>
+  <int value="1" label="TS"/>
+  <int value="2" label="MID"/>
+  <int value="3" label="XMF"/>
+  <int value="4" label="MXMF"/>
+  <int value="5" label="RTTTL"/>
+  <int value="6" label="RTX"/>
+  <int value="7" label="OTA"/>
+  <int value="8" label="IMY"/>
+</enum>
+
 <enum name="UpdateEngineAttemptResult">
   <int value="0" label="Update Succeeded"/>
   <int value="1" label="Internal Error"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 6c1a5189..40eaa90 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -27796,6 +27796,21 @@
   <summary>Records events related to local discovery notifications.</summary>
 </histogram>
 
+<histogram name="LocalNetworkRequests.PrivatePage" units="requests">
+  <owner>uthakore@chromium.org</owner>
+  <summary>
+    Number of requests made by a private page to resources besides itself.
+  </summary>
+</histogram>
+
+<histogram name="LocalNetworkRequests.PublicPage" units="requests">
+  <owner>uthakore@chromium.org</owner>
+  <summary>
+    Number of requests made by a public page to resources with reserved IP
+    addresses.
+  </summary>
+</histogram>
+
 <histogram name="LocalStorage.BrowserLocalStorageCachePurgedInKB" units="KB">
   <owner>ssid@chromium.org</owner>
   <summary>
@@ -31325,6 +31340,16 @@
   </summary>
 </histogram>
 
+<histogram name="Media.WebView.UnsupportedContainer"
+    enum="UnsupportedContainers">
+  <owner>dalecurtis@chromium.org</owner>
+  <summary>
+    Media container extensions seen by WebView that are not supported by the
+    standard HTML5 playback path. This value is recorded every time a
+    WebMediaPlayer is created with one of the unsupported containers.
+  </summary>
+</histogram>
+
 <histogram name="Media.WindowsCoreAudioInput" enum="BooleanSuccess">
   <obsolete>
     Deprecated 01/2017 as Windows Core Audio is now the only audio input
@@ -53158,6 +53183,16 @@
 </histogram>
 
 <histogram
+    name="PaymentRequest.UserDidNotHaveInitialFormOfPayment.EffectOnCompletion"
+    enum="PaymentRequestFlowCompletionStatus">
+  <owner>sebsg@chromium.org</owner>
+  <summary>
+    Whether the flow was completed when the user did not have a form of payment
+    on file when the Payment Request was shown.
+  </summary>
+</histogram>
+
+<histogram
     name="PaymentRequest.UserDidNotHaveSuggestionsForEverything.EffectOnCompletion"
     enum="PaymentRequestFlowCompletionStatus">
   <owner>sebsg@chromium.org</owner>
@@ -53167,6 +53202,15 @@
   </summary>
 </histogram>
 
+<histogram name="PaymentRequest.UserHadInitialFormOfPayment.EffectOnCompletion"
+    enum="PaymentRequestFlowCompletionStatus">
+  <owner>sebsg@chromium.org</owner>
+  <summary>
+    Whether the flow was completed when the user had an a form of payment on
+    file when the Payment Request was shown.
+  </summary>
+</histogram>
+
 <histogram
     name="PaymentRequest.UserHadSuggestionsForEverything.EffectOnCompletion"
     enum="PaymentRequestFlowCompletionStatus">
@@ -91369,6 +91413,71 @@
   <affected-histogram name="LevelDB.Open"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="LocalNetReqsLocalhostResources" separator=".">
+  <suffix name="Localhost.DbRequests"
+      label="Requests made to localhost on a database server port."/>
+  <suffix name="Localhost.DevRequests"
+      label="Requests made to localhost on a development server port."/>
+  <suffix name="Localhost.OtherRequests"
+      label="Requests made to localhost on any port not otherwise monitored
+             by the other local network request metrics."/>
+  <suffix name="Localhost.PrinterRequests"
+      label="Requests made to localhost on a printer server port."/>
+  <suffix name="Localhost.WebRequests"
+      label="Requests made to localhost on a web server port."/>
+  <affected-histogram name="LocalNetworkRequests.PrivatePage"/>
+  <affected-histogram name="LocalNetworkRequests.PublicPage"/>
+</histogram_suffixes>
+
+<histogram_suffixes name="LocalNetReqsPrivatePage" separator=".">
+  <suffix name="DifferentSubnetRequests"
+      label="Requests made to local resources on a different subnet."/>
+  <suffix name="PublicRequests" label="Requests made to public resources."/>
+  <suffix name="SameSubnetRequests"
+      label="Requests made to local resources on the same reserved IP space
+             as the page."/>
+  <affected-histogram name="LocalNetworkRequests.PrivatePage"/>
+</histogram_suffixes>
+
+<histogram_suffixes name="LocalNetReqsPublicPage" separator=".">
+  <suffix name="PrivateRequests" label="Requests made to private resources."/>
+  <suffix name="RouterRequests"
+      label="Requests made to resources likely to be routers."/>
+  <affected-histogram name="LocalNetworkRequests.PublicPage"/>
+</histogram_suffixes>
+
+<histogram_suffixes name="LocalNetReqsStatuses" separator=".">
+  <suffix name="Failed" label="Failed requests."/>
+  <suffix name="Successful" label="Successful requests."/>
+  <affected-histogram
+      name="LocalNetworkRequests.PrivatePage.DifferentSubnetRequests"/>
+  <affected-histogram
+      name="LocalNetworkRequests.PrivatePage.Localhost.DbRequests"/>
+  <affected-histogram
+      name="LocalNetworkRequests.PrivatePage.Localhost.DevRequests"/>
+  <affected-histogram
+      name="LocalNetworkRequests.PrivatePage.Localhost.OtherRequests"/>
+  <affected-histogram
+      name="LocalNetworkRequests.PrivatePage.Localhost.PrinterRequests"/>
+  <affected-histogram
+      name="LocalNetworkRequests.PrivatePage.Localhost.WebRequests"/>
+  <affected-histogram name="LocalNetworkRequests.PrivatePage.PublicRequests"/>
+  <affected-histogram
+      name="LocalNetworkRequests.PrivatePage.SameSubnetRequests"/>
+  <affected-histogram
+      name="LocalNetworkRequests.PublicPage.Localhost.DbRequests"/>
+  <affected-histogram
+      name="LocalNetworkRequests.PublicPage.Localhost.DevRequests"/>
+  <affected-histogram
+      name="LocalNetworkRequests.PublicPage.Localhost.OtherRequests"/>
+  <affected-histogram
+      name="LocalNetworkRequests.PublicPage.Localhost.PrinterRequests"/>
+  <affected-histogram
+      name="LocalNetworkRequests.PublicPage.Localhost.WebRequests"/>
+  <affected-histogram name="LocalNetworkRequests.PublicPage.PrivateRequests"/>
+  <affected-histogram name="LocalNetworkRequests.PublicPage.RouterRequests"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="LocalStorageCachePurgeReason" separator=".">
   <suffix name="AggressivePurgeTriggered"
       label="Aggressive purge was triggered on memory pressure."/>
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index a983545..d9cb199 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -395,6 +395,52 @@
   </metric>
 </event>
 
+<event name="LocalNetworkRequests">
+  <owner>uthakore@chromium.org</owner>
+  <summary>
+    Metrics that describe the resource request behavior of pages for which
+    navigation successfully commits. A separate entry is generated for every
+    unique IP address or localhost port number to which the loaded page makes a
+    resource request.
+  </summary>
+  <metric name="Count.Failed">
+    <summary>
+      The count of requests made by the page to the given resource for which a
+      response is not received or a network error occurs (i.e. |net_error| !=
+      |net::OK|) between the time navigation to the page commits and the time it
+      completes.
+    </summary>
+  </metric>
+  <metric name="Count.Successful">
+    <summary>
+      The count of requests made by the page to the given resource for which a
+      successful response is received (i.e. |net_error| == |net::OK|) between
+      the time navigation to the page commits and the time it completes.
+    </summary>
+  </metric>
+  <metric name="PortType">
+    <summary>
+      An enum value representing the type of port for requests to localhost. The
+      enum is defined in |LocalNetworkRequestsPageLoadMetricsObserver|. Possible
+      values are 1 for common web server ports, 2 for common database server
+      ports, 4 for common print server ports, 8 for common development server
+      ports, and 0 for all other ports.
+    </summary>
+  </metric>
+  <metric name="ResourceType">
+    <summary>
+      An enum value representing the type of resource requested. The enum is
+      defined in |LocalNetworkRequestsPageLoadMetricsObserver|. Possible values
+      are 0 for public resources requested by private pages, 1 for private
+      resources requested by public pages, 2 for private resources within the
+      same reserved IP space as the loaded private page, 4 for for private
+      resources within a different reserved IP space than the loaded private
+      page, 8 for resources requested by public pages that are suspected to be
+      routers, and 16 for localhost resources.
+    </summary>
+  </metric>
+</event>
+
 <event name="Media.WatchTime">
   <owner>dalecurtis@chromium.org</owner>
   <summary>
@@ -443,6 +489,24 @@
   <metric name="AudioVideo.SRC"/>
 </event>
 
+<event name="PageDomainInfo">
+  <owner>uthakore@chromium.org</owner>
+  <summary>
+    Metrics that describe the domain of pages that successfully commit. One
+    event is generated per page load that commits. Currently associated with
+    LocalNetworkRequests UKM metric collection.
+  </summary>
+  <metric name="DomainType">
+    <summary>
+      An enum value representing the type of domain of the loaded page. The enum
+      is defined in |LocalNetworkRequestsPageLoadMetricsObserver|. Possible
+      values are 0 for unknown (should never be logged), 1 for pages with public
+      domains, 2 for pages with private domains (IP addresses that are part of a
+      reserved IP space not including localhost), or 4 for localhost.
+    </summary>
+  </metric>
+</event>
+
 <event name="PageLoad" singular="True">
   <owner>bmcquade@chromium.org</owner>
   <summary>
diff --git a/tools/perf/benchmarks/tab_switching.py b/tools/perf/benchmarks/tab_switching.py
index 9eec5c8b..ad9b94ac 100644
--- a/tools/perf/benchmarks/tab_switching.py
+++ b/tools/perf/benchmarks/tab_switching.py
@@ -15,8 +15,6 @@
 @benchmark.Owner(emails=['vovoy@chromium.org'],
                  component='OS>Performance')
 @benchmark.Enabled('has tabs')
-@benchmark.Disabled('mac')  # http://crbug.com/612774
-@benchmark.Disabled('android')  # http://crbug.com/460084
 class TabSwitchingTypical25(perf_benchmark.PerfBenchmark):
   """This test records the MPArch.RWH_TabSwitchPaintDuration histogram.
 
diff --git a/tools/perf/chromium.perf.fyi.extras.json b/tools/perf/chromium.perf.fyi.extras.json
new file mode 100644
index 0000000..bbb1dbe
--- /dev/null
+++ b/tools/perf/chromium.perf.fyi.extras.json
@@ -0,0 +1,31 @@
+{
+  "comment": [
+    "This file contains manually-specified tests that should be merged into",
+    "testing/buildbot/chromium.perf.fyi.json."
+  ],
+  "Mojo Linux Perf": {
+    "isolated_scripts": [
+      {
+        "args": [
+          "loading.desktop.network_service",
+          "-v",
+          "--upload-results",
+          "--output-format=chartjson",
+          "--browser=release"
+        ],
+        "isolate_name": "telemetry_perf_tests",
+        "name": "loading.desktop.network_service",
+        "override_compile_targets": [
+          "telemetry_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 3600
+        }
+      }
+    ]
+  }
+}
diff --git a/tools/perf/contrib/network_service/__init__.py b/tools/perf/contrib/network_service/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/perf/contrib/network_service/__init__.py
diff --git a/tools/perf/contrib/network_service/loading.py b/tools/perf/contrib/network_service/loading.py
new file mode 100644
index 0000000..c2d29917
--- /dev/null
+++ b/tools/perf/contrib/network_service/loading.py
@@ -0,0 +1,30 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from benchmarks import loading
+
+from telemetry import benchmark
+
+
+@benchmark.Owner(emails=['yzshen@chromium.org'])
+class LoadingDesktopNetworkService(loading.LoadingDesktop):
+  """Measures loading performance of desktop sites, with the network service
+  enabled.
+  """
+  @classmethod
+  def Name(cls):
+    return 'loading.desktop.network_service'
+
+  def SetExtraBrowserOptions(self, options):
+    enable_features_arg = '--enable-features=NetworkService'
+
+    # If an "--enable-features" argument has been specified, append to the value
+    # list of that argument.
+    for arg in options.extra_browser_args:
+      if arg.startswith('--enable-features='):
+        options.extra_browser_args.remove(arg)
+        enable_features_arg = arg + ',NetworkService'
+        break
+
+    options.AppendExtraBrowserArgs([ enable_features_arg, '--incognito' ])
diff --git a/tools/perf/core/perf_data_generator.py b/tools/perf/core/perf_data_generator.py
index a4119dee..a0e40c2 100755
--- a/tools/perf/core/perf_data_generator.py
+++ b/tools/perf/core/perf_data_generator.py
@@ -853,17 +853,42 @@
   return os.path.join(buildbot_dir, filename)
 
 
+def get_extras_json_config_file_for_waterfall(waterfall):
+  filename = '%s.extras.json' % waterfall['name']
+  buildbot_dir = os.path.join(path_util.GetChromiumSrcDir(), 'tools', 'perf')
+  return os.path.join(buildbot_dir, filename)
+
+
+def append_extra_tests(waterfall, tests):
+  """Appends extra tests to |tests|.
+
+  Those extra tests are loaded from tools/perf/<waterfall name>.extras.json.
+  """
+  extra_config_file = get_extras_json_config_file_for_waterfall(waterfall)
+  if os.path.isfile(extra_config_file):
+    with open(extra_config_file) as extra_fp:
+      extra_tests = json.load(extra_fp)
+      for key, value in extra_tests.iteritems():
+        if key == 'comment':
+          continue
+        assert key not in tests
+        tests[key] = value
+
+
 def tests_are_up_to_date(waterfalls):
   up_to_date = True
   all_tests = {}
   for w in waterfalls:
     tests = generate_all_tests(w)
+    # Note: |all_tests| don't cover those manually-specified tests added by
+    # append_extra_tests().
+    all_tests.update(tests)
+    append_extra_tests(w, tests)
     tests_data = json.dumps(tests, indent=2, separators=(',', ': '),
                             sort_keys=True)
     config_file = get_json_config_file_for_waterfall(w)
     with open(config_file, 'r') as fp:
       config_data = fp.read().strip()
-    all_tests.update(tests)
     up_to_date &= tests_data == config_data
   verify_all_tests_in_benchmark_csv(all_tests,
                                     get_all_waterfall_benchmarks_metadata())
@@ -874,11 +899,14 @@
   all_tests = {}
   for w in waterfalls:
     tests = generate_all_tests(w)
+    # Note: |all_tests| don't cover those manually-specified tests added by
+    # append_extra_tests().
+    all_tests.update(tests)
+    append_extra_tests(w, tests)
     config_file = get_json_config_file_for_waterfall(w)
     with open(config_file, 'w') as fp:
       json.dump(tests, fp, indent=2, separators=(',', ': '), sort_keys=True)
       fp.write('\n')
-    all_tests.update(tests)
   verify_all_tests_in_benchmark_csv(all_tests,
                                     get_all_waterfall_benchmarks_metadata())
 
diff --git a/tools/perf/core/perf_data_generator_unittest.py b/tools/perf/core/perf_data_generator_unittest.py
index 6240d16..786e3fe3 100644
--- a/tools/perf/core/perf_data_generator_unittest.py
+++ b/tools/perf/core/perf_data_generator_unittest.py
@@ -275,3 +275,30 @@
                 'build1-b1': ['test'],
                 'build2-b1': ['other_test', 'test'],
             }))
+
+  def testExtraTestsAreLoadedFromFile(self):
+    tests = {
+        'Linux Perf': {}
+    }
+
+    mock_extras_json = '''
+        {
+            "comment": [ "This is comment and therefore should be skipped." ],
+            "Mojo Linux Perf": {}
+        }
+    '''
+
+    mock_waterfall_name = 'hello'
+
+    def mockIsFile(path):
+      return path.endswith('%s.extras.json' % mock_waterfall_name)
+
+    with mock.patch('os.path.isfile', side_effect=mockIsFile):
+      with mock.patch('__builtin__.open',
+                      mock.mock_open(read_data=mock_extras_json)):
+        perf_data_generator.append_extra_tests({'name': mock_waterfall_name},
+                                               tests)
+
+    self.assertTrue('Linux Perf' in tests)
+    self.assertTrue('Mojo Linux Perf' in tests)
+    self.assertFalse('comment' in tests)
diff --git a/tools/perf/page_sets/system_health/browsing_stories.py b/tools/perf/page_sets/system_health/browsing_stories.py
index f5d1a590..c9bd4c67 100644
--- a/tools/perf/page_sets/system_health/browsing_stories.py
+++ b/tools/perf/page_sets/system_health/browsing_stories.py
@@ -10,7 +10,6 @@
 from page_sets.login_helpers import facebook_login
 from page_sets.login_helpers import pinterest_login
 
-from telemetry.core import exceptions
 from telemetry.util import js_template
 
 
@@ -788,7 +787,6 @@
   SCROLL_DISTANCE = 25000
   SCROLL_STEP = 1000
   MAX_SCROLL_RETRIES = 3
-  TIME_BEFORE_SCROLL_RETRY_IN_SECONDS = 1
   TIME_TO_WAIT_BEFORE_STARTING_IN_SECONDS = 5
 
   def __init__(self, story_set, take_memory_measurement):
@@ -812,26 +810,25 @@
     """ This function scrolls the webpage by the given scroll distance in
     multiple steps, where each step (except the last one) has the given size.
 
-    If scrolling gets stuck, the functions retries scrolling MAX_SCROLL_RETRIES
-    times waiting TIME_BEFORE_SCROLL_RETRY_IN_SECONDS seconds between retries.
+    If scrolling gets stuck, the function waits for the page's scroll height to
+    expand then retries scrolling.
     """
     remaining = distance - action_runner.EvaluateJavaScript('window.scrollY')
-    retry_count = 0
     # Scroll until the window.scrollY is within 1 pixel of the target distance.
     while remaining > 1:
+      last_scroll_height = action_runner.EvaluateJavaScript(
+          'document.body.scrollHeight')
       action_runner.ScrollPage(distance=min(remaining, step_size) + 1)
       new_remaining = (distance -
           action_runner.EvaluateJavaScript('window.scrollY'))
       if remaining <= new_remaining:
         # Scrolling is stuck. This can happen if the page is loading
-        # resources. Give the page some time and retry scrolling.
-        if retry_count == self.MAX_SCROLL_RETRIES:
-          raise exceptions.StoryActionError(
-              'Scrolling stuck at %d' % remaining)
-        retry_count += 1
-        action_runner.Wait(self.TIME_BEFORE_SCROLL_RETRY_IN_SECONDS)
+        # resources. Wait for the page's scrollheight to expand and retry
+        # scrolling.
+        action_runner.WaitForJavaScriptCondition(
+            'document.body.scrollHeight > {{ last_scroll_height }} ',
+            last_scroll_height=last_scroll_height)
       else:
-        retry_count = 0
         remaining = new_remaining
 
   @classmethod
diff --git a/tools/perf/page_sets/system_health/expectations.py b/tools/perf/page_sets/system_health/expectations.py
index 45e87cd6..93b3b4c 100644
--- a/tools/perf/page_sets/system_health/expectations.py
+++ b/tools/perf/page_sets/system_health/expectations.py
@@ -28,8 +28,6 @@
                       [expectations.ALL_WIN], 'crbug.com/728152')
     self.DisableStory('browse:news:cnn',
                       [expectations.ALL_MAC], 'crbug.com/728576')
-    self.DisableStory('browse:social:twitter_infinite_scroll',
-                      [expectations.ALL], 'crbug.com/728464')
     self.DisableStory('browse:media:flickr_infinite_scroll',
                       [expectations.ALL],
                       'crbug.com/728785')
diff --git a/ui/android/BUILD.gn b/ui/android/BUILD.gn
index 7ddbe98..22a9c49 100644
--- a/ui/android/BUILD.gn
+++ b/ui/android/BUILD.gn
@@ -66,6 +66,7 @@
     "//ui/base",
     "//ui/display",
     "//ui/events",
+    "//ui/events/devices",
     "//ui/gfx",
     "//ui/gfx/geometry",
     "//url",
@@ -213,6 +214,7 @@
     "java/src/org/chromium/ui/display/DisplaySwitches.java",
     "java/src/org/chromium/ui/display/PhysicalDisplayAndroid.java",
     "java/src/org/chromium/ui/display/VirtualDisplayAndroid.java",
+    "java/src/org/chromium/ui/events/devices/InputDeviceObserver.java",
     "java/src/org/chromium/ui/gfx/BitmapHelper.java",
     "java/src/org/chromium/ui/gfx/ViewConfigurationHelper.java",
     "java/src/org/chromium/ui/gl/SurfaceTextureListener.java",
diff --git a/ui/android/delegated_frame_host_android.cc b/ui/android/delegated_frame_host_android.cc
index 396c4324a..1423adb2 100644
--- a/ui/android/delegated_frame_host_android.cc
+++ b/ui/android/delegated_frame_host_android.cc
@@ -11,9 +11,9 @@
 #include "cc/layers/surface_layer.h"
 #include "cc/output/compositor_frame.h"
 #include "cc/output/copy_output_result.h"
+#include "cc/surfaces/frame_sink_manager.h"
 #include "cc/surfaces/surface.h"
 #include "cc/surfaces/surface_id.h"
-#include "cc/surfaces/surface_manager.h"
 #include "ui/android/view_android.h"
 #include "ui/android/window_android_compositor.h"
 #include "ui/display/display.h"
@@ -51,18 +51,18 @@
 
 DelegatedFrameHostAndroid::DelegatedFrameHostAndroid(
     ui::ViewAndroid* view,
-    cc::SurfaceManager* surface_manager,
+    cc::FrameSinkManager* frame_sink_manager,
     Client* client,
     const cc::FrameSinkId& frame_sink_id)
     : frame_sink_id_(frame_sink_id),
       view_(view),
-      surface_manager_(surface_manager),
+      frame_sink_manager_(frame_sink_manager),
       client_(client),
       begin_frame_source_(this) {
   DCHECK(view_);
   DCHECK(client_);
 
-  surface_manager_->RegisterFrameSinkId(frame_sink_id_);
+  frame_sink_manager_->RegisterFrameSinkId(frame_sink_id_);
   CreateNewCompositorFrameSinkSupport();
 }
 
@@ -70,7 +70,7 @@
   DestroyDelegatedContent();
   DetachFromCompositor();
   support_.reset();
-  surface_manager_->InvalidateFrameSinkId(frame_sink_id_);
+  frame_sink_manager_->InvalidateFrameSinkId(frame_sink_id_);
 }
 
 void DelegatedFrameHostAndroid::SubmitCompositorFrame(
@@ -90,8 +90,9 @@
         support_->SubmitCompositorFrame(local_surface_id, std::move(frame));
     DCHECK(result);
 
-    content_layer_ = CreateSurfaceLayer(surface_manager_, surface_info_,
-                                        !has_transparent_background_);
+    content_layer_ =
+        CreateSurfaceLayer(frame_sink_manager_->surface_manager(),
+                           surface_info_, !has_transparent_background_);
     view_->GetLayer()->AddChild(content_layer_);
   } else {
     support_->SubmitCompositorFrame(local_surface_id, std::move(frame));
@@ -114,8 +115,9 @@
   DCHECK(surface_info_.is_valid());
   DCHECK(!result_callback.is_null());
 
-  scoped_refptr<cc::Layer> readback_layer = CreateSurfaceLayer(
-      surface_manager_, surface_info_, !has_transparent_background_);
+  scoped_refptr<cc::Layer> readback_layer =
+      CreateSurfaceLayer(frame_sink_manager_->surface_manager(), surface_info_,
+                         !has_transparent_background_);
   readback_layer->SetHideLayerAndSubtree(true);
   compositor->AttachLayerForReadback(readback_layer);
   std::unique_ptr<cc::CopyOutputRequest> copy_output_request =
@@ -198,7 +200,7 @@
   constexpr bool needs_sync_points = true;
   support_.reset();
   support_ = cc::CompositorFrameSinkSupport::Create(
-      this, surface_manager_, frame_sink_id_, is_root,
+      this, frame_sink_manager_, frame_sink_id_, is_root,
       handles_frame_sink_id_invalidation, needs_sync_points);
 }
 
diff --git a/ui/android/delegated_frame_host_android.h b/ui/android/delegated_frame_host_android.h
index e2dd6569..8ddc3e8 100644
--- a/ui/android/delegated_frame_host_android.h
+++ b/ui/android/delegated_frame_host_android.h
@@ -17,7 +17,7 @@
 namespace cc {
 
 class CompositorFrame;
-class SurfaceManager;
+class FrameSinkManager;
 class SurfaceLayer;
 enum class SurfaceDrawStatus;
 
@@ -40,7 +40,7 @@
   };
 
   DelegatedFrameHostAndroid(ViewAndroid* view,
-                            cc::SurfaceManager* surface_manager,
+                            cc::FrameSinkManager* frame_sink_manager,
                             Client* client,
                             const cc::FrameSinkId& frame_sink_id);
 
@@ -91,7 +91,7 @@
 
   ViewAndroid* view_;
 
-  cc::SurfaceManager* surface_manager_;
+  cc::FrameSinkManager* const frame_sink_manager_;
   WindowAndroidCompositor* registered_parent_compositor_ = nullptr;
   Client* client_;
 
diff --git a/ui/android/java/src/org/chromium/ui/events/devices/InputDeviceObserver.java b/ui/android/java/src/org/chromium/ui/events/devices/InputDeviceObserver.java
new file mode 100644
index 0000000..df2254f
--- /dev/null
+++ b/ui/android/java/src/org/chromium/ui/events/devices/InputDeviceObserver.java
@@ -0,0 +1,81 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.ui.events.devices;
+
+import android.content.Context;
+import android.hardware.input.InputManager;
+import android.hardware.input.InputManager.InputDeviceListener;
+
+import org.chromium.base.ContextUtils;
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+
+/**
+ * A singleton that helps detecting changes in input devices through the interface
+ * {@link InputDeviceObserver}.
+ */
+@JNINamespace("ui")
+public class InputDeviceObserver implements InputDeviceListener {
+    private static final InputDeviceObserver INSTANCE = new InputDeviceObserver();
+
+    /**
+     * Notifies the InputDeviceObserver that an observer is attached and it
+     * should prepare itself for listening input changes.
+     */
+    @CalledByNative
+    public static void addObserver() {
+        assert ThreadUtils.runningOnUiThread();
+        INSTANCE.attachObserver();
+    }
+
+    /**
+     * Notifies the InputDeviceObserver that an observer has been removed.
+     */
+    @CalledByNative
+    public static void removeObserver() {
+        assert ThreadUtils.runningOnUiThread();
+        INSTANCE.detachObserver();
+    }
+
+    private InputManager mInputManager;
+    private InputDeviceListener mInputDeviceListener;
+    private int mObserversCounter;
+
+    // Override InputDeviceListener methods
+    @Override
+    public void onInputDeviceChanged(int deviceId) {
+        nativeInputConfigurationChanged();
+    }
+
+    @Override
+    public void onInputDeviceRemoved(int deviceId) {
+        nativeInputConfigurationChanged();
+    }
+
+    @Override
+    public void onInputDeviceAdded(int deviceId) {
+        nativeInputConfigurationChanged();
+    }
+
+    private void attachObserver() {
+        if (mObserversCounter++ == 0) {
+            Context context = ContextUtils.getApplicationContext();
+            mInputManager = (InputManager) context.getSystemService(Context.INPUT_SERVICE);
+            // Register an input device listener.
+            mInputManager.registerInputDeviceListener(this, null);
+        }
+    }
+
+    private void detachObserver() {
+        assert mObserversCounter > 0;
+        if (--mObserversCounter == 0) {
+            mInputManager.unregisterInputDeviceListener(this);
+            mInputManager = null;
+        }
+    }
+
+    private native void nativeInputConfigurationChanged();
+}
\ No newline at end of file
diff --git a/ui/aura/demo/demo_main.cc b/ui/aura/demo/demo_main.cc
index a416ebd1..c8a5afc 100644
--- a/ui/aura/demo/demo_main.cc
+++ b/ui/aura/demo/demo_main.cc
@@ -138,10 +138,10 @@
 #endif
 
   // The ContextFactory must exist before any Compositors are created.
-  viz::HostFrameSinkManager frame_sink_manager;
-  cc::SurfaceManager surface_manager;
+  viz::HostFrameSinkManager host_frame_sink_manager;
+  cc::FrameSinkManager frame_sink_manager;
   auto context_factory = base::MakeUnique<ui::InProcessContextFactory>(
-      &frame_sink_manager, &surface_manager);
+      &host_frame_sink_manager, &frame_sink_manager);
   context_factory->set_use_test_surface(false);
 
   // Create the message-loop here before creating the root window.
diff --git a/ui/aura/local/layer_tree_frame_sink_local.cc b/ui/aura/local/layer_tree_frame_sink_local.cc
index 174dd676..196a7c4 100644
--- a/ui/aura/local/layer_tree_frame_sink_local.cc
+++ b/ui/aura/local/layer_tree_frame_sink_local.cc
@@ -17,10 +17,10 @@
 
 LayerTreeFrameSinkLocal::LayerTreeFrameSinkLocal(
     const cc::FrameSinkId& frame_sink_id,
-    cc::SurfaceManager* surface_manager)
+    cc::FrameSinkManager* frame_sink_manager)
     : cc::LayerTreeFrameSink(nullptr, nullptr, nullptr, nullptr),
       frame_sink_id_(frame_sink_id),
-      surface_manager_(surface_manager) {}
+      frame_sink_manager_(frame_sink_manager) {}
 
 LayerTreeFrameSinkLocal::~LayerTreeFrameSinkLocal() {}
 
@@ -32,7 +32,7 @@
   thread_checker_ = base::MakeUnique<base::ThreadChecker>();
 
   support_ = cc::CompositorFrameSinkSupport::Create(
-      this, surface_manager_, frame_sink_id_, false /* is_root */,
+      this, frame_sink_manager_, frame_sink_id_, false /* is_root */,
       true /* handles_frame_sink_id_invalidation */,
       true /* needs_sync_points */);
   begin_frame_source_ = base::MakeUnique<cc::ExternalBeginFrameSource>(this);
diff --git a/ui/aura/local/layer_tree_frame_sink_local.h b/ui/aura/local/layer_tree_frame_sink_local.h
index 55d05fe..7f8beaa 100644
--- a/ui/aura/local/layer_tree_frame_sink_local.h
+++ b/ui/aura/local/layer_tree_frame_sink_local.h
@@ -17,7 +17,7 @@
 
 namespace cc {
 class CompositorFrameSinkSupport;
-class SurfaceManager;
+class FrameSinkManager;
 }  // namespace cc
 
 namespace aura {
@@ -31,7 +31,7 @@
                                 public cc::ExternalBeginFrameSourceClient {
  public:
   LayerTreeFrameSinkLocal(const cc::FrameSinkId& frame_sink_id,
-                          cc::SurfaceManager* surface_manager);
+                          cc::FrameSinkManager* frame_sink_manager);
   ~LayerTreeFrameSinkLocal() override;
 
   using SurfaceChangedCallback =
@@ -59,7 +59,7 @@
 
  private:
   const cc::FrameSinkId frame_sink_id_;
-  cc::SurfaceManager* const surface_manager_;
+  cc::FrameSinkManager* const frame_sink_manager_;
   std::unique_ptr<cc::CompositorFrameSinkSupport> support_;
   gfx::Size surface_size_;
   float device_scale_factor_ = 0;
diff --git a/ui/aura/local/window_port_local.cc b/ui/aura/local/window_port_local.cc
index 64540ba..c2b2ad2 100644
--- a/ui/aura/local/window_port_local.cc
+++ b/ui/aura/local/window_port_local.cc
@@ -4,7 +4,7 @@
 
 #include "ui/aura/local/window_port_local.h"
 
-#include "cc/surfaces/surface_manager.h"
+#include "cc/surfaces/frame_sink_manager.h"
 #include "ui/aura/client/cursor_client.h"
 #include "ui/aura/env.h"
 #include "ui/aura/local/layer_tree_frame_sink_local.h"
@@ -103,7 +103,7 @@
       aura::Env::GetInstance()->context_factory_private();
   frame_sink_id_ = context_factory_private->AllocateFrameSinkId();
   auto frame_sink = base::MakeUnique<LayerTreeFrameSinkLocal>(
-      frame_sink_id_, context_factory_private->GetSurfaceManager());
+      frame_sink_id_, context_factory_private->GetFrameSinkManager());
   frame_sink->SetSurfaceChangedCallback(base::Bind(
       &WindowPortLocal::OnSurfaceChanged, weak_factory_.GetWeakPtr()));
   if (window_->GetRootWindow())
@@ -133,7 +133,8 @@
   scoped_refptr<cc::SurfaceReferenceFactory> reference_factory =
       aura::Env::GetInstance()
           ->context_factory_private()
-          ->GetSurfaceManager()
+          ->GetFrameSinkManager()
+          ->surface_manager()
           ->reference_factory();
   window_->layer()->SetShowPrimarySurface(surface_info, reference_factory);
   window_->layer()->SetFallbackSurface(surface_info);
diff --git a/ui/base/BUILD.gn b/ui/base/BUILD.gn
index 4441ecb..f7b9f78 100644
--- a/ui/base/BUILD.gn
+++ b/ui/base/BUILD.gn
@@ -367,7 +367,6 @@
     sources += [ "touch/touch_device_win.cc" ]
   } else if (is_android) {
     sources += [ "touch/touch_device_android.cc" ]
-    sources -= [ "touch/touch_device_util.cc" ]
   } else if (is_ios) {
     sources += [ "touch/touch_device_ios.cc" ]
   } else if (is_linux) {
diff --git a/ui/base/touch/touch_device_android.cc b/ui/base/touch/touch_device_android.cc
index 2a65323..92febc1 100644
--- a/ui/base/touch/touch_device_android.cc
+++ b/ui/base/touch/touch_device_android.cc
@@ -20,7 +20,7 @@
   return Java_TouchDevice_maxTouchPoints(AttachCurrentThread());
 }
 
-std::pair<int, int> GetAvailablePointerAndHoverTypes() {
+std::pair<int, int> AvailablePointerAndHoverTypes() {
   JNIEnv* env = AttachCurrentThread();
   std::vector<int> pointer_and_hover_types;
   base::android::JavaIntArrayToIntVector(
@@ -30,6 +30,14 @@
   return std::make_pair(pointer_and_hover_types[0], pointer_and_hover_types[1]);
 }
 
+int GetAvailableHoverTypes() {
+  return AvailablePointerAndHoverTypes().second;
+}
+
+int GetAvailablePointerTypes() {
+  return AvailablePointerAndHoverTypes().first;
+}
+
 PointerType GetPrimaryPointerType(int available_pointer_types) {
   if (available_pointer_types & POINTER_TYPE_COARSE)
     return POINTER_TYPE_COARSE;
diff --git a/ui/compositor/compositor.cc b/ui/compositor/compositor.cc
index 01c52ef..d851a9b 100644
--- a/ui/compositor/compositor.cc
+++ b/ui/compositor/compositor.cc
@@ -30,8 +30,8 @@
 #include "cc/output/context_provider.h"
 #include "cc/output/latency_info_swap_promise.h"
 #include "cc/scheduler/begin_frame_source.h"
+#include "cc/surfaces/frame_sink_manager.h"
 #include "cc/surfaces/local_surface_id_allocator.h"
-#include "cc/surfaces/surface_manager.h"
 #include "cc/trees/layer_tree_host.h"
 #include "cc/trees/layer_tree_settings.h"
 #include "third_party/skia/include/core/SkBitmap.h"
@@ -66,7 +66,7 @@
       weak_ptr_factory_(this),
       lock_timeout_weak_ptr_factory_(this) {
   if (context_factory_private) {
-    context_factory_private->GetSurfaceManager()->RegisterFrameSinkId(
+    context_factory_private->GetFrameSinkManager()->RegisterFrameSinkId(
         frame_sink_id_);
   }
   root_web_layer_ = cc::Layer::Create();
@@ -200,7 +200,7 @@
 
   context_factory_->RemoveCompositor(this);
   if (context_factory_private_) {
-    auto* manager = context_factory_private_->GetSurfaceManager();
+    auto* manager = context_factory_private_->GetFrameSinkManager();
     for (auto& client : child_frame_sinks_) {
       DCHECK(client.is_valid());
       manager->UnregisterFrameSinkHierarchy(frame_sink_id_, client);
@@ -216,7 +216,7 @@
 void Compositor::AddFrameSink(const cc::FrameSinkId& frame_sink_id) {
   if (!context_factory_private_)
     return;
-  context_factory_private_->GetSurfaceManager()->RegisterFrameSinkHierarchy(
+  context_factory_private_->GetFrameSinkManager()->RegisterFrameSinkHierarchy(
       frame_sink_id_, frame_sink_id);
   child_frame_sinks_.insert(frame_sink_id);
 }
@@ -227,7 +227,7 @@
   auto it = child_frame_sinks_.find(frame_sink_id);
   DCHECK(it != child_frame_sinks_.end());
   DCHECK(it->is_valid());
-  context_factory_private_->GetSurfaceManager()->UnregisterFrameSinkHierarchy(
+  context_factory_private_->GetFrameSinkManager()->UnregisterFrameSinkHierarchy(
       frame_sink_id_, *it);
   child_frame_sinks_.erase(it);
 }
diff --git a/ui/compositor/compositor.h b/ui/compositor/compositor.h
index 3245d5e..292d734 100644
--- a/ui/compositor/compositor.h
+++ b/ui/compositor/compositor.h
@@ -41,13 +41,13 @@
 class AnimationHost;
 class AnimationTimeline;
 class ContextProvider;
+class FrameSinkManager;
 class Layer;
 class LayerTreeDebugState;
 class LayerTreeFrameSink;
 class LayerTreeHost;
 class LocalSurfaceId;
 class ResourceSettings;
-class SurfaceManager;
 class TaskGraphRunner;
 }
 
@@ -105,8 +105,10 @@
   // Allocate a new client ID for the display compositor.
   virtual cc::FrameSinkId AllocateFrameSinkId() = 0;
 
-  // Gets the surface manager.
-  virtual cc::SurfaceManager* GetSurfaceManager() = 0;
+  // Gets the frame sink manager.
+  // TODO(staraz): Remove GetFrameSinkManager once FrameSinkManager is merged
+  // into FrameSinkManagerImpl.
+  virtual cc::FrameSinkManager* GetFrameSinkManager() = 0;
 
   // Gets the frame sink manager host instance.
   virtual viz::HostFrameSinkManager* GetHostFrameSinkManager() = 0;
diff --git a/ui/compositor/test/context_factories_for_test.cc b/ui/compositor/test/context_factories_for_test.cc
index ff5f348..0d1130bc 100644
--- a/ui/compositor/test/context_factories_for_test.cc
+++ b/ui/compositor/test/context_factories_for_test.cc
@@ -6,6 +6,7 @@
 
 #include "base/command_line.h"
 #include "base/sys_info.h"
+#include "cc/surfaces/frame_sink_manager.h"
 #include "components/viz/host/host_frame_sink_manager.h"
 #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
 #include "ui/compositor/compositor.h"
@@ -56,7 +57,8 @@
   g_host_frame_sink_manager = new viz::HostFrameSinkManager;
   g_frame_sink_manager_impl = new viz::FrameSinkManagerImpl(false, nullptr);
   g_implicit_factory = new InProcessContextFactory(
-      g_host_frame_sink_manager, g_frame_sink_manager_impl->surface_manager());
+      g_host_frame_sink_manager,
+      g_frame_sink_manager_impl->frame_sink_manager());
   g_implicit_factory->SetUseFastRefreshRateForTests();
   *context_factory = g_implicit_factory;
   *context_factory_private = g_implicit_factory;
diff --git a/ui/compositor/test/in_process_context_factory.cc b/ui/compositor/test/in_process_context_factory.cc
index fcb27bc..0693e7e 100644
--- a/ui/compositor/test/in_process_context_factory.cc
+++ b/ui/compositor/test/in_process_context_factory.cc
@@ -21,6 +21,7 @@
 #include "cc/surfaces/direct_layer_tree_frame_sink.h"
 #include "cc/surfaces/display.h"
 #include "cc/surfaces/display_scheduler.h"
+#include "cc/surfaces/frame_sink_manager.h"
 #include "cc/surfaces/local_surface_id_allocator.h"
 #include "cc/test/pixel_test_output_surface.h"
 #include "components/viz/host/host_frame_sink_manager.h"
@@ -135,13 +136,13 @@
 };
 
 InProcessContextFactory::InProcessContextFactory(
-    viz::HostFrameSinkManager* frame_sink_manager,
-    cc::SurfaceManager* surface_manager)
+    viz::HostFrameSinkManager* host_frame_sink_manager,
+    cc::FrameSinkManager* frame_sink_manager)
     : frame_sink_id_allocator_(kDefaultClientId),
       use_test_surface_(true),
-      frame_sink_manager_(frame_sink_manager),
-      surface_manager_(surface_manager) {
-  DCHECK(frame_sink_manager);
+      host_frame_sink_manager_(host_frame_sink_manager),
+      frame_sink_manager_(frame_sink_manager) {
+  DCHECK(host_frame_sink_manager);
   DCHECK_NE(gl::GetGLImplementation(), gl::kGLImplementationNone)
       << "If running tests, ensure that main() is calling "
       << "gl::GLSurfaceTestSupport::InitializeOneOff()";
@@ -244,15 +245,15 @@
       std::move(scheduler),
       base::MakeUnique<cc::TextureMailboxDeleter>(
           compositor->task_runner().get()));
-  GetSurfaceManager()->RegisterBeginFrameSource(begin_frame_source.get(),
-                                                compositor->frame_sink_id());
+  GetFrameSinkManager()->RegisterBeginFrameSource(begin_frame_source.get(),
+                                                  compositor->frame_sink_id());
   // Note that we are careful not to destroy a prior |data->begin_frame_source|
   // until we have reset |data->display|.
   data->begin_frame_source = std::move(begin_frame_source);
 
   auto* display = per_compositor_data_[compositor.get()]->display.get();
   auto layer_tree_frame_sink = base::MakeUnique<cc::DirectLayerTreeFrameSink>(
-      compositor->frame_sink_id(), GetSurfaceManager(), display,
+      compositor->frame_sink_id(), GetFrameSinkManager(), display,
       context_provider, shared_worker_context_provider_,
       &gpu_memory_buffer_manager_, &shared_bitmap_manager_);
   compositor->SetLayerTreeFrameSink(std::move(layer_tree_frame_sink));
@@ -290,7 +291,7 @@
   if (it == per_compositor_data_.end())
     return;
   PerCompositorData* data = it->second.get();
-  GetSurfaceManager()->UnregisterBeginFrameSource(
+  GetFrameSinkManager()->UnregisterBeginFrameSource(
       data->begin_frame_source.get());
   DCHECK(data);
 #if !defined(GPU_SURFACE_HANDLE_IS_ACCELERATED_WINDOW)
@@ -317,12 +318,8 @@
   return frame_sink_id_allocator_.NextFrameSinkId();
 }
 
-cc::SurfaceManager* InProcessContextFactory::GetSurfaceManager() {
-  return surface_manager_;
-}
-
 viz::HostFrameSinkManager* InProcessContextFactory::GetHostFrameSinkManager() {
-  return frame_sink_manager_;
+  return host_frame_sink_manager_;
 }
 
 void InProcessContextFactory::SetDisplayVisible(ui::Compositor* compositor,
@@ -352,6 +349,10 @@
   observer_list_.RemoveObserver(observer);
 }
 
+cc::FrameSinkManager* InProcessContextFactory::GetFrameSinkManager() {
+  return frame_sink_manager_;
+}
+
 InProcessContextFactory::PerCompositorData*
 InProcessContextFactory::CreatePerCompositorData(ui::Compositor* compositor) {
   DCHECK(!per_compositor_data_[compositor]);
diff --git a/ui/compositor/test/in_process_context_factory.h b/ui/compositor/test/in_process_context_factory.h
index 151c86be..2bb2b20 100644
--- a/ui/compositor/test/in_process_context_factory.h
+++ b/ui/compositor/test/in_process_context_factory.h
@@ -11,6 +11,7 @@
 #include "base/macros.h"
 #include "cc/surfaces/display.h"
 #include "cc/surfaces/frame_sink_id_allocator.h"
+#include "cc/surfaces/frame_sink_manager.h"
 #include "cc/test/test_gpu_memory_buffer_manager.h"
 #include "cc/test/test_image_factory.h"
 #include "cc/test/test_shared_bitmap_manager.h"
@@ -19,8 +20,8 @@
 #include "ui/compositor/compositor.h"
 
 namespace cc {
+class FrameSinkManager;
 class ResourceSettings;
-class SurfaceManager;
 }
 
 namespace viz {
@@ -33,12 +34,12 @@
 class InProcessContextFactory : public ContextFactory,
                                 public ContextFactoryPrivate {
  public:
-  // Both |host_frame_sink_manager| and |surface_manager| must outlive the
+  // Both |host_frame_sink_manager| and |frame_sink_manager| must outlive the
   // ContextFactory.
-  // TODO(crbug.com/657959): |surface_manager| should go away and we should use
-  // the LayerTreeFrameSink from the HostFrameSinkManager.
+  // TODO(crbug.com/657959): |frame_sink_manager| should go away and we should
+  // use the LayerTreeFrameSink from the HostFrameSinkManager.
   InProcessContextFactory(viz::HostFrameSinkManager* host_frame_sink_manager,
-                          cc::SurfaceManager* surface_manager);
+                          cc::FrameSinkManager* frame_sink_manager);
   ~InProcessContextFactory() override;
 
   // If true (the default) an OutputSurface is created that does not display
@@ -68,7 +69,6 @@
   gpu::GpuMemoryBufferManager* GetGpuMemoryBufferManager() override;
   cc::TaskGraphRunner* GetTaskGraphRunner() override;
   cc::FrameSinkId AllocateFrameSinkId() override;
-  cc::SurfaceManager* GetSurfaceManager() override;
   viz::HostFrameSinkManager* GetHostFrameSinkManager() override;
   void SetDisplayVisible(ui::Compositor* compositor, bool visible) override;
   void ResizeDisplay(ui::Compositor* compositor,
@@ -86,6 +86,7 @@
   const cc::ResourceSettings& GetResourceSettings() const override;
   void AddObserver(ContextFactoryObserver* observer) override;
   void RemoveObserver(ContextFactoryObserver* observer) override;
+  cc::FrameSinkManager* GetFrameSinkManager() override;
 
  private:
   struct PerCompositorData;
@@ -101,8 +102,8 @@
   cc::FrameSinkIdAllocator frame_sink_id_allocator_;
   bool use_test_surface_;
   double refresh_rate_ = 60.0;
-  viz::HostFrameSinkManager* frame_sink_manager_;
-  cc::SurfaceManager* surface_manager_;
+  viz::HostFrameSinkManager* const host_frame_sink_manager_;
+  cc::FrameSinkManager* const frame_sink_manager_;
   base::ObserverList<ContextFactoryObserver> observer_list_;
 
   cc::RendererSettings renderer_settings_;
diff --git a/ui/events/devices/BUILD.gn b/ui/events/devices/BUILD.gn
index 9565cc7..ca8c4e8 100644
--- a/ui/events/devices/BUILD.gn
+++ b/ui/events/devices/BUILD.gn
@@ -2,6 +2,11 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+if (is_android) {
+  import("//build/config/android/config.gni")
+  import("//build/config/android/rules.gni")
+}
+
 component("devices") {
   sources = [
     "device_data_manager.cc",
@@ -15,6 +20,8 @@
     "input_device_event_observer.h",
     "input_device_manager.cc",
     "input_device_manager.h",
+    "input_device_observer_android.cc",
+    "input_device_observer_android.h",
     "input_device_observer_win.cc",
     "input_device_observer_win.h",
     "stylus_state.h",
@@ -35,4 +42,17 @@
     "//ui/display/types",
     "//ui/gfx/geometry",
   ]
+
+  if (is_android) {
+    deps += [ ":ui_events_devices_jni_headers" ]
+  }
+}
+
+if (is_android) {
+  generate_jni("ui_events_devices_jni_headers") {
+    sources = [
+      "../../android/java/src/org/chromium/ui/events/devices/InputDeviceObserver.java",
+    ]
+    jni_package = "events/devices"
+  }
 }
diff --git a/ui/events/devices/DEPS b/ui/events/devices/DEPS
new file mode 100644
index 0000000..1d2c11b
--- /dev/null
+++ b/ui/events/devices/DEPS
@@ -0,0 +1,5 @@
+specific_include_rules = {
+  "input_device_observer_android.cc": [
+    "+jni",
+  ],
+}
diff --git a/ui/events/devices/input_device_observer_android.cc b/ui/events/devices/input_device_observer_android.cc
new file mode 100644
index 0000000..3313b7fe
--- /dev/null
+++ b/ui/events/devices/input_device_observer_android.cc
@@ -0,0 +1,67 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/events/devices/input_device_observer_android.h"
+
+#include "base/memory/singleton.h"
+#include "jni/InputDeviceObserver_jni.h"
+
+using base::android::AttachCurrentThread;
+using base::android::JavaParamRef;
+
+// This macro provides the implementation for the observer notification methods.
+#define NOTIFY_OBSERVERS(method_decl, observer_call)           \
+  void InputDeviceObserverAndroid::method_decl {               \
+    for (ui::InputDeviceEventObserver & observer : observers_) \
+      observer.observer_call;                                  \
+  }
+
+namespace ui {
+
+bool InputDeviceObserverAndroid::RegisterInputDeviceObserver(JNIEnv* env) {
+  return RegisterNativesImpl(env);
+}
+
+InputDeviceObserverAndroid::InputDeviceObserverAndroid() {}
+
+InputDeviceObserverAndroid::~InputDeviceObserverAndroid() {}
+
+InputDeviceObserverAndroid* InputDeviceObserverAndroid::GetInstance() {
+  return base::Singleton<
+      InputDeviceObserverAndroid,
+      base::LeakySingletonTraits<InputDeviceObserverAndroid>>::get();
+}
+
+void InputDeviceObserverAndroid::AddObserver(
+    ui::InputDeviceEventObserver* observer) {
+  observers_.AddObserver(observer);
+  JNIEnv* env = AttachCurrentThread();
+  Java_InputDeviceObserver_addObserver(env);
+}
+
+void InputDeviceObserverAndroid::RemoveObserver(
+    ui::InputDeviceEventObserver* observer) {
+  observers_.RemoveObserver(observer);
+  JNIEnv* env = AttachCurrentThread();
+  Java_InputDeviceObserver_removeObserver(env);
+}
+
+static void InputConfigurationChanged(JNIEnv* env,
+                                      const JavaParamRef<jobject>& obj) {
+  InputDeviceObserverAndroid::GetInstance()
+      ->NotifyObserversTouchpadDeviceConfigurationChanged();
+  InputDeviceObserverAndroid::GetInstance()
+      ->NotifyObserversKeyboardDeviceConfigurationChanged();
+  InputDeviceObserverAndroid::GetInstance()
+      ->NotifyObserversMouseDeviceConfigurationChanged();
+}
+
+NOTIFY_OBSERVERS(NotifyObserversMouseDeviceConfigurationChanged(),
+                 OnMouseDeviceConfigurationChanged());
+NOTIFY_OBSERVERS(NotifyObserversTouchpadDeviceConfigurationChanged(),
+                 OnTouchpadDeviceConfigurationChanged());
+NOTIFY_OBSERVERS(NotifyObserversKeyboardDeviceConfigurationChanged(),
+                 OnKeyboardDeviceConfigurationChanged());
+
+}  // namespace ui
\ No newline at end of file
diff --git a/ui/events/devices/input_device_observer_android.h b/ui/events/devices/input_device_observer_android.h
new file mode 100644
index 0000000..311dfa82
--- /dev/null
+++ b/ui/events/devices/input_device_observer_android.h
@@ -0,0 +1,48 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_EVENTS_DEVICES_INPUT_DEVICE_OBSERVER_ANDROID_H_
+#define UI_EVENTS_DEVICES_INPUT_DEVICE_OBSERVER_ANDROID_H_
+
+#include <jni.h>
+
+#include "base/observer_list.h"
+#include "ui/events/devices/input_device_event_observer.h"
+
+namespace base {
+template <typename T>
+struct DefaultSingletonTraits;
+}
+
+namespace ui {
+
+// This class is a singleton responsible to notify the
+// InputDeviceChangeObserver whenever an input change
+// happened on the Java side.
+class EVENTS_DEVICES_EXPORT InputDeviceObserverAndroid {
+ public:
+  static InputDeviceObserverAndroid* GetInstance();
+  ~InputDeviceObserverAndroid();
+
+  static bool RegisterInputDeviceObserver(JNIEnv* env);
+
+  void AddObserver(ui::InputDeviceEventObserver* observer);
+  void RemoveObserver(ui::InputDeviceEventObserver* observer);
+
+  void NotifyObserversTouchpadDeviceConfigurationChanged();
+  void NotifyObserversKeyboardDeviceConfigurationChanged();
+  void NotifyObserversMouseDeviceConfigurationChanged();
+
+ private:
+  InputDeviceObserverAndroid();
+
+  base::ObserverList<ui::InputDeviceEventObserver> observers_;
+
+  friend struct base::DefaultSingletonTraits<InputDeviceObserverAndroid>;
+  DISALLOW_COPY_AND_ASSIGN(InputDeviceObserverAndroid);
+};
+
+}  // namespace ui
+
+#endif  // UI_EVENTS_DEVICES_INPUT_DEVICE_OBSERVER_ANDROID_H_
diff --git a/ui/events/devices/input_device_observer_win.h b/ui/events/devices/input_device_observer_win.h
index fd0f5b4..3390507 100644
--- a/ui/events/devices/input_device_observer_win.h
+++ b/ui/events/devices/input_device_observer_win.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_BROWSER_RENDERER_HOST_INPUT_INPUT_DEVICE_OBSERVER_WIN_H_
-#define CONTENT_BROWSER_RENDERER_HOST_INPUT_INPUT_DEVICE_OBSERVER_WIN_H_
+#ifndef UI_EVENTS_DEVICES_INPUT_DEVICE_OBSERVER_WIN_H_
+#define UI_EVENTS_DEVICES_INPUT_DEVICE_OBSERVER_WIN_H_
 
 #include "base/observer_list.h"
 #include "base/win/registry.h"
@@ -45,4 +45,4 @@
 
 }  // namespace ui
 
-#endif  // CONTENT_BROWSER_RENDERER_HOST_INPUT_INPUT_DEVICE_OBSERVER_WIN_H_
\ No newline at end of file
+#endif  // UI_EVENTS_DEVICES_INPUT_DEVICE_OBSERVER_WIN_H_
\ No newline at end of file
diff --git a/ui/events/keycodes/dom/dom_key_data.inc b/ui/events/keycodes/dom/dom_key_data.inc
index c598dcdd..4362d55c 100644
--- a/ui/events/keycodes/dom/dom_key_data.inc
+++ b/ui/events/keycodes/dom/dom_key_data.inc
@@ -361,6 +361,10 @@
   DOM_KEY_MAP("LaunchWebBrowser",     LAUNCH_WEB_BROWSER,       0x0B09),
   DOM_KEY_MAP("LaunchWebCam",         LAUNCH_WEB_CAM,           0x0B0A),
   DOM_KEY_MAP("LaunchWordProcessor",  LAUNCH_WORD_PROCESSOR,    0x0B0B),
+  // Note: Launch Assistant entry is currently Chrome-specific.
+  // It is not included in any web standard because it is not exposed
+  // to the web platform.
+  DOM_KEY_MAP("LaunchAssistant",      LAUNCH_ASSISTANT,         0x0B0E),
 
   // =========================================================
   // Browser Keys
diff --git a/ui/events/keycodes/dom/keycode_converter_data.inc b/ui/events/keycodes/dom/keycode_converter_data.inc
index a62d390c..6c64672 100644
--- a/ui/events/keycodes/dom/keycode_converter_data.inc
+++ b/ui/events/keycodes/dom/keycode_converter_data.inc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// This file has no header guard because it is explicily intended
+// This file has no header guard because it is explicitly intended
 // to be included more than once with different definitions of the
 // macros USB_KEYMAP and USB_KEYMAP_DECLARATION.
 
@@ -97,6 +97,9 @@
   USB_KEYMAP(0x000014, 0x0000, 0x0000, 0x0000, 0xffff, "Suspend", SUSPEND),
   USB_KEYMAP(0x000015, 0x0000, 0x0000, 0x0000, 0xffff, "Resume", RESUME),
   USB_KEYMAP(0x000016, 0x0000, 0x0000, 0x0000, 0xffff, "Turbo", TURBO),
+  // AL Context-aware desktop assistant, not in HID specification (yet?)
+  USB_KEYMAP(0x000017, 0x0247, 0x024f, 0x0000, 0xffff, "LaunchAssistant",
+             LAUNCH_ASSISTANT),
 
   // =========================================
   // USB Usage Page 0x01: Generic Desktop Page
diff --git a/ui/events/keycodes/dom/keycode_converter_unittest.cc b/ui/events/keycodes/dom/keycode_converter_unittest.cc
index b6bab47..2fa7249 100644
--- a/ui/events/keycodes/dom/keycode_converter_unittest.cc
+++ b/ui/events/keycodes/dom/keycode_converter_unittest.cc
@@ -23,8 +23,8 @@
 // These are in the same order as the columns in keycode_converter_data.inc
 // as reflected in the USB_KEYMAP() macro below.
 const size_t expected_mapped_key_count[] = {
-  207, // evdev
-  207, // xkb
+  208, // evdev
+  208, // xkb
   157, // windows
   118, // mac
 };
diff --git a/ui/events/keycodes/dom_us_layout_data.h b/ui/events/keycodes/dom_us_layout_data.h
index 4cf29e9..f6d295a 100644
--- a/ui/events/keycodes/dom_us_layout_data.h
+++ b/ui/events/keycodes/dom_us_layout_data.h
@@ -177,6 +177,7 @@
     {DomCode::LANG5, DomKey::ZENKAKU_HANKAKU},
     {DomCode::LAUNCH_APP1, DomKey::LAUNCH_MY_COMPUTER},
     {DomCode::LAUNCH_APP2, DomKey::LAUNCH_CALCULATOR},
+    {DomCode::LAUNCH_ASSISTANT, DomKey::LAUNCH_ASSISTANT},
     {DomCode::LAUNCH_AUDIO_BROWSER, DomKey::LAUNCH_MUSIC_PLAYER},
     {DomCode::LAUNCH_CALENDAR, DomKey::LAUNCH_CALENDAR},
     {DomCode::LAUNCH_CONTACTS, DomKey::LAUNCH_CONTACTS},
@@ -308,6 +309,7 @@
     // Device Keys
     // http://www.w3.org/TR/DOM-Level-3-Events-key/#keys-device
 #if defined(OS_POSIX)
+    {DomKey::LAUNCH_ASSISTANT, VKEY_ASSISTANT},
     {DomKey::BRIGHTNESS_DOWN, VKEY_BRIGHTNESS_DOWN},
     {DomKey::BRIGHTNESS_UP, VKEY_BRIGHTNESS_UP},
     {DomKey::POWER, VKEY_POWER},
@@ -413,6 +415,10 @@
     // DomCode::SUSPEND                            0x000014 Suspend
     // DomCode::RESUME                             0x000015 Resume
     // DomCode::TURBO                              0x000016 Turbo
+#if defined(OS_POSIX)
+    {DomCode::LAUNCH_ASSISTANT,
+     VKEY_ASSISTANT},                          // 0x000017 Launch Assistant
+#endif
     {DomCode::SLEEP, VKEY_SLEEP},               // 0x010082 Sleep
     // DomCode::WAKE_UP                            0x010083 WakeUp
     {DomCode::US_A, VKEY_A},                   // 0x070004 KeyA
diff --git a/ui/events/keycodes/keyboard_code_conversion.cc b/ui/events/keycodes/keyboard_code_conversion.cc
index aad6acf..c112545 100644
--- a/ui/events/keycodes/keyboard_code_conversion.cc
+++ b/ui/events/keycodes/keyboard_code_conversion.cc
@@ -33,6 +33,19 @@
   return 0;
 }
 
+bool DomCodeToNonPrintableDomKey(DomCode dom_code,
+                                 DomKey* out_dom_key,
+                                 KeyboardCode* out_key_code) {
+  for (const auto& it : kNonPrintableCodeMap) {
+    if (it.dom_code == dom_code) {
+      *out_dom_key = it.dom_key;
+      *out_key_code = NonPrintableDomKeyToKeyboardCode(it.dom_key);
+      return true;
+    }
+  }
+  return false;
+}
+
 bool DomCodeToUsLayoutDomKey(DomCode dom_code,
                              int flags,
                              DomKey* out_dom_key,
@@ -51,14 +64,8 @@
       return true;
     }
   }
-  for (const auto& it : kNonPrintableCodeMap) {
-    if (it.dom_code == dom_code) {
-      *out_dom_key = it.dom_key;
-      *out_key_code = NonPrintableDomKeyToKeyboardCode(it.dom_key);
-      return true;
-    }
-  }
-  return false;
+
+  return DomCodeToNonPrintableDomKey(dom_code, out_dom_key, out_key_code);
 }
 
 bool DomCodeToControlCharacter(DomCode dom_code,
diff --git a/ui/events/keycodes/keyboard_code_conversion.h b/ui/events/keycodes/keyboard_code_conversion.h
index 10a26150..2815c8df 100644
--- a/ui/events/keycodes/keyboard_code_conversion.h
+++ b/ui/events/keycodes/keyboard_code_conversion.h
@@ -48,6 +48,16 @@
                                                 KeyboardCode* key_code)
     WARN_UNUSED_RESULT;
 
+// Helper function to map a physical key (dom_code) to a meaning (dom_key
+// and character, together corresponding to the DOM keyboard event |key|
+// value), along with a corresponding non-located Windows-based key_code.
+// Unlike |DomCodeToUsLayoutDomKey| this function only maps non-printable,
+// or action, keys.
+EVENTS_BASE_EXPORT bool DomCodeToNonPrintableDomKey(DomCode dom_code,
+                                                    DomKey* dom_key,
+                                                    KeyboardCode* key_code)
+    WARN_UNUSED_RESULT;
+
 // Obtains the control character corresponding to a physical key;
 // that is, the meaning of the physical key state (dom_code, and flags
 // containing EF_CONTROL_DOWN) under the base US English layout.
diff --git a/ui/events/keycodes/keyboard_codes_posix.h b/ui/events/keycodes/keyboard_codes_posix.h
index 9c67648..31fe7195 100644
--- a/ui/events/keycodes/keyboard_codes_posix.h
+++ b/ui/events/keycodes/keyboard_codes_posix.h
@@ -217,6 +217,7 @@
   // and 0xE8 are unassigned.
   VKEY_WLAN = 0x97,
   VKEY_POWER = 0x98,
+  VKEY_ASSISTANT = 0x99,
   VKEY_BRIGHTNESS_DOWN = 0xD8,
   VKEY_BRIGHTNESS_UP = 0xD9,
   VKEY_KBD_BRIGHTNESS_DOWN = 0xDA,
diff --git a/ui/events/ozone/layout/keyboard_layout_engine_unittest.cc b/ui/events/ozone/layout/keyboard_layout_engine_unittest.cc
index 474f2e9..f2f17545 100644
--- a/ui/events/ozone/layout/keyboard_layout_engine_unittest.cc
+++ b/ui/events/ozone/layout/keyboard_layout_engine_unittest.cc
@@ -191,6 +191,8 @@
        'A'},
       {DomCode::US_A, EF_CONTROL_DOWN, DomKey::Constant<'a'>::Character,
        VKEY_A, 1},
+      {DomCode::LAUNCH_ASSISTANT, EF_NONE, DomKey::LAUNCH_ASSISTANT,
+       VKEY_ASSISTANT, 0},
   };
 
   KeyboardLayoutEngineManager::SetKeyboardLayoutEngine(
diff --git a/ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine.cc b/ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine.cc
index fbe0fc6..6885d14 100644
--- a/ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine.cc
+++ b/ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine.cc
@@ -740,8 +740,12 @@
   // Obtain keysym and character.
   xkb_keysym_t xkb_keysym;
   uint32_t character = 0;
-  if (!XkbLookup(xkb_keycode, xkb_flags, &xkb_keysym, &character))
-    return false;
+  if (!XkbLookup(xkb_keycode, xkb_flags, &xkb_keysym, &character)) {
+    // If we do not have matching legacy Xkb keycode for the Dom code,
+    // we could be dealing with a newer application launcher or similar
+    // key. Let's see if we have a basic mapping for it.
+    return DomCodeToNonPrintableDomKey(dom_code, dom_key, key_code);
+  }
 
   // Classify the keysym and convert to DOM and VKEY representations.
   if (xkb_keysym != XKB_KEY_at || (flags & EF_CONTROL_DOWN) == 0) {
diff --git a/ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine_unittest.cc b/ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine_unittest.cc
index b7e4cb3..02a8fd9 100644
--- a/ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine_unittest.cc
+++ b/ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine_unittest.cc
@@ -819,6 +819,9 @@
     {{DomCode::ENTER, EF_NONE, XKB_KEY_Return}, VKEY_RETURN},
     {{DomCode::NUMPAD_ENTER, EF_NONE, XKB_KEY_KP_Enter}, VKEY_RETURN},
     {{DomCode::SLEEP, EF_NONE, XKB_KEY_XF86Sleep}, VKEY_SLEEP},
+    // Verify that we can translate some Dom codes even if they are not
+    // known to XKB.
+    {{DomCode::LAUNCH_ASSISTANT, EF_NONE}, VKEY_ASSISTANT},
     // Verify that number pad digits produce located VKEY codes.
     {{DomCode::NUMPAD0, EF_NONE, XKB_KEY_KP_0, '0'}, VKEY_NUMPAD0},
     {{DomCode::NUMPAD9, EF_NONE, XKB_KEY_KP_9, '9'}, VKEY_NUMPAD9},
diff --git a/ui/gl/BUILD.gn b/ui/gl/BUILD.gn
index b333021..f4c414d 100644
--- a/ui/gl/BUILD.gn
+++ b/ui/gl/BUILD.gn
@@ -15,7 +15,7 @@
                        (target_cpu == "x86" || target_cpu == "x64")
 }
 
-use_egl = is_win || is_android || is_linux
+use_egl = is_win || is_android || is_linux || is_fuchsia
 use_glx = use_x11 || ozone_platform_x11
 
 if (is_android) {
diff --git a/ui/gl/generate_bindings.py b/ui/gl/generate_bindings.py
index 30b08d8..ab9ccede 100755
--- a/ui/gl/generate_bindings.py
+++ b/ui/gl/generate_bindings.py
@@ -3001,6 +3001,9 @@
   file.write('\n')
   file.write('}  // namespace gl\n')
 
+def SamePrefix(a, b):
+  return a[:a.rfind("_")] == b[:b.rfind("_")]
+
 def GenerateEnumUtils(out_file, input_filenames):
   enum_re = re.compile(r'\#define\s+(GL_[a-zA-Z0-9_]+)\s+([0-9A-Fa-fx]+)')
   dict = {}
@@ -3015,9 +3018,11 @@
           if not value in dict:
             dict[value] = name
           # check our own _CHROMIUM macro conflicts with khronos GL headers.
-          elif dict[value] != name and (name.endswith('_CHROMIUM') or
-              dict[value].endswith('_CHROMIUM')):
-            raise RunTimeError("code collision: %s and %s have the same code %s"
+          # we allow for name duplication if they have the same prefix.
+          elif dict[value] != name and ((name.endswith('_CHROMIUM') or
+              dict[value].endswith('_CHROMIUM')) and
+              not SamePrefix(name, dict[value])):
+            raise RuntimeError("code collision: %s and %s have the same code %s"
                                %  (dict[value], name, value))
 
   out_file.write(LICENSE_AND_HEADER)
diff --git a/ui/gl/gl_bindings_autogen_egl.cc b/ui/gl/gl_bindings_autogen_egl.cc
index 79cc51c..e6d452ca 100644
--- a/ui/gl/gl_bindings_autogen_egl.cc
+++ b/ui/gl/gl_bindings_autogen_egl.cc
@@ -164,8 +164,6 @@
   ext.b_EGL_KHR_image = extensions.find("EGL_KHR_image ") != std::string::npos;
   ext.b_EGL_KHR_image_base =
       extensions.find("EGL_KHR_image_base ") != std::string::npos;
-  ext.b_EGL_KHR_reusable_sync =
-      extensions.find("EGL_KHR_reusable_sync ") != std::string::npos;
   ext.b_EGL_KHR_stream =
       extensions.find("EGL_KHR_stream ") != std::string::npos;
   ext.b_EGL_KHR_stream_consumer_gltexture =
diff --git a/ui/gl/gl_bindings_autogen_egl.h b/ui/gl/gl_bindings_autogen_egl.h
index 4eb2e9dd..b8099205 100644
--- a/ui/gl/gl_bindings_autogen_egl.h
+++ b/ui/gl/gl_bindings_autogen_egl.h
@@ -219,7 +219,6 @@
   bool b_EGL_KHR_gl_texture_2D_image;
   bool b_EGL_KHR_image;
   bool b_EGL_KHR_image_base;
-  bool b_EGL_KHR_reusable_sync;
   bool b_EGL_KHR_stream;
   bool b_EGL_KHR_stream_consumer_gltexture;
   bool b_EGL_KHR_swap_buffers_with_damage;
diff --git a/ui/gl/gl_enums_implementation_autogen.h b/ui/gl/gl_enums_implementation_autogen.h
index bbbbed9..a746908 100644
--- a/ui/gl/gl_enums_implementation_autogen.h
+++ b/ui/gl/gl_enums_implementation_autogen.h
@@ -13,7 +13,7 @@
         0, "GL_FALSE",
     },
     {
-        0x00, "GL_CLOSE_PATH_CHROMIUM",
+        0x00, "GL_CLOSE_PATH_NV",
     },
     {
         0x0000, "GL_POINTS",
@@ -28,13 +28,13 @@
         0x00000002, "GL_CONTEXT_FLAG_DEBUG_BIT_KHR",
     },
     {
-        0x00000004, "GL_GEOMETRY_SHADER_BIT_EXT",
+        0x00000004, "GL_GEOMETRY_SHADER_BIT_OES",
     },
     {
-        0x00000008, "GL_TESS_CONTROL_SHADER_BIT_EXT",
+        0x00000008, "GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR",
     },
     {
-        0x00000010, "GL_TESS_EVALUATION_SHADER_BIT_EXT",
+        0x00000010, "GL_TESS_EVALUATION_SHADER_BIT_OES",
     },
     {
         0x00000020, "GL_COLOR_BUFFER_BIT5_QCOM",
@@ -73,13 +73,13 @@
         0x0001, "GL_LINES",
     },
     {
-        0x00010000, "GL_STENCIL_BUFFER_BIT0_QCOM",
+        0x00010000, "GL_FONT_X_MIN_BOUNDS_BIT_NV",
     },
     {
         0x0002, "GL_LINE_LOOP",
     },
     {
-        0x00020000, "GL_STENCIL_BUFFER_BIT1_QCOM",
+        0x00020000, "GL_FONT_Y_MIN_BOUNDS_BIT_NV",
     },
     {
         0x0003, "GL_LINE_STRIP",
@@ -88,7 +88,7 @@
         0x0004, "GL_TRIANGLES",
     },
     {
-        0x00040000, "GL_STENCIL_BUFFER_BIT2_QCOM",
+        0x00040000, "GL_FONT_X_MAX_BOUNDS_BIT_NV",
     },
     {
         0x0005, "GL_TRIANGLE_STRIP",
@@ -97,58 +97,70 @@
         0x0006, "GL_TRIANGLE_FAN",
     },
     {
-        0x0007, "GL_QUADS_EXT",
+        0x0007, "GL_QUADS_OES",
     },
     {
         0x0008, "GL_MAP_INVALIDATE_BUFFER_BIT_EXT",
     },
     {
-        0x00080000, "GL_STENCIL_BUFFER_BIT3_QCOM",
+        0x00080000, "GL_FONT_Y_MAX_BOUNDS_BIT_NV",
     },
     {
-        0x000A, "GL_LINES_ADJACENCY_EXT",
+        0x000A, "GL_LINES_ADJACENCY_OES",
     },
     {
-        0x000B, "GL_LINE_STRIP_ADJACENCY_EXT",
+        0x000B, "GL_LINE_STRIP_ADJACENCY_OES",
     },
     {
-        0x000C, "GL_TRIANGLES_ADJACENCY_EXT",
+        0x000C, "GL_TRIANGLES_ADJACENCY_OES",
     },
     {
-        0x000D, "GL_TRIANGLE_STRIP_ADJACENCY_EXT",
+        0x000D, "GL_TRIANGLE_STRIP_ADJACENCY_OES",
     },
     {
-        0x000E, "GL_PATCHES_EXT",
+        0x000E, "GL_PATCHES_OES",
     },
     {
         0x0010, "GL_MAP_FLUSH_EXPLICIT_BIT_EXT",
     },
     {
-        0x00100000, "GL_STENCIL_BUFFER_BIT4_QCOM",
+        0x00100000, "GL_FONT_UNITS_PER_EM_BIT_NV",
     },
     {
         0x0020, "GL_MAP_UNSYNCHRONIZED_BIT_EXT",
     },
     {
-        0x00200000, "GL_STENCIL_BUFFER_BIT5_QCOM",
+        0x00200000, "GL_FONT_ASCENDER_BIT_NV",
     },
     {
-        0x00400000, "GL_STENCIL_BUFFER_BIT6_QCOM",
+        0x0040, "GL_MAP_PERSISTENT_BIT_EXT",
     },
     {
-        0x00800000, "GL_STENCIL_BUFFER_BIT7_QCOM",
+        0x00400000, "GL_FONT_DESCENDER_BIT_NV",
     },
     {
-        0x01000000, "GL_MULTISAMPLE_BUFFER_BIT0_QCOM",
+        0x0080, "GL_MAP_COHERENT_BIT_EXT",
     },
     {
-        0x02, "GL_MOVE_TO_CHROMIUM",
+        0x00800000, "GL_FONT_HEIGHT_BIT_NV",
+    },
+    {
+        0x01, "GL_BOLD_BIT_NV",
+    },
+    {
+        0x0100, "GL_DYNAMIC_STORAGE_BIT_EXT",
+    },
+    {
+        0x01000000, "GL_FONT_MAX_ADVANCE_WIDTH_BIT_NV",
+    },
+    {
+        0x02, "GL_MOVE_TO_NV",
     },
     {
         0x0200, "GL_NEVER",
     },
     {
-        0x02000000, "GL_MULTISAMPLE_BUFFER_BIT1_QCOM",
+        0x02000000, "GL_FONT_MAX_ADVANCE_HEIGHT_BIT_NV",
     },
     {
         0x0201, "GL_LESS",
@@ -172,6 +184,9 @@
         0x0207, "GL_ALWAYS",
     },
     {
+        0x03, "GL_RELATIVE_MOVE_TO_NV",
+    },
+    {
         0x0300, "GL_SRC_COLOR",
     },
     {
@@ -199,10 +214,10 @@
         0x0308, "GL_SRC_ALPHA_SATURATE",
     },
     {
-        0x04, "GL_LINE_TO_CHROMIUM",
+        0x04, "GL_LINE_TO_NV",
     },
     {
-        0x04000000, "GL_MULTISAMPLE_BUFFER_BIT2_QCOM",
+        0x04000000, "GL_FONT_UNDERLINE_POSITION_BIT_NV",
     },
     {
         0x0404, "GL_FRONT",
@@ -214,6 +229,9 @@
         0x0408, "GL_FRONT_AND_BACK",
     },
     {
+        0x05, "GL_RELATIVE_LINE_TO_NV",
+    },
+    {
         0x0500, "GL_INVALID_ENUM",
     },
     {
@@ -238,7 +256,19 @@
         0x0507, "GL_CONTEXT_LOST_KHR",
     },
     {
-        0x08000000, "GL_MULTISAMPLE_BUFFER_BIT3_QCOM",
+        0x06, "GL_HORIZONTAL_LINE_TO_NV",
+    },
+    {
+        0x07, "GL_RELATIVE_HORIZONTAL_LINE_TO_NV",
+    },
+    {
+        0x08, "GL_VERTICAL_LINE_TO_NV",
+    },
+    {
+        0x08000000, "GL_FONT_UNDERLINE_THICKNESS_BIT_NV",
+    },
+    {
+        0x09, "GL_RELATIVE_VERTICAL_LINE_TO_NV",
     },
     {
         0x0900, "GL_CW",
@@ -247,12 +277,18 @@
         0x0901, "GL_CCW",
     },
     {
-        0x0A, "GL_QUADRATIC_CURVE_TO_CHROMIUM",
+        0x0A, "GL_QUADRATIC_CURVE_TO_NV",
+    },
+    {
+        0x0B, "GL_RELATIVE_QUADRATIC_CURVE_TO_NV",
     },
     {
         0x0B21, "GL_LINE_WIDTH",
     },
     {
+        0x0B40, "GL_POLYGON_MODE_NV",
+    },
+    {
         0x0B44, "GL_CULL_FACE",
     },
     {
@@ -307,10 +343,16 @@
         0x0BA2, "GL_VIEWPORT",
     },
     {
-        0x0BA6, "GL_PATH_MODELVIEW_MATRIX_CHROMIUM",
+        0x0BA3, "GL_PATH_MODELVIEW_STACK_DEPTH_NV",
     },
     {
-        0x0BA7, "GL_PATH_PROJECTION_MATRIX_CHROMIUM",
+        0x0BA4, "GL_PATH_PROJECTION_STACK_DEPTH_NV",
+    },
+    {
+        0x0BA6, "GL_PATH_MODELVIEW_MATRIX_NV",
+    },
+    {
+        0x0BA7, "GL_PATH_PROJECTION_MATRIX_NV",
     },
     {
         0x0BC0, "GL_ALPHA_TEST_QCOM",
@@ -328,7 +370,7 @@
         0x0BE2, "GL_BLEND",
     },
     {
-        0x0C, "GL_CUBIC_CURVE_TO_CHROMIUM",
+        0x0C, "GL_CUBIC_CURVE_TO_NV",
     },
     {
         0x0C01, "GL_DRAW_BUFFER_EXT",
@@ -361,6 +403,9 @@
         0x0CF5, "GL_UNPACK_ALIGNMENT",
     },
     {
+        0x0D, "GL_RELATIVE_CUBIC_CURVE_TO_NV",
+    },
+    {
         0x0D02, "GL_PACK_ROW_LENGTH",
     },
     {
@@ -379,6 +424,12 @@
         0x0D33, "GL_MAX_TEXTURE_SIZE",
     },
     {
+        0x0D36, "GL_PATH_MAX_MODELVIEW_STACK_DEPTH_NV",
+    },
+    {
+        0x0D38, "GL_PATH_MAX_PROJECTION_STACK_DEPTH_NV",
+    },
+    {
         0x0D3A, "GL_MAX_VIEWPORT_DIMS",
     },
     {
@@ -406,13 +457,28 @@
         0x0DE1, "GL_TEXTURE_2D",
     },
     {
+        0x0E, "GL_SMOOTH_QUADRATIC_CURVE_TO_NV",
+    },
+    {
+        0x0F, "GL_RELATIVE_SMOOTH_QUADRATIC_CURVE_TO_NV",
+    },
+    {
         0x1, "GL_CA_LAYER_EDGE_LEFT_CHROMIUM",
     },
     {
-        0x10000000, "GL_MULTISAMPLE_BUFFER_BIT4_QCOM",
+        0x10, "GL_SMOOTH_CUBIC_CURVE_TO_NV",
     },
     {
-        0x1004, "GL_TEXTURE_BORDER_COLOR_EXT",
+        0x100, "GL_GLYPH_HAS_KERNING_BIT_NV",
+    },
+    {
+        0x10000000, "GL_FONT_HAS_KERNING_BIT_NV",
+    },
+    {
+        0x1004, "GL_TEXTURE_BORDER_COLOR_OES",
+    },
+    {
+        0x11, "GL_RELATIVE_SMOOTH_CUBIC_CURVE_TO_NV",
     },
     {
         0x1100, "GL_DONT_CARE",
@@ -424,6 +490,15 @@
         0x1102, "GL_NICEST",
     },
     {
+        0x12, "GL_SMALL_CCW_ARC_TO_NV",
+    },
+    {
+        0x13, "GL_RELATIVE_SMALL_CCW_ARC_TO_NV",
+    },
+    {
+        0x14, "GL_SMALL_CW_ARC_TO_NV",
+    },
+    {
         0x1400, "GL_BYTE",
     },
     {
@@ -451,21 +526,39 @@
         0x140C, "GL_FIXED",
     },
     {
+        0x140E, "GL_INT64_NV",
+    },
+    {
+        0x140F, "GL_UNSIGNED_INT64_NV",
+    },
+    {
+        0x15, "GL_RELATIVE_SMALL_CW_ARC_TO_NV",
+    },
+    {
         0x1506, "GL_XOR_NV",
     },
     {
         0x150A, "GL_INVERT",
     },
     {
-        0x1700, "GL_PATH_MODELVIEW_CHROMIUM",
+        0x16, "GL_LARGE_CCW_ARC_TO_NV",
     },
     {
-        0x1701, "GL_PATH_PROJECTION_CHROMIUM",
+        0x17, "GL_RELATIVE_LARGE_CCW_ARC_TO_NV",
+    },
+    {
+        0x1700, "GL_PATH_MODELVIEW_NV",
+    },
+    {
+        0x1701, "GL_PATH_PROJECTION_NV",
     },
     {
         0x1702, "GL_TEXTURE",
     },
     {
+        0x18, "GL_LARGE_CW_ARC_TO_NV",
+    },
+    {
         0x1800, "GL_COLOR_EXT",
     },
     {
@@ -475,6 +568,9 @@
         0x1802, "GL_STENCIL_EXT",
     },
     {
+        0x19, "GL_RELATIVE_LARGE_CW_ARC_TO_NV",
+    },
+    {
         0x1901, "GL_STENCIL_INDEX_OES",
     },
     {
@@ -505,7 +601,19 @@
         0x190A, "GL_LUMINANCE_ALPHA",
     },
     {
-        0x1A, "GL_CONIC_CURVE_TO_CHROMIUM",
+        0x1A, "GL_CONIC_CURVE_TO_NV",
+    },
+    {
+        0x1B, "GL_RELATIVE_CONIC_CURVE_TO_NV",
+    },
+    {
+        0x1B00, "GL_POINT_NV",
+    },
+    {
+        0x1B01, "GL_LINE_NV",
+    },
+    {
+        0x1B02, "GL_FILL_NV",
     },
     {
         0x1D00, "GL_FLAT_CHROMIUM",
@@ -538,7 +646,10 @@
         0x2, "GL_CA_LAYER_EDGE_RIGHT_CHROMIUM",
     },
     {
-        0x20000000, "GL_MULTISAMPLE_BUFFER_BIT5_QCOM",
+        0x20, "GL_GLYPH_VERTICAL_BEARING_X_BIT_NV",
+    },
+    {
+        0x20000000, "GL_FONT_NUM_GLYPH_INDICES_BIT_NV",
     },
     {
         0x2400, "GL_EYE_LINEAR_CHROMIUM",
@@ -583,6 +694,12 @@
         0x2A00, "GL_POLYGON_OFFSET_UNITS",
     },
     {
+        0x2A01, "GL_POLYGON_OFFSET_POINT_NV",
+    },
+    {
+        0x2A02, "GL_POLYGON_OFFSET_LINE_NV",
+    },
+    {
         0x3000, "GL_CLIP_DISTANCE0_APPLE",
     },
     {
@@ -613,6 +730,9 @@
         0x4, "GL_CA_LAYER_EDGE_BOTTOM_CHROMIUM",
     },
     {
+        0x40, "GL_GLYPH_VERTICAL_BEARING_Y_BIT_NV",
+    },
+    {
         0x40000000, "GL_MULTISAMPLE_BUFFER_BIT6_QCOM",
     },
     {
@@ -652,6 +772,9 @@
         0x8, "GL_CA_LAYER_EDGE_TOP_CHROMIUM",
     },
     {
+        0x80, "GL_GLYPH_VERTICAL_BEARING_ADVANCE_BIT_NV",
+    },
+    {
         0x80000000, "GL_MULTISAMPLE_BUFFER_BIT7_QCOM",
     },
     {
@@ -673,10 +796,10 @@
         0x8006, "GL_FUNC_ADD",
     },
     {
-        0x8007, "GL_MIN_EXT",
+        0x8007, "GL_MIN",
     },
     {
-        0x8008, "GL_MAX_EXT",
+        0x8008, "GL_MAX",
     },
     {
         0x8009, "GL_BLEND_EQUATION",
@@ -718,6 +841,9 @@
         0x8052, "GL_RGB10_EXT",
     },
     {
+        0x8054, "GL_RGB16_EXT",
+    },
+    {
         0x8056, "GL_RGBA4",
     },
     {
@@ -730,6 +856,9 @@
         0x8059, "GL_RGB10_A2_EXT",
     },
     {
+        0x805B, "GL_RGBA16_EXT",
+    },
+    {
         0x8069, "GL_TEXTURE_BINDING_2D",
     },
     {
@@ -799,7 +928,7 @@
         0x80E9, "GL_MAX_ELEMENTS_INDICES",
     },
     {
-        0x812D, "GL_CLAMP_TO_BORDER_EXT",
+        0x812D, "GL_CLAMP_TO_BORDER_OES",
     },
     {
         0x812F, "GL_CLAMP_TO_EDGE",
@@ -871,7 +1000,13 @@
         0x821D, "GL_NUM_EXTENSIONS",
     },
     {
-        0x8221, "GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED",
+        0x821F, "GL_BUFFER_IMMUTABLE_STORAGE_EXT",
+    },
+    {
+        0x8220, "GL_BUFFER_STORAGE_FLAGS_EXT",
+    },
+    {
+        0x8221, "GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED_OES",
     },
     {
         0x8227, "GL_RG_EXT",
@@ -883,9 +1018,15 @@
         0x8229, "GL_R8_EXT",
     },
     {
+        0x822A, "GL_R16_EXT",
+    },
+    {
         0x822B, "GL_RG8_EXT",
     },
     {
+        0x822C, "GL_RG16_EXT",
+    },
+    {
         0x822D, "GL_R16F_EXT",
     },
     {
@@ -1009,10 +1150,22 @@
         0x825A, "GL_PROGRAM_PIPELINE_BINDING_EXT",
     },
     {
-        0x825E, "GL_LAYER_PROVOKING_VERTEX_EXT",
+        0x825B, "GL_MAX_VIEWPORTS_OES",
     },
     {
-        0x8260, "GL_UNDEFINED_VERTEX_EXT",
+        0x825C, "GL_VIEWPORT_SUBPIXEL_BITS_OES",
+    },
+    {
+        0x825D, "GL_VIEWPORT_BOUNDS_RANGE_OES",
+    },
+    {
+        0x825E, "GL_LAYER_PROVOKING_VERTEX_OES",
+    },
+    {
+        0x825F, "GL_VIEWPORT_INDEX_PROVOKING_VERTEX_OES",
+    },
+    {
+        0x8260, "GL_UNDEFINED_VERTEX_OES",
     },
     {
         0x8261, "GL_NO_RESET_NOTIFICATION_KHR",
@@ -1036,16 +1189,16 @@
         0x826D, "GL_DEBUG_GROUP_STACK_DEPTH_KHR",
     },
     {
-        0x82DB, "GL_TEXTURE_VIEW_MIN_LEVEL_EXT",
+        0x82DB, "GL_TEXTURE_VIEW_MIN_LEVEL_OES",
     },
     {
-        0x82DC, "GL_TEXTURE_VIEW_NUM_LEVELS_EXT",
+        0x82DC, "GL_TEXTURE_VIEW_NUM_LEVELS_OES",
     },
     {
-        0x82DD, "GL_TEXTURE_VIEW_MIN_LAYER_EXT",
+        0x82DD, "GL_TEXTURE_VIEW_MIN_LAYER_OES",
     },
     {
-        0x82DE, "GL_TEXTURE_VIEW_NUM_LAYERS_EXT",
+        0x82DE, "GL_TEXTURE_VIEW_NUM_LAYERS_OES",
     },
     {
         0x82DF, "GL_TEXTURE_IMMUTABLE_LEVELS",
@@ -1063,12 +1216,21 @@
         0x82E3, "GL_QUERY_KHR",
     },
     {
+        0x82E4, "GL_PROGRAM_PIPELINE_KHR",
+    },
+    {
         0x82E6, "GL_SAMPLER_KHR",
     },
     {
         0x82E8, "GL_MAX_LABEL_LENGTH_KHR",
     },
     {
+        0x82F9, "GL_MAX_CULL_DISTANCES_EXT",
+    },
+    {
+        0x82FA, "GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES_EXT",
+    },
+    {
         0x82FB, "GL_CONTEXT_RELEASE_BEHAVIOR_KHR",
     },
     {
@@ -1111,6 +1273,9 @@
         0x83FB, "GL_PERFQUERY_WAIT_INTEL",
     },
     {
+        0x83FE, "GL_CONSERVATIVE_RASTERIZATION_INTEL",
+    },
+    {
         0x846D, "GL_ALIASED_POINT_SIZE_RANGE",
     },
     {
@@ -1216,6 +1381,12 @@
         0x84E0, "GL_ACTIVE_TEXTURE",
     },
     {
+        0x84E3, "GL_PATH_TRANSPOSE_MODELVIEW_MATRIX_NV",
+    },
+    {
+        0x84E4, "GL_PATH_TRANSPOSE_PROJECTION_MATRIX_NV",
+    },
+    {
         0x84E8, "GL_MAX_RENDERBUFFER_SIZE",
     },
     {
@@ -1477,16 +1648,16 @@
         0x886A, "GL_VERTEX_ATTRIB_ARRAY_NORMALIZED",
     },
     {
-        0x886C, "GL_MAX_TESS_CONTROL_INPUT_COMPONENTS_EXT",
+        0x886C, "GL_MAX_TESS_CONTROL_INPUT_COMPONENTS_OES",
     },
     {
-        0x886D, "GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS_EXT",
+        0x886D, "GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS_OES",
     },
     {
         0x8872, "GL_MAX_TEXTURE_IMAGE_UNITS",
     },
     {
-        0x887F, "GL_GEOMETRY_SHADER_INVOCATIONS_EXT",
+        0x887F, "GL_GEOMETRY_SHADER_INVOCATIONS_OES",
     },
     {
         0x8892, "GL_ARRAY_BUFFER",
@@ -1597,13 +1768,13 @@
         0x8914, "GL_SAMPLES_PASSED_ARB",
     },
     {
-        0x8916, "GL_GEOMETRY_LINKED_VERTICES_OUT_EXT",
+        0x8916, "GL_GEOMETRY_LINKED_VERTICES_OUT_OES",
     },
     {
-        0x8917, "GL_GEOMETRY_LINKED_INPUT_TYPE_EXT",
+        0x8917, "GL_GEOMETRY_LINKED_INPUT_TYPE_OES",
     },
     {
-        0x8918, "GL_GEOMETRY_LINKED_OUTPUT_TYPE_EXT",
+        0x8918, "GL_GEOMETRY_LINKED_OUTPUT_TYPE_OES",
     },
     {
         0x8919, "GL_SAMPLER_BINDING",
@@ -1627,7 +1798,7 @@
         0x8A2B, "GL_MAX_VERTEX_UNIFORM_BLOCKS",
     },
     {
-        0x8A2C, "GL_MAX_GEOMETRY_UNIFORM_BLOCKS_EXT",
+        0x8A2C, "GL_MAX_GEOMETRY_UNIFORM_BLOCKS_OES",
     },
     {
         0x8A2D, "GL_MAX_FRAGMENT_UNIFORM_BLOCKS",
@@ -1645,7 +1816,7 @@
         0x8A31, "GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS",
     },
     {
-        0x8A32, "GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS_EXT",
+        0x8A32, "GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS_OES",
     },
     {
         0x8A33, "GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS",
@@ -1972,6 +2143,12 @@
         0x8BDC, "GL_STATE_RESTORE",
     },
     {
+        0x8BE7, "GL_SAMPLER_EXTERNAL_2D_Y2Y_EXT",
+    },
+    {
+        0x8BFA, "GL_TEXTURE_PROTECTED_EXT",
+    },
+    {
         0x8C00, "GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG",
     },
     {
@@ -1996,19 +2173,19 @@
         0x8C1D, "GL_TEXTURE_BINDING_2D_ARRAY",
     },
     {
-        0x8C29, "GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_EXT",
+        0x8C29, "GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_OES",
     },
     {
-        0x8C2A, "GL_TEXTURE_BUFFER_EXT",
+        0x8C2A, "GL_TEXTURE_BUFFER_OES",
     },
     {
-        0x8C2B, "GL_MAX_TEXTURE_BUFFER_SIZE_EXT",
+        0x8C2B, "GL_MAX_TEXTURE_BUFFER_SIZE_OES",
     },
     {
-        0x8C2C, "GL_TEXTURE_BINDING_BUFFER_EXT",
+        0x8C2C, "GL_TEXTURE_BINDING_BUFFER_OES",
     },
     {
-        0x8C2D, "GL_TEXTURE_BUFFER_DATA_STORE_BINDING_EXT",
+        0x8C2D, "GL_TEXTURE_BUFFER_DATA_STORE_BINDING_OES",
     },
     {
         0x8C2F, "GL_ANY_SAMPLES_PASSED_EXT",
@@ -2086,7 +2263,7 @@
         0x8C85, "GL_TRANSFORM_FEEDBACK_BUFFER_SIZE",
     },
     {
-        0x8C87, "GL_PRIMITIVES_GENERATED_EXT",
+        0x8C87, "GL_PRIMITIVES_GENERATED_OES",
     },
     {
         0x8C88, "GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN",
@@ -2233,6 +2410,54 @@
         0x8CEF, "GL_COLOR_ATTACHMENT15_EXT",
     },
     {
+        0x8CF0, "GL_COLOR_ATTACHMENT16",
+    },
+    {
+        0x8CF1, "GL_COLOR_ATTACHMENT17",
+    },
+    {
+        0x8CF2, "GL_COLOR_ATTACHMENT18",
+    },
+    {
+        0x8CF3, "GL_COLOR_ATTACHMENT19",
+    },
+    {
+        0x8CF4, "GL_COLOR_ATTACHMENT20",
+    },
+    {
+        0x8CF5, "GL_COLOR_ATTACHMENT21",
+    },
+    {
+        0x8CF6, "GL_COLOR_ATTACHMENT22",
+    },
+    {
+        0x8CF7, "GL_COLOR_ATTACHMENT23",
+    },
+    {
+        0x8CF8, "GL_COLOR_ATTACHMENT24",
+    },
+    {
+        0x8CF9, "GL_COLOR_ATTACHMENT25",
+    },
+    {
+        0x8CFA, "GL_COLOR_ATTACHMENT26",
+    },
+    {
+        0x8CFB, "GL_COLOR_ATTACHMENT27",
+    },
+    {
+        0x8CFC, "GL_COLOR_ATTACHMENT28",
+    },
+    {
+        0x8CFD, "GL_COLOR_ATTACHMENT29",
+    },
+    {
+        0x8CFE, "GL_COLOR_ATTACHMENT30",
+    },
+    {
+        0x8CFF, "GL_COLOR_ATTACHMENT31",
+    },
+    {
         0x8D00, "GL_DEPTH_ATTACHMENT",
     },
     {
@@ -2368,10 +2593,10 @@
         0x8D9F, "GL_INT_2_10_10_10_REV",
     },
     {
-        0x8DA7, "GL_FRAMEBUFFER_ATTACHMENT_LAYERED_EXT",
+        0x8DA7, "GL_FRAMEBUFFER_ATTACHMENT_LAYERED_OES",
     },
     {
-        0x8DA8, "GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT",
+        0x8DA8, "GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_OES",
     },
     {
         0x8DAD, "GL_FLOAT_32_UNSIGNED_INT_24_8_REV",
@@ -2383,7 +2608,7 @@
         0x8DC1, "GL_SAMPLER_2D_ARRAY",
     },
     {
-        0x8DC2, "GL_SAMPLER_BUFFER_EXT",
+        0x8DC2, "GL_SAMPLER_BUFFER_OES",
     },
     {
         0x8DC4, "GL_SAMPLER_2D_ARRAY_SHADOW_NV",
@@ -2413,7 +2638,7 @@
         0x8DCF, "GL_INT_SAMPLER_2D_ARRAY",
     },
     {
-        0x8DD0, "GL_INT_SAMPLER_BUFFER_EXT",
+        0x8DD0, "GL_INT_SAMPLER_BUFFER_OES",
     },
     {
         0x8DD2, "GL_UNSIGNED_INT_SAMPLER_2D",
@@ -2428,19 +2653,19 @@
         0x8DD7, "GL_UNSIGNED_INT_SAMPLER_2D_ARRAY",
     },
     {
-        0x8DD8, "GL_UNSIGNED_INT_SAMPLER_BUFFER_EXT",
+        0x8DD8, "GL_UNSIGNED_INT_SAMPLER_BUFFER_OES",
     },
     {
-        0x8DD9, "GL_GEOMETRY_SHADER_EXT",
+        0x8DD9, "GL_GEOMETRY_SHADER_OES",
     },
     {
-        0x8DDF, "GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_EXT",
+        0x8DDF, "GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_OES",
     },
     {
-        0x8DE0, "GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT",
+        0x8DE0, "GL_MAX_GEOMETRY_OUTPUT_VERTICES_OES",
     },
     {
-        0x8DE1, "GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_EXT",
+        0x8DE1, "GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_OES",
     },
     {
         0x8DF0, "GL_LOW_FLOAT",
@@ -2485,10 +2710,28 @@
         0x8DFD, "GL_MAX_FRAGMENT_UNIFORM_VECTORS",
     },
     {
-        0x8E1E, "GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS_EXT",
+        0x8E13, "GL_QUERY_WAIT_NV",
     },
     {
-        0x8E1F, "GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS_EXT",
+        0x8E14, "GL_QUERY_NO_WAIT_NV",
+    },
+    {
+        0x8E15, "GL_QUERY_BY_REGION_WAIT_NV",
+    },
+    {
+        0x8E16, "GL_QUERY_BY_REGION_NO_WAIT_NV",
+    },
+    {
+        0x8E1B, "GL_POLYGON_OFFSET_CLAMP_EXT",
+    },
+    {
+        0x8E1E, "GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS_OES",
+    },
+    {
+        0x8E1F, "GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS_OES",
+    },
+    {
+        0x8E20, "GL_COLOR_SAMPLES_NV",
     },
     {
         0x8E22, "GL_TRANSFORM_FEEDBACK",
@@ -2521,13 +2764,16 @@
         0x8E45, "GL_TEXTURE_SWIZZLE_A",
     },
     {
-        0x8E4D, "GL_FIRST_VERTEX_CONVENTION_EXT",
+        0x8E4D, "GL_FIRST_VERTEX_CONVENTION_OES",
     },
     {
-        0x8E4E, "GL_LAST_VERTEX_CONVENTION_EXT",
+        0x8E4E, "GL_LAST_VERTEX_CONVENTION_OES",
     },
     {
-        0x8E5A, "GL_MAX_GEOMETRY_SHADER_INVOCATIONS_EXT",
+        0x8E50, "GL_SAMPLE_LOCATION_NV",
+    },
+    {
+        0x8E5A, "GL_MAX_GEOMETRY_SHADER_INVOCATIONS_OES",
     },
     {
         0x8E5B, "GL_MIN_FRAGMENT_INTERPOLATION_OFFSET_OES",
@@ -2539,73 +2785,73 @@
         0x8E5D, "GL_FRAGMENT_INTERPOLATION_OFFSET_BITS_OES",
     },
     {
-        0x8E72, "GL_PATCH_VERTICES_EXT",
+        0x8E72, "GL_PATCH_VERTICES_OES",
     },
     {
-        0x8E75, "GL_TESS_CONTROL_OUTPUT_VERTICES_EXT",
+        0x8E75, "GL_TESS_CONTROL_OUTPUT_VERTICES_OES",
     },
     {
-        0x8E76, "GL_TESS_GEN_MODE_EXT",
+        0x8E76, "GL_TESS_GEN_MODE_OES",
     },
     {
-        0x8E77, "GL_TESS_GEN_SPACING_EXT",
+        0x8E77, "GL_TESS_GEN_SPACING_OES",
     },
     {
-        0x8E78, "GL_TESS_GEN_VERTEX_ORDER_EXT",
+        0x8E78, "GL_TESS_GEN_VERTEX_ORDER_OES",
     },
     {
-        0x8E79, "GL_TESS_GEN_POINT_MODE_EXT",
+        0x8E79, "GL_TESS_GEN_POINT_MODE_OES",
     },
     {
-        0x8E7A, "GL_ISOLINES_EXT",
+        0x8E7A, "GL_ISOLINES_OES",
     },
     {
-        0x8E7B, "GL_FRACTIONAL_ODD_EXT",
+        0x8E7B, "GL_FRACTIONAL_ODD_OES",
     },
     {
-        0x8E7C, "GL_FRACTIONAL_EVEN_EXT",
+        0x8E7C, "GL_FRACTIONAL_EVEN_OES",
     },
     {
-        0x8E7D, "GL_MAX_PATCH_VERTICES_EXT",
+        0x8E7D, "GL_MAX_PATCH_VERTICES_OES",
     },
     {
-        0x8E7E, "GL_MAX_TESS_GEN_LEVEL_EXT",
+        0x8E7E, "GL_MAX_TESS_GEN_LEVEL_OES",
     },
     {
-        0x8E7F, "GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS_EXT",
+        0x8E7F, "GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS_OES",
     },
     {
-        0x8E80, "GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS_EXT",
+        0x8E80, "GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS_OES",
     },
     {
-        0x8E81, "GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS_EXT",
+        0x8E81, "GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS_OES",
     },
     {
-        0x8E82, "GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS_EXT",
+        0x8E82, "GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS_OES",
     },
     {
-        0x8E83, "GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS_EXT",
+        0x8E83, "GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS_OES",
     },
     {
-        0x8E84, "GL_MAX_TESS_PATCH_COMPONENTS_EXT",
+        0x8E84, "GL_MAX_TESS_PATCH_COMPONENTS_OES",
     },
     {
-        0x8E85, "GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS_EXT",
+        0x8E85, "GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS_OES",
     },
     {
-        0x8E86, "GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS_EXT",
+        0x8E86, "GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS_OES",
     },
     {
-        0x8E87, "GL_TESS_EVALUATION_SHADER_EXT",
+        0x8E87, "GL_TESS_EVALUATION_SHADER_OES",
     },
     {
-        0x8E88, "GL_TESS_CONTROL_SHADER_EXT",
+        0x8E88, "GL_TESS_CONTROL_SHADER_OES",
     },
     {
-        0x8E89, "GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS_EXT",
+        0x8E89, "GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS_OES",
     },
     {
-        0x8E8A, "GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS_EXT",
+        0x8E8A, "GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS_OES",
     },
     {
         0x8ED0, "GL_COVERAGE_COMPONENT_NV",
@@ -2632,6 +2878,24 @@
         0x8ED7, "GL_COVERAGE_AUTOMATIC_NV",
     },
     {
+        0x8F10, "GL_INCLUSIVE_EXT",
+    },
+    {
+        0x8F11, "GL_EXCLUSIVE_EXT",
+    },
+    {
+        0x8F12, "GL_WINDOW_RECTANGLE_EXT",
+    },
+    {
+        0x8F13, "GL_WINDOW_RECTANGLE_MODE_EXT",
+    },
+    {
+        0x8F14, "GL_MAX_WINDOW_RECTANGLES_EXT",
+    },
+    {
+        0x8F15, "GL_NUM_WINDOW_RECTANGLES_EXT",
+    },
+    {
         0x8F36, "GL_COPY_READ_BUFFER_NV",
     },
     {
@@ -2659,6 +2923,9 @@
         0x8F67, "GL_MAX_SHADER_PIXEL_LOCAL_STORAGE_SIZE_EXT",
     },
     {
+        0x8F69, "GL_TEXTURE_ASTC_DECODE_PRECISION_EXT",
+    },
+    {
         0x8F94, "GL_R8_SNORM",
     },
     {
@@ -2671,6 +2938,18 @@
         0x8F97, "GL_RGBA8_SNORM",
     },
     {
+        0x8F98, "GL_R16_SNORM_EXT",
+    },
+    {
+        0x8F99, "GL_RG16_SNORM_EXT",
+    },
+    {
+        0x8F9A, "GL_RGB16_SNORM_EXT",
+    },
+    {
+        0x8F9B, "GL_RGBA16_SNORM_EXT",
+    },
+    {
         0x8F9C, "GL_SIGNED_NORMALIZED",
     },
     {
@@ -2692,136 +2971,355 @@
         0x8FBB, "GL_GPU_DISJOINT_EXT",
     },
     {
+        0x8FBD, "GL_SR8_EXT",
+    },
+    {
+        0x8FBE, "GL_SRG8_EXT",
+    },
+    {
         0x8FC4, "GL_SHADER_BINARY_VIV",
     },
     {
-        0x9009, "GL_TEXTURE_CUBE_MAP_ARRAY_EXT",
+        0x8FE0, "GL_INT8_NV",
     },
     {
-        0x900A, "GL_TEXTURE_BINDING_CUBE_MAP_ARRAY_EXT",
+        0x8FE1, "GL_INT8_VEC2_NV",
     },
     {
-        0x900C, "GL_SAMPLER_CUBE_MAP_ARRAY_EXT",
+        0x8FE2, "GL_INT8_VEC3_NV",
     },
     {
-        0x900D, "GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW_EXT",
+        0x8FE3, "GL_INT8_VEC4_NV",
     },
     {
-        0x900E, "GL_INT_SAMPLER_CUBE_MAP_ARRAY_EXT",
+        0x8FE4, "GL_INT16_NV",
     },
     {
-        0x900F, "GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY_EXT",
+        0x8FE5, "GL_INT16_VEC2_NV",
     },
     {
-        0x9051, "GL_IMAGE_BUFFER_EXT",
+        0x8FE6, "GL_INT16_VEC3_NV",
     },
     {
-        0x9054, "GL_IMAGE_CUBE_MAP_ARRAY_EXT",
+        0x8FE7, "GL_INT16_VEC4_NV",
     },
     {
-        0x905C, "GL_INT_IMAGE_BUFFER_EXT",
+        0x8FE9, "GL_INT64_VEC2_NV",
     },
     {
-        0x905F, "GL_INT_IMAGE_CUBE_MAP_ARRAY_EXT",
+        0x8FEA, "GL_INT64_VEC3_NV",
     },
     {
-        0x9067, "GL_UNSIGNED_INT_IMAGE_BUFFER_EXT",
+        0x8FEB, "GL_INT64_VEC4_NV",
     },
     {
-        0x906A, "GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY_EXT",
+        0x8FEC, "GL_UNSIGNED_INT8_NV",
+    },
+    {
+        0x8FED, "GL_UNSIGNED_INT8_VEC2_NV",
+    },
+    {
+        0x8FEE, "GL_UNSIGNED_INT8_VEC3_NV",
+    },
+    {
+        0x8FEF, "GL_UNSIGNED_INT8_VEC4_NV",
+    },
+    {
+        0x8FF0, "GL_UNSIGNED_INT16_NV",
+    },
+    {
+        0x8FF1, "GL_UNSIGNED_INT16_VEC2_NV",
+    },
+    {
+        0x8FF2, "GL_UNSIGNED_INT16_VEC3_NV",
+    },
+    {
+        0x8FF3, "GL_UNSIGNED_INT16_VEC4_NV",
+    },
+    {
+        0x8FF5, "GL_UNSIGNED_INT64_VEC2_NV",
+    },
+    {
+        0x8FF6, "GL_UNSIGNED_INT64_VEC3_NV",
+    },
+    {
+        0x8FF7, "GL_UNSIGNED_INT64_VEC4_NV",
+    },
+    {
+        0x8FF8, "GL_FLOAT16_NV",
+    },
+    {
+        0x8FF9, "GL_FLOAT16_VEC2_NV",
+    },
+    {
+        0x8FFA, "GL_FLOAT16_VEC3_NV",
+    },
+    {
+        0x8FFB, "GL_FLOAT16_VEC4_NV",
+    },
+    {
+        0x9009, "GL_TEXTURE_CUBE_MAP_ARRAY_OES",
+    },
+    {
+        0x900A, "GL_TEXTURE_BINDING_CUBE_MAP_ARRAY_OES",
+    },
+    {
+        0x900C, "GL_SAMPLER_CUBE_MAP_ARRAY_OES",
+    },
+    {
+        0x900D, "GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW_OES",
+    },
+    {
+        0x900E, "GL_INT_SAMPLER_CUBE_MAP_ARRAY_OES",
+    },
+    {
+        0x900F, "GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY_OES",
+    },
+    {
+        0x9051, "GL_IMAGE_BUFFER_OES",
+    },
+    {
+        0x9054, "GL_IMAGE_CUBE_MAP_ARRAY_OES",
+    },
+    {
+        0x905C, "GL_INT_IMAGE_BUFFER_OES",
+    },
+    {
+        0x905F, "GL_INT_IMAGE_CUBE_MAP_ARRAY_OES",
+    },
+    {
+        0x9067, "GL_UNSIGNED_INT_IMAGE_BUFFER_OES",
+    },
+    {
+        0x906A, "GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY_OES",
     },
     {
         0x906F, "GL_RGB10_A2UI",
     },
     {
-        0x9075, "GL_PATH_STROKE_WIDTH_CHROMIUM",
+        0x9070, "GL_PATH_FORMAT_SVG_NV",
     },
     {
-        0x9076, "GL_PATH_END_CAPS_CHROMIUM",
+        0x9071, "GL_PATH_FORMAT_PS_NV",
     },
     {
-        0x9079, "GL_PATH_JOIN_STYLE_CHROMIUM",
+        0x9072, "GL_STANDARD_FONT_NAME_NV",
+    },
+    {
+        0x9073, "GL_SYSTEM_FONT_NAME_NV",
+    },
+    {
+        0x9074, "GL_FILE_NAME_NV",
+    },
+    {
+        0x9075, "GL_PATH_STROKE_WIDTH_NV",
+    },
+    {
+        0x9076, "GL_PATH_END_CAPS_NV",
+    },
+    {
+        0x9077, "GL_PATH_INITIAL_END_CAP_NV",
+    },
+    {
+        0x9078, "GL_PATH_TERMINAL_END_CAP_NV",
+    },
+    {
+        0x9079, "GL_PATH_JOIN_STYLE_NV",
+    },
+    {
+        0x907A, "GL_PATH_MITER_LIMIT_NV",
+    },
+    {
+        0x907B, "GL_PATH_DASH_CAPS_NV",
+    },
+    {
+        0x907C, "GL_PATH_INITIAL_DASH_CAP_NV",
+    },
+    {
+        0x907D, "GL_PATH_TERMINAL_DASH_CAP_NV",
+    },
+    {
+        0x907E, "GL_PATH_DASH_OFFSET_NV",
+    },
+    {
+        0x907F, "GL_PATH_CLIENT_LENGTH_NV",
     },
     {
         0x907a, "GL_PATH_MITER_LIMIT_CHROMIUM",
     },
     {
+        0x9080, "GL_PATH_FILL_MODE_NV",
+    },
+    {
+        0x9081, "GL_PATH_FILL_MASK_NV",
+    },
+    {
+        0x9082, "GL_PATH_FILL_COVER_MODE_NV",
+    },
+    {
+        0x9083, "GL_PATH_STROKE_COVER_MODE_NV",
+    },
+    {
+        0x9084, "GL_PATH_STROKE_MASK_NV",
+    },
+    {
         0x9086, "GL_PATH_STROKE_BOUND_CHROMIUM",
     },
     {
-        0x9088, "GL_COUNT_UP_CHROMIUM",
+        0x9088, "GL_COUNT_UP_NV",
     },
     {
-        0x9089, "GL_COUNT_DOWN_CHROMIUM",
+        0x9089, "GL_COUNT_DOWN_NV",
     },
     {
-        0x908B, "GL_CONVEX_HULL_CHROMIUM",
+        0x908A, "GL_PATH_OBJECT_BOUNDING_BOX_NV",
     },
     {
-        0x908D, "GL_BOUNDING_BOX_CHROMIUM",
+        0x908B, "GL_CONVEX_HULL_NV",
     },
     {
-        0x908E, "GL_TRANSLATE_X_CHROMIUM",
+        0x908D, "GL_BOUNDING_BOX_NV",
     },
     {
-        0x908F, "GL_TRANSLATE_Y_CHROMIUM",
+        0x908E, "GL_TRANSLATE_X_NV",
     },
     {
-        0x9090, "GL_TRANSLATE_2D_CHROMIUM",
+        0x908F, "GL_TRANSLATE_Y_NV",
     },
     {
-        0x9091, "GL_TRANSLATE_3D_CHROMIUM",
+        0x9090, "GL_TRANSLATE_2D_NV",
     },
     {
-        0x9092, "GL_AFFINE_2D_CHROMIUM",
+        0x9091, "GL_TRANSLATE_3D_NV",
     },
     {
-        0x9094, "GL_AFFINE_3D_CHROMIUM",
+        0x9092, "GL_AFFINE_2D_NV",
     },
     {
-        0x9096, "GL_TRANSPOSE_AFFINE_2D_CHROMIUM",
+        0x9094, "GL_AFFINE_3D_NV",
     },
     {
-        0x9098, "GL_TRANSPOSE_AFFINE_3D_CHROMIUM",
+        0x9096, "GL_TRANSPOSE_AFFINE_2D_NV",
     },
     {
-        0x909C, "GL_BOUNDING_BOX_OF_BOUNDING_BOXES_CHROMIUM",
+        0x9098, "GL_TRANSPOSE_AFFINE_3D_NV",
     },
     {
-        0x90A4, "GL_ROUND_CHROMIUM",
+        0x909A, "GL_UTF8_NV",
     },
     {
-        0x90A6, "GL_BEVEL_CHROMIUM",
+        0x909B, "GL_UTF16_NV",
     },
     {
-        0x90A7, "GL_MITER_REVERT_CHROMIUM",
+        0x909C, "GL_BOUNDING_BOX_OF_BOUNDING_BOXES_NV",
     },
     {
-        0x90B7, "GL_PATH_STENCIL_FUNC_CHROMIUM",
+        0x909D, "GL_PATH_COMMAND_COUNT_NV",
     },
     {
-        0x90B8, "GL_PATH_STENCIL_REF_CHROMIUM",
+        0x909E, "GL_PATH_COORD_COUNT_NV",
     },
     {
-        0x90B9, "GL_PATH_STENCIL_VALUE_MASK_CHROMIUM",
+        0x909F, "GL_PATH_DASH_ARRAY_COUNT_NV",
     },
     {
-        0x90CB, "GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS_EXT",
+        0x90A0, "GL_PATH_COMPUTED_LENGTH_NV",
     },
     {
-        0x90CC, "GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS_EXT",
+        0x90A1, "GL_PATH_FILL_BOUNDING_BOX_NV",
     },
     {
-        0x90CD, "GL_MAX_GEOMETRY_IMAGE_UNIFORMS_EXT",
+        0x90A2, "GL_PATH_STROKE_BOUNDING_BOX_NV",
     },
     {
-        0x90D7, "GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS_EXT",
+        0x90A3, "GL_SQUARE_NV",
     },
     {
-        0x90D8, "GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS_EXT",
+        0x90A4, "GL_ROUND_NV",
     },
     {
-        0x90D9, "GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS_EXT",
+        0x90A5, "GL_TRIANGULAR_NV",
+    },
+    {
+        0x90A6, "GL_BEVEL_NV",
+    },
+    {
+        0x90A7, "GL_MITER_REVERT_NV",
+    },
+    {
+        0x90A8, "GL_MITER_TRUNCATE_NV",
+    },
+    {
+        0x90A9, "GL_SKIP_MISSING_GLYPH_NV",
+    },
+    {
+        0x90AA, "GL_USE_MISSING_GLYPH_NV",
+    },
+    {
+        0x90AB, "GL_PATH_ERROR_POSITION_NV",
+    },
+    {
+        0x90AD, "GL_ACCUM_ADJACENT_PAIRS_NV",
+    },
+    {
+        0x90AE, "GL_ADJACENT_PAIRS_NV",
+    },
+    {
+        0x90AF, "GL_FIRST_TO_REST_NV",
+    },
+    {
+        0x90B0, "GL_PATH_GEN_MODE_NV",
+    },
+    {
+        0x90B1, "GL_PATH_GEN_COEFF_NV",
+    },
+    {
+        0x90B3, "GL_PATH_GEN_COMPONENTS_NV",
+    },
+    {
+        0x90B4, "GL_PATH_DASH_OFFSET_RESET_NV",
+    },
+    {
+        0x90B5, "GL_MOVE_TO_RESETS_NV",
+    },
+    {
+        0x90B6, "GL_MOVE_TO_CONTINUES_NV",
+    },
+    {
+        0x90B7, "GL_PATH_STENCIL_FUNC_NV",
+    },
+    {
+        0x90B8, "GL_PATH_STENCIL_REF_NV",
+    },
+    {
+        0x90B9, "GL_PATH_STENCIL_VALUE_MASK_NV",
+    },
+    {
+        0x90BD, "GL_PATH_STENCIL_DEPTH_OFFSET_FACTOR_NV",
+    },
+    {
+        0x90BE, "GL_PATH_STENCIL_DEPTH_OFFSET_UNITS_NV",
+    },
+    {
+        0x90BF, "GL_PATH_COVER_DEPTH_FUNC_NV",
+    },
+    {
+        0x90CB, "GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS_OES",
+    },
+    {
+        0x90CC, "GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS_OES",
+    },
+    {
+        0x90CD, "GL_MAX_GEOMETRY_IMAGE_UNIFORMS_OES",
+    },
+    {
+        0x90D7, "GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS_OES",
+    },
+    {
+        0x90D8, "GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS_OES",
+    },
+    {
+        0x90D9, "GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS_OES",
     },
     {
         0x90F0, "GL_COLOR_ATTACHMENT_EXT",
@@ -2842,6 +3340,9 @@
         0x90a4, "GL_ROUND_CHROMIUM",
     },
     {
+        0x9100, "GL_TEXTURE_2D_MULTISAMPLE",
+    },
+    {
         0x9102, "GL_TEXTURE_2D_MULTISAMPLE_ARRAY_OES",
     },
     {
@@ -2908,10 +3409,10 @@
         0x9122, "GL_MAX_VERTEX_OUTPUT_COMPONENTS",
     },
     {
-        0x9123, "GL_MAX_GEOMETRY_INPUT_COMPONENTS_EXT",
+        0x9123, "GL_MAX_GEOMETRY_INPUT_COMPONENTS_OES",
     },
     {
-        0x9124, "GL_MAX_GEOMETRY_OUTPUT_COMPONENTS_EXT",
+        0x9124, "GL_MAX_GEOMETRY_OUTPUT_COMPONENTS_OES",
     },
     {
         0x9125, "GL_MAX_FRAGMENT_INPUT_COMPONENTS",
@@ -2941,6 +3442,27 @@
         0x9138, "GL_COMPRESSED_RGBA_PVRTC_4BPPV2_IMG",
     },
     {
+        0x9139, "GL_CUBIC_IMG",
+    },
+    {
+        0x913A, "GL_CUBIC_MIPMAP_NEAREST_IMG",
+    },
+    {
+        0x913B, "GL_CUBIC_MIPMAP_LINEAR_IMG",
+    },
+    {
+        0x913C, "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_AND_DOWNSAMPLE_IMG",
+    },
+    {
+        0x913D, "GL_NUM_DOWNSAMPLE_SCALES_IMG",
+    },
+    {
+        0x913E, "GL_DOWNSAMPLE_SCALES_IMG",
+    },
+    {
+        0x913F, "GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_SCALE_IMG",
+    },
+    {
         0x9143, "GL_MAX_DEBUG_MESSAGE_LENGTH_KHR",
     },
     {
@@ -2968,13 +3490,46 @@
         0x9154, "GL_VERTEX_ARRAY_OBJECT_EXT",
     },
     {
-        0x919D, "GL_TEXTURE_BUFFER_OFFSET_EXT",
+        0x9195, "GL_VIRTUAL_PAGE_SIZE_X_EXT",
     },
     {
-        0x919E, "GL_TEXTURE_BUFFER_SIZE_EXT",
+        0x9196, "GL_VIRTUAL_PAGE_SIZE_Y_EXT",
     },
     {
-        0x919F, "GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT_EXT",
+        0x9197, "GL_VIRTUAL_PAGE_SIZE_Z_EXT",
+    },
+    {
+        0x9198, "GL_MAX_SPARSE_TEXTURE_SIZE_EXT",
+    },
+    {
+        0x9199, "GL_MAX_SPARSE_3D_TEXTURE_SIZE_EXT",
+    },
+    {
+        0x919A, "GL_MAX_SPARSE_ARRAY_TEXTURE_LAYERS_EXT",
+    },
+    {
+        0x919D, "GL_TEXTURE_BUFFER_OFFSET_OES",
+    },
+    {
+        0x919E, "GL_TEXTURE_BUFFER_SIZE_OES",
+    },
+    {
+        0x919F, "GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT_OES",
+    },
+    {
+        0x91A6, "GL_TEXTURE_SPARSE_EXT",
+    },
+    {
+        0x91A7, "GL_VIRTUAL_PAGE_SIZE_INDEX_EXT",
+    },
+    {
+        0x91A8, "GL_NUM_VIRTUAL_PAGE_SIZES_EXT",
+    },
+    {
+        0x91A9, "GL_SPARSE_TEXTURE_FULL_ARRAY_CUBE_MIPMAPS_EXT",
+    },
+    {
+        0x91AA, "GL_NUM_SPARSE_LEVELS_EXT",
     },
     {
         0x9243, "GL_UNPACK_COLORSPACE_CONVERSION_CHROMIUM",
@@ -3184,49 +3739,196 @@
         0x92B4, "GL_INVERT_OVG_NV",
     },
     {
-        0x92BE, "GL_PRIMITIVE_BOUNDING_BOX_EXT",
+        0x92BE, "GL_PRIMITIVE_BOUNDING_BOX_OES",
     },
     {
-        0x92CD, "GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS_EXT",
+        0x92CD, "GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS_OES",
     },
     {
-        0x92CE, "GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS_EXT",
+        0x92CE, "GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS_OES",
     },
     {
-        0x92CF, "GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS_EXT",
+        0x92CF, "GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS_OES",
     },
     {
-        0x92D3, "GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS_EXT",
+        0x92D3, "GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS_OES",
     },
     {
-        0x92D4, "GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS_EXT",
+        0x92D4, "GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS_OES",
     },
     {
-        0x92D5, "GL_MAX_GEOMETRY_ATOMIC_COUNTERS_EXT",
+        0x92D5, "GL_MAX_GEOMETRY_ATOMIC_COUNTERS_OES",
+    },
+    {
+        0x92DD, "GL_FRAGMENT_COVERAGE_TO_COLOR_NV",
+    },
+    {
+        0x92DE, "GL_FRAGMENT_COVERAGE_COLOR_NV",
     },
     {
         0x92E0, "GL_DEBUG_OUTPUT_KHR",
     },
     {
-        0x92E7, "GL_IS_PER_PATCH_EXT",
+        0x92E7, "GL_IS_PER_PATCH_OES",
     },
     {
-        0x9307, "GL_REFERENCED_BY_TESS_CONTROL_SHADER_EXT",
+        0x9307, "GL_REFERENCED_BY_TESS_CONTROL_SHADER_OES",
     },
     {
-        0x9308, "GL_REFERENCED_BY_TESS_EVALUATION_SHADER_EXT",
+        0x9308, "GL_REFERENCED_BY_TESS_EVALUATION_SHADER_OES",
     },
     {
-        0x9309, "GL_REFERENCED_BY_GEOMETRY_SHADER_EXT",
+        0x9309, "GL_REFERENCED_BY_GEOMETRY_SHADER_OES",
     },
     {
-        0x9312, "GL_FRAMEBUFFER_DEFAULT_LAYERS_EXT",
+        0x930F, "GL_LOCATION_INDEX_EXT",
     },
     {
-        0x9317, "GL_MAX_FRAMEBUFFER_LAYERS_EXT",
+        0x9312, "GL_FRAMEBUFFER_DEFAULT_LAYERS_OES",
     },
     {
-        0x9332, "GL_COVERAGE_MODULATION_CHROMIUM",
+        0x9317, "GL_MAX_FRAMEBUFFER_LAYERS_OES",
+    },
+    {
+        0x9327, "GL_RASTER_MULTISAMPLE_EXT",
+    },
+    {
+        0x9328, "GL_RASTER_SAMPLES_EXT",
+    },
+    {
+        0x9329, "GL_MAX_RASTER_SAMPLES_EXT",
+    },
+    {
+        0x932A, "GL_RASTER_FIXED_SAMPLE_LOCATIONS_EXT",
+    },
+    {
+        0x932B, "GL_MULTISAMPLE_RASTERIZATION_ALLOWED_EXT",
+    },
+    {
+        0x932C, "GL_EFFECTIVE_RASTER_SAMPLES_EXT",
+    },
+    {
+        0x932D, "GL_DEPTH_SAMPLES_NV",
+    },
+    {
+        0x932E, "GL_STENCIL_SAMPLES_NV",
+    },
+    {
+        0x932F, "GL_MIXED_DEPTH_SAMPLES_SUPPORTED_NV",
+    },
+    {
+        0x9330, "GL_MIXED_STENCIL_SAMPLES_SUPPORTED_NV",
+    },
+    {
+        0x9331, "GL_COVERAGE_MODULATION_TABLE_NV",
+    },
+    {
+        0x9332, "GL_COVERAGE_MODULATION_NV",
+    },
+    {
+        0x9333, "GL_COVERAGE_MODULATION_TABLE_SIZE_NV",
+    },
+    {
+        0x933C, "GL_FILL_RECTANGLE_NV",
+    },
+    {
+        0x933D, "GL_SAMPLE_LOCATION_SUBPIXEL_BITS_NV",
+    },
+    {
+        0x933E, "GL_SAMPLE_LOCATION_PIXEL_GRID_WIDTH_NV",
+    },
+    {
+        0x933F, "GL_SAMPLE_LOCATION_PIXEL_GRID_HEIGHT_NV",
+    },
+    {
+        0x9340, "GL_PROGRAMMABLE_SAMPLE_LOCATION_TABLE_SIZE_NV",
+    },
+    {
+        0x9341, "GL_PROGRAMMABLE_SAMPLE_LOCATION_NV",
+    },
+    {
+        0x9342, "GL_FRAMEBUFFER_PROGRAMMABLE_SAMPLE_LOCATIONS_NV",
+    },
+    {
+        0x9343, "GL_FRAMEBUFFER_SAMPLE_LOCATION_PIXEL_GRID_NV",
+    },
+    {
+        0x9346, "GL_CONSERVATIVE_RASTERIZATION_NV",
+    },
+    {
+        0x9347, "GL_SUBPIXEL_PRECISION_BIAS_X_BITS_NV",
+    },
+    {
+        0x9348, "GL_SUBPIXEL_PRECISION_BIAS_Y_BITS_NV",
+    },
+    {
+        0x9349, "GL_MAX_SUBPIXEL_PRECISION_BIAS_BITS_NV",
+    },
+    {
+        0x9350, "GL_VIEWPORT_SWIZZLE_POSITIVE_X_NV",
+    },
+    {
+        0x9351, "GL_VIEWPORT_SWIZZLE_NEGATIVE_X_NV",
+    },
+    {
+        0x9352, "GL_VIEWPORT_SWIZZLE_POSITIVE_Y_NV",
+    },
+    {
+        0x9353, "GL_VIEWPORT_SWIZZLE_NEGATIVE_Y_NV",
+    },
+    {
+        0x9354, "GL_VIEWPORT_SWIZZLE_POSITIVE_Z_NV",
+    },
+    {
+        0x9355, "GL_VIEWPORT_SWIZZLE_NEGATIVE_Z_NV",
+    },
+    {
+        0x9356, "GL_VIEWPORT_SWIZZLE_POSITIVE_W_NV",
+    },
+    {
+        0x9357, "GL_VIEWPORT_SWIZZLE_NEGATIVE_W_NV",
+    },
+    {
+        0x9358, "GL_VIEWPORT_SWIZZLE_X_NV",
+    },
+    {
+        0x9359, "GL_VIEWPORT_SWIZZLE_Y_NV",
+    },
+    {
+        0x935A, "GL_VIEWPORT_SWIZZLE_Z_NV",
+    },
+    {
+        0x935B, "GL_VIEWPORT_SWIZZLE_W_NV",
+    },
+    {
+        0x9368, "GL_FONT_GLYPHS_AVAILABLE_NV",
+    },
+    {
+        0x9369, "GL_FONT_TARGET_UNAVAILABLE_NV",
+    },
+    {
+        0x936A, "GL_FONT_UNAVAILABLE_NV",
+    },
+    {
+        0x936B, "GL_FONT_UNINTELLIGIBLE_NV",
+    },
+    {
+        0x936C, "GL_STANDARD_FONT_FORMAT_NV",
+    },
+    {
+        0x936D, "GL_FRAGMENT_INPUT_NV",
+    },
+    {
+        0x9371, "GL_MULTISAMPLES_NV",
+    },
+    {
+        0x9372, "GL_SUPERSAMPLE_SCALE_X_NV",
+    },
+    {
+        0x9373, "GL_SUPERSAMPLE_SCALE_Y_NV",
+    },
+    {
+        0x9374, "GL_CONFORMANT_NV",
     },
     {
         0x9380, "GL_NUM_SAMPLE_COUNTS",
@@ -3445,10 +4147,99 @@
         0x9500, "GL_PERFQUERY_GPA_EXTENDED_COUNTERS_INTEL",
     },
     {
+        0x954D, "GL_CONSERVATIVE_RASTER_MODE_NV",
+    },
+    {
+        0x954E, "GL_CONSERVATIVE_RASTER_MODE_POST_SNAP_NV",
+    },
+    {
+        0x954F, "GL_CONSERVATIVE_RASTER_MODE_PRE_SNAP_TRIANGLES_NV",
+    },
+    {
+        0x9630, "GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_NUM_VIEWS_OVR",
+    },
+    {
+        0x9631, "GL_MAX_VIEWS_OVR",
+    },
+    {
+        0x9632, "GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_BASE_VIEW_INDEX_OVR",
+    },
+    {
+        0x9633, "GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR",
+    },
+    {
+        0x9650, "GL_MAX_SHADER_COMBINED_LOCAL_STORAGE_FAST_SIZE_EXT",
+    },
+    {
+        0x9651, "GL_MAX_SHADER_COMBINED_LOCAL_STORAGE_SIZE_EXT",
+    },
+    {
+        0x9652,
+        "GL_FRAMEBUFFER_INCOMPLETE_INSUFFICIENT_SHADER_COMBINED_LOCAL_STORAGE_"
+        "EXT",
+    },
+    {
+        0xC0, "GL_SHARED_EDGE_NV",
+    },
+    {
+        0xE8, "GL_ROUNDED_RECT_NV",
+    },
+    {
+        0xE9, "GL_RELATIVE_ROUNDED_RECT_NV",
+    },
+    {
+        0xEA, "GL_ROUNDED_RECT2_NV",
+    },
+    {
+        0xEB, "GL_RELATIVE_ROUNDED_RECT2_NV",
+    },
+    {
+        0xEC, "GL_ROUNDED_RECT4_NV",
+    },
+    {
+        0xED, "GL_RELATIVE_ROUNDED_RECT4_NV",
+    },
+    {
+        0xEE, "GL_ROUNDED_RECT8_NV",
+    },
+    {
+        0xEF, "GL_RELATIVE_ROUNDED_RECT8_NV",
+    },
+    {
+        0xF0, "GL_RESTART_PATH_NV",
+    },
+    {
+        0xF2, "GL_DUP_FIRST_CUBIC_CURVE_TO_NV",
+    },
+    {
+        0xF4, "GL_DUP_LAST_CUBIC_CURVE_TO_NV",
+    },
+    {
+        0xF6, "GL_RECT_NV",
+    },
+    {
+        0xF7, "GL_RELATIVE_RECT_NV",
+    },
+    {
+        0xF8, "GL_CIRCULAR_CCW_ARC_TO_NV",
+    },
+    {
+        0xFA, "GL_CIRCULAR_CW_ARC_TO_NV",
+    },
+    {
+        0xFC, "GL_CIRCULAR_TANGENT_ARC_TO_NV",
+    },
+    {
+        0xFE, "GL_ARC_TO_NV",
+    },
+    {
+        0xFF, "GL_RELATIVE_ARC_TO_NV",
+    },
+    {
         0xFFFFFFFF, "GL_ALL_SHADER_BITS_EXT",
     },
     {
-        1, "GL_ES_VERSION_2_0",
+        1, "GL_GLES_PROTOTYPES",
     },
     {
         16, "GL_MAILBOX_SIZE_CHROMIUM",
diff --git a/ui/message_center/message_center_impl_unittest.cc b/ui/message_center/message_center_impl_unittest.cc
index 84310e1..3ed12ed 100644
--- a/ui/message_center/message_center_impl_unittest.cc
+++ b/ui/message_center/message_center_impl_unittest.cc
@@ -351,7 +351,7 @@
   // Fast forward the |task_runner| by one second less than the auto-close timer
   // frequency for Web Notifications. (As set by the |notifier_id|.)
   task_runner->FastForwardBy(
-      base::TimeDelta::FromSeconds(kAutocloseWebPageDelaySeconds - 1));
+      base::TimeDelta::FromSeconds(kAutocloseHighPriorityDelaySeconds - 1));
   ASSERT_EQ(popup_timers_controller->timer_finished(), 0);
 
   // Trigger a replacement of the notification in the timer controller.
@@ -360,7 +360,7 @@
   // Fast forward the |task_runner| by one second less than the auto-close timer
   // frequency for Web Notifications again. It should have been reset.
   task_runner->FastForwardBy(
-      base::TimeDelta::FromSeconds(kAutocloseWebPageDelaySeconds - 1));
+      base::TimeDelta::FromSeconds(kAutocloseHighPriorityDelaySeconds - 1));
   ASSERT_EQ(popup_timers_controller->timer_finished(), 0);
 
   // Now fast forward the |task_runner| by two seconds (to avoid flakiness),
diff --git a/ui/message_center/message_center_style.h b/ui/message_center/message_center_style.h
index 3eee4ae..be5acf24 100644
--- a/ui/message_center/message_center_style.h
+++ b/ui/message_center/message_center_style.h
@@ -112,13 +112,11 @@
 // Not used when --enabled-new-style-notification is set.
 const size_t kNotificationMaximumItems = 5;
 
-// Timing.
+// Timing. Web Notifications always use high-priority timings. Given the absence
+// of a notification center on non-Chrome OS platforms, this improves users'
+// ability to interact with the toasts.
 const int kAutocloseDefaultDelaySeconds = 8;
 const int kAutocloseHighPriorityDelaySeconds = 25;
-// Web notifications use a larger timeout for now, which improves re-engagement.
-// TODO(johnme): Use Finch to experiment with different values, then consider
-// replacing kAutocloseDefaultDelaySeconds with this (https://crbug.com/530697)
-const int kAutocloseWebPageDelaySeconds = 20;
 
 // Buttons.
 const int kButtonHeight = 38;              // In DIPs.
diff --git a/ui/message_center/popup_timers_controller.cc b/ui/message_center/popup_timers_controller.cc
index a967681..9be0a9e 100644
--- a/ui/message_center/popup_timers_controller.cc
+++ b/ui/message_center/popup_timers_controller.cc
@@ -14,10 +14,10 @@
 namespace {
 
 base::TimeDelta GetTimeoutForNotification(Notification* notification) {
-  if (notification->priority() > DEFAULT_PRIORITY)
+  if (notification->notifier_id().type == NotifierId::WEB_PAGE ||
+      notification->priority() > DEFAULT_PRIORITY) {
     return base::TimeDelta::FromSeconds(kAutocloseHighPriorityDelaySeconds);
-  if (notification->notifier_id().type == NotifierId::WEB_PAGE)
-    return base::TimeDelta::FromSeconds(kAutocloseWebPageDelaySeconds);
+  }
   return base::TimeDelta::FromSeconds(kAutocloseDefaultDelaySeconds);
 }
 
diff --git a/ui/views/examples/examples_main.cc b/ui/views/examples/examples_main.cc
index 67138c38..fd3c10d9 100644
--- a/ui/views/examples/examples_main.cc
+++ b/ui/views/examples/examples_main.cc
@@ -67,10 +67,10 @@
   gl::init::InitializeGLOneOff();
 
   // The ContextFactory must exist before any Compositors are created.
-  viz::HostFrameSinkManager frame_sink_manager_;
-  cc::SurfaceManager surface_manager_;
+  viz::HostFrameSinkManager host_frame_sink_manager_;
+  cc::FrameSinkManager frame_sink_manager_;
   auto context_factory = base::MakeUnique<ui::InProcessContextFactory>(
-      &frame_sink_manager_, &surface_manager_);
+      &host_frame_sink_manager_, &frame_sink_manager_);
   context_factory->set_use_test_surface(false);
 
   base::MessageLoopForUI message_loop;
diff --git a/ui/webui/resources/cr_elements/cr_expand_button/cr_expand_button.html b/ui/webui/resources/cr_elements/cr_expand_button/cr_expand_button.html
index b806cb8..8651c39 100644
--- a/ui/webui/resources/cr_elements/cr_expand_button/cr_expand_button.html
+++ b/ui/webui/resources/cr_elements/cr_expand_button/cr_expand_button.html
@@ -1,11 +1,11 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
-<link rel="import" href="chrome://resources/cr_elements/icons.html">
+<link rel="import" href="chrome://resources/cr_elements/cr_icons_css.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html">
 
 <dom-module id="cr-expand-button">
   <template>
-    <style include="cr-shared-style">
+    <style include="cr-icons cr-shared-style">
       :host {
         display: inline-block;
       }
@@ -14,16 +14,15 @@
         pointer-events: none;
       }
 
-      paper-icon-button {
+      button[is=paper-icon-button-light] {
         @apply(--cr-paper-icon-button-margin);
       }
     </style>
     <content></content>
-    <paper-icon-button toggles active="{{expanded}}" disabled="[[disabled]]"
-        icon="[[iconName_(expanded)]]" alt="[[alt]]"
-        aria-active-attribute="aria-expanded"
-        on-tap="stopTap_">
-    </paper-icon-button>
+    <button is="paper-icon-button-light" class$="[[iconName_(expanded)]]"
+        toggles active="{{expanded}}" disabled="[[disabled]]" alt="[[alt]]"
+        aria-active-attribute="aria-expanded" on-tap="toggleExpand_">
+    </button>
   </template>
   <script src="cr_expand_button.js"></script>
 </dom-module>
diff --git a/ui/webui/resources/cr_elements/cr_expand_button/cr_expand_button.js b/ui/webui/resources/cr_elements/cr_expand_button/cr_expand_button.js
index 27d57d37..3756574 100644
--- a/ui/webui/resources/cr_elements/cr_expand_button/cr_expand_button.js
+++ b/ui/webui/resources/cr_elements/cr_expand_button/cr_expand_button.js
@@ -4,12 +4,8 @@
 
 /**
  * @fileoverview
- * 'cr-expand-button' is a chrome-specific wrapper around paper-icon-button that
- * toggles between an opened (expanded) and closed state.
- *
- * Example:
- *
- *    <cr-expand-button expanded="{{sectionIsExpanded}}"></cr-expand-button>
+ * 'cr-expand-button' is a chrome-specific wrapper around a button that toggles
+ * between an opened (expanded) and closed state.
  */
 Polymer({
   is: 'cr-expand-button',
@@ -22,7 +18,7 @@
     expanded: {type: Boolean, value: false, notify: true},
 
     /**
-     * If true, the button will be disabled and greyed out.
+     * If true, the button will be disabled and grayed out.
      */
     disabled: {type: Boolean, value: false, reflectToAttribute: true},
 
@@ -30,15 +26,17 @@
     alt: String,
   },
 
+  /** @private */
   iconName_: function(expanded) {
-    return expanded ? 'cr:expand-less' : 'cr:expand-more';
+    return expanded ? 'icon-expand-less' : 'icon-expand-more';
   },
 
   /**
-   * @param {Event} event
+   * @param {!Event} event
    * @private
    */
-  stopTap_: function(event) {
+  toggleExpand_: function(event) {
+    this.expanded = !this.expanded;
     event.stopPropagation();
   },
 });
diff --git a/ui/webui/resources/cr_elements/cr_icons_css.html b/ui/webui/resources/cr_elements/cr_icons_css.html
index c9e35876..c8d7acb3 100644
--- a/ui/webui/resources/cr_elements/cr_icons_css.html
+++ b/ui/webui/resources/cr_elements/cr_icons_css.html
@@ -4,11 +4,6 @@
 <dom-module id="cr-icons">
   <template>
     <style>
-      button[is='paper-icon-button-light'].subpage-arrow,
-      paper-icon-button.subpage-arrow {
-        background-image: url(../images/arrow_right.svg);
-      }
-
       button[is='paper-icon-button-light'].icon-cancel {
         background-image: url(../images/icon_cancel.svg);
       }
@@ -25,6 +20,14 @@
         background-image: url(../images/icon_delete.svg);
       }
 
+      button[is='paper-icon-button-light'].icon-expand-less {
+        background-image: url(../images/icon_expand_less.svg);
+      }
+
+      button[is='paper-icon-button-light'].icon-expand-more {
+        background-image: url(../images/icon_expand_more.svg);
+      }
+
       button[is='paper-icon-button-light'].icon-external {
         background-image: url(../images/open_in_new.svg);
       }
@@ -44,6 +47,10 @@
       button[is='paper-icon-button-light'].icon-arrow-dropdown {
         background-image: url(../images/icon_arrow_dropdown.svg);
       }
+
+      button[is='paper-icon-button-light'].subpage-arrow {
+        background-image: url(../images/arrow_right.svg);
+      }
     </style>
   </template>
 </dom-module>
diff --git a/ui/webui/resources/cr_elements/shared_vars_css.html b/ui/webui/resources/cr_elements/shared_vars_css.html
index 9b854cf5..4ce2020 100644
--- a/ui/webui/resources/cr_elements/shared_vars_css.html
+++ b/ui/webui/resources/cr_elements/shared_vars_css.html
@@ -13,7 +13,7 @@
 
     --cr-focused-item-color: var(--google-grey-300);
 
-    /* The inner icon is 20px in size. paper-icon-button has 8px padding. */
+    /* The inner icon is 20px in size. The button has 8px * 2 padding. */
     --cr-icon-ripple-size: 36px;
     --cr-icon-ripple-padding: 8px;
 
diff --git a/ui/webui/resources/cr_elements_images.grdp b/ui/webui/resources/cr_elements_images.grdp
index 9cf3cab..c962220a 100644
--- a/ui/webui/resources/cr_elements_images.grdp
+++ b/ui/webui/resources/cr_elements_images.grdp
@@ -1,5 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
 <grit-part>
+  <!-- TODO(dschuyler): Many of these may be included for the unit tests which
+       don't appear to flatten the html. We may be able to avoid including
+       these if the browser_tests would not try to load them. -->
   <include name="IDR_WEBUI_IMAGES_ARROW_DOWN"
            file="images/arrow_down.svg" type="BINDATA" />
   <include name="IDR_WEBUI_IMAGES_ARROW_RIGHT"
@@ -14,6 +17,10 @@
            file="images/icon_clear.svg" type="BINDATA" />
   <include name="IDR_WEBUI_IMAGES_ICON_DELETE"
            file="images/icon_delete.svg" type="BINDATA" />
+  <include name="IDR_WEBUI_IMAGES_ICON_EXPAND_LESS"
+           file="images/icon_expand_less.svg" type="BINDATA" />
+  <include name="IDR_WEBUI_IMAGES_ICON_EXPAND_MORE"
+           file="images/icon_expand_more.svg" type="BINDATA" />
   <include name="IDR_WEBUI_IMAGES_ICON_EXTERNAL"
            file="images/open_in_new.svg" type="BINDATA" />
   <include name="IDR_WEBUI_IMAGES_ICON_MORE_VERT"
diff --git a/ui/webui/resources/images/icon_expand_less.svg b/ui/webui/resources/images/icon_expand_less.svg
new file mode 100644
index 0000000..e8c6b5c
--- /dev/null
+++ b/ui/webui/resources/images/icon_expand_less.svg
@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" fill="#757575" width="24" height="24"
+    viewbox="0 0 24 24">
+  <path d="M12 8l-6 6 1.41 1.41L12 10.83l4.59 4.58L18 14z"></path>
+</svg>
diff --git a/ui/webui/resources/images/icon_expand_more.svg b/ui/webui/resources/images/icon_expand_more.svg
new file mode 100644
index 0000000..05d9cbc
--- /dev/null
+++ b/ui/webui/resources/images/icon_expand_more.svg
@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" fill="#757575" width="24" height="24"
+    viewBox="0 0 24 24">
+  <path d="M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6z"></path>
+</svg>