diff --git a/DEPS b/DEPS
index 36f449b8..4a60272 100644
--- a/DEPS
+++ b/DEPS
@@ -40,11 +40,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '75c7afb5af4dc02e226e35aa9ed9d76d2135ea28',
+  'skia_revision': '9b0b32fda4871776eb9afdf9553e523e5c28aa63',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': 'f914089eae2ea692c6ae29d1ee4d1fe8aea59629',
+  'v8_revision': '67e8ea430029d3b96ec16c40ef229a35d96ccfb7',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -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': '8ada2ef8000abce4eb98506cc6195c78067f1000',
+  'pdfium_revision': 'ed48c1a42b2f9a0c8cb04185c180c6424bad3b83',
   # 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.
diff --git a/android_webview/tools/system_webview_shell/apk/AndroidManifest.xml b/android_webview/tools/system_webview_shell/apk/AndroidManifest.xml
index 1de315de..fb33f532 100644
--- a/android_webview/tools/system_webview_shell/apk/AndroidManifest.xml
+++ b/android_webview/tools/system_webview_shell/apk/AndroidManifest.xml
@@ -33,6 +33,8 @@
         android:label="@string/app_name"
         android:theme="@android:style/Theme.Light"
         android:debuggable="true" >
+        <meta-data android:name="android.webkit.WebView.EnableSafeBrowsing"
+            android:value="true" />
         <activity
             android:name="org.chromium.webview_shell.TelemetryActivity"
             android:launchMode="singleTask"
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 30e7a6c..11f96396c 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -459,6 +459,8 @@
     "system/power/battery_notification.h",
     "system/power/dual_role_notification.cc",
     "system/power/dual_role_notification.h",
+    "system/power/power_button_display_controller.cc",
+    "system/power/power_button_display_controller.h",
     "system/power/power_event_observer.cc",
     "system/power/power_event_observer.h",
     "system/power/power_status.cc",
diff --git a/ash/palette_delegate.h b/ash/palette_delegate.h
index 1051328..b908aac 100644
--- a/ash/palette_delegate.h
+++ b/ash/palette_delegate.h
@@ -55,13 +55,8 @@
   // Cancels any active partial screenshot session.
   virtual void CancelPartialScreenshot() = 0;
 
-  // Returns true if the metalayer is available.
-  virtual bool IsMetalayerSupported() = 0;
-
   // Shows the metalayer.
-  // |closed| will be invoked when the metalayer is closed or
-  // if the metalayer could not be shown.
-  virtual void ShowMetalayer(const base::Closure& closed) = 0;
+  virtual void ShowMetalayer() = 0;
 
   // Hides the metalayer.
   virtual void HideMetalayer() = 0;
diff --git a/ash/shell.cc b/ash/shell.cc
index 652fd0d..e02b64d 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -623,6 +623,11 @@
     observer.OnVoiceInteractionEnabled(enabled);
 }
 
+void Shell::NotifyVoiceInteractionContextEnabled(bool enabled) {
+  for (auto& observer : shell_observers_)
+    observer.OnVoiceInteractionContextEnabled(enabled);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // Shell, private:
 
diff --git a/ash/shell.h b/ash/shell.h
index 91a18a3c..e8673c81 100644
--- a/ash/shell.h
+++ b/ash/shell.h
@@ -605,10 +605,14 @@
 
   void NotifyAppListVisibilityChanged(bool visible, aura::Window* root_window);
 
+  // TODO(kaznacheev) Move voice interaction related methods to a separate
+  // controller (crbug.com/758650)
   void NotifyVoiceInteractionStatusChanged(VoiceInteractionState state);
 
   void NotifyVoiceInteractionEnabled(bool enabled);
 
+  void NotifyVoiceInteractionContextEnabled(bool enabled);
+
  private:
   FRIEND_TEST_ALL_PREFIXES(ExtendedDesktopTest, TestCursor);
   FRIEND_TEST_ALL_PREFIXES(WindowManagerTest, MouseEventCursors);
diff --git a/ash/shell/shell_delegate_impl.cc b/ash/shell/shell_delegate_impl.cc
index a6010b33..f0f9807 100644
--- a/ash/shell/shell_delegate_impl.cc
+++ b/ash/shell/shell_delegate_impl.cc
@@ -50,8 +50,7 @@
       done.Run();
   }
   void CancelPartialScreenshot() override {}
-  bool IsMetalayerSupported() override { return false; }
-  void ShowMetalayer(const base::Closure& closed) override {}
+  void ShowMetalayer() override {}
   void HideMetalayer() override {}
 
  private:
diff --git a/ash/shell_observer.h b/ash/shell_observer.h
index 8232fd8..9636de1 100644
--- a/ash/shell_observer.h
+++ b/ash/shell_observer.h
@@ -70,12 +70,18 @@
   // Called when a new KeyboardController is created.
   virtual void OnKeyboardControllerCreated() {}
 
+  // TODO(kaznacheev) Move voice interaction related methods to a separate
+  // observer (crbug.com/758650)
   // Called when voice interaction session state changes.
   virtual void OnVoiceInteractionStatusChanged(VoiceInteractionState state) {}
 
-  // Called when voice interaction is enabled/disabled
+  // Called when voice interaction is enabled/disabled.
   virtual void OnVoiceInteractionEnabled(bool enabled) {}
 
+  // Called when voice interaction service is allowed/disallowed to access
+  // the "context" (text and graphic content that is currently on screen).
+  virtual void OnVoiceInteractionContextEnabled(bool enabled) {}
+
   // Called at the end of Shell::Init.
   virtual void OnShellInitialized() {}
 
diff --git a/ash/system/palette/palette_tray_unittest.cc b/ash/system/palette/palette_tray_unittest.cc
index be04dcc..031d325 100644
--- a/ash/system/palette/palette_tray_unittest.cc
+++ b/ash/system/palette/palette_tray_unittest.cc
@@ -218,6 +218,8 @@
 TEST_F(PaletteTrayTestWithVoiceInteraction, MetalayerToolActivatesHighlighter) {
   Shell::Get()->NotifyVoiceInteractionStatusChanged(
       VoiceInteractionState::RUNNING);
+  Shell::Get()->NotifyVoiceInteractionEnabled(true);
+  Shell::Get()->NotifyVoiceInteractionContextEnabled(true);
 
   HighlighterController highlighter_controller;
   HighlighterControllerTestApi highlighter_test_api(&highlighter_controller);
@@ -225,8 +227,6 @@
   ui::test::EventGenerator& generator = GetEventGenerator();
   generator.EnterPenPointerMode();
 
-  test_palette_delegate()->SetMetalayerSupported(true);
-
   // Press/drag does not activate the highlighter unless the palette tool is
   // activated.
   generator.MoveTouch(gfx::Point(1, 1));
@@ -273,7 +273,7 @@
   // Disabling metalayer support in the delegate should disable the palette
   // tool.
   test_api_->GetPaletteToolManager()->ActivateTool(PaletteToolId::METALAYER);
-  test_palette_delegate()->SetMetalayerSupported(false);
+  Shell::Get()->NotifyVoiceInteractionContextEnabled(false);
   EXPECT_FALSE(test_api_->GetPaletteToolManager()->IsToolActive(
       PaletteToolId::METALAYER));
 
@@ -292,7 +292,9 @@
 TEST_F(PaletteTrayTestWithVoiceInteraction,
        StylusBarrelButtonActivatesHighlighter) {
   Shell::Get()->NotifyVoiceInteractionStatusChanged(
-      VoiceInteractionState::RUNNING);
+      VoiceInteractionState::NOT_READY);
+  Shell::Get()->NotifyVoiceInteractionEnabled(false);
+  Shell::Get()->NotifyVoiceInteractionContextEnabled(false);
 
   HighlighterController highlighter_controller;
   HighlighterControllerTestApi highlighter_test_api(&highlighter_controller);
@@ -300,8 +302,8 @@
   ui::test::EventGenerator& generator = GetEventGenerator();
   generator.EnterPenPointerMode();
 
-  // Press and drag while holding down the stylus button, no highlighter
-  // unless the metalayer support is enabled.
+  // Press and drag while holding down the stylus button, no highlighter unless
+  // the metalayer support is fully enabled and the the framework is ready.
   generator.set_flags(ui::EF_LEFT_MOUSE_BUTTON);
   generator.PressTouch();
   EXPECT_FALSE(highlighter_test_api.IsShowingHighlighter());
@@ -312,8 +314,34 @@
   EXPECT_FALSE(highlighter_test_api.IsShowingHighlighter());
   generator.ReleaseTouch();
 
-  // Now enable the metalayer support.
-  test_palette_delegate()->SetMetalayerSupported(true);
+  // Enable one of the two user prefs, should not be sufficient.
+  Shell::Get()->NotifyVoiceInteractionContextEnabled(true);
+  generator.set_flags(ui::EF_LEFT_MOUSE_BUTTON);
+  generator.PressTouch();
+  EXPECT_FALSE(highlighter_test_api.IsShowingHighlighter());
+  generator.MoveTouch(gfx::Point(2, 2));
+  EXPECT_FALSE(highlighter_test_api.IsShowingHighlighter());
+  generator.set_flags(ui::EF_NONE);
+  generator.MoveTouch(gfx::Point(3, 3));
+  EXPECT_FALSE(highlighter_test_api.IsShowingHighlighter());
+  generator.ReleaseTouch();
+
+  // Enable the other user pref, still not sufficient.
+  Shell::Get()->NotifyVoiceInteractionEnabled(true);
+  Shell::Get()->NotifyVoiceInteractionContextEnabled(true);
+  generator.set_flags(ui::EF_LEFT_MOUSE_BUTTON);
+  generator.PressTouch();
+  EXPECT_FALSE(highlighter_test_api.IsShowingHighlighter());
+  generator.MoveTouch(gfx::Point(2, 2));
+  EXPECT_FALSE(highlighter_test_api.IsShowingHighlighter());
+  generator.set_flags(ui::EF_NONE);
+  generator.MoveTouch(gfx::Point(3, 3));
+  EXPECT_FALSE(highlighter_test_api.IsShowingHighlighter());
+  generator.ReleaseTouch();
+
+  // Once the service is ready, the button should start working.
+  Shell::Get()->NotifyVoiceInteractionStatusChanged(
+      VoiceInteractionState::RUNNING);
 
   // Press and drag with no button, still no highlighter.
   generator.MoveTouch(gfx::Point(1, 1));
@@ -368,7 +396,7 @@
 
   // Disable the metalayer support.
   // This should deactivate both the palette tool and the highlighter.
-  test_palette_delegate()->SetMetalayerSupported(false);
+  Shell::Get()->NotifyVoiceInteractionContextEnabled(false);
   EXPECT_FALSE(test_api_->GetPaletteToolManager()->IsToolActive(
       PaletteToolId::METALAYER));
   EXPECT_FALSE(highlighter_test_api.IsShowingHighlighter());
diff --git a/ash/system/palette/test_palette_delegate.cc b/ash/system/palette/test_palette_delegate.cc
index fa68435..43d0d40 100644
--- a/ash/system/palette/test_palette_delegate.cc
+++ b/ash/system/palette/test_palette_delegate.cc
@@ -13,12 +13,6 @@
 
 TestPaletteDelegate::~TestPaletteDelegate() {}
 
-void TestPaletteDelegate::SetMetalayerSupported(bool supported) {
-  is_metalayer_supported_ = supported;
-  if (!is_metalayer_supported_ && !metalayer_closed_.is_null())
-    base::ResetAndReturn(&metalayer_closed_).Run();
-}
-
 std::unique_ptr<PaletteDelegate::EnableListenerSubscription>
 TestPaletteDelegate::AddPaletteEnableListener(
     const EnableListener& on_state_changed) {
@@ -53,20 +47,14 @@
 
 void TestPaletteDelegate::CancelPartialScreenshot() {}
 
-bool TestPaletteDelegate::IsMetalayerSupported() {
-  return is_metalayer_supported_;
-}
-
-void TestPaletteDelegate::ShowMetalayer(const base::Closure& closed) {
+void TestPaletteDelegate::ShowMetalayer() {
   ++show_metalayer_count_;
-  metalayer_closed_ = closed;
   if (highlighter_test_api_)
     highlighter_test_api_->SetEnabled(true);
 }
 
 void TestPaletteDelegate::HideMetalayer() {
   ++hide_metalayer_count_;
-  metalayer_closed_ = base::Closure();
   if (highlighter_test_api_)
     highlighter_test_api_->SetEnabled(false);
 }
diff --git a/ash/system/palette/test_palette_delegate.h b/ash/system/palette/test_palette_delegate.h
index a4bb26d..65fe806 100644
--- a/ash/system/palette/test_palette_delegate.h
+++ b/ash/system/palette/test_palette_delegate.h
@@ -42,14 +42,10 @@
     should_show_palette_ = should_show_palette;
   }
 
-  void SetMetalayerSupported(bool is_metalayer_supported);
-
   int show_metalayer_count() const { return show_metalayer_count_; }
 
   int hide_metalayer_count() const { return hide_metalayer_count_; }
 
-  base::Closure metalayer_closed() const { return metalayer_closed_; }
-
   void set_highlighter_test_api(HighlighterControllerTestApi* api) {
     highlighter_test_api_ = api;
   }
@@ -65,8 +61,7 @@
   void TakeScreenshot() override;
   void TakePartialScreenshot(const base::Closure& done) override;
   void CancelPartialScreenshot() override;
-  bool IsMetalayerSupported() override;
-  void ShowMetalayer(const base::Closure& closed) override;
+  void ShowMetalayer() override;
   void HideMetalayer() override;
 
   int create_note_count_ = 0;
@@ -80,7 +75,6 @@
   bool is_metalayer_supported_ = false;
   int show_metalayer_count_ = 0;
   int hide_metalayer_count_ = 0;
-  base::Closure metalayer_closed_;
 
   HighlighterControllerTestApi* highlighter_test_api_ = nullptr;
 
diff --git a/ash/system/palette/tools/metalayer_mode.cc b/ash/system/palette/tools/metalayer_mode.cc
index 4fe2b55c..53645ea 100644
--- a/ash/system/palette/tools/metalayer_mode.cc
+++ b/ash/system/palette/tools/metalayer_mode.cc
@@ -30,8 +30,7 @@
 
 }  // namespace
 
-MetalayerMode::MetalayerMode(Delegate* delegate)
-    : CommonPaletteTool(delegate), weak_factory_(this) {
+MetalayerMode::MetalayerMode(Delegate* delegate) : CommonPaletteTool(delegate) {
   Shell::Get()->AddPreTargetHandler(this);
   Shell::Get()->AddShellObserver(this);
 }
@@ -52,8 +51,7 @@
 void MetalayerMode::OnEnable() {
   CommonPaletteTool::OnEnable();
 
-  Shell::Get()->palette_delegate()->ShowMetalayer(
-      base::Bind(&MetalayerMode::OnMetalayerDone, weak_factory_.GetWeakPtr()));
+  Shell::Get()->palette_delegate()->ShowMetalayer();
   delegate()->HidePalette();
 }
 
@@ -77,11 +75,14 @@
   return view;
 }
 
-void MetalayerMode::OnMetalayerDone() {
-  delegate()->DisableTool(GetToolId());
-}
-
 void MetalayerMode::OnTouchEvent(ui::TouchEvent* event) {
+  if (!feature_enabled())
+    return;
+
+  // The metalayer tool is already selected, no need to do anything.
+  if (enabled())
+    return;
+
   if (event->pointer_details().pointer_type !=
       ui::EventPointerType::POINTER_TYPE_PEN)
     return;
@@ -96,10 +97,7 @@
   if (palette_utils::PaletteContainsPointInScreen(event->root_location()))
     return;
 
-  if (enabled())
-    return;
-
-  if (voice_interaction_state_ == ash::VoiceInteractionState::NOT_READY) {
+  if (loading()) {
     // Repetitive presses will create toasts with the same id which will be
     // ignored.
     ToastData toast(
@@ -107,53 +105,50 @@
         l10n_util::GetStringUTF16(IDS_ASH_STYLUS_TOOLS_METALAYER_TOAST_LOADING),
         kToastDurationMs, base::Optional<base::string16>());
     Shell::Get()->toast_manager()->Show(toast);
-    event->StopPropagation();
-    return;
+  } else {
+    delegate()->RecordPaletteOptionsUsage(
+        PaletteToolIdToPaletteTrayOptions(GetToolId()),
+        PaletteInvocationMethod::SHORTCUT);
+    delegate()->EnableTool(GetToolId());
   }
-  // TODO(kaznacheev) When DISABLED state is introduced, check it here and
-  // bail out.
-
-  // TODO(kaznacheev) Use prefs instead of IsMetalayerSupported
-  // when crbug.com/727873 is fixed.
-  // Shell::palette_delegate() might return null in some tests that are not
-  // concerned with palette but generate touch events.
-  if (!Shell::Get()->palette_delegate() ||
-      !Shell::Get()->palette_delegate()->IsMetalayerSupported())
-    return;
-
-  delegate()->RecordPaletteOptionsUsage(
-      PaletteToolIdToPaletteTrayOptions(GetToolId()),
-      PaletteInvocationMethod::SHORTCUT);
-  delegate()->EnableTool(GetToolId());
   event->StopPropagation();
 }
 
 void MetalayerMode::OnVoiceInteractionStatusChanged(
-    ash::VoiceInteractionState state) {
+    VoiceInteractionState state) {
   voice_interaction_state_ = state;
-  UpdateView();
-  if (voice_interaction_state_ != ash::VoiceInteractionState::NOT_READY)
+  UpdateState();
+}
+
+void MetalayerMode::OnVoiceInteractionEnabled(bool enabled) {
+  voice_interaction_enabled_ = enabled;
+  UpdateState();
+}
+
+void MetalayerMode::OnVoiceInteractionContextEnabled(bool enabled) {
+  voice_interaction_context_enabled_ = enabled;
+  UpdateState();
+}
+
+void MetalayerMode::UpdateState() {
+  if (enabled() && !selectable())
+    delegate()->DisableTool(GetToolId());
+
+  if (!loading())
     Shell::Get()->toast_manager()->Cancel(kToastId);
+
+  UpdateView();
 }
 
 void MetalayerMode::UpdateView() {
   if (!highlight_view_)
     return;
 
-  const bool ready =
-      voice_interaction_state_ != ash::VoiceInteractionState::NOT_READY;
-
-  // TODO(kaznacheev) Use prefs instead of IsMetalayerSupported
-  // when crbug.com/727873 is fixed.
-  const bool supported =
-      Shell::Get()->palette_delegate() &&
-      Shell::Get()->palette_delegate()->IsMetalayerSupported();
-
   highlight_view_->text_label()->SetText(l10n_util::GetStringUTF16(
-      ready ? IDS_ASH_STYLUS_TOOLS_METALAYER_MODE
-            : IDS_ASH_STYLUS_TOOLS_METALAYER_MODE_LOADING));
+      loading() ? IDS_ASH_STYLUS_TOOLS_METALAYER_MODE_LOADING
+                : IDS_ASH_STYLUS_TOOLS_METALAYER_MODE));
 
-  highlight_view_->SetEnabled(ready && supported);
+  highlight_view_->SetEnabled(selectable());
 
   TrayPopupItemStyle style(TrayPopupItemStyle::FontStyle::DETAILED_VIEW_LABEL);
   style.set_color_style(highlight_view_->enabled()
diff --git a/ash/system/palette/tools/metalayer_mode.h b/ash/system/palette/tools/metalayer_mode.h
index 6675a55..1d92774 100644
--- a/ash/system/palette/tools/metalayer_mode.h
+++ b/ash/system/palette/tools/metalayer_mode.h
@@ -9,7 +9,6 @@
 #include "ash/public/cpp/voice_interaction_state.h"
 #include "ash/shell_observer.h"
 #include "ash/system/palette/common_palette_tool.h"
-#include "base/memory/weak_ptr.h"
 #include "ui/events/event_handler.h"
 
 namespace ash {
@@ -27,6 +26,26 @@
   ~MetalayerMode() override;
 
  private:
+  // Whether the metalayer feature is enabled by the user. This is different
+  // from |enabled| which means that the palette tool is currently selected by
+  // the user.
+  bool feature_enabled() const {
+    return voice_interaction_enabled_ && voice_interaction_context_enabled_;
+  }
+
+  // Whether the tool is in "loading" state.
+  bool loading() const {
+    return feature_enabled() &&
+           voice_interaction_state_ == VoiceInteractionState::NOT_READY;
+  }
+
+  // Whether the tool can be selected from the menu (only true when enabled
+  // by the user and fully loaded).
+  bool selectable() const {
+    return feature_enabled() &&
+           voice_interaction_state_ != VoiceInteractionState::NOT_READY;
+  }
+
   // PaletteTool:
   PaletteGroup GetGroup() const override;
   PaletteToolId GetToolId() const override;
@@ -44,8 +63,11 @@
   // ShellObserver:
   void OnVoiceInteractionStatusChanged(
       ash::VoiceInteractionState state) override;
+  void OnVoiceInteractionEnabled(bool enabled) override;
+  void OnVoiceInteractionContextEnabled(bool enabled) override;
 
-  void OnMetalayerDone();
+  // Update the state of the tool based on the current availability of the tool.
+  void UpdateState();
 
   // Update the palette menu item based on the current availability of the tool.
   void UpdateView();
@@ -53,7 +75,9 @@
   ash::VoiceInteractionState voice_interaction_state_ =
       ash::VoiceInteractionState::NOT_READY;
 
-  base::WeakPtrFactory<MetalayerMode> weak_factory_;
+  bool voice_interaction_enabled_ = false;
+
+  bool voice_interaction_context_enabled_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(MetalayerMode);
 };
diff --git a/ash/system/palette/tools/metalayer_unittest.cc b/ash/system/palette/tools/metalayer_unittest.cc
index db539db..1365eca 100644
--- a/ash/system/palette/tools/metalayer_unittest.cc
+++ b/ash/system/palette/tools/metalayer_unittest.cc
@@ -9,9 +9,12 @@
 #include "ash/system/palette/palette_tool.h"
 #include "ash/system/palette/test_palette_delegate.h"
 #include "ash/system/palette/tools/metalayer_mode.h"
+#include "ash/system/tray/hover_highlight_view.h"
 #include "ash/test/ash_test_base.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "ui/views/controls/label.h"
 #include "ui/views/view.h"
 
 namespace ash {
@@ -54,50 +57,40 @@
 
 }  // namespace
 
-// The metalayer tool is always visible, but only enabled when the delegate
-// supports metalayer AND the voice interaction framework is ready.
+// The metalayer tool is always visible, but only enabled when the user
+// has enabled the metalayer AND the voice interaction framework is ready.
 TEST_F(MetalayerToolTest, PaletteMenuState) {
-  ash::Shell::Get()->NotifyVoiceInteractionStatusChanged(
-      ash::VoiceInteractionState::NOT_READY);
+  const VoiceInteractionState kStates[] = {VoiceInteractionState::NOT_READY,
+                                           VoiceInteractionState::STOPPED,
+                                           VoiceInteractionState::RUNNING};
+  const base::string16 kLoading(base::ASCIIToUTF16("loading"));
 
-  {
-    // Voice interaction not ready, metalayer not supported.
-    test_palette_delegate()->SetMetalayerSupported(false);
-    std::unique_ptr<views::View> view = base::WrapUnique(tool_->CreateView());
-    EXPECT_TRUE(view);
-    EXPECT_FALSE(view->enabled());
-    tool_->OnViewDestroyed();
-  }
+  // Iterate over every possible combination of states.
+  for (VoiceInteractionState state : kStates) {
+    for (int enabled = 0; enabled <= 1; enabled++) {
+      for (int context = 0; context <= 1; context++) {
+        const bool ready = state != VoiceInteractionState::NOT_READY;
+        const bool selectable = enabled && context && ready;
 
-  {
-    // Voice interaction not ready, metalayer supported.
-    test_palette_delegate()->SetMetalayerSupported(true);
-    std::unique_ptr<views::View> view = base::WrapUnique(tool_->CreateView());
-    EXPECT_TRUE(view);
-    EXPECT_FALSE(view->enabled());
-    tool_->OnViewDestroyed();
-  }
+        Shell::Get()->NotifyVoiceInteractionStatusChanged(state);
+        Shell::Get()->NotifyVoiceInteractionEnabled(enabled);
+        Shell::Get()->NotifyVoiceInteractionContextEnabled(context);
 
-  ash::Shell::Get()->NotifyVoiceInteractionStatusChanged(
-      ash::VoiceInteractionState::RUNNING);
+        std::unique_ptr<views::View> view =
+            base::WrapUnique(tool_->CreateView());
+        EXPECT_TRUE(view);
+        EXPECT_EQ(selectable, view->enabled());
 
-  {
-    // Voice interaction ready, metalayer not supported.
-    test_palette_delegate()->SetMetalayerSupported(false);
-    std::unique_ptr<views::View> view = base::WrapUnique(tool_->CreateView());
-    EXPECT_TRUE(view);
-    EXPECT_FALSE(view->enabled());
-    tool_->OnViewDestroyed();
-  }
+        const base::string16 label_text =
+            static_cast<HoverHighlightView*>(view.get())->text_label()->text();
 
-  {
-    // Voice interaction ready, metalayer supported: the only combination when
-    // the view should be enabled.
-    test_palette_delegate()->SetMetalayerSupported(true);
-    std::unique_ptr<views::View> view = base::WrapUnique(tool_->CreateView());
-    EXPECT_TRUE(view);
-    EXPECT_TRUE(view->enabled());
-    tool_->OnViewDestroyed();
+        const bool label_contains_loading =
+            label_text.find(kLoading) != base::string16::npos;
+
+        EXPECT_EQ(enabled && context && !ready, label_contains_loading);
+        tool_->OnViewDestroyed();
+      }
+    }
   }
 }
 
@@ -118,24 +111,49 @@
   testing::Mock::VerifyAndClearExpectations(palette_tool_delegate_.get());
 }
 
-// Verifies that invoking the callback passed to the delegate disables the tool.
-TEST_F(MetalayerToolTest, MetalayerCallbackDisablesPaletteTool) {
-  tool_->OnEnable();
-  // Calling the associated callback (metalayer closed) will disable the tool.
-  EXPECT_CALL(*palette_tool_delegate_.get(),
-              DisableTool(PaletteToolId::METALAYER));
-  test_palette_delegate()->metalayer_closed().Run();
-}
-
 // Verifies that disabling the metalayer support in the delegate disables the
 // tool.
 TEST_F(MetalayerToolTest, MetalayerUnsupportedDisablesPaletteTool) {
-  test_palette_delegate()->SetMetalayerSupported(true);
+  Shell::Get()->NotifyVoiceInteractionStatusChanged(
+      VoiceInteractionState::RUNNING);
+  Shell::Get()->NotifyVoiceInteractionEnabled(true);
+  Shell::Get()->NotifyVoiceInteractionContextEnabled(true);
+
+  // Disabling the user prefs individually should disable the tool.
   tool_->OnEnable();
-  // Disabling the metalayer support in the delegate will disable the tool.
   EXPECT_CALL(*palette_tool_delegate_.get(),
               DisableTool(PaletteToolId::METALAYER));
-  test_palette_delegate()->SetMetalayerSupported(false);
+  Shell::Get()->NotifyVoiceInteractionEnabled(false);
+  testing::Mock::VerifyAndClearExpectations(palette_tool_delegate_.get());
+  Shell::Get()->NotifyVoiceInteractionEnabled(true);
+
+  tool_->OnEnable();
+  EXPECT_CALL(*palette_tool_delegate_.get(),
+              DisableTool(PaletteToolId::METALAYER));
+  Shell::Get()->NotifyVoiceInteractionContextEnabled(false);
+  testing::Mock::VerifyAndClearExpectations(palette_tool_delegate_.get());
+  Shell::Get()->NotifyVoiceInteractionContextEnabled(true);
+
+  // Test VoiceInteractionState changes.
+  tool_->OnEnable();
+
+  // Changing the state from RUNNING to STOPPED and back should not disable the
+  // tool.
+  EXPECT_CALL(*palette_tool_delegate_.get(),
+              DisableTool(PaletteToolId::METALAYER))
+      .Times(0);
+  Shell::Get()->NotifyVoiceInteractionStatusChanged(
+      VoiceInteractionState::STOPPED);
+  Shell::Get()->NotifyVoiceInteractionStatusChanged(
+      VoiceInteractionState::RUNNING);
+  testing::Mock::VerifyAndClearExpectations(palette_tool_delegate_.get());
+
+  // Changing the state to NOT_READY should disable the tool.
+  EXPECT_CALL(*palette_tool_delegate_.get(),
+              DisableTool(PaletteToolId::METALAYER));
+  Shell::Get()->NotifyVoiceInteractionStatusChanged(
+      VoiceInteractionState::NOT_READY);
+  testing::Mock::VerifyAndClearExpectations(palette_tool_delegate_.get());
 }
 
 }  // namespace ash
diff --git a/ash/system/power/power_button_display_controller.cc b/ash/system/power/power_button_display_controller.cc
new file mode 100644
index 0000000..2e830cbf
--- /dev/null
+++ b/ash/system/power/power_button_display_controller.cc
@@ -0,0 +1,160 @@
+// 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 "ash/system/power/power_button_display_controller.h"
+
+#include "ash/accessibility_delegate.h"
+#include "ash/shell.h"
+#include "ash/shell_delegate.h"
+#include "ash/wm/tablet_mode/tablet_mode_controller.h"
+#include "base/logging.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "ui/events/devices/input_device_manager.h"
+#include "ui/events/devices/stylus_state.h"
+#include "ui/events/event.h"
+
+namespace ash {
+
+namespace {
+
+// Returns true if device is a convertible/tablet device, otherwise false.
+bool IsTabletModeSupported() {
+  TabletModeController* tablet_mode_controller =
+      Shell::Get()->tablet_mode_controller();
+  return tablet_mode_controller && tablet_mode_controller->CanEnterTabletMode();
+}
+
+// Returns true if device is currently in tablet/tablet mode, otherwise false.
+bool IsTabletModeActive() {
+  TabletModeController* tablet_mode_controller =
+      Shell::Get()->tablet_mode_controller();
+  return tablet_mode_controller &&
+         tablet_mode_controller->IsTabletModeWindowManagerEnabled();
+}
+
+}  // namespace
+
+PowerButtonDisplayController::PowerButtonDisplayController()
+    : weak_ptr_factory_(this) {
+  chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver(
+      this);
+  // TODO(mash): Provide a way for this class to observe stylus events:
+  // http://crbug.com/682460
+  if (ui::InputDeviceManager::HasInstance())
+    ui::InputDeviceManager::GetInstance()->AddObserver(this);
+  Shell::Get()->PrependPreTargetHandler(this);
+
+  GetInitialBacklightsForcedOff();
+}
+
+PowerButtonDisplayController::~PowerButtonDisplayController() {
+  Shell::Get()->RemovePreTargetHandler(this);
+  if (ui::InputDeviceManager::HasInstance())
+    ui::InputDeviceManager::GetInstance()->RemoveObserver(this);
+  chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->RemoveObserver(
+      this);
+}
+
+void PowerButtonDisplayController::SetDisplayForcedOff(bool forced_off) {
+  if (backlights_forced_off_ == forced_off)
+    return;
+
+  // Set the display and keyboard backlights (if present) to |forced_off|.
+  chromeos::DBusThreadManager::Get()
+      ->GetPowerManagerClient()
+      ->SetBacklightsForcedOff(forced_off);
+  backlights_forced_off_ = forced_off;
+  UpdateTouchscreenStatus();
+
+  if (backlights_forced_off_)
+    Shell::Get()->shell_delegate()->SuspendMediaSessions();
+
+  // Send an a11y alert.
+  Shell::Get()->accessibility_delegate()->TriggerAccessibilityAlert(
+      forced_off ? A11Y_ALERT_SCREEN_OFF : A11Y_ALERT_SCREEN_ON);
+}
+
+void PowerButtonDisplayController::PowerManagerRestarted() {
+  chromeos::DBusThreadManager::Get()
+      ->GetPowerManagerClient()
+      ->SetBacklightsForcedOff(backlights_forced_off_);
+}
+
+void PowerButtonDisplayController::BrightnessChanged(int level,
+                                                     bool user_initiated) {
+  const ScreenState old_state = screen_state_;
+  if (level != 0)
+    screen_state_ = ScreenState::ON;
+  else
+    screen_state_ = user_initiated ? ScreenState::OFF : ScreenState::OFF_AUTO;
+
+  // Disable the touchscreen when the screen is turned off due to inactivity:
+  // https://crbug.com/743291
+  if ((screen_state_ == ScreenState::OFF_AUTO) !=
+      (old_state == ScreenState::OFF_AUTO))
+    UpdateTouchscreenStatus();
+}
+
+void PowerButtonDisplayController::SuspendDone(
+    const base::TimeDelta& sleep_duration) {
+  // Stop forcing backlights off on resume to handle situations where the power
+  // button resumed but we didn't receive the event (crbug.com/735291).
+  SetDisplayForcedOff(false);
+}
+
+void PowerButtonDisplayController::LidEventReceived(
+    chromeos::PowerManagerClient::LidState state,
+    const base::TimeTicks& timestamp) {
+  SetDisplayForcedOff(false);
+}
+
+void PowerButtonDisplayController::OnKeyEvent(ui::KeyEvent* event) {
+  // Ignore key events generated by the power button since power button activity
+  // is already handled elsewhere.
+  if (event->key_code() == ui::VKEY_POWER)
+    return;
+
+  if (!IsTabletModeActive() && backlights_forced_off_)
+    SetDisplayForcedOff(false);
+}
+
+void PowerButtonDisplayController::OnMouseEvent(ui::MouseEvent* event) {
+  if (event->flags() & ui::EF_IS_SYNTHESIZED)
+    return;
+
+  if (!IsTabletModeActive() && backlights_forced_off_)
+    SetDisplayForcedOff(false);
+}
+
+void PowerButtonDisplayController::OnStylusStateChanged(ui::StylusState state) {
+  if (IsTabletModeSupported() && state == ui::StylusState::REMOVED &&
+      backlights_forced_off_) {
+    SetDisplayForcedOff(false);
+  }
+}
+
+void PowerButtonDisplayController::GetInitialBacklightsForcedOff() {
+  chromeos::DBusThreadManager::Get()
+      ->GetPowerManagerClient()
+      ->GetBacklightsForcedOff(base::Bind(
+          &PowerButtonDisplayController::OnGotInitialBacklightsForcedOff,
+          weak_ptr_factory_.GetWeakPtr()));
+}
+
+void PowerButtonDisplayController::OnGotInitialBacklightsForcedOff(
+    bool is_forced_off) {
+  backlights_forced_off_ = is_forced_off;
+  UpdateTouchscreenStatus();
+}
+
+void PowerButtonDisplayController::UpdateTouchscreenStatus() {
+  const bool enable_touchscreen =
+      !backlights_forced_off_ && (screen_state_ != ScreenState::OFF_AUTO);
+  ShellDelegate* delegate = Shell::Get()->shell_delegate();
+  delegate->SetTouchscreenEnabledInPrefs(enable_touchscreen,
+                                         true /* use_local_state */);
+  delegate->UpdateTouchscreenStatusFromPrefs();
+}
+
+}  // namespace ash
diff --git a/ash/system/power/power_button_display_controller.h b/ash/system/power/power_button_display_controller.h
new file mode 100644
index 0000000..cecf589
--- /dev/null
+++ b/ash/system/power/power_button_display_controller.h
@@ -0,0 +1,86 @@
+// 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 ASH_SYSTEM_POWER_POWER_BUTTON_DISPLAY_CONTROLLER_H_
+#define ASH_SYSTEM_POWER_POWER_BUTTON_DISPLAY_CONTROLLER_H_
+
+#include "ash/ash_export.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chromeos/dbus/power_manager_client.h"
+#include "ui/events/devices/input_device_event_observer.h"
+#include "ui/events/event_handler.h"
+
+namespace ash {
+
+// PowerButtonDisplayController performs display-related tasks (e.g. forcing
+// backlights off or disabling the touchscreen) on behalf of
+// PowerButtonController and TabletPowerButtonController.
+class ASH_EXPORT PowerButtonDisplayController
+    : public chromeos::PowerManagerClient::Observer,
+      public ui::EventHandler,
+      public ui::InputDeviceEventObserver {
+ public:
+  // Screen state as communicated by D-Bus signals from powerd about backlight
+  // brightness changes.
+  enum class ScreenState {
+    // The screen is on.
+    ON,
+    // The screen is off.
+    OFF,
+    // The screen is off, specifically due to an automated change like user
+    // inactivity.
+    OFF_AUTO,
+  };
+
+  PowerButtonDisplayController();
+  ~PowerButtonDisplayController() override;
+
+  ScreenState screen_state() const { return screen_state_; }
+
+  // Updates the power manager's backlights-forced-off state and enables or
+  // disables the touchscreen. No-op if |backlights_forced_off_| already equals
+  // |forced_off|.
+  void SetDisplayForcedOff(bool forced_off);
+
+  // Overridden from chromeos::PowerManagerClient::Observer:
+  void PowerManagerRestarted() override;
+  void BrightnessChanged(int level, bool user_initiated) override;
+  void SuspendDone(const base::TimeDelta& sleep_duration) override;
+  void LidEventReceived(chromeos::PowerManagerClient::LidState state,
+                        const base::TimeTicks& timestamp) override;
+
+  // Overridden from ui::EventHandler:
+  void OnKeyEvent(ui::KeyEvent* event) override;
+  void OnMouseEvent(ui::MouseEvent* event) override;
+
+  // Overridden from ui::InputDeviceObserver:
+  void OnStylusStateChanged(ui::StylusState state) override;
+
+ private:
+  // Sends a request to powerd to get the backlights forced off state so that
+  // |backlights_forced_off_| can be initialized.
+  void GetInitialBacklightsForcedOff();
+
+  // Initializes |backlights_forced_off_|.
+  void OnGotInitialBacklightsForcedOff(bool is_forced_off);
+
+  // Enables or disables the touchscreen, also writing its state to a pref in
+  // local state. The touchscreen is disabled when backlights are forced off.
+  void UpdateTouchscreenStatus();
+
+  // Current screen state.
+  ScreenState screen_state_ = ScreenState::ON;
+
+  // Current forced-off state of backlights.
+  bool backlights_forced_off_ = false;
+
+  base::WeakPtrFactory<PowerButtonDisplayController> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(PowerButtonDisplayController);
+};
+
+}  // namespace ash
+
+#endif  // ASH_SYSTEM_POWER_POWER_BUTTON_DISPLAY_CONTROLLER_H_
diff --git a/ash/system/power/tablet_power_button_controller.cc b/ash/system/power/tablet_power_button_controller.cc
index 588018e..9331a82 100644
--- a/ash/system/power/tablet_power_button_controller.cc
+++ b/ash/system/power/tablet_power_button_controller.cc
@@ -10,6 +10,7 @@
 #include "ash/shell.h"
 #include "ash/shell_delegate.h"
 #include "ash/shutdown_reason.h"
+#include "ash/system/power/power_button_display_controller.h"
 #include "ash/wm/lock_state_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "base/command_line.h"
@@ -53,14 +54,6 @@
   return tablet_mode_controller && tablet_mode_controller->CanEnterTabletMode();
 }
 
-// Returns true if device is currently in tablet/tablet mode, otherwise false.
-bool IsTabletModeActive() {
-  TabletModeController* tablet_mode_controller =
-      Shell::Get()->tablet_mode_controller();
-  return tablet_mode_controller &&
-         tablet_mode_controller->IsTabletModeWindowManagerEnabled();
-}
-
 // Returns the value for the command-line switch identified by |name|. Returns 0
 // if the switch was unset or contained a non-float value.
 double GetNumSwitch(const base::CommandLine& command_line,
@@ -124,29 +117,24 @@
   return controller_->IsSpuriousPowerButtonEvent();
 }
 
+void TabletPowerButtonController::TestApi::SendKeyEvent(ui::KeyEvent* event) {
+  controller_->display_controller_->OnKeyEvent(event);
+}
+
 TabletPowerButtonController::TabletPowerButtonController(
     LockStateController* controller)
-    : tick_clock_(new base::DefaultTickClock()),
+    : display_controller_(base::MakeUnique<PowerButtonDisplayController>()),
+      tick_clock_(new base::DefaultTickClock()),
       controller_(controller),
-      accelerometer_scoped_observer_(this),
-      weak_ptr_factory_(this) {
+      accelerometer_scoped_observer_(this) {
   chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver(
       this);
   Shell::Get()->tablet_mode_controller()->AddObserver(this);
-  // TODO(mash): Provide a way for this class to observe stylus events:
-  // http://crbug.com/682460
-  if (ui::InputDeviceManager::HasInstance())
-    ui::InputDeviceManager::GetInstance()->AddObserver(this);
-  Shell::Get()->PrependPreTargetHandler(this);
 
   ParseSpuriousPowerButtonSwitches(*base::CommandLine::ForCurrentProcess());
-  GetInitialBacklightsForcedOff();
 }
 
 TabletPowerButtonController::~TabletPowerButtonController() {
-  Shell::Get()->RemovePreTargetHandler(this);
-  if (ui::InputDeviceManager::HasInstance())
-    ui::InputDeviceManager::GetInstance()->RemoveObserver(this);
   if (Shell::Get()->tablet_mode_controller())
     Shell::Get()->tablet_mode_controller()->RemoveObserver(this);
   chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->RemoveObserver(
@@ -178,8 +166,10 @@
     }
 
     last_button_down_time_ = tick_clock_->NowTicks();
-    screen_off_when_power_button_down_ = screen_state_ != ScreenState::ON;
-    SetDisplayForcedOff(false);
+    screen_off_when_power_button_down_ =
+        display_controller_->screen_state() !=
+        PowerButtonDisplayController::ScreenState::ON;
+    display_controller_->SetDisplayForcedOff(false);
     StartShutdownTimer();
   } else {
     // Don't process the up event if we previously ignored the down event.
@@ -210,7 +200,7 @@
     if (shutdown_timer_.IsRunning()) {
       shutdown_timer_.Stop();
       if (!screen_off_when_power_button_down_ && force_off_on_button_up_) {
-        SetDisplayForcedOff(true);
+        display_controller_->SetDisplayForcedOff(true);
         LockScreenIfRequired();
       }
     }
@@ -236,39 +226,9 @@
   }
 }
 
-void TabletPowerButtonController::PowerManagerRestarted() {
-  chromeos::DBusThreadManager::Get()
-      ->GetPowerManagerClient()
-      ->SetBacklightsForcedOff(backlights_forced_off_);
-}
-
-void TabletPowerButtonController::BrightnessChanged(int level,
-                                                    bool user_initiated) {
-  const ScreenState old_state = screen_state_;
-  if (level != 0)
-    screen_state_ = ScreenState::ON;
-  else
-    screen_state_ = user_initiated ? ScreenState::OFF : ScreenState::OFF_AUTO;
-
-  // Disable the touchscreen when the screen is turned off due to inactivity:
-  // https://crbug.com/743291
-  if ((screen_state_ == ScreenState::OFF_AUTO) !=
-      (old_state == ScreenState::OFF_AUTO))
-    UpdateTouchscreenStatus();
-}
-
 void TabletPowerButtonController::SuspendDone(
     const base::TimeDelta& sleep_duration) {
   last_resume_time_ = tick_clock_->NowTicks();
-  // Stop forcing backlights off on resume to handle situations where the power
-  // button resumed but we didn't receive the event (crbug.com/735291).
-  SetDisplayForcedOff(false);
-}
-
-void TabletPowerButtonController::LidEventReceived(
-    chromeos::PowerManagerClient::LidState state,
-    const base::TimeTicks& timestamp) {
-  SetDisplayForcedOff(false);
 }
 
 void TabletPowerButtonController::OnTabletModeStarted() {
@@ -283,31 +243,6 @@
     controller_->CancelShutdownAnimation();
 }
 
-void TabletPowerButtonController::OnKeyEvent(ui::KeyEvent* event) {
-  // Ignore key events generated by the power button since power button activity
-  // is already handled by OnPowerButtonEvent().
-  if (event->key_code() == ui::VKEY_POWER)
-    return;
-
-  if (!IsTabletModeActive() && backlights_forced_off_)
-    SetDisplayForcedOff(false);
-}
-
-void TabletPowerButtonController::OnMouseEvent(ui::MouseEvent* event) {
-  if (event->flags() & ui::EF_IS_SYNTHESIZED)
-    return;
-
-  if (!IsTabletModeActive() && backlights_forced_off_)
-    SetDisplayForcedOff(false);
-}
-
-void TabletPowerButtonController::OnStylusStateChanged(ui::StylusState state) {
-  if (IsTabletModeSupported() && state == ui::StylusState::REMOVED &&
-      backlights_forced_off_) {
-    SetDisplayForcedOff(false);
-  }
-}
-
 void TabletPowerButtonController::SetTickClockForTesting(
     std::unique_ptr<base::TickClock> tick_clock) {
   DCHECK(tick_clock);
@@ -422,48 +357,6 @@
           max_angle_dist >= spurious_lid_angle_change_);
 }
 
-void TabletPowerButtonController::SetDisplayForcedOff(bool forced_off) {
-  if (backlights_forced_off_ == forced_off)
-    return;
-
-  // Set the display and keyboard backlights (if present) to |forced_off|.
-  chromeos::DBusThreadManager::Get()
-      ->GetPowerManagerClient()
-      ->SetBacklightsForcedOff(forced_off);
-  backlights_forced_off_ = forced_off;
-  UpdateTouchscreenStatus();
-
-  if (backlights_forced_off_)
-    Shell::Get()->shell_delegate()->SuspendMediaSessions();
-
-  // Send an a11y alert.
-  Shell::Get()->accessibility_delegate()->TriggerAccessibilityAlert(
-      forced_off ? A11Y_ALERT_SCREEN_OFF : A11Y_ALERT_SCREEN_ON);
-}
-
-void TabletPowerButtonController::GetInitialBacklightsForcedOff() {
-  chromeos::DBusThreadManager::Get()
-      ->GetPowerManagerClient()
-      ->GetBacklightsForcedOff(base::Bind(
-          &TabletPowerButtonController::OnGotInitialBacklightsForcedOff,
-          weak_ptr_factory_.GetWeakPtr()));
-}
-
-void TabletPowerButtonController::OnGotInitialBacklightsForcedOff(
-    bool is_forced_off) {
-  backlights_forced_off_ = is_forced_off;
-  UpdateTouchscreenStatus();
-}
-
-void TabletPowerButtonController::UpdateTouchscreenStatus() {
-  const bool enable_touchscreen =
-      !backlights_forced_off_ && (screen_state_ != ScreenState::OFF_AUTO);
-  ShellDelegate* delegate = Shell::Get()->shell_delegate();
-  delegate->SetTouchscreenEnabledInPrefs(enable_touchscreen,
-                                         true /* use_local_state */);
-  delegate->UpdateTouchscreenStatusFromPrefs();
-}
-
 void TabletPowerButtonController::StartShutdownTimer() {
   base::TimeDelta timeout = base::TimeDelta::FromMilliseconds(
       screen_off_when_power_button_down_ ? kShutdownWhenScreenOffTimeoutMs
diff --git a/ash/system/power/tablet_power_button_controller.h b/ash/system/power/tablet_power_button_controller.h
index 8c91af7b..366cdc3 100644
--- a/ash/system/power/tablet_power_button_controller.h
+++ b/ash/system/power/tablet_power_button_controller.h
@@ -11,7 +11,6 @@
 #include "ash/ash_export.h"
 #include "ash/wm/tablet_mode/tablet_mode_observer.h"
 #include "base/macros.h"
-#include "base/memory/weak_ptr.h"
 #include "base/scoped_observer.h"
 #include "base/time/tick_clock.h"
 #include "base/time/time.h"
@@ -30,15 +29,14 @@
 namespace ash {
 
 class LockStateController;
+class PowerButtonDisplayController;
 
 // Handles power button events on convertible/tablet device. This class is
 // instantiated and used in PowerButtonController.
 class ASH_EXPORT TabletPowerButtonController
     : public chromeos::AccelerometerReader::Observer,
       public chromeos::PowerManagerClient::Observer,
-      public TabletModeObserver,
-      public ui::EventHandler,
-      public ui::InputDeviceEventObserver {
+      public TabletModeObserver {
  public:
   // Helper class used by tablet power button tests to access internal state.
   class ASH_EXPORT TestApi {
@@ -63,6 +61,9 @@
     // Calls |controller_|'s IsSpuriousPowerButtonEvent() method.
     bool IsSpuriousPowerButtonEvent() const;
 
+    // Sends |event| to |display_controller_|.
+    void SendKeyEvent(ui::KeyEvent* event);
+
    private:
     TabletPowerButtonController* controller_;  // Not owned.
 
@@ -87,23 +88,12 @@
       scoped_refptr<const chromeos::AccelerometerUpdate> update) override;
 
   // Overridden from chromeos::PowerManagerClient::Observer:
-  void PowerManagerRestarted() override;
-  void BrightnessChanged(int level, bool user_initiated) override;
   void SuspendDone(const base::TimeDelta& sleep_duration) override;
-  void LidEventReceived(chromeos::PowerManagerClient::LidState state,
-                        const base::TimeTicks& timestamp) override;
 
   // TabletModeObserver:
   void OnTabletModeStarted() override;
   void OnTabletModeEnded() override;
 
-  // Overridden from ui::EventHandler:
-  void OnKeyEvent(ui::KeyEvent* event) override;
-  void OnMouseEvent(ui::MouseEvent* event) override;
-
-  // Overridden from ui::InputDeviceObserver:
-  void OnStylusStateChanged(ui::StylusState state) override;
-
   // Overrides the tick clock used by |this| for testing.
   void SetTickClockForTesting(std::unique_ptr<base::TickClock> tick_clock);
 
@@ -117,22 +107,6 @@
   // received to be accidental and ignore it.
   bool IsSpuriousPowerButtonEvent() const;
 
-  // Updates the power manager's backlights-forced-off state and enables or
-  // disables the touchscreen. No-op if |backlights_forced_off_| already equals
-  // |forced_off|.
-  void SetDisplayForcedOff(bool forced_off);
-
-  // Sends a request to powerd to get the backlights forced off state so that
-  // |backlights_forced_off_| can be initialized.
-  void GetInitialBacklightsForcedOff();
-
-  // Initializes |backlights_forced_off_|.
-  void OnGotInitialBacklightsForcedOff(bool is_forced_off);
-
-  // Enables or disables the touchscreen, also writing its state to a pref in
-  // local state. The touchscreen is disabled when backlights are forced off.
-  void UpdateTouchscreenStatus();
-
   // Starts |shutdown_timer_| when the power button is pressed while in
   // tablet mode.
   void StartShutdownTimer();
@@ -144,23 +118,8 @@
   // and locking is possible.
   void LockScreenIfRequired();
 
-  // Screen state as communicated by D-Bus signals from powerd about backlight
-  // brightness changes.
-  enum class ScreenState {
-    // The screen is on.
-    ON,
-    // The screen is off.
-    OFF,
-    // The screen is off, specifically due to an automated change like user
-    // inactivity.
-    OFF_AUTO,
-  };
-
-  // Current screen state.
-  ScreenState screen_state_ = ScreenState::ON;
-
-  // Current forced-off state of backlights.
-  bool backlights_forced_off_ = false;
+  // Used to interact with the display.
+  std::unique_ptr<PowerButtonDisplayController> display_controller_;
 
   // True if the screen was off when the power button was pressed.
   bool screen_off_when_power_button_down_ = false;
@@ -213,8 +172,6 @@
   // in order for a power button event to be considered spurious.
   float spurious_lid_angle_change_ = 0;
 
-  base::WeakPtrFactory<TabletPowerButtonController> weak_ptr_factory_;
-
   DISALLOW_COPY_AND_ASSIGN(TabletPowerButtonController);
 };
 
diff --git a/ash/system/power/tablet_power_button_controller_unittest.cc b/ash/system/power/tablet_power_button_controller_unittest.cc
index 39ab058..bb31bb71 100644
--- a/ash/system/power/tablet_power_button_controller_unittest.cc
+++ b/ash/system/power/tablet_power_button_controller_unittest.cc
@@ -396,15 +396,15 @@
   // generated for each pressing and releasing, and multiple repeating pressed
   // events depending on holding.
   ASSERT_EQ(0, power_manager_client_->num_set_backlights_forced_off_calls());
-  tablet_controller_->OnKeyEvent(&power_key_pressed);
-  tablet_controller_->OnKeyEvent(&power_key_pressed);
+  test_api_->SendKeyEvent(&power_key_pressed);
+  test_api_->SendKeyEvent(&power_key_pressed);
   PressPowerButton();
-  tablet_controller_->OnKeyEvent(&power_key_pressed);
-  tablet_controller_->OnKeyEvent(&power_key_pressed);
-  tablet_controller_->OnKeyEvent(&power_key_pressed);
+  test_api_->SendKeyEvent(&power_key_pressed);
+  test_api_->SendKeyEvent(&power_key_pressed);
+  test_api_->SendKeyEvent(&power_key_pressed);
   ReleasePowerButton();
-  tablet_controller_->OnKeyEvent(&power_key_released);
-  tablet_controller_->OnKeyEvent(&power_key_released);
+  test_api_->SendKeyEvent(&power_key_released);
+  test_api_->SendKeyEvent(&power_key_released);
   EXPECT_EQ(1, power_manager_client_->num_set_backlights_forced_off_calls());
 }
 
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 9aada78..82f7a7fd 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -1237,6 +1237,8 @@
       "base_paths_fuchsia.cc",
       "debug/stack_trace_fuchsia.cc",
       "files/file_path_watcher_fuchsia.cc",
+      "fuchsia/default_job.cc",
+      "fuchsia/default_job.h",
       "fuchsia/scoped_mx_handle.h",
       "memory/shared_memory_fuchsia.cc",
       "memory/shared_memory_handle_fuchsia.cc",
@@ -2076,6 +2078,7 @@
     "metrics/histogram_delta_serialization_unittest.cc",
     "metrics/histogram_functions_unittest.cc",
     "metrics/histogram_macros_unittest.cc",
+    "metrics/histogram_samples_unittest.cc",
     "metrics/histogram_snapshot_manager_unittest.cc",
     "metrics/histogram_unittest.cc",
     "metrics/metrics_hashes_unittest.cc",
diff --git a/base/base_paths_fuchsia.cc b/base/base_paths_fuchsia.cc
index b2f28ce..41883d9 100644
--- a/base/base_paths_fuchsia.cc
+++ b/base/base_paths_fuchsia.cc
@@ -4,28 +4,34 @@
 
 #include "base/base_paths.h"
 
+#include <stdlib.h>
+
+#include "base/command_line.h"
 #include "base/files/file_path.h"
 
 namespace base {
 
 bool PathProviderFuchsia(int key, FilePath* result) {
-  // TODO(fuchsia): There's no API to retrieve these on Fuchsia. The app name
-  // itself should be dynamic (i.e. not always "chrome") but other paths are
-  // correct as fixed paths like this. See https://crbug.com/726124.
   switch (key) {
-    case FILE_EXE:
     case FILE_MODULE:
-      // TODO(fuchsia): This is incorrect per
-      // https://fuchsia.googlesource.com/docs/+/master/namespaces.md, and
-      // should be /pkg/{bin,lib}/something. However, binaries are currently run
-      // by packing them into the system bootfs rather than running a "real"
-      // installer (which doesn't currently exist). Additionally, to the
-      // installer not existing, mmap() currently only works on bootfs file
-      // systems (like /system) but won't for files installed dynamically in
-      // other locations on other types of file systems. So, for now, we use
-      // /system/ as the location for everything.
-      *result = FilePath("/system/chrome");
+    // Not supported in debug or component builds. Fall back on using the EXE
+    // path for now.
+    // TODO(fuchsia): Get this value from an API. See crbug.com/726124
+    case FILE_EXE: {
+      // Use the binary name as specified on the command line.
+      // TODO(fuchsia): It would be nice to get the canonical executable path
+      // from a kernel API. See https://crbug.com/726124
+      char bin_dir[PATH_MAX + 1];
+      if (realpath(base::CommandLine::ForCurrentProcess()
+                       ->GetProgram()
+                       .AsUTF8Unsafe()
+                       .c_str(),
+                   bin_dir) == NULL) {
+        return false;
+      }
+      *result = FilePath(bin_dir);
       return true;
+    }
     case DIR_SOURCE_ROOT:
       // This is only used for tests, so we return the binary location for now.
       *result = FilePath("/system");
diff --git a/base/fuchsia/default_job.cc b/base/fuchsia/default_job.cc
new file mode 100644
index 0000000..3cc49f1
--- /dev/null
+++ b/base/fuchsia/default_job.cc
@@ -0,0 +1,28 @@
+// 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/fuchsia/default_job.h"
+
+#include <magenta/process.h>
+
+#include "base/logging.h"
+
+namespace base {
+
+namespace {
+mx_handle_t g_job = MX_HANDLE_INVALID;
+}  // namespace
+
+mx_handle_t GetDefaultJob() {
+  if (g_job == MX_HANDLE_INVALID)
+    return mx_job_default();
+  return g_job;
+}
+
+void SetDefaultJob(ScopedMxHandle job) {
+  DCHECK_EQ(MX_HANDLE_INVALID, g_job);
+  g_job = job.release();
+}
+
+}  // namespace base
diff --git a/base/fuchsia/default_job.h b/base/fuchsia/default_job.h
new file mode 100644
index 0000000..17c3212
--- /dev/null
+++ b/base/fuchsia/default_job.h
@@ -0,0 +1,21 @@
+// 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 BASE_FUCHSIA_DEFAULT_JOB_H_
+#define BASE_FUCHSIA_DEFAULT_JOB_H_
+
+#include "base/fuchsia/scoped_mx_handle.h"
+
+namespace base {
+
+// Gets and sets the job object used for creating new child processes,
+// and looking them up by their process IDs.
+// mx_job_default() will be returned if no job is explicitly set here.
+// Only valid handles may be passed to SetDefaultJob().
+mx_handle_t GetDefaultJob();
+void SetDefaultJob(ScopedMxHandle job);
+
+}  // namespace base
+
+#endif  // BASE_FUCHSIA_DEFAULT_JOB_H_
diff --git a/base/metrics/histogram_samples.cc b/base/metrics/histogram_samples.cc
index 965d1783..019cc5ab 100644
--- a/base/metrics/histogram_samples.cc
+++ b/base/metrics/histogram_samples.cc
@@ -104,13 +104,19 @@
     return true;
 
   // Convert the parameters to 16-bit variables because it's all 16-bit below.
-  if (count < std::numeric_limits<uint16_t>::min() ||
+  // To support decrements/subtractions, divide the |count| into sign/value and
+  // do the proper operation below. The alternative is to change the single-
+  // sample's count to be a signed integer (int16_t) and just add an int16_t
+  // |count16| but that is somewhat wasteful given that the single-sample is
+  // never expected to have a count less than zero.
+  if (count < -std::numeric_limits<uint16_t>::max() ||
       count > std::numeric_limits<uint16_t>::max() ||
       bucket > std::numeric_limits<uint16_t>::max()) {
     return false;
   }
+  bool count_is_negative = count < 0;
+  uint16_t count16 = static_cast<uint16_t>(count_is_negative ? -count : count);
   uint16_t bucket16 = static_cast<uint16_t>(bucket);
-  uint16_t count16 = static_cast<uint16_t>(count);
 
   // A local, unshared copy of the single-sample is necessary so the parts
   // can be manipulated without worrying about atomicity.
@@ -135,7 +141,10 @@
 
     // Update count, making sure that it doesn't overflow.
     CheckedNumeric<uint16_t> new_count(single_sample.as_parts.count);
-    new_count += count16;
+    if (count_is_negative)
+      new_count -= count16;
+    else
+      new_count += count16;
     if (!new_count.AssignIfValid(&single_sample.as_parts.count))
       return false;
 
diff --git a/base/metrics/histogram_samples.h b/base/metrics/histogram_samples.h
index e6257da..3efb05d 100644
--- a/base/metrics/histogram_samples.h
+++ b/base/metrics/histogram_samples.h
@@ -46,7 +46,7 @@
   // A structure for managing an atomic single sample. Because this is generally
   // used in association with other atomic values, the defined methods use
   // acquire/release operations to guarantee ordering with outside values.
-  union AtomicSingleSample {
+  union BASE_EXPORT AtomicSingleSample {
     AtomicSingleSample() : as_atomic(0) {}
     AtomicSingleSample(subtle::Atomic32 rhs) : as_atomic(rhs) {}
 
diff --git a/base/metrics/histogram_samples_unittest.cc b/base/metrics/histogram_samples_unittest.cc
new file mode 100644
index 0000000..74c743b6
--- /dev/null
+++ b/base/metrics/histogram_samples_unittest.cc
@@ -0,0 +1,84 @@
+// Copyright (c) 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/metrics/histogram_samples.h"
+
+#include <limits>
+
+#include "base/test/gtest_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+using SingleSample = HistogramSamples::SingleSample;
+using AtomicSingleSample = HistogramSamples::AtomicSingleSample;
+
+TEST(SingleSampleTest, Load) {
+  AtomicSingleSample sample;
+  ASSERT_TRUE(sample.Accumulate(9, 1));
+
+  SingleSample s = sample.Load();
+  EXPECT_EQ(9U, s.bucket);
+  EXPECT_EQ(1U, s.count);
+
+  s = sample.Load();
+  EXPECT_EQ(9U, s.bucket);
+  EXPECT_EQ(1U, s.count);
+}
+
+TEST(SingleSampleTest, Extract) {
+  AtomicSingleSample sample;
+  ASSERT_TRUE(sample.Accumulate(9, 1));
+
+  SingleSample s = sample.Extract(/*disable=*/false);
+  EXPECT_EQ(9U, s.bucket);
+  EXPECT_EQ(1U, s.count);
+
+  s = sample.Extract(/*disable=*/false);
+  EXPECT_EQ(0U, s.bucket);
+  EXPECT_EQ(0U, s.count);
+}
+
+TEST(SingleSampleTest, Disable) {
+  AtomicSingleSample sample;
+  EXPECT_EQ(0U, sample.Extract(/*disable=*/false).count);
+  EXPECT_FALSE(sample.IsDisabled());
+
+  ASSERT_TRUE(sample.Accumulate(9, 1));
+  EXPECT_EQ(1U, sample.Extract(/*disable=*/true).count);
+  EXPECT_TRUE(sample.IsDisabled());
+
+  ASSERT_FALSE(sample.Accumulate(9, 1));
+  EXPECT_EQ(0U, sample.Extract(/*disable=*/false).count);
+  EXPECT_FALSE(sample.IsDisabled());
+}
+
+TEST(SingleSampleTest, Accumulate) {
+  AtomicSingleSample sample;
+
+  ASSERT_TRUE(sample.Accumulate(9, 1));
+  ASSERT_TRUE(sample.Accumulate(9, 2));
+  ASSERT_TRUE(sample.Accumulate(9, 4));
+  EXPECT_EQ(7U, sample.Extract(/*disable=*/false).count);
+
+  ASSERT_TRUE(sample.Accumulate(9, 4));
+  ASSERT_TRUE(sample.Accumulate(9, -2));
+  ASSERT_TRUE(sample.Accumulate(9, 1));
+  EXPECT_EQ(3U, sample.Extract(/*disable=*/false).count);
+}
+
+TEST(SingleSampleTest, Overflow) {
+  AtomicSingleSample sample;
+
+  ASSERT_TRUE(sample.Accumulate(9, 1));
+  ASSERT_FALSE(sample.Accumulate(9, -2));
+  EXPECT_EQ(1U, sample.Extract(/*disable=*/false).count);
+
+  ASSERT_TRUE(sample.Accumulate(9, std::numeric_limits<uint16_t>::max()));
+  ASSERT_FALSE(sample.Accumulate(9, 1));
+  EXPECT_EQ(std::numeric_limits<uint16_t>::max(),
+            sample.Extract(/*disable=*/false).count);
+}
+
+}  // namespace base
diff --git a/base/path_service_unittest.cc b/base/path_service_unittest.cc
index ad3334a..9b81aee 100644
--- a/base/path_service_unittest.cc
+++ b/base/path_service_unittest.cc
@@ -84,7 +84,10 @@
       continue;  // Android doesn't implement these.
 #elif defined(OS_IOS)
     if (key == DIR_USER_DESKTOP)
-      continue;  // iOS doesn't implement DIR_USER_DESKTOP;
+      continue;  // iOS doesn't implement DIR_USER_DESKTOP.
+#elif defined(OS_FUCHSIA)
+    if (key == DIR_USER_DESKTOP)
+      continue;  // Fuchsia doesn't implement DIR_USER_DESKTOP.
 #endif
     EXPECT_PRED1(ReturnsValidPath, key);
   }
diff --git a/base/posix/eintr_wrapper.h b/base/posix/eintr_wrapper.h
index 5a5dc75..c0ffced5 100644
--- a/base/posix/eintr_wrapper.h
+++ b/base/posix/eintr_wrapper.h
@@ -8,7 +8,8 @@
 // that should be masked) to go unnoticed, there is a limit after which the
 // caller will nonetheless see an EINTR in Debug builds.
 //
-// On Windows, this wrapper macro does nothing.
+// On Windows and Fuchsia, this wrapper macro does nothing because there are no
+// signals.
 //
 // Don't wrap close calls in HANDLE_EINTR. Use IGNORE_EINTR if the return
 // value of close is significant. See http://crbug.com/269623.
@@ -18,7 +19,7 @@
 
 #include "build/build_config.h"
 
-#if defined(OS_POSIX)
+#if defined(OS_POSIX) && !defined(OS_FUCHSIA)
 
 #include <errno.h>
 
@@ -57,11 +58,11 @@
   eintr_wrapper_result; \
 })
 
-#else
+#else  // !OS_POSIX || OS_FUCHSIA
 
 #define HANDLE_EINTR(x) (x)
 #define IGNORE_EINTR(x) (x)
 
-#endif  // OS_POSIX
+#endif  // !OS_POSIX || OS_FUCHSIA
 
 #endif  // BASE_POSIX_EINTR_WRAPPER_H_
diff --git a/base/task_scheduler/task_scheduler.cc b/base/task_scheduler/task_scheduler.cc
index abeb602..53ff541 100644
--- a/base/task_scheduler/task_scheduler.cc
+++ b/base/task_scheduler/task_scheduler.cc
@@ -27,15 +27,13 @@
     const SchedulerWorkerPoolParams& background_worker_pool_params_in,
     const SchedulerWorkerPoolParams& background_blocking_worker_pool_params_in,
     const SchedulerWorkerPoolParams& foreground_worker_pool_params_in,
-    const SchedulerWorkerPoolParams& foreground_blocking_worker_pool_params_in,
-    TaskPriorityAdjustment task_priority_adjustment_in)
+    const SchedulerWorkerPoolParams& foreground_blocking_worker_pool_params_in)
     : background_worker_pool_params(background_worker_pool_params_in),
       background_blocking_worker_pool_params(
           background_blocking_worker_pool_params_in),
       foreground_worker_pool_params(foreground_worker_pool_params_in),
       foreground_blocking_worker_pool_params(
-          foreground_blocking_worker_pool_params_in),
-      task_priority_adjustment(task_priority_adjustment_in) {}
+          foreground_blocking_worker_pool_params_in) {}
 
 TaskScheduler::InitParams::~InitParams() = default;
 
diff --git a/base/task_scheduler/task_scheduler.h b/base/task_scheduler/task_scheduler.h
index 4967ea8..75f156d 100644
--- a/base/task_scheduler/task_scheduler.h
+++ b/base/task_scheduler/task_scheduler.h
@@ -53,17 +53,6 @@
 // process's instance.
 class BASE_EXPORT TaskScheduler {
  public:
-  enum class TaskPriorityAdjustment {
-    // Honor the TaskPriority with which tasks are posted.
-    NONE,
-
-    // Handle all tasks with TaskPriority::USER_BLOCKING. This is strictly for
-    // an experiment -- not something regular users should consider.
-    //
-    // TODO(fdoray): Remove after experiment. https://crbug.com/757022
-    EXPERIMENTAL_ALL_TASKS_USER_BLOCKING,
-  };
-
   struct BASE_EXPORT InitParams {
     InitParams(
         const SchedulerWorkerPoolParams& background_worker_pool_params_in,
@@ -71,17 +60,13 @@
             background_blocking_worker_pool_params_in,
         const SchedulerWorkerPoolParams& foreground_worker_pool_params_in,
         const SchedulerWorkerPoolParams&
-            foreground_blocking_worker_pool_params_in,
-        TaskPriorityAdjustment task_priority_adjustment_in =
-            TaskPriorityAdjustment::NONE);
+            foreground_blocking_worker_pool_params_in);
     ~InitParams();
 
     SchedulerWorkerPoolParams background_worker_pool_params;
     SchedulerWorkerPoolParams background_blocking_worker_pool_params;
     SchedulerWorkerPoolParams foreground_worker_pool_params;
     SchedulerWorkerPoolParams foreground_blocking_worker_pool_params;
-    TaskPriorityAdjustment task_priority_adjustment =
-        TaskPriorityAdjustment::NONE;
   };
 
   // Destroying a TaskScheduler is not allowed in production; it is always
diff --git a/base/task_scheduler/task_scheduler_impl.cc b/base/task_scheduler/task_scheduler_impl.cc
index 55664e7..6db8cdb 100644
--- a/base/task_scheduler/task_scheduler_impl.cc
+++ b/base/task_scheduler/task_scheduler_impl.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "base/metrics/field_trial_params.h"
 #include "base/task_scheduler/delayed_task_manager.h"
 #include "base/task_scheduler/environment_config.h"
 #include "base/task_scheduler/scheduler_worker_pool_params.h"
@@ -47,9 +48,10 @@
 }
 
 void TaskSchedulerImpl::Start(const TaskScheduler::InitParams& init_params) {
-  if (init_params.task_priority_adjustment ==
-      TaskScheduler::TaskPriorityAdjustment::
-          EXPERIMENTAL_ALL_TASKS_USER_BLOCKING) {
+  // This is set in Start() and not in the constructor because variation params
+  // are usually not ready when TaskSchedulerImpl is instantiated in a process.
+  if (base::GetFieldTrialParamValue("BrowserScheduler",
+                                    "AllTasksUserBlocking") == "true") {
     all_tasks_user_blocking_.Set();
   }
 
diff --git a/base/task_scheduler/task_scheduler_impl.h b/base/task_scheduler/task_scheduler_impl.h
index 5cb8a26..c22de25 100644
--- a/base/task_scheduler/task_scheduler_impl.h
+++ b/base/task_scheduler/task_scheduler_impl.h
@@ -102,6 +102,8 @@
   // TaskPriority::USER_BLOCKING. Since this is set in Start(), it doesn't apply
   // to tasks posted before Start() or to tasks posted to TaskRunners created
   // before Start().
+  //
+  // TODO(fdoray): Remove after experiment. https://crbug.com/757022
   AtomicFlag all_tasks_user_blocking_;
 
   // There are 4 SchedulerWorkerPoolImpl in this array to match the 4
diff --git a/base/task_scheduler/task_scheduler_impl_unittest.cc b/base/task_scheduler/task_scheduler_impl_unittest.cc
index bd2763b..c842633 100644
--- a/base/task_scheduler/task_scheduler_impl_unittest.cc
+++ b/base/task_scheduler/task_scheduler_impl_unittest.cc
@@ -15,6 +15,8 @@
 #include "base/callback.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
+#include "base/metrics/field_trial.h"
+#include "base/metrics/field_trial_params.h"
 #include "base/synchronization/lock.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/task_scheduler/scheduler_worker_pool_params.h"
@@ -189,22 +191,31 @@
 class TaskSchedulerImplTest
     : public testing::TestWithParam<TraitsExecutionModePair> {
  protected:
-  TaskSchedulerImplTest() : scheduler_("Test") {}
+  TaskSchedulerImplTest() : scheduler_("Test"), field_trial_list_(nullptr) {}
 
-  void StartTaskScheduler(
-      TaskScheduler::TaskPriorityAdjustment task_priority_adjustment =
-          TaskScheduler::TaskPriorityAdjustment::NONE) {
+  void EnableAllTasksUserBlocking() {
+    constexpr char kFieldTrialName[] = "BrowserScheduler";
+    constexpr char kFieldTrialTestGroup[] = "DummyGroup";
+    std::map<std::string, std::string> variation_params;
+    variation_params["AllTasksUserBlocking"] = "true";
+    base::AssociateFieldTrialParams(kFieldTrialName, kFieldTrialTestGroup,
+                                    variation_params);
+    base::FieldTrialList::CreateFieldTrial(kFieldTrialName,
+                                           kFieldTrialTestGroup);
+  }
+
+  void StartTaskScheduler() {
     constexpr TimeDelta kSuggestedReclaimTime = TimeDelta::FromSeconds(30);
     constexpr int kMaxNumBackgroundThreads = 1;
     constexpr int kMaxNumBackgroundBlockingThreads = 3;
     constexpr int kMaxNumForegroundThreads = 4;
     constexpr int kMaxNumForegroundBlockingThreads = 12;
 
-    scheduler_.Start({{kMaxNumBackgroundThreads, kSuggestedReclaimTime},
-                      {kMaxNumBackgroundBlockingThreads, kSuggestedReclaimTime},
-                      {kMaxNumForegroundThreads, kSuggestedReclaimTime},
-                      {kMaxNumForegroundBlockingThreads, kSuggestedReclaimTime},
-                      task_priority_adjustment});
+    scheduler_.Start(
+        {{kMaxNumBackgroundThreads, kSuggestedReclaimTime},
+         {kMaxNumBackgroundBlockingThreads, kSuggestedReclaimTime},
+         {kMaxNumForegroundThreads, kSuggestedReclaimTime},
+         {kMaxNumForegroundBlockingThreads, kSuggestedReclaimTime}});
   }
 
   void TearDown() override {
@@ -215,6 +226,8 @@
   TaskSchedulerImpl scheduler_;
 
  private:
+  base::FieldTrialList field_trial_list_;
+
   DISALLOW_COPY_AND_ASSIGN(TaskSchedulerImplTest);
 };
 
@@ -342,11 +355,11 @@
 }
 
 // Verify that all tasks posted to a TaskRunner after Start() run in a
-// USER_BLOCKING environment when |all_tasks_are_user_blocking_in| is true in
-// TaskScheduler::InitParams.
+// USER_BLOCKING environment when the AllTasksUserBlocking variation param of
+// the BrowserScheduler experiment is true.
 TEST_P(TaskSchedulerImplTest, AllTasksAreUserBlockingTaskRunner) {
-  StartTaskScheduler(TaskScheduler::TaskPriorityAdjustment::
-                         EXPERIMENTAL_ALL_TASKS_USER_BLOCKING);
+  EnableAllTasksUserBlocking();
+  StartTaskScheduler();
 
   WaitableEvent task_running(WaitableEvent::ResetPolicy::MANUAL,
                              WaitableEvent::InitialState::NOT_SIGNALED);
@@ -361,11 +374,11 @@
 }
 
 // Verify that all tasks posted via PostDelayedTaskWithTraits() after Start()
-// run in a USER_BLOCKING environment when |all_tasks_are_user_blocking_in| is
-// true in TaskScheduler::InitParams.
+// run in a USER_BLOCKING environment when the AllTasksUserBlocking variation
+// param of the BrowserScheduler experiment is true.
 TEST_P(TaskSchedulerImplTest, AllTasksAreUserBlocking) {
-  StartTaskScheduler(TaskScheduler::TaskPriorityAdjustment::
-                         EXPERIMENTAL_ALL_TASKS_USER_BLOCKING);
+  EnableAllTasksUserBlocking();
+  StartTaskScheduler();
 
   WaitableEvent task_running(WaitableEvent::ResetPolicy::MANUAL,
                              WaitableEvent::InitialState::NOT_SIGNALED);
diff --git a/build/android/gyp/write_build_config.py b/build/android/gyp/write_build_config.py
index ee5151a..392673b 100755
--- a/build/android/gyp/write_build_config.py
+++ b/build/android/gyp/write_build_config.py
@@ -222,7 +222,9 @@
         line = line.rstrip()
         if not line.endswith('.so'):
           continue
-        ret.append(os.path.normpath(line))
+        # Only unstripped .so files are listed in runtime deps.
+        # Convert to the stripped .so by going up one directory.
+        ret.append(os.path.normpath(line.replace('lib.unstripped/', '')))
   ret.reverse()
   return ret
 
diff --git a/build/android/pylib/gtest/filter/unit_tests_disabled b/build/android/pylib/gtest/filter/unit_tests_disabled
index 93a0b4b..6cf6acc 100644
--- a/build/android/pylib/gtest/filter/unit_tests_disabled
+++ b/build/android/pylib/gtest/filter/unit_tests_disabled
@@ -111,5 +111,8 @@
 # crbug.com/256259
 DiagnosticsModelTest.RunAll
 
+# crbug.com/758996
+ChromeExpectCTReporterTest.*
+
 # Death tests are not supported with apks.
 *DeathTest*
diff --git a/build/linux/unbundle/icu.gn b/build/linux/unbundle/icu.gn
index d830b3e..5bdd915 100644
--- a/build/linux/unbundle/icu.gn
+++ b/build/linux/unbundle/icu.gn
@@ -16,6 +16,7 @@
   defines = [
     "USING_SYSTEM_ICU=1",
     "ICU_UTIL_DATA_IMPL=ICU_UTIL_DATA_STATIC",
+    "UCHAR_TYPE=uint16_t",
   ]
 }
 
diff --git a/build/toolchain/android/BUILD.gn b/build/toolchain/android/BUILD.gn
index 9b3b162..9beaef9 100644
--- a/build/toolchain/android/BUILD.gn
+++ b/build/toolchain/android/BUILD.gn
@@ -65,6 +65,7 @@
     readelf = _tool_prefix + "readelf"
     nm = _tool_prefix + "nm"
     strip = "${_tool_prefix}strip"
+    use_unstripped_as_runtime_outputs = true
 
     # Don't use .cr.so for loadable_modules since they are always loaded via
     # absolute path.
diff --git a/build/toolchain/gcc_toolchain.gni b/build/toolchain/gcc_toolchain.gni
index 60aee31..e80d4d9 100644
--- a/build/toolchain/gcc_toolchain.gni
+++ b/build/toolchain/gcc_toolchain.gni
@@ -60,6 +60,9 @@
 #      The content of this array, if specified, will be added to the list of
 #      outputs from the link command. This can be useful in conjunction with
 #      the post_link parameter.
+#  - use_unstripped_as_runtime_outputs
+#      When |strip| is set, mark unstripped executables as runtime deps rather
+#      than stripped ones.
 #  - post_link
 #      The content of this string, if specified, will be run as a separate
 #      command following the the link command.
@@ -423,6 +426,10 @@
       }
       if (sofile != unstripped_sofile) {
         outputs += [ unstripped_sofile ]
+        if (defined(invoker.use_unstripped_as_runtime_outputs) &&
+            invoker.use_unstripped_as_runtime_outputs) {
+          runtime_outputs = [ unstripped_sofile ]
+        }
       }
       if (defined(map_file)) {
         outputs += [ map_file ]
@@ -474,6 +481,10 @@
       ]
       if (sofile != unstripped_sofile) {
         outputs += [ unstripped_sofile ]
+        if (defined(invoker.use_unstripped_as_runtime_outputs) &&
+            invoker.use_unstripped_as_runtime_outputs) {
+          runtime_outputs = [ unstripped_sofile ]
+        }
       }
     }
 
@@ -529,6 +540,10 @@
       ]
       if (outfile != unstripped_outfile) {
         outputs += [ unstripped_outfile ]
+        if (defined(invoker.use_unstripped_as_runtime_outputs) &&
+            invoker.use_unstripped_as_runtime_outputs) {
+          runtime_outputs = [ unstripped_outfile ]
+        }
       }
       if (defined(invoker.link_outputs)) {
         outputs += invoker.link_outputs
diff --git a/build/vs_toolchain.py b/build/vs_toolchain.py
index 41098d1..93a04ceb 100755
--- a/build/vs_toolchain.py
+++ b/build/vs_toolchain.py
@@ -352,8 +352,8 @@
     # Update 3 final with 10.0.15063.468 SDK and no vctip.exe.
     return ['f53e4598951162bad6330f7a167486c7ae5db1e5']
   if env_version == '2017':
-    # VS 2017 Update 3 with 10.0.15063.468 SDK.
-    return ['ea9991706520e21292abf38b9d94285aeca709d0']
+    # VS 2017 Update 3.2 with 10.0.15063.468 SDK.
+    return ['9bc7ccbf9f4bd50d4a3bd185e8ca94ff1618de0b']
   raise Exception('Unsupported VS version %s' % env_version)
 
 
diff --git a/cc/blink/web_layer_impl.cc b/cc/blink/web_layer_impl.cc
index 35a3a0a..5fa1e7b 100644
--- a/cc/blink/web_layer_impl.cc
+++ b/cc/blink/web_layer_impl.cc
@@ -454,10 +454,15 @@
                    base::Unretained(scroll_client)));
   } else {
     layer_->set_did_scroll_callback(
-        base::Callback<void(const gfx::ScrollOffset&)>());
+        base::Callback<void(const gfx::ScrollOffset&, const cc::ElementId&)>());
   }
 }
 
+void WebLayerImpl::SetScrollOffsetFromImplSideForTesting(
+    const gfx::ScrollOffset& offset) {
+  layer_->SetScrollOffsetFromImplSide(offset);
+}
+
 void WebLayerImpl::SetLayerClient(cc::LayerClient* client) {
   layer_->SetLayerClient(client);
 }
diff --git a/cc/blink/web_layer_impl.h b/cc/blink/web_layer_impl.h
index 7c35072..40300b1 100644
--- a/cc/blink/web_layer_impl.h
+++ b/cc/blink/web_layer_impl.h
@@ -124,6 +124,7 @@
   blink::WebLayerStickyPositionConstraint StickyPositionConstraint()
       const override;
   void SetScrollClient(blink::WebLayerScrollClient* client) override;
+  void SetScrollOffsetFromImplSideForTesting(const gfx::ScrollOffset&) override;
   void SetLayerClient(cc::LayerClient* client) override;
   const cc::Layer* CcLayer() const override;
   cc::Layer* CcLayer() override;
diff --git a/cc/ipc/filter_operation_struct_traits.h b/cc/ipc/filter_operation_struct_traits.h
index 93aaf92..5deb0d7 100644
--- a/cc/ipc/filter_operation_struct_traits.h
+++ b/cc/ipc/filter_operation_struct_traits.h
@@ -5,6 +5,7 @@
 #ifndef CC_IPC_FILTER_OPERATION_STRUCT_TRAITS_H_
 #define CC_IPC_FILTER_OPERATION_STRUCT_TRAITS_H_
 
+#include "base/containers/span.h"
 #include "cc/base/filter_operation.h"
 #include "cc/ipc/filter_operation.mojom-shared.h"
 #include "skia/public/interfaces/blur_image_filter_tile_mode_struct_traits.h"
@@ -130,17 +131,17 @@
     return operation.image_filter();
   }
 
-  static ConstCArray<float> matrix(const cc::FilterOperation& operation) {
+  static base::span<const float> matrix(const cc::FilterOperation& operation) {
     if (operation.type() != cc::FilterOperation::COLOR_MATRIX)
-      return ConstCArray<float>();
-    return ConstCArray<float>(operation.matrix());
+      return base::span<const float>();
+    return operation.matrix();
   }
 
-  static ConstCArray<gfx::Rect> shape(const cc::FilterOperation& operation) {
+  static base::span<const gfx::Rect> shape(
+      const cc::FilterOperation& operation) {
     if (operation.type() != cc::FilterOperation::ALPHA_THRESHOLD)
-      return ConstCArray<gfx::Rect>();
-    return ConstCArray<gfx::Rect>(operation.shape().data(),
-                                  operation.shape().size());
+      return base::span<gfx::Rect>();
+    return operation.shape();
   }
 
   static int32_t zoom_inset(const cc::FilterOperation& operation) {
@@ -192,7 +193,7 @@
         // TODO(fsamuel): It would be nice to modify cc::FilterOperation to
         // avoid this extra copy.
         cc::FilterOperation::Matrix matrix_buffer = {};
-        CArray<float> matrix(matrix_buffer);
+        base::span<float> matrix(matrix_buffer);
         if (!data.ReadMatrix(&matrix))
           return false;
         out->set_matrix(matrix_buffer);
diff --git a/cc/layers/layer.cc b/cc/layers/layer.cc
index 178aaf87..3f334cd 100644
--- a/cc/layers/layer.cc
+++ b/cc/layers/layer.cc
@@ -807,7 +807,7 @@
   UpdateScrollOffset(scroll_offset);
 
   if (!inputs_.did_scroll_callback.is_null())
-    inputs_.did_scroll_callback.Run(scroll_offset);
+    inputs_.did_scroll_callback.Run(scroll_offset, element_id());
 
   // The callback could potentially change the layer structure:
   // "this" may have been destroyed during the process.
diff --git a/cc/layers/layer.h b/cc/layers/layer.h
index 923df99d..482fc22 100644
--- a/cc/layers/layer.h
+++ b/cc/layers/layer.h
@@ -267,7 +267,8 @@
   }
 
   void set_did_scroll_callback(
-      base::Callback<void(const gfx::ScrollOffset&)> callback) {
+      base::Callback<void(const gfx::ScrollOffset&, const ElementId&)>
+          callback) {
     inputs_.did_scroll_callback = std::move(callback);
   }
 
@@ -626,7 +627,8 @@
 
     // The following elements can not and are not serialized.
     LayerClient* client;
-    base::Callback<void(const gfx::ScrollOffset&)> did_scroll_callback;
+    base::Callback<void(const gfx::ScrollOffset&, const ElementId&)>
+        did_scroll_callback;
     std::vector<std::unique_ptr<viz::CopyOutputRequest>> copy_requests;
 
     ScrollBoundaryBehavior scroll_boundary_behavior;
diff --git a/cc/trees/layer_tree_host_unittest_scroll.cc b/cc/trees/layer_tree_host_unittest_scroll.cc
index 70d9019..fd34715 100644
--- a/cc/trees/layer_tree_host_unittest_scroll.cc
+++ b/cc/trees/layer_tree_host_unittest_scroll.cc
@@ -145,7 +145,9 @@
     }
   }
 
-  void DidScrollOuterViewport(const gfx::ScrollOffset&) { num_scrolls_++; }
+  void DidScrollOuterViewport(const gfx::ScrollOffset&, const ElementId&) {
+    num_scrolls_++;
+  }
 
   void AfterTest() override { EXPECT_EQ(1, num_scrolls_); }
 
@@ -228,7 +230,9 @@
     }
   }
 
-  void DidScrollOuterViewport(const gfx::ScrollOffset&) { num_scrolls_++; }
+  void DidScrollOuterViewport(const gfx::ScrollOffset&, const ElementId&) {
+    num_scrolls_++;
+  }
 
   void AfterTest() override { EXPECT_EQ(1, num_scrolls_); }
 
@@ -411,7 +415,9 @@
     }
   }
 
-  void DidScrollOuterViewport(const gfx::ScrollOffset&) { num_impl_scrolls_++; }
+  void DidScrollOuterViewport(const gfx::ScrollOffset&, const ElementId&) {
+    num_impl_scrolls_++;
+  }
 
   void AfterTest() override {
     EXPECT_EQ(3, num_impl_scrolls_);
@@ -629,12 +635,15 @@
     }
   }
 
-  void DidScroll(const gfx::ScrollOffset& offset) {
+  void DidScroll(const gfx::ScrollOffset& offset, const ElementId& element_id) {
     final_scroll_offset_ = expected_scroll_layer_->scroll_offset();
     EXPECT_VECTOR_EQ(offset, final_scroll_offset_);
+    EXPECT_EQ(element_id, expected_scroll_layer_->element_id());
   }
 
-  void DidScrollOuterViewport(const gfx::ScrollOffset&) { num_scrolls_++; }
+  void DidScrollOuterViewport(const gfx::ScrollOffset&, const ElementId&) {
+    num_scrolls_++;
+  }
 
   void UpdateLayerTreeHost() override {
     EXPECT_VECTOR_EQ(gfx::Vector2d(),
@@ -926,7 +935,9 @@
     }
   }
 
-  void DidScrollOuterViewport(const gfx::ScrollOffset&) { num_scrolls_++; }
+  void DidScrollOuterViewport(const gfx::ScrollOffset&, const ElementId&) {
+    num_scrolls_++;
+  }
 
   void AfterTest() override { EXPECT_EQ(1, num_scrolls_); }
 
@@ -1438,7 +1449,9 @@
  protected:
   class FakeLayerScrollClient {
    public:
-    void DidScroll(const gfx::ScrollOffset&) { owner_->DidScroll(layer_); }
+    void DidScroll(const gfx::ScrollOffset&, const ElementId&) {
+      owner_->DidScroll(layer_);
+    }
     LayerTreeHostScrollTestLayerStructureChange* owner_;
     Layer* layer_;
   };
@@ -1588,7 +1601,9 @@
     }
   }
 
-  void DidScrollOuterViewport(const gfx::ScrollOffset&) { num_scrolls_++; }
+  void DidScrollOuterViewport(const gfx::ScrollOffset&, const ElementId&) {
+    num_scrolls_++;
+  }
 
   void AfterTest() override {
     EXPECT_EQ(3, num_commits_);
@@ -1807,7 +1822,9 @@
     num_draws_++;
   }
 
-  void DidScrollOuterViewport(const gfx::ScrollOffset&) { num_impl_scrolls_++; }
+  void DidScrollOuterViewport(const gfx::ScrollOffset&, const ElementId&) {
+    num_impl_scrolls_++;
+  }
 
   void AfterTest() override {
     EXPECT_EQ(3, num_impl_scrolls_);
@@ -2097,7 +2114,7 @@
     PostSetNeedsCommitToMainThread();
   }
 
-  void DidScrollOuterViewport(const gfx::ScrollOffset& offset) {
+  void DidScrollOuterViewport(const gfx::ScrollOffset&, const ElementId&) {
     // Defer responding to the main frame until an impl-side pending tree is
     // created for the invalidation request.
     {
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml
index 9d41eb8..960694fe 100644
--- a/chrome/android/java/res/values/dimens.xml
+++ b/chrome/android/java/res/values/dimens.xml
@@ -120,8 +120,6 @@
 
     <!-- Overlay panel dimensions -->
     <dimen name="overlay_panel_bar_height">56dp</dimen>
-    <dimen name="overlay_panel_bar_handle_offset_y">6dp</dimen>
-    <dimen name="overlay_panel_bar_padding_bottom">4dp</dimen>
 
     <!-- Password generation popup dimensions -->
     <dimen name="password_generation_divider_height">1dp</dimen>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ActivityTabTaskDescriptionHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/ActivityTabTaskDescriptionHelper.java
index 2521e662..8c45aa8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ActivityTabTaskDescriptionHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ActivityTabTaskDescriptionHelper.java
@@ -121,10 +121,7 @@
 
             private boolean hasSecurityWarningOrError(Tab tab) {
                 int securityLevel = tab.getSecurityLevel();
-                return securityLevel == ConnectionSecurityLevel.DANGEROUS
-                        || securityLevel == ConnectionSecurityLevel.SECURITY_WARNING
-                        || securityLevel
-                        == ConnectionSecurityLevel.SECURE_WITH_POLICY_INSTALLED_CERT;
+                return securityLevel == ConnectionSecurityLevel.DANGEROUS;
             }
         };
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelBase.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelBase.java
index c8399e60..fad120a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelBase.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelBase.java
@@ -97,13 +97,13 @@
     private float mToolbarHeight;
 
     /** The height of the Bar when the Panel is peeking, in dps. */
-    private float mBarHeightPeeking;
+    private final float mBarHeightPeeking;
 
     /** The height of the Bar when the Panel is expanded, in dps. */
-    private float mBarHeightExpanded;
+    private final float mBarHeightExpanded;
 
     /** The height of the Bar when the Panel is maximized, in dps. */
-    private float mBarHeightMaximized;
+    private final float mBarHeightMaximized;
 
     /**
      * The Y coordinate to apply to the Base Page in order to keep the selection
@@ -1139,53 +1139,6 @@
     }
 
     // ============================================================================================
-    // Bar Handle
-    // ============================================================================================
-    protected int mBarHandleResourceId;
-    protected float mBarHandleOffsetY;
-    protected float mBarPaddingBottom;
-
-    /**
-     * Adds a handle to the bar.
-     * @param barHeightPx The new bar height in px needed to accommodate the handle.
-     */
-    public void addBarHandle(int barHeightPx) {
-        mBarHandleResourceId = R.drawable.toolbar_handle_dark;
-        mBarHeightPeeking = barHeightPx * mPxToDp;
-        mBarHeightMaximized = mBarHeightPeeking;
-        mBarHeightExpanded = mBarHeightPeeking;
-    }
-
-    /**
-     * @return The resource id for the bar handle.
-     */
-    public int getBarHandleResourceId() {
-        return mBarHandleResourceId;
-    }
-
-    /** @return The y-offset for the bar handle in px. */
-    public float getBarHandleOffsetY() {
-        if (mBarHandleOffsetY == 0.f && mBarHandleResourceId != 0) {
-            mBarHandleOffsetY =
-                    mContext.getResources().getDimension(R.dimen.overlay_panel_bar_handle_offset_y);
-        }
-        return mBarHandleOffsetY;
-    }
-
-    /** @return The bottom padding for the bar in px. */
-    public float getBarPaddingBottom() {
-        // When there is no bar handle, the bar contents are vertically centered. When there is a
-        // bar handle, the contents are displayed below the handle. Bottom padding is needed so that
-        // the contents don't appear too low in the bar when centered in the space beneath the
-        // handle.
-        if (mBarPaddingBottom == 0.f && mBarHandleResourceId != 0) {
-            mBarPaddingBottom =
-                    mContext.getResources().getDimension(R.dimen.overlay_panel_bar_padding_bottom);
-        }
-        return mBarPaddingBottom;
-    }
-
-    // ============================================================================================
     // Test Infrastructure
     // ============================================================================================
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java
index bc4b592..4554684d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java
@@ -13,7 +13,6 @@
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.compositor.LayerTitleCache;
 import org.chromium.chrome.browser.compositor.bottombar.OverlayContentProgressObserver;
 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel;
@@ -407,15 +406,6 @@
     }
 
     @Override
-    public void setChromeActivity(ChromeActivity activity) {
-        super.setChromeActivity(activity);
-
-        if (mActivity.getBottomSheet() == null) return;
-
-        addBarHandle(mActivity.getToolbarManager().getToolbar().getHeight());
-    }
-
-    @Override
     protected boolean doesMatchFullWidthCriteria(float containerWidth) {
         if (!mOverrideIsFullWidthSizePanelForTesting && mActivity != null
                 && mActivity.getBottomSheet() != null) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ContextualSearchSceneLayer.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ContextualSearchSceneLayer.java
index 7f9e197..cb26d3b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ContextualSearchSceneLayer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ContextualSearchSceneLayer.java
@@ -127,10 +127,6 @@
         float touchHighlightXOffset = searchBarControl.getTouchHighlightXOffsetPx();
         float touchHighlightWidth = searchBarControl.getTouchHighlightWidthPx();
 
-        int barHandleResId = panel.getBarHandleResourceId();
-        float barHandleOffsetY = panel.getBarHandleOffsetY();
-        float barPaddingBottom = panel.getBarPaddingBottom();
-
         WebContents panelWebContents = panel.getContentViewCore() != null
                 ? panel.getContentViewCore().getWebContents() : null;
 
@@ -156,8 +152,7 @@
                 progressBarHeight * mDpToPx, progressBarOpacity, progressBarCompletion,
                 dividerLineVisibilityPercentage, dividerLineWidth, dividerLineHeight,
                 dividerLineColor, dividerLineXOffset, touchHighlightVisible, touchHighlightXOffset,
-                touchHighlightWidth, barHandleResId, barHandleOffsetY, barPaddingBottom,
-                Profile.getLastUsedProfile());
+                touchHighlightWidth, Profile.getLastUsedProfile());
     }
 
     @CalledByNative
@@ -228,6 +223,5 @@
             float progressBarHeight, float progressBarOpacity, int progressBarCompletion,
             float dividerLineVisibilityPercentage, float dividerLineWidth, float dividerLineHeight,
             int dividerLineColor, float dividerLineXOffset, boolean touchHighlightVisible,
-            float touchHighlightXOffset, float toucHighlightWidth, int barHandleResId,
-            float barHandleOffsetY, float barPaddingBottom, Profile profile);
+            float touchHighlightXOffset, float toucHighlightWidth, Profile profile);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/ClientManager.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/ClientManager.java
index e6a8c47..3bae386 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/ClientManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/ClientManager.java
@@ -15,6 +15,7 @@
 import android.support.annotation.NonNull;
 import android.support.customtabs.CustomTabsCallback;
 import android.support.customtabs.CustomTabsService;
+import android.support.customtabs.CustomTabsService.Relation;
 import android.support.customtabs.CustomTabsSessionToken;
 import android.text.TextUtils;
 import android.util.SparseBooleanArray;
@@ -344,13 +345,13 @@
     }
 
     /**
-     * See {@link PostMessageHandler#verifyAndInitializeWithOrigin(Uri)}.
+     * See {@link PostMessageHandler#verifyAndInitializeWithOrigin(Uri, int)}.
      */
     public synchronized void verifyAndInitializeWithPostMessageOriginForSession(
-            CustomTabsSessionToken session, Uri origin) {
+            CustomTabsSessionToken session, Uri origin, @Relation int relation) {
         SessionParams params = mSessionParams.get(session);
         if (params == null) return;
-        params.postMessageHandler.verifyAndInitializeWithOrigin(origin);
+        params.postMessageHandler.verifyAndInitializeWithOrigin(origin, relation);
     }
 
     /**
@@ -523,7 +524,9 @@
     public synchronized boolean isFirstPartyOriginForSession(
             CustomTabsSessionToken session, Uri origin) {
         SessionParams params = mSessionParams.get(session);
-        return params == null ? false : OriginVerifier.isValidOrigin(params.packageName, origin);
+        return params == null ? false
+                              : OriginVerifier.isValidOrigin(params.packageName, origin,
+                                        CustomTabsService.RELATION_USE_AS_ORIGIN);
     }
 
     /** Tries to bind to a client to keep it alive, and returns true for success. */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java
index 2c070da..04daf45 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java
@@ -603,7 +603,7 @@
                 Uri verifiedOrigin = verifyOriginForSession(session, uid, postMessageOrigin);
                 if (verifiedOrigin == null) {
                     mClientManager.verifyAndInitializeWithPostMessageOriginForSession(
-                            session, postMessageOrigin);
+                            session, postMessageOrigin, CustomTabsService.RELATION_USE_AS_ORIGIN);
                 } else {
                     mClientManager.initializeWithPostMessageOriginForSession(
                             session, verifiedOrigin);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/OriginVerifier.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/OriginVerifier.java
index 9754d33..5d390ab 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/OriginVerifier.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/OriginVerifier.java
@@ -8,6 +8,10 @@
 import android.content.pm.PackageManager;
 import android.net.Uri;
 import android.support.annotation.NonNull;
+import android.support.customtabs.CustomTabsService;
+import android.support.customtabs.CustomTabsService.Relation;
+import android.support.v4.util.Pair;
+import android.text.TextUtils;
 
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
@@ -49,10 +53,14 @@
 class OriginVerifier {
     private static final String TAG = "OriginVerifier";
     private static final char[] HEX_CHAR_LOOKUP = "0123456789ABCDEF".toCharArray();
-    private static Map<String, Set<Uri>> sPackageToCachedOrigins;
+    private static final String USE_AS_ORIGIN = "delegate_permission/common.use_as_origin";
+    private static final String HANDLE_ALL_URLS = "delegate_permission/common.handle_all_urls";
+
+    private static Map<Pair<String, Integer>, Set<Uri>> sPackageToCachedOrigins;
     private final OriginVerificationListener mListener;
     private final String mPackageName;
     private final String mSignatureFingerprint;
+    private final @Relation int mRelation;
     private long mNativeOriginVerifier = 0;
     private Uri mOrigin;
 
@@ -70,8 +78,7 @@
         }
     }
 
-    private static Uri getPostMessageOriginFromVerifiedOrigin(
-            String packageName, Uri verifiedOrigin) {
+    static Uri getPostMessageOriginFromVerifiedOrigin(String packageName, Uri verifiedOrigin) {
         return Uri.parse(IntentHandler.ANDROID_APP_REFERRER_SCHEME + "://"
                 + verifiedOrigin.getHost() + "/" + packageName);
     }
@@ -87,14 +94,18 @@
      * Mark an origin as verified for a package.
      * @param packageName The package name to prepopulate for.
      * @param origin The origin to add as verified.
+     * @param relation The Digital Asset Links relation verified.
      */
-    static void addVerifiedOriginForPackage(String packageName, Uri origin) {
+    static void addVerifiedOriginForPackage(
+            String packageName, Uri origin, @Relation int relation) {
         ThreadUtils.assertOnUiThread();
         if (sPackageToCachedOrigins == null) sPackageToCachedOrigins = new HashMap<>();
-        Set<Uri> cachedOrigins = sPackageToCachedOrigins.get(packageName);
+        Set<Uri> cachedOrigins =
+                sPackageToCachedOrigins.get(new Pair<String, Integer>(packageName, relation));
         if (cachedOrigins == null) {
             cachedOrigins = new HashSet<Uri>();
-            sPackageToCachedOrigins.put(packageName, cachedOrigins);
+            sPackageToCachedOrigins.put(
+                    new Pair<String, Integer>(packageName, relation), cachedOrigins);
         }
         cachedOrigins.add(origin);
     }
@@ -107,11 +118,13 @@
      *
      * @param packageName The package name
      * @param origin The origin to verify
+     * @param relation The Digital Asset Links relation to verify for.
      */
-    static boolean isValidOrigin(String packageName, Uri origin) {
+    static boolean isValidOrigin(String packageName, Uri origin, @Relation int relation) {
         ThreadUtils.assertOnUiThread();
         if (sPackageToCachedOrigins == null) return false;
-        Set<Uri> cachedOrigins = sPackageToCachedOrigins.get(packageName);
+        Set<Uri> cachedOrigins =
+                sPackageToCachedOrigins.get(new Pair<String, Integer>(packageName, relation));
         if (cachedOrigins == null) return false;
         return cachedOrigins.contains(origin);
     }
@@ -134,11 +147,14 @@
      * Use {@link OriginVerifier#start(Uri)}
      * @param listener The listener who will get the verification result.
      * @param packageName The package for the Android application for verification.
+     * @param relation Digital Asset Links {@link Relation} to use during verification.
      */
-    public OriginVerifier(OriginVerificationListener listener, String packageName) {
+    public OriginVerifier(
+            OriginVerificationListener listener, String packageName, @Relation int relation) {
         mListener = listener;
         mPackageName = packageName;
         mSignatureFingerprint = getCertificateSHA256FingerprintForPackage(mPackageName);
+        mRelation = relation;
     }
 
     /**
@@ -150,13 +166,15 @@
     public void start(@NonNull Uri origin) {
         ThreadUtils.assertOnUiThread();
         mOrigin = origin;
-        if (!UrlConstants.HTTPS_SCHEME.equals(mOrigin.getScheme().toLowerCase(Locale.US))) {
+        String scheme = mOrigin.getScheme();
+        if (TextUtils.isEmpty(scheme)
+                || !UrlConstants.HTTPS_SCHEME.equals(scheme.toLowerCase(Locale.US))) {
             ThreadUtils.runOnUiThread(new VerifiedCallback(false));
             return;
         }
 
         // If this origin is cached as verified already, use that.
-        if (isValidOrigin(mPackageName, origin)) {
+        if (isValidOrigin(mPackageName, origin, mRelation)) {
             ThreadUtils.runOnUiThread(new VerifiedCallback(true));
             return;
         }
@@ -168,8 +186,20 @@
         }
         mNativeOriginVerifier = nativeInit(Profile.getLastUsedProfile().getOriginalProfile());
         assert mNativeOriginVerifier != 0;
-        boolean success = nativeVerifyOrigin(
-                mNativeOriginVerifier, mPackageName, mSignatureFingerprint, mOrigin.toString());
+        String relationship = null;
+        switch (mRelation) {
+            case CustomTabsService.RELATION_USE_AS_ORIGIN:
+                relationship = USE_AS_ORIGIN;
+                break;
+            case CustomTabsService.RELATION_HANDLE_ALL_URLS:
+                relationship = HANDLE_ALL_URLS;
+                break;
+            default:
+                assert false;
+                break;
+        }
+        boolean success = nativeVerifyOrigin(mNativeOriginVerifier, mPackageName,
+                mSignatureFingerprint, mOrigin.toString(), relationship);
         if (!success) ThreadUtils.runOnUiThread(new VerifiedCallback(false));
     }
 
@@ -240,7 +270,7 @@
     @CalledByNative
     private void originVerified(boolean originVerified) {
         if (originVerified) {
-            addVerifiedOriginForPackage(mPackageName, mOrigin);
+            addVerifiedOriginForPackage(mPackageName, mOrigin, mRelation);
             mOrigin = getPostMessageOriginFromVerifiedOrigin(mPackageName, mOrigin);
         }
         mListener.onOriginVerified(mPackageName, mOrigin, originVerified);
@@ -249,6 +279,6 @@
 
     private native long nativeInit(Profile profile);
     private native boolean nativeVerifyOrigin(long nativeOriginVerifier, String packageName,
-            String signatureFingerprint, String origin);
+            String signatureFingerprint, String origin, String relationship);
     private native void nativeDestroy(long nativeOriginVerifier);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/PostMessageHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/PostMessageHandler.java
index 4ac2c95..128340f1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/PostMessageHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/PostMessageHandler.java
@@ -8,6 +8,7 @@
 import android.net.Uri;
 import android.support.annotation.NonNull;
 import android.support.customtabs.CustomTabsService;
+import android.support.customtabs.CustomTabsService.Relation;
 import android.support.customtabs.CustomTabsSessionToken;
 import android.support.customtabs.PostMessageServiceConnection;
 
@@ -35,6 +36,7 @@
     private AppWebMessagePort[] mChannel;
     private Uri mOrigin;
     private String mPackageName;
+    private @Relation int mRelation;
 
     /**
      * Basic constructor. Everytime the given {@link CustomTabsSessionToken} is associated with a
@@ -146,8 +148,9 @@
      * will be overridden.
      * @param origin The origin to verify for.
      */
-    public void verifyAndInitializeWithOrigin(final Uri origin) {
-        if (mOriginVerifier == null) mOriginVerifier = new OriginVerifier(this, mPackageName);
+    public void verifyAndInitializeWithOrigin(final Uri origin, @Relation int relation) {
+        mRelation = relation;
+        mOriginVerifier = new OriginVerifier(this, mPackageName, mRelation);
         ThreadUtils.postOnUiThread(new Runnable() {
             @Override
             public void run() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationConstants.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationConstants.java
index 4b998464..fe940af 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationConstants.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationConstants.java
@@ -31,6 +31,7 @@
      */
     public static final String EXTRA_NOTIFICATION_ID = "notification_id";
     static final String EXTRA_NOTIFICATION_INFO_ORIGIN = "notification_info_origin";
+    static final String EXTRA_NOTIFICATION_INFO_SCOPE = "notification_info_scope";
     static final String EXTRA_NOTIFICATION_INFO_PROFILE_ID = "notification_info_profile_id";
     static final String EXTRA_NOTIFICATION_INFO_PROFILE_INCOGNITO =
             "notification_info_profile_incognito";
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridge.java
index 2371fb9..a71b73d6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridge.java
@@ -169,6 +169,9 @@
         String notificationId = intent.getStringExtra(NotificationConstants.EXTRA_NOTIFICATION_ID);
 
         String origin = intent.getStringExtra(NotificationConstants.EXTRA_NOTIFICATION_INFO_ORIGIN);
+        String scopeUrl =
+                intent.getStringExtra(NotificationConstants.EXTRA_NOTIFICATION_INFO_SCOPE);
+        if (scopeUrl == null) scopeUrl = "";
         String profileId =
                 intent.getStringExtra(NotificationConstants.EXTRA_NOTIFICATION_INFO_PROFILE_ID);
         boolean incognito = intent.getBooleanExtra(
@@ -185,8 +188,8 @@
             }
             int actionIndex = intent.getIntExtra(
                     NotificationConstants.EXTRA_NOTIFICATION_INFO_ACTION_INDEX, -1);
-            sInstance.onNotificationClicked(notificationId, origin, profileId, incognito, tag,
-                    webApkPackage, actionIndex, getNotificationReply(intent));
+            sInstance.onNotificationClicked(notificationId, origin, scopeUrl, profileId, incognito,
+                    tag, webApkPackage, actionIndex, getNotificationReply(intent));
             return true;
         } else if (NotificationConstants.ACTION_CLOSE_NOTIFICATION.equals(intent.getAction())) {
             // Notification deleteIntent is executed only "when the notification is explicitly
@@ -310,21 +313,24 @@
      * @param action The action this pending intent will represent.
      * @param notificationId The id of the notification.
      * @param origin The origin to whom the notification belongs.
+     * @param scopeUrl The scope of the service worker registered by the site where the notification
+     *                 comes from.
      * @param profileId Id of the profile to which the notification belongs.
      * @param incognito Whether the profile was in incognito mode.
      * @param tag The tag of the notification. May be NULL.
      * @param webApkPackage The package of the WebAPK associated with the notification. Empty if
-*        the notification is not associated with a WebAPK.
+     *        the notification is not associated with a WebAPK.
      * @param actionIndex The zero-based index of the action button, or -1 if not applicable.
      */
     private PendingIntent makePendingIntent(Context context, String action, String notificationId,
-            String origin, String profileId, boolean incognito, @Nullable String tag,
-            String webApkPackage, int actionIndex) {
+            String origin, String scopeUrl, String profileId, boolean incognito,
+            @Nullable String tag, String webApkPackage, int actionIndex) {
         Uri intentData = makeIntentData(notificationId, origin, actionIndex);
         Intent intent = new Intent(action, intentData);
         intent.setClass(context, NotificationService.Receiver.class);
         intent.putExtra(NotificationConstants.EXTRA_NOTIFICATION_ID, notificationId);
         intent.putExtra(NotificationConstants.EXTRA_NOTIFICATION_INFO_ORIGIN, origin);
+        intent.putExtra(NotificationConstants.EXTRA_NOTIFICATION_INFO_SCOPE, scopeUrl);
         intent.putExtra(NotificationConstants.EXTRA_NOTIFICATION_INFO_PROFILE_ID, profileId);
         intent.putExtra(NotificationConstants.EXTRA_NOTIFICATION_INFO_PROFILE_INCOGNITO, incognito);
         intent.putExtra(NotificationConstants.EXTRA_NOTIFICATION_INFO_TAG, tag);
@@ -480,6 +486,8 @@
      *
      * @param notificationId The id of the notification.
      * @param origin Full text of the origin, including the protocol, owning this notification.
+     * @param scopeUrl The scope of the service worker registered by the site where the notification
+     *                 comes from.
      * @param profileId Id of the profile that showed the notification.
      * @param incognito if the session of the profile is an off the record one.
      * @param tag A string identifier for this notification. If the tag is not empty, the new
@@ -505,18 +513,19 @@
      */
     @CalledByNative
     private void displayNotification(final String notificationId, final String origin,
-            final String profileId, final boolean incognito, final String tag, final String title,
-            final String body, final Bitmap image, final Bitmap icon, final Bitmap badge,
-            final int[] vibrationPattern, final long timestamp, final boolean renotify,
-            final boolean silent, final ActionInfo[] actions) {
+            final String scopeUrl, final String profileId, final boolean incognito,
+            final String tag, final String title, final String body, final Bitmap image,
+            final Bitmap icon, final Bitmap badge, final int[] vibrationPattern,
+            final long timestamp, final boolean renotify, final boolean silent,
+            final ActionInfo[] actions) {
         final String webApkPackage =
-                WebApkValidator.queryWebApkPackage(ContextUtils.getApplicationContext(), origin);
+                WebApkValidator.queryWebApkPackage(ContextUtils.getApplicationContext(), scopeUrl);
         if (webApkPackage != null) {
             WebApkIdentityServiceClient.CheckBrowserBacksWebApkCallback callback =
                     new WebApkIdentityServiceClient.CheckBrowserBacksWebApkCallback() {
                         @Override
                         public void onChecked(boolean doesBrowserBackWebApk) {
-                            displayNotificationInternal(notificationId, origin, profileId,
+                            displayNotificationInternal(notificationId, origin, scopeUrl, profileId,
                                     incognito, tag, title, body, image, icon, badge,
                                     vibrationPattern, timestamp, renotify, silent, actions,
                                     doesBrowserBackWebApk ? webApkPackage : "");
@@ -526,15 +535,16 @@
             return;
         }
 
-        displayNotificationInternal(notificationId, origin, profileId, incognito, tag, title, body,
-                image, icon, badge, vibrationPattern, timestamp, renotify, silent, actions, "");
+        displayNotificationInternal(notificationId, origin, scopeUrl, profileId, incognito, tag,
+                title, body, image, icon, badge, vibrationPattern, timestamp, renotify, silent,
+                actions, "");
     }
 
     /** Called after querying whether the browser backs the given WebAPK. */
-    private void displayNotificationInternal(String notificationId, String origin, String profileId,
-            boolean incognito, String tag, String title, String body, Bitmap image, Bitmap icon,
-            Bitmap badge, int[] vibrationPattern, long timestamp, boolean renotify, boolean silent,
-            ActionInfo[] actions, String webApkPackage) {
+    private void displayNotificationInternal(String notificationId, String origin, String scopeUrl,
+            String profileId, boolean incognito, String tag, String title, String body,
+            Bitmap image, Bitmap icon, Bitmap badge, int[] vibrationPattern, long timestamp,
+            boolean renotify, boolean silent, ActionInfo[] actions, String webApkPackage) {
         nativeStoreCachedWebApkPackageForNotificationId(
                 mNativeNotificationPlatformBridge, notificationId, webApkPackage);
 
@@ -547,11 +557,11 @@
                 NotificationSystemStatusUtil.APP_NOTIFICATIONS_STATUS_BOUNDARY);
 
         PendingIntent clickIntent = makePendingIntent(context,
-                NotificationConstants.ACTION_CLICK_NOTIFICATION, notificationId, origin, profileId,
-                incognito, tag, webApkPackage, -1 /* actionIndex */);
+                NotificationConstants.ACTION_CLICK_NOTIFICATION, notificationId, origin, scopeUrl,
+                profileId, incognito, tag, webApkPackage, -1 /* actionIndex */);
         PendingIntent closeIntent = makePendingIntent(context,
-                NotificationConstants.ACTION_CLOSE_NOTIFICATION, notificationId, origin, profileId,
-                incognito, tag, webApkPackage, -1 /* actionIndex */);
+                NotificationConstants.ACTION_CLOSE_NOTIFICATION, notificationId, origin, scopeUrl,
+                profileId, incognito, tag, webApkPackage, -1 /* actionIndex */);
 
         boolean hasImage = image != null;
         boolean forWebApk = !webApkPackage.isEmpty();
@@ -574,7 +584,7 @@
         for (int actionIndex = 0; actionIndex < actions.length; actionIndex++) {
             PendingIntent intent = makePendingIntent(context,
                     NotificationConstants.ACTION_CLICK_NOTIFICATION, notificationId, origin,
-                    profileId, incognito, tag, webApkPackage, actionIndex);
+                    scopeUrl, profileId, incognito, tag, webApkPackage, actionIndex);
             ActionInfo action = actions[actionIndex];
             // Don't show action button icons when there's an image, as then action buttons go on
             // the same row as the Site Settings button, so icons wouldn't leave room for text.
@@ -721,6 +731,8 @@
      *
      * @param notificationId The id of the notification.
      * @param origin The origin to which the notification belongs.
+     * @param scopeUrl The scope of the service worker registered by the site where the notification
+     *                 comes from.
      * @param tag The tag of the notification. May be NULL.
      * @param hasQueriedWebApkPackage Whether has done the query of is there a WebAPK can handle
      *                                this notification.
@@ -729,10 +741,11 @@
      */
     @CalledByNative
     private void closeNotification(final String notificationId, final String origin,
-            final String tag, boolean hasQueriedWebApkPackage, String webApkPackage) {
+            String scopeUrl, final String tag, boolean hasQueriedWebApkPackage,
+            String webApkPackage) {
         if (!hasQueriedWebApkPackage) {
             final String webApkPackageFound = WebApkValidator.queryWebApkPackage(
-                    ContextUtils.getApplicationContext(), origin);
+                    ContextUtils.getApplicationContext(), scopeUrl);
             if (webApkPackageFound != null) {
                 WebApkIdentityServiceClient.CheckBrowserBacksWebApkCallback callback =
                         new WebApkIdentityServiceClient.CheckBrowserBacksWebApkCallback() {
@@ -769,21 +782,23 @@
      *
      * @param notificationId The id of the notification.
      * @param origin The origin of the notification.
+     * @param scopeUrl The scope of the service worker registered by the site where the notification
+     *                 comes from.
      * @param profileId Id of the profile that showed the notification.
      * @param incognito if the profile session was an off the record one.
      * @param tag The tag of the notification. May be NULL.
      * @param webApkPackage The package of the WebAPK associated with the notification.
-     *        Empty if the notification is not associated with a WebAPK.
+     *                      Empty if the notification is not associated with a WebAPK.
      * @param actionIndex The index of the action button that was clicked, or -1 if not applicable.
      * @param reply User reply to a text action on the notification. Null if the user did not click
      *              on a text action or if inline replies are not supported.
      */
-    private void onNotificationClicked(String notificationId, String origin, String profileId,
-            boolean incognito, String tag, String webApkPackage, int actionIndex,
+    private void onNotificationClicked(String notificationId, String origin, String scopeUrl,
+            String profileId, boolean incognito, String tag, String webApkPackage, int actionIndex,
             @Nullable String reply) {
         mLastNotificationClickMs = System.currentTimeMillis();
         nativeOnNotificationClicked(mNativeNotificationPlatformBridge, notificationId, origin,
-                profileId, incognito, tag, webApkPackage, actionIndex, reply);
+                scopeUrl, profileId, incognito, tag, webApkPackage, actionIndex, reply);
     }
 
     /**
@@ -806,8 +821,8 @@
     private static native void nativeInitializeNotificationPlatformBridge();
 
     private native void nativeOnNotificationClicked(long nativeNotificationPlatformBridgeAndroid,
-            String notificationId, String origin, String profileId, boolean incognito, String tag,
-            String webApkPackage, int actionIndex, String reply);
+            String notificationId, String origin, String scopeUrl, String profileId,
+            boolean incognito, String tag, String webApkPackage, int actionIndex, String reply);
     private native void nativeOnNotificationClosed(long nativeNotificationPlatformBridgeAndroid,
             String notificationId, String origin, String profileId, boolean incognito, String tag,
             boolean byUser);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
index 0081964..19984d9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
@@ -1319,10 +1319,9 @@
                 return isSmallDevice ? 0 : R.drawable.omnibox_info;
             case ConnectionSecurityLevel.HTTP_SHOW_WARNING:
                 return R.drawable.omnibox_info;
-            case ConnectionSecurityLevel.SECURITY_WARNING:
-                return R.drawable.omnibox_info;
             case ConnectionSecurityLevel.DANGEROUS:
                 return R.drawable.omnibox_https_invalid;
+            case ConnectionSecurityLevel.SECURE_WITH_POLICY_INSTALLED_CERT:
             case ConnectionSecurityLevel.SECURE:
             case ConnectionSecurityLevel.EV_SECURE:
                 return R.drawable.omnibox_https_valid;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/OmniboxUrlEmphasizer.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/OmniboxUrlEmphasizer.java
index 8d9b7b31..af56d4e4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/OmniboxUrlEmphasizer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/OmniboxUrlEmphasizer.java
@@ -134,11 +134,11 @@
      * @param isInternalPage Whether this page is an internal Chrome page.
      * @param useDarkColors Whether the text colors should be dark (i.e.
      *                      appropriate for use on a light background).
-     * @param emphasizeHttpsScheme Whether the https scheme should be emphasized.
+     * @param emphasizeScheme Whether the scheme should be emphasized.
      */
     public static void emphasizeUrl(Spannable url, Resources resources, Profile profile,
-            int securityLevel, boolean isInternalPage,
-            boolean useDarkColors, boolean emphasizeHttpsScheme) {
+            int securityLevel, boolean isInternalPage, boolean useDarkColors,
+            boolean emphasizeScheme) {
         String urlString = url.toString();
         EmphasizeComponentsResponse emphasizeResponse =
                 parseForEmphasizeComponents(profile, urlString);
@@ -154,7 +154,7 @@
         int startHostIndex = emphasizeResponse.hostStart;
         int endHostIndex = emphasizeResponse.hostStart + emphasizeResponse.hostLength;
 
-        // Color the HTTPS scheme.
+        // Format the scheme, if present, based on the security level.
         ForegroundColorSpan span;
         if (emphasizeResponse.hasScheme()) {
             int colorId = nonEmphasizedColorId;
@@ -164,8 +164,6 @@
                     case ConnectionSecurityLevel.NONE:
                     // Intentional fall-through:
                     case ConnectionSecurityLevel.HTTP_SHOW_WARNING:
-                    // Intentional fall-through:
-                    case ConnectionSecurityLevel.SECURITY_WARNING:
                         // Draw attention to the data: URI scheme for anti-spoofing reasons.
                         if (UrlConstants.DATA_SCHEME.equals(
                                     emphasizeResponse.extractScheme(urlString))) {
@@ -174,13 +172,13 @@
                         }
                         break;
                     case ConnectionSecurityLevel.DANGEROUS:
-                        if (emphasizeHttpsScheme) colorId = R.color.google_red_700;
+                        if (emphasizeScheme) colorId = R.color.google_red_700;
                         strikeThroughScheme = true;
                         break;
                     case ConnectionSecurityLevel.EV_SECURE:
                     // Intentional fall-through:
                     case ConnectionSecurityLevel.SECURE:
-                        if (emphasizeHttpsScheme) colorId = R.color.google_green_700;
+                        if (emphasizeScheme) colorId = R.color.google_green_700;
                         break;
                     default:
                         assert false;
@@ -196,9 +194,8 @@
             url.setSpan(
                     span, startSchemeIndex, endSchemeIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
 
-            // Highlight the portion of the URL visible between the scheme and the host. For
-            // https, this will be ://. For normal pages, this will be empty as we trim off
-            // http://.
+            // Highlight the portion of the URL visible between the scheme and the host,
+            // typically :// or : depending on the scheme.
             if (emphasizeResponse.hasHost()) {
                 span = new UrlEmphasisColorSpan(
                         ApiCompatibilityUtils.getColor(resources, nonEmphasizedColorId));
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentApp.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentApp.java
index 0121da3..3ca17f8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentApp.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentApp.java
@@ -245,11 +245,6 @@
     }
 
     @Override
-    public Set<String> getPreferredRelatedApplicationIds() {
-        return null;
-    }
-
-    @Override
     public URI getCanDedupedApplicationId() {
         return mCanDedupedApplicationId;
     }
@@ -562,9 +557,4 @@
 
     @Override
     public void dismissInstrument() {}
-
-    @Override
-    public int getAdditionalAppTextResourceId() {
-        return 0;
-    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/AutofillPaymentApp.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/AutofillPaymentApp.java
index a3c6dce3..436e8f5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/AutofillPaymentApp.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/AutofillPaymentApp.java
@@ -180,11 +180,6 @@
         return merchantSupportsAutofillPaymentInstruments(methodDataMap);
     }
 
-    @Override
-    public Set<String> getPreferredRelatedApplicationIds() {
-        return null;
-    }
-
     /** @return True if the merchant methodDataMap supports autofill payment instruments. */
     public static boolean merchantSupportsAutofillPaymentInstruments(
             Map<String, PaymentMethodData> methodDataMap) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentApp.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentApp.java
index d3df3a5..d5d05bf 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentApp.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentApp.java
@@ -75,7 +75,9 @@
      * "com.alicepay"}.
      */
     @Nullable
-    Set<String> getPreferredRelatedApplicationIds();
+    default Set<String> getPreferredRelatedApplicationIds() {
+        return null;
+    }
 
     /**
      * Gets the app Id this application can dedupe. The return, for example, could be
@@ -99,5 +101,7 @@
      * @return The resource identifier for the additional text that should be displayed to the user
      * when selecting a payment instrument from this payment app or 0 if not needed.
      */
-    int getAdditionalAppTextResourceId();
+    default int getAdditionalAppTextResourceId() {
+        return 0;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ServiceWorkerPaymentApp.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ServiceWorkerPaymentApp.java
index 3d3c363..2a23b11f4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ServiceWorkerPaymentApp.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ServiceWorkerPaymentApp.java
@@ -111,11 +111,6 @@
     }
 
     @Override
-    public int getAdditionalAppTextResourceId() {
-        return 0;
-    }
-
-    @Override
     public Set<String> getInstrumentMethodNames() {
         return getAppMethodNames();
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/PhysicalWeb.java b/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/PhysicalWeb.java
index dc405100..26a0237 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/PhysicalWeb.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/PhysicalWeb.java
@@ -131,11 +131,12 @@
      */
     private static boolean shouldAutoEnablePhysicalWeb() {
         LocationUtils locationUtils = LocationUtils.getInstance();
-        return locationUtils.isSystemLocationSettingEnabled()
+        // LowEndDevice check must come first in order to short circuit more intensive routines
+        // potentially run by LocationUtils.
+        return !SysUtils.isLowEndDevice() && locationUtils.isSystemLocationSettingEnabled()
                 && locationUtils.hasAndroidLocationPermission()
                 && TemplateUrlService.getInstance().isDefaultSearchEngineGoogle()
-                && !Profile.getLastUsedProfile().isOffTheRecord()
-                && !SysUtils.isLowEndDevice();
+                && !Profile.getLastUsedProfile().isOffTheRecord();
     }
 
     /**
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 1c3630b..8103456 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
@@ -948,7 +948,6 @@
         // Do not apply the theme color if there are any security issues on the page.
         int securityLevel = getSecurityLevel();
         if (securityLevel == ConnectionSecurityLevel.DANGEROUS
-                || securityLevel == ConnectionSecurityLevel.SECURITY_WARNING
                 || securityLevel == ConnectionSecurityLevel.SECURE_WITH_POLICY_INSTALLED_CERT) {
             themeColor = getDefaultThemeColor();
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabStateBrowserControlsVisibilityDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabStateBrowserControlsVisibilityDelegate.java
index 35a74d17..d022cd3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabStateBrowserControlsVisibilityDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabStateBrowserControlsVisibilityDelegate.java
@@ -141,8 +141,7 @@
         enableHidingBrowserControls &= !url.startsWith(UrlConstants.CHROME_NATIVE_URL_PREFIX);
 
         int securityState = mTab.getSecurityLevel();
-        enableHidingBrowserControls &= (securityState != ConnectionSecurityLevel.DANGEROUS
-                && securityState != ConnectionSecurityLevel.SECURITY_WARNING);
+        enableHidingBrowserControls &= (securityState != ConnectionSecurityLevel.DANGEROUS);
 
         enableHidingBrowserControls &= !AccessibilityUtil.isAccessibilityEnabled();
 
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 f7c818d2..ff29d562 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
@@ -867,6 +867,7 @@
         ScreenOrientationDelegateManager.setOrientationDelegate(null);
         mRestoreOrientation = null;
         clearVrModeWindowFlags();
+        if (mVrShell != null) mVrShell.getContainer().setOnSystemUiVisibilityChangeListener(null);
     }
 
     /* package */ boolean canEnterVr(Tab tab, boolean justCompletedDon) {
@@ -1445,6 +1446,10 @@
     private void removeVrViews() {
         mVrShell.onBeforeWindowDetached();
         mActivity.onExitVr();
+        if (mActivity.getCompositorViewHolder() != null) {
+            mActivity.getCompositorViewHolder().onExitVr(mTabModelSelector);
+        }
+        mTabModelSelector = null;
         FrameLayout decor = (FrameLayout) mActivity.getWindow().getDecorView();
         decor.removeView(mVrShell.getContainer());
     }
@@ -1472,13 +1477,8 @@
      */
     private void destroyVrShell() {
         if (mVrShell != null) {
-            mVrShell.getContainer().setOnSystemUiVisibilityChangeListener(null);
             mVrShell.teardown();
             mVrShell = null;
-            if (mActivity.getCompositorViewHolder() != null) {
-                mActivity.getCompositorViewHolder().onExitVr(mTabModelSelector);
-            }
-            mTabModelSelector = null;
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java
index a49b6c9..3e6f790 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java
@@ -507,6 +507,7 @@
     @Override
     public void onBeforeWindowDetached() {
         mRenderToSurfaceLayout.getViewTreeObserver().removeOnPreDrawListener(mPredrawListener);
+        if (mNativeVrShell != 0) nativeRestoreLayer(mNativeVrShell);
     }
 
     @Override
@@ -735,6 +736,7 @@
     private native void nativeSetSurface(long nativeVrShell, Surface surface);
     private native void nativeSwapContents(
             long nativeVrShell, Tab tab, AndroidUiGestureTarget androidUiGestureTarget);
+    private native void nativeRestoreLayer(long nativeVrShell);
     private native void nativeDestroy(long nativeVrShell);
     private native void nativeOnTriggerEvent(long nativeVrShell, boolean touched);
     private native void nativeOnPause(long nativeVrShell);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
index bca6844b..82bad9c0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
@@ -188,6 +188,12 @@
             resetSavedInstanceState();
         }
 
+        if (info == null) {
+            // If {@link info} is null, there isn't much we can do, abort.
+            ApiCompatibilityUtils.finishAndRemoveTask(this);
+            return;
+        }
+
         mWebappInfo = info;
 
         // Initialize the WebappRegistry and warm up the shared preferences for this web app. No-ops
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappBrowserControlsDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappBrowserControlsDelegate.java
index c8d88ba..2118f6a1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappBrowserControlsDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappBrowserControlsDelegate.java
@@ -64,8 +64,7 @@
     }
 
     private boolean shouldShowBrowserControlsForSecurityLevel(int securityLevel) {
-        return securityLevel == ConnectionSecurityLevel.DANGEROUS
-                || securityLevel == ConnectionSecurityLevel.SECURITY_WARNING;
+        return securityLevel == ConnectionSecurityLevel.DANGEROUS;
     }
 
     private boolean shouldShowBrowserControlsForDisplayMode(WebappInfo info) {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/ClientManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/ClientManagerTest.java
index 34e16276..ec18fb7d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/ClientManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/ClientManagerTest.java
@@ -7,6 +7,7 @@
 import android.content.Context;
 import android.net.Uri;
 import android.os.Process;
+import android.support.customtabs.CustomTabsService;
 import android.support.customtabs.CustomTabsSessionToken;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
@@ -178,13 +179,16 @@
             @Override
             public void run() {
                 // With no prepopulated origins, this verification should fail.
-                cm.verifyAndInitializeWithPostMessageOriginForSession(mSession, Uri.parse(URL));
+                cm.verifyAndInitializeWithPostMessageOriginForSession(
+                        mSession, Uri.parse(URL), CustomTabsService.RELATION_USE_AS_ORIGIN);
                 Assert.assertNull(cm.getPostMessageOriginForSessionForTesting(mSession));
 
                 // If there is a prepopulated origin, we should get a synchronous verification.
                 OriginVerifier.addVerifiedOriginForPackage(
-                        ContextUtils.getApplicationContext().getPackageName(), Uri.parse(URL));
-                cm.verifyAndInitializeWithPostMessageOriginForSession(mSession, Uri.parse(URL));
+                        ContextUtils.getApplicationContext().getPackageName(), Uri.parse(URL),
+                        CustomTabsService.RELATION_USE_AS_ORIGIN);
+                cm.verifyAndInitializeWithPostMessageOriginForSession(
+                        mSession, Uri.parse(URL), CustomTabsService.RELATION_USE_AS_ORIGIN);
             }
         });
 
@@ -212,6 +216,48 @@
 
     @Test
     @SmallTest
+    public void testPostMessageOriginDifferentRelations() {
+        final ClientManager cm = mClientManager;
+        Assert.assertTrue(cm.newSession(mSession, mUid, null, new PostMessageHandler(mSession)));
+        // Should always start with no origin.
+        Assert.assertNull(cm.getPostMessageOriginForSessionForTesting(mSession));
+
+        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+            @Override
+            public void run() {
+                // With no prepopulated origins, this verification should fail.
+                cm.verifyAndInitializeWithPostMessageOriginForSession(
+                        mSession, Uri.parse(URL), CustomTabsService.RELATION_USE_AS_ORIGIN);
+                Assert.assertNull(cm.getPostMessageOriginForSessionForTesting(mSession));
+
+                // Prepopulated origins should depend on the relation used.
+                OriginVerifier.addVerifiedOriginForPackage(
+                        ContextUtils.getApplicationContext().getPackageName(), Uri.parse(URL),
+                        CustomTabsService.RELATION_HANDLE_ALL_URLS);
+                // This uses CustomTabsService.RELATION_USE_AS_ORIGIN by default.
+                Assert.assertFalse(cm.isFirstPartyOriginForSession(mSession, Uri.parse(URL)));
+                cm.verifyAndInitializeWithPostMessageOriginForSession(
+                        mSession, Uri.parse(URL), CustomTabsService.RELATION_HANDLE_ALL_URLS);
+            }
+        });
+
+        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+            @Override
+            public void run() {
+                Uri verifiedOrigin = cm.getPostMessageOriginForSessionForTesting(mSession);
+                Assert.assertEquals(
+                        IntentHandler.ANDROID_APP_REFERRER_SCHEME, verifiedOrigin.getScheme());
+
+                // initializeWithPostMessageOriginForSession should override without checking
+                // origin.
+                cm.initializeWithPostMessageOriginForSession(mSession, null);
+                Assert.assertNull(cm.getPostMessageOriginForSessionForTesting(mSession));
+            }
+        });
+    }
+
+    @Test
+    @SmallTest
     public void testPostMessageOriginHttpNotAllowed() {
         final ClientManager cm = mClientManager;
         Assert.assertTrue(cm.newSession(mSession, mUid, null, new PostMessageHandler(mSession)));
@@ -223,14 +269,17 @@
             public void run() {
                 Uri uri = Uri.parse(HTTP_URL);
                 // With no prepopulated origins, this verification should fail.
-                cm.verifyAndInitializeWithPostMessageOriginForSession(mSession, uri);
+                cm.verifyAndInitializeWithPostMessageOriginForSession(
+                        mSession, uri, CustomTabsService.RELATION_USE_AS_ORIGIN);
                 Assert.assertNull(cm.getPostMessageOriginForSessionForTesting(mSession));
 
                 // Even if there is a prepopulated origin, non-https origins should get an early
                 // return with false.
                 OriginVerifier.addVerifiedOriginForPackage(
-                        ContextUtils.getApplicationContext().getPackageName(), uri);
-                cm.verifyAndInitializeWithPostMessageOriginForSession(mSession, uri);
+                        ContextUtils.getApplicationContext().getPackageName(), uri,
+                        CustomTabsService.RELATION_USE_AS_ORIGIN);
+                cm.verifyAndInitializeWithPostMessageOriginForSession(
+                        mSession, uri, CustomTabsService.RELATION_USE_AS_ORIGIN);
                 Assert.assertNull(cm.getPostMessageOriginForSessionForTesting(mSession));
             }
         });
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/OriginVerifierTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/OriginVerifierTest.java
index 4d84859..a980b1f 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/OriginVerifierTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/OriginVerifierTest.java
@@ -4,17 +4,27 @@
 
 package org.chromium.chrome.browser.customtabs;
 
+import android.net.Uri;
+import android.support.customtabs.CustomTabsService;
 import android.support.test.filters.SmallTest;
 
 import org.junit.Assert;
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import org.chromium.base.ThreadUtils;
 import org.chromium.base.test.BaseJUnit4ClassRunner;
+import org.chromium.chrome.browser.customtabs.OriginVerifier.OriginVerificationListener;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
 
 /** Tests for OriginVerifier. */
 @RunWith(BaseJUnit4ClassRunner.class)
 public class OriginVerifierTest {
+    private static final long TIMEOUT_MS = 1000;
     private static final byte[] BYTE_ARRAY = new byte[] {(byte) 0xaa, (byte) 0xbb, (byte) 0xcc,
             (byte) 0x10, (byte) 0x20, (byte) 0x30, (byte) 0x01, (byte) 0x02};
     private static final String STRING_ARRAY = "AA:BB:CC:10:20:30:01:02";
@@ -23,6 +33,35 @@
     private static final String SHA_256_FINGERPRINT =
             "32:A2:FC:74:D7:31:10:58:59:E5:A8:5D:F1:6D:95:F1:02:D8:5B"
             + ":22:09:9B:80:64:C5:D8:91:5C:61:DA:D1:E0";
+    private static final Uri TEST_HTTPS_ORIGIN_1 = Uri.parse("https://www.example.com");
+    private static final Uri TEST_HTTPS_ORIGIN_2 = Uri.parse("https://www.android.com");
+    private static final Uri TEST_HTTP_ORIGIN = Uri.parse("http://www.android.com");
+
+    private class TestOriginVerificationListener implements OriginVerificationListener {
+        @Override
+        public void onOriginVerified(String packageName, Uri origin, boolean verified) {
+            mLastPackageName = packageName;
+            mLastOrigin = origin;
+            mLastVerified = verified;
+            mVerificationResultSemaphore.release();
+        }
+    }
+
+    private Semaphore mVerificationResultSemaphore;
+    private OriginVerifier mUseAsOriginVerifier;
+    private OriginVerifier mHandleAllUrlsVerifier;
+    private volatile String mLastPackageName;
+    private volatile Uri mLastOrigin;
+    private volatile boolean mLastVerified;
+
+    @Before
+    public void setUp() throws Exception {
+        mHandleAllUrlsVerifier = new OriginVerifier(new TestOriginVerificationListener(),
+                PACKAGE_NAME, CustomTabsService.RELATION_HANDLE_ALL_URLS);
+        mUseAsOriginVerifier = new OriginVerifier(new TestOriginVerificationListener(),
+                PACKAGE_NAME, CustomTabsService.RELATION_USE_AS_ORIGIN);
+        mVerificationResultSemaphore = new Semaphore(0);
+    }
 
     @Test
     @SmallTest
@@ -31,4 +70,48 @@
         Assert.assertEquals(OriginVerifier.getCertificateSHA256FingerprintForPackage(PACKAGE_NAME),
                 SHA_256_FINGERPRINT);
     }
+
+    @Test
+    @SmallTest
+    public void testOnlyHttpsAllowed() throws InterruptedException {
+        ThreadUtils.postOnUiThread(() -> mHandleAllUrlsVerifier.start(Uri.parse("LOL")));
+        Assert.assertTrue(
+                mVerificationResultSemaphore.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        Assert.assertFalse(mLastVerified);
+        ThreadUtils.postOnUiThread(() -> mHandleAllUrlsVerifier.start(TEST_HTTP_ORIGIN));
+        Assert.assertTrue(
+                mVerificationResultSemaphore.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        Assert.assertFalse(mLastVerified);
+    }
+
+    @Test
+    @SmallTest
+    public void testMultipleRelationships() throws Exception {
+        ThreadUtils.postOnUiThread(
+                ()
+                        -> OriginVerifier.addVerifiedOriginForPackage(PACKAGE_NAME,
+                                TEST_HTTPS_ORIGIN_1, CustomTabsService.RELATION_USE_AS_ORIGIN));
+        ThreadUtils.postOnUiThread(() -> mUseAsOriginVerifier.start(TEST_HTTPS_ORIGIN_1));
+        Assert.assertTrue(
+                mVerificationResultSemaphore.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        Assert.assertTrue(mLastVerified);
+        Assert.assertTrue(ThreadUtils.runOnUiThreadBlocking(new Callable<Boolean>() {
+            @Override
+            public Boolean call() throws Exception {
+                return OriginVerifier.isValidOrigin(PACKAGE_NAME, TEST_HTTPS_ORIGIN_1,
+                        CustomTabsService.RELATION_USE_AS_ORIGIN);
+            }
+        }));
+        Assert.assertFalse(ThreadUtils.runOnUiThreadBlocking(new Callable<Boolean>() {
+            @Override
+            public Boolean call() throws Exception {
+                return OriginVerifier.isValidOrigin(PACKAGE_NAME, TEST_HTTPS_ORIGIN_1,
+                        CustomTabsService.RELATION_HANDLE_ALL_URLS);
+            }
+        }));
+        Assert.assertEquals(mLastPackageName, PACKAGE_NAME);
+        Assert.assertEquals(mLastOrigin,
+                OriginVerifier.getPostMessageOriginFromVerifiedOrigin(
+                        PACKAGE_NAME, TEST_HTTPS_ORIGIN_1));
+    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/OmniboxUrlEmphasizerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/OmniboxUrlEmphasizerTest.java
index 836ebd9..79d468c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/OmniboxUrlEmphasizerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/OmniboxUrlEmphasizerTest.java
@@ -214,29 +214,26 @@
     }
 
     /**
-     * Verify that a very short, warning HTTPS URL is colored correctly by
+     * Verify that a very short, HTTP Warning URL is colored correctly by
      * OmniboxUrlEmphasizer.emphasizeUrl().
      */
     @Test
     @MediumTest
     @UiThreadTest
     @Feature({"Browser", "Main"})
-    public void testVeryShortWarningHTTPSUrl() throws Throwable {
-        Spannable url = new SpannableStringBuilder("https://www.dodgysite.com");
+    public void testVeryShortHTTPWarningUrl() throws Throwable {
+        Spannable url = new SpannableStringBuilder("m.w.co/p");
         OmniboxUrlEmphasizer.emphasizeUrl(url, mResources, mProfile,
-                ConnectionSecurityLevel.SECURITY_WARNING, false, true, true);
+                ConnectionSecurityLevel.HTTP_SHOW_WARNING, false, true, false);
         EmphasizedUrlSpanHelper[] spans = EmphasizedUrlSpanHelper.getSpansForEmphasizedUrl(url);
 
-        Assert.assertEquals("Unexpected number of spans:", 3, spans.length);
-        spans[0].assertIsColoredSpan("https", 0,
-                ApiCompatibilityUtils.getColor(
-                        mResources, R.color.url_emphasis_non_emphasized_text));
-        spans[1].assertIsColoredSpan("://", 5,
-                ApiCompatibilityUtils.getColor(
-                        mResources, R.color.url_emphasis_non_emphasized_text));
-        spans[2].assertIsColoredSpan("www.dodgysite.com", 8,
+        Assert.assertEquals("Unexpected number of spans:", 2, spans.length);
+        spans[0].assertIsColoredSpan("m.w.co", 0,
                 ApiCompatibilityUtils.getColor(
                         mResources, R.color.url_emphasis_domain_and_registry));
+        spans[1].assertIsColoredSpan("/p", 6,
+                ApiCompatibilityUtils.getColor(
+                        mResources, R.color.url_emphasis_non_emphasized_text));
     }
 
     /**
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestTestCommon.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestTestCommon.java
index 7e2d48ae..c412777 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestTestCommon.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestTestCommon.java
@@ -856,19 +856,9 @@
         }
 
         @Override
-        public Set<String> getPreferredRelatedApplicationIds() {
-            return null;
-        }
-
-        @Override
         public String getAppIdentifier() {
             return TestPay.this.toString();
         }
-
-        @Override
-        public int getAdditionalAppTextResourceId() {
-            return 0;
-        }
     }
 
     /** A payment instrument implementation for test. */
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappUrlBarTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappUrlBarTest.java
index 926714d7..2c53dbd 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappUrlBarTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappUrlBarTest.java
@@ -49,13 +49,11 @@
         final String urlExpectedWhenIconShown = host;
         final int[] securityLevels = {ConnectionSecurityLevel.NONE,
                 ConnectionSecurityLevel.EV_SECURE, ConnectionSecurityLevel.SECURE,
-                ConnectionSecurityLevel.SECURITY_WARNING,
+                ConnectionSecurityLevel.HTTP_SHOW_WARNING,
                 ConnectionSecurityLevel.SECURE_WITH_POLICY_INSTALLED_CERT,
                 ConnectionSecurityLevel.DANGEROUS};
 
         for (int i : securityLevels) {
-            // TODO(palmer): http://crbug.com/297249
-            if (i == ConnectionSecurityLevel.SECURE_WITH_POLICY_INSTALLED_CERT) continue;
             urlBar.update(url, i);
 
             int iconResource = urlBar.getCurrentIconResourceForTests();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappVisibilityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappVisibilityTest.java
index 9b87711..6f171c28 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappVisibilityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappVisibilityTest.java
@@ -69,8 +69,6 @@
         Assert.assertTrue(
                 canAutoHideBrowserControls(type, ConnectionSecurityLevel.HTTP_SHOW_WARNING));
         Assert.assertFalse(canAutoHideBrowserControls(type, ConnectionSecurityLevel.DANGEROUS));
-        Assert.assertFalse(
-                canAutoHideBrowserControls(type, ConnectionSecurityLevel.SECURITY_WARNING));
     }
 
     private static void testShouldShowBrowserControls(Type type, int displayMode) {
@@ -104,19 +102,13 @@
         Assert.assertFalse(shouldShowBrowserControls(
                         WEBAPP_URL, "", ConnectionSecurityLevel.NONE, type, displayMode));
 
-        // Show browser controls for non secure URLs.
+        // Show browser controls for Dangerous URLs.
         Assert.assertTrue(shouldShowBrowserControls(WEBAPP_URL, WEBAPP_URL,
-                ConnectionSecurityLevel.SECURITY_WARNING, type, displayMode));
-        Assert.assertTrue(shouldShowBrowserControls(WEBAPP_URL, WEBAPP_URL + "/things.html",
-                ConnectionSecurityLevel.SECURITY_WARNING, type, displayMode));
-        Assert.assertTrue(shouldShowBrowserControls(WEBAPP_URL, WEBAPP_URL + "/stuff.html",
-                ConnectionSecurityLevel.SECURITY_WARNING, type, displayMode));
+                ConnectionSecurityLevel.DANGEROUS, type, displayMode));
         Assert.assertTrue(shouldShowBrowserControls(WEBAPP_URL, WEBAPP_URL + "/stuff.html",
                 ConnectionSecurityLevel.DANGEROUS, type, displayMode));
         Assert.assertTrue(shouldShowBrowserControls(WEBAPP_URL, WEBAPP_URL + "/things.html",
                 ConnectionSecurityLevel.DANGEROUS, type, displayMode));
-        Assert.assertTrue(shouldShowBrowserControls(WEBAPP_URL, "http://sub.originalwebsite.com",
-                ConnectionSecurityLevel.SECURITY_WARNING, type, displayMode));
         Assert.assertTrue(shouldShowBrowserControls(WEBAPP_URL, "http://notoriginalwebsite.com",
                 ConnectionSecurityLevel.DANGEROUS, type, displayMode));
         Assert.assertTrue(shouldShowBrowserControls(WEBAPP_URL, "http://otherwebsite.com",
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/LocationBarLayoutTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/LocationBarLayoutTest.java
index 9c7d644..cc354734 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/LocationBarLayoutTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/LocationBarLayoutTest.java
@@ -28,10 +28,9 @@
 public final class LocationBarLayoutTest {
     private static final boolean IS_SMALL_DEVICE = true;
     private static final boolean IS_OFFLINE_PAGE = true;
-    private static final int[] SECURITY_LEVELS =
-            new int[] {ConnectionSecurityLevel.NONE, ConnectionSecurityLevel.HTTP_SHOW_WARNING,
-                    ConnectionSecurityLevel.SECURITY_WARNING, ConnectionSecurityLevel.DANGEROUS,
-                    ConnectionSecurityLevel.SECURE, ConnectionSecurityLevel.EV_SECURE};
+    private static final int[] SECURITY_LEVELS = new int[] {ConnectionSecurityLevel.NONE,
+            ConnectionSecurityLevel.HTTP_SHOW_WARNING, ConnectionSecurityLevel.DANGEROUS,
+            ConnectionSecurityLevel.SECURE, ConnectionSecurityLevel.EV_SECURE};
 
     @Mock
     private Tab mTab;
@@ -82,13 +81,6 @@
                 LocationBarLayout.getSecurityIconResource(ConnectionSecurityLevel.HTTP_SHOW_WARNING,
                         !IS_SMALL_DEVICE, !IS_OFFLINE_PAGE));
 
-        assertEquals(R.drawable.omnibox_info,
-                LocationBarLayout.getSecurityIconResource(ConnectionSecurityLevel.SECURITY_WARNING,
-                        IS_SMALL_DEVICE, !IS_OFFLINE_PAGE));
-        assertEquals(R.drawable.omnibox_info,
-                LocationBarLayout.getSecurityIconResource(ConnectionSecurityLevel.SECURITY_WARNING,
-                        !IS_SMALL_DEVICE, !IS_OFFLINE_PAGE));
-
         assertEquals(R.drawable.omnibox_https_invalid,
                 LocationBarLayout.getSecurityIconResource(
                         ConnectionSecurityLevel.DANGEROUS, IS_SMALL_DEVICE, !IS_OFFLINE_PAGE));
@@ -98,6 +90,15 @@
 
         assertEquals(R.drawable.omnibox_https_valid,
                 LocationBarLayout.getSecurityIconResource(
+                        ConnectionSecurityLevel.SECURE_WITH_POLICY_INSTALLED_CERT, IS_SMALL_DEVICE,
+                        !IS_OFFLINE_PAGE));
+        assertEquals(R.drawable.omnibox_https_valid,
+                LocationBarLayout.getSecurityIconResource(
+                        ConnectionSecurityLevel.SECURE_WITH_POLICY_INSTALLED_CERT, !IS_SMALL_DEVICE,
+                        !IS_OFFLINE_PAGE));
+
+        assertEquals(R.drawable.omnibox_https_valid,
+                LocationBarLayout.getSecurityIconResource(
                         ConnectionSecurityLevel.SECURE, IS_SMALL_DEVICE, !IS_OFFLINE_PAGE));
         assertEquals(R.drawable.omnibox_https_valid,
                 LocationBarLayout.getSecurityIconResource(
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 6f095e00..4f27643 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -2102,6 +2102,9 @@
       </message>
 
       <!-- Content blocking strings -->
+      <message name="IDS_MANAGE" desc="Text for a button on permission bubbles, which opens a more detailed content settings page where users can manage that particular setting.">
+        Manage
+      </message>
       <message name="IDS_AUTOMATIC_DOWNLOADS_TAB_LABEL" desc="Header for multiple automatic downloads page on Content Settings dialog">
         Automatic Downloads
       </message>
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index aa5aa01..8f0c0e3 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -2208,16 +2208,10 @@
 
   if (enable_offline_pages) {
     sources += [
-      "android/offline_pages/downloads/offline_page_download_bridge.cc",
-      "android/offline_pages/downloads/offline_page_download_bridge.h",
-      "android/offline_pages/downloads/offline_page_infobar_delegate.cc",
-      "android/offline_pages/downloads/offline_page_infobar_delegate.h",
-      "android/offline_pages/downloads/offline_page_notification_bridge.cc",
-      "android/offline_pages/downloads/offline_page_notification_bridge.h",
-      "android/offline_pages/downloads/resource_throttle.cc",
-      "android/offline_pages/downloads/resource_throttle.h",
       "offline_pages/background_loader_offliner.cc",
       "offline_pages/background_loader_offliner.h",
+      "offline_pages/downloads/resource_throttle.cc",
+      "offline_pages/downloads/resource_throttle.h",
       "offline_pages/offline_page_bookmark_observer.cc",
       "offline_pages/offline_page_bookmark_observer.h",
       "offline_pages/offline_page_info_handler.cc",
@@ -2269,6 +2263,12 @@
         "offline_pages/android/cct_origin_observer.h",
         "offline_pages/android/cct_request_observer.cc",
         "offline_pages/android/cct_request_observer.h",
+        "offline_pages/android/downloads/offline_page_download_bridge.cc",
+        "offline_pages/android/downloads/offline_page_download_bridge.h",
+        "offline_pages/android/downloads/offline_page_infobar_delegate.cc",
+        "offline_pages/android/downloads/offline_page_infobar_delegate.h",
+        "offline_pages/android/downloads/offline_page_notification_bridge.cc",
+        "offline_pages/android/downloads/offline_page_notification_bridge.h",
         "offline_pages/android/load_termination_listener_impl.cc",
         "offline_pages/android/load_termination_listener_impl.h",
         "offline_pages/android/offline_page_bridge.cc",
@@ -2293,17 +2293,19 @@
       "//components/offline_pages/core/renovations",
       "//components/offline_pages/core/request_header:request_header",
     ]
-  }
 
-  # Used to build test harness locally. The harness is used manually to
-  # produce multiple offline pages to evaluate quality of the snapshots.
-  if (enable_offline_pages_harness) {
-    sources += [
-      "android/offline_pages/evaluation/evaluation_test_scheduler.cc",
-      "android/offline_pages/evaluation/evaluation_test_scheduler.h",
-      "android/offline_pages/evaluation/offline_page_evaluation_bridge.cc",
-      "android/offline_pages/evaluation/offline_page_evaluation_bridge.h",
-    ]
+    # Used to build test harness locally. The harness is used manually to
+    # produce multiple offline pages to evaluate quality of the snapshots.
+    # This will only be built iff. |enable_offline_pages_harness| is set while
+    # |enable_offline_pages| and |is_android| are both true.
+    if (enable_offline_pages_harness && is_android) {
+      sources += [
+        "offline_pages/android/evaluation/evaluation_test_scheduler.cc",
+        "offline_pages/android/evaluation/evaluation_test_scheduler.h",
+        "offline_pages/android/evaluation/offline_page_evaluation_bridge.cc",
+        "offline_pages/android/evaluation/offline_page_evaluation_bridge.h",
+      ]
+    }
   }
 
   if (enable_hotwording) {
diff --git a/chrome/browser/android/compositor/layer/contextual_search_layer.cc b/chrome/browser/android/compositor/layer/contextual_search_layer.cc
index fbc3c75..f011b50 100644
--- a/chrome/browser/android/compositor/layer/contextual_search_layer.cc
+++ b/chrome/browser/android/compositor/layer/contextual_search_layer.cc
@@ -97,10 +97,7 @@
     float divider_line_x_offset,
     bool touch_highlight_visible,
     float touch_highlight_x_offset,
-    float touch_highlight_width,
-    int bar_handle_resource_id,
-    float bar_handle_offset_y,
-    float bar_padding_bottom) {
+    float touch_highlight_width) {
   // Round values to avoid pixel gap between layers.
   search_bar_height = floor(search_bar_height);
 
@@ -112,7 +109,7 @@
   OverlayPanelLayer::SetResourceIds(
       search_term_resource_id, panel_shadow_resource_id,
       search_bar_shadow_resource_id, search_provider_icon_resource_id,
-      close_icon_resource_id, bar_handle_resource_id);
+      close_icon_resource_id);
 
   float content_view_top = search_bar_bottom + search_promo_height;
   float should_render_bar_border = search_bar_border_visible
@@ -126,8 +123,7 @@
       search_panel_width, search_panel_height, search_bar_margin_side,
       search_bar_height, search_bar_top, search_term_opacity,
       should_render_bar_border, search_bar_border_height,
-      search_bar_shadow_visible, search_bar_shadow_opacity, close_icon_opacity,
-      bar_handle_offset_y, bar_padding_bottom);
+      search_bar_shadow_visible, search_bar_shadow_opacity, close_icon_opacity);
 
   bool is_rtl = l10n_util::IsLayoutRtl();
 
@@ -226,11 +222,11 @@
   // ---------------------------------------------------------------------------
   // Search Term, Context and Search Caption
   // ---------------------------------------------------------------------------
-  SetupTextLayer(bar_content_height_, search_text_layer_min_height,
-                 search_caption_resource_id, search_caption_visible,
-                 search_caption_animation_percentage, search_term_opacity,
-                 search_context_resource_id, search_context_opacity,
-                 search_term_caption_spacing);
+  SetupTextLayer(search_bar_top, search_bar_height,
+                 search_text_layer_min_height, search_caption_resource_id,
+                 search_caption_visible, search_caption_animation_percentage,
+                 search_term_opacity, search_context_resource_id,
+                 search_context_opacity, search_term_caption_spacing);
 
   // ---------------------------------------------------------------------------
   // Arrow Icon
@@ -249,8 +245,8 @@
   }
 
   // Centers the Arrow Icon vertically in the bar.
-  float arrow_icon_top =
-      (bar_content_height_ - arrow_icon_resource->size().height()) / 2;
+  float arrow_icon_top = search_bar_top + search_bar_height / 2 -
+                         arrow_icon_resource->size().height() / 2;
 
   arrow_icon_->SetUIResourceId(arrow_icon_resource->ui_resource()->id());
   arrow_icon_->SetBounds(arrow_icon_resource->size());
@@ -371,11 +367,11 @@
   // ---------------------------------------------------------------------------
   if (divider_line_visibility_percentage > 0.f) {
     if (divider_line_->parent() != layer_)
-      bar_content_layer_->AddChild(divider_line_);
+      layer_->AddChild(divider_line_);
 
     // The divider line animates in from the bottom.
     float divider_line_y_offset =
-        ((bar_content_height_ - divider_line_height) / 2) +
+        ((search_bar_height - divider_line_height) / 2) +
         (divider_line_height * (1.f - divider_line_visibility_percentage));
     divider_line_->SetPosition(gfx::PointF(divider_line_x_offset,
                                            divider_line_y_offset));
@@ -506,7 +502,8 @@
       gfx::PointF(side_margin, custom_image_y_offset));
 }
 
-void ContextualSearchLayer::SetupTextLayer(float bar_height,
+void ContextualSearchLayer::SetupTextLayer(float bar_top,
+                                           float bar_height,
                                            float search_text_layer_min_height,
                                            int caption_resource_id,
                                            bool caption_visible,
@@ -577,7 +574,7 @@
   float layer_width =
       std::max(main_text->bounds().width(), search_caption_->bounds().width());
 
-  float layer_top = (bar_height - layer_height) / 2;
+  float layer_top = bar_top + (bar_height - layer_height) / 2;
   text_layer_->SetBounds(gfx::Size(layer_width, layer_height));
   text_layer_->SetPosition(gfx::PointF(0.f, layer_top));
   text_layer_->SetMasksToBounds(true);
@@ -756,7 +753,7 @@
 
   // Arrow Icon
   arrow_icon_->SetIsDrawable(true);
-  bar_content_layer_->AddChild(arrow_icon_);
+  layer_->AddChild(arrow_icon_);
 
   // Search Opt Out Promo
   search_promo_container_->SetIsDrawable(true);
@@ -773,7 +770,7 @@
 
   // Icon - holds thumbnail, search provider icon and/or quick action icon
   icon_layer_->SetIsDrawable(true);
-  bar_content_layer_->AddChild(icon_layer_);
+  layer_->AddChild(icon_layer_);
 
   // Search provider icon
   search_provider_icon_layer_->SetIsDrawable(true);
diff --git a/chrome/browser/android/compositor/layer/contextual_search_layer.h b/chrome/browser/android/compositor/layer/contextual_search_layer.h
index 83fc74a..1c0f1b78 100644
--- a/chrome/browser/android/compositor/layer/contextual_search_layer.h
+++ b/chrome/browser/android/compositor/layer/contextual_search_layer.h
@@ -90,10 +90,7 @@
                      float divider_line_x_offset,
                      bool touch_highlight_visible,
                      float touch_highlight_x_offset,
-                     float touch_highlight_width,
-                     int bar_handle_resource_id,
-                     float bar_handle_offset_y,
-                     float bar_padding_bottom);
+                     float touch_highlight_width);
 
   void SetThumbnail(const SkBitmap* thumbnail);
 
@@ -119,7 +116,8 @@
 
   // Sets up |text_layer_|, which contains |bar_text_|, |search_context_| and
   // |search_caption_|.
-  void SetupTextLayer(float search_bar_height,
+  void SetupTextLayer(float search_bar_top,
+                      float search_bar_height,
                       float search_text_layer_min_height,
                       int search_caption_resource_id,
                       bool search_caption_visible,
diff --git a/chrome/browser/android/compositor/layer/overlay_panel_layer.cc b/chrome/browser/android/compositor/layer/overlay_panel_layer.cc
index 8b40970..2ce7323 100644
--- a/chrome/browser/android/compositor/layer/overlay_panel_layer.cc
+++ b/chrome/browser/android/compositor/layer/overlay_panel_layer.cc
@@ -51,14 +51,12 @@
                                        int panel_shadow_resource_id,
                                        int bar_shadow_resource_id,
                                        int panel_icon_resource_id,
-                                       int close_icon_resource_id,
-                                       int handle_resource_id) {
+                                       int close_icon_resource_id) {
   bar_text_resource_id_ = bar_text_resource_id;
   panel_shadow_resource_id_ = panel_shadow_resource_id;
   bar_shadow_resource_id_ = bar_shadow_resource_id;
   panel_icon_resource_id_ = panel_icon_resource_id;
   close_icon_resource_id_ = close_icon_resource_id;
-  bar_handle_resource_id_ = handle_resource_id;
 }
 
 void OverlayPanelLayer::SetProperties(
@@ -77,9 +75,7 @@
     float bar_border_height,
     bool bar_shadow_visible,
     float bar_shadow_opacity,
-    float close_icon_opacity,
-    float bar_handle_offset_y,
-    float bar_padding_bottom) {
+    float close_icon_opacity) {
   // Grabs required static resources.
   ui::NinePatchResource* panel_shadow_resource =
       ui::NinePatchResource::From(resource_manager_->GetResource(
@@ -121,43 +117,6 @@
   bar_background_->SetPosition(gfx::PointF(0.f, bar_top));
 
   // ---------------------------------------------------------------------------
-  // Bar Handle
-  // ---------------------------------------------------------------------------
-
-  float bar_content_top = bar_top;
-  bar_content_height_ = bar_height - bar_padding_bottom;
-
-  if (bar_handle_resource_id_ != 0) {
-    if (bar_handle_ == nullptr) {
-      bar_handle_ = cc::UIResourceLayer::Create();
-      bar_handle_->SetIsDrawable(true);
-      layer_->AddChild(bar_handle_);
-    }
-
-    // Grab the bar handle resource.
-    ui::Resource* bar_handle_resource = resource_manager_->GetResource(
-        ui::ANDROID_RESOURCE_TYPE_STATIC, bar_handle_resource_id_);
-
-    // Center the handle horizontally.
-    float bar_handle_left =
-        (panel_width - bar_handle_resource->size().width()) / 2;
-    float bar_handle_top = bar_top + bar_handle_offset_y;
-
-    bar_handle_->SetUIResourceId(bar_handle_resource->ui_resource()->id());
-    bar_handle_->SetBounds(bar_handle_resource->size());
-    bar_handle_->SetPosition(gfx::PointF(bar_handle_left, bar_handle_top));
-
-    bar_content_top = bar_handle_top + bar_handle_resource->size().height();
-    bar_content_height_ = bar_height - bar_content_top - bar_padding_bottom;
-  }
-
-  // ---------------------------------------------------------------------------
-  // Bar Content Layer
-  // ---------------------------------------------------------------------------
-  bar_content_layer_->SetBounds(gfx::Size(panel_width, bar_content_height_));
-  bar_content_layer_->SetPosition(gfx::PointF(0.f, bar_content_top));
-
-  // ---------------------------------------------------------------------------
   // Bar Text
   // ---------------------------------------------------------------------------
   ui::Resource* bar_text_resource = resource_manager_->GetResource(
@@ -166,7 +125,7 @@
   if (bar_text_resource) {
     // Centers the text vertically in the Search Bar.
     float bar_padding_top =
-        (bar_content_height_ - bar_text_resource->size().height()) / 2;
+        bar_top + bar_height / 2 - bar_text_resource->size().height() / 2;
     bar_text_->SetUIResourceId(bar_text_resource->ui_resource()->id());
     bar_text_->SetBounds(bar_text_resource->size());
     bar_text_->SetPosition(gfx::PointF(0.f, bar_padding_top));
@@ -193,7 +152,7 @@
     }
 
     // Centers the Icon vertically in the bar.
-    float icon_y = (bar_content_height_ - icon_layer->bounds().height()) / 2;
+    float icon_y = bar_top + bar_height / 2 - icon_layer->bounds().height() / 2;
 
     icon_layer->SetPosition(gfx::PointF(icon_x, icon_y));
   }
@@ -216,7 +175,7 @@
 
   // Centers the Close Icon vertically in the bar.
   float close_icon_top =
-      (bar_content_height_ - close_icon_resource->size().height()) / 2;
+      bar_top + bar_height / 2 - close_icon_resource->size().height() / 2;
 
   close_icon_->SetUIResourceId(close_icon_resource->ui_resource()->id());
   close_icon_->SetBounds(close_icon_resource->size());
@@ -293,8 +252,7 @@
       close_icon_(cc::UIResourceLayer::Create()),
       content_container_(cc::SolidColorLayer::Create()),
       text_container_(cc::Layer::Create()),
-      bar_border_(cc::SolidColorLayer::Create()),
-      bar_content_layer_(cc::UIResourceLayer::Create()) {
+      bar_border_(cc::SolidColorLayer::Create()) {
   layer_->SetMasksToBounds(false);
   layer_->SetIsDrawable(true);
 
@@ -308,14 +266,10 @@
   bar_background_->SetBackgroundColor(kBarBackgroundColor);
   layer_->AddChild(bar_background_);
 
-  // Bar Content Layer
-  bar_content_layer_->SetIsDrawable(true);
-  layer_->AddChild(bar_content_layer_);
-
   // Bar Text
   bar_text_->SetIsDrawable(true);
   AddBarTextLayer(bar_text_);
-  bar_content_layer_->AddChild(text_container_);
+  layer_->AddChild(text_container_);
 
   // Panel Icon
   panel_icon_->SetIsDrawable(true);
@@ -325,7 +279,7 @@
 
   // Close Icon
   close_icon_->SetIsDrawable(true);
-  bar_content_layer_->AddChild(close_icon_);
+  layer_->AddChild(close_icon_);
 
   // Content Container
   content_container_->SetIsDrawable(true);
diff --git a/chrome/browser/android/compositor/layer/overlay_panel_layer.h b/chrome/browser/android/compositor/layer/overlay_panel_layer.h
index f842bd5..147def7 100644
--- a/chrome/browser/android/compositor/layer/overlay_panel_layer.h
+++ b/chrome/browser/android/compositor/layer/overlay_panel_layer.h
@@ -28,8 +28,7 @@
                       int panel_shadow_resource_id,
                       int bar_shadow_resource_id,
                       int panel_icon_resource_id,
-                      int close_icon_resource_id,
-                      int handle_resource_id);
+                      int close_icon_resource_id);
 
   void SetProperties(float dp_to_px,
                      const scoped_refptr<cc::Layer>& content_layer,
@@ -46,9 +45,7 @@
                      float bar_border_height,
                      bool bar_shadow_visible,
                      float bar_shadow_opacity,
-                     float close_icon_opacity,
-                     float bar_handle_offset_y,
-                     float bar_padding_bottom);
+                     float close_icon_opacity);
 
   scoped_refptr<cc::Layer> layer() override;
 
@@ -71,17 +68,12 @@
   scoped_refptr<cc::Layer> content_container_;
   scoped_refptr<cc::Layer> text_container_;
   scoped_refptr<cc::SolidColorLayer> bar_border_;
-  scoped_refptr<cc::UIResourceLayer> bar_handle_;
-  scoped_refptr<cc::Layer> bar_content_layer_;
 
   int panel_icon_resource_id_;
   int bar_text_resource_id_;
   int panel_shadow_resource_id_;
   int bar_shadow_resource_id_;
   int close_icon_resource_id_;
-  int bar_handle_resource_id_;
-
-  int bar_content_height_;
 };
 
 }  //  namespace android
diff --git a/chrome/browser/android/compositor/scene_layer/contextual_search_scene_layer.cc b/chrome/browser/android/compositor/scene_layer/contextual_search_scene_layer.cc
index b93fef2e..665d1f08 100644
--- a/chrome/browser/android/compositor/scene_layer/contextual_search_scene_layer.cc
+++ b/chrome/browser/android/compositor/scene_layer/contextual_search_scene_layer.cc
@@ -124,9 +124,6 @@
     jboolean touch_highlight_visible,
     jfloat touch_highlight_x_offset,
     jfloat touch_highlight_width,
-    jint bar_handle_resource_id,
-    jfloat bar_handle_offset_y,
-    jfloat bar_padding_bottom,
     const JavaRef<jobject>& j_profile) {
   // Load the thumbnail if necessary.
   std::string thumbnail_url =
@@ -183,8 +180,7 @@
       progress_bar_height, progress_bar_opacity, progress_bar_completion,
       divider_line_visibility_percentage, divider_line_width,
       divider_line_height, divider_line_color, divider_line_x_offset,
-      touch_highlight_visible, touch_highlight_x_offset, touch_highlight_width,
-      bar_handle_resource_id, bar_handle_offset_y, bar_padding_bottom);
+      touch_highlight_visible, touch_highlight_x_offset, touch_highlight_width);
 
   // Make the layer visible if it is not already.
   contextual_search_layer_->layer()->SetHideLayerAndSubtree(false);
diff --git a/chrome/browser/android/compositor/scene_layer/contextual_search_scene_layer.h b/chrome/browser/android/compositor/scene_layer/contextual_search_scene_layer.h
index d70c9dd4..86b90fd 100644
--- a/chrome/browser/android/compositor/scene_layer/contextual_search_scene_layer.h
+++ b/chrome/browser/android/compositor/scene_layer/contextual_search_scene_layer.h
@@ -101,9 +101,6 @@
       jboolean touch_highlight_visible,
       jfloat touch_highlight_x_offset,
       jfloat touch_highlight_width,
-      jint bar_handle_resource_id,
-      jfloat bar_handle_offset_y,
-      jfloat bar_padding_bottom,
       const base::android::JavaRef<jobject>& j_profile);
 
   // Inherited from BitmapFetcherDelegate
diff --git a/chrome/browser/android/customtabs/origin_verifier.cc b/chrome/browser/android/customtabs/origin_verifier.cc
index 811046c..5a123c02 100644
--- a/chrome/browser/android/customtabs/origin_verifier.cc
+++ b/chrome/browser/android/customtabs/origin_verifier.cc
@@ -37,13 +37,15 @@
                                   const JavaParamRef<jobject>& obj,
                                   const JavaParamRef<jstring>& j_package_name,
                                   const JavaParamRef<jstring>& j_fingerprint,
-                                  const JavaParamRef<jstring>& j_origin) {
-  if (!j_package_name || !j_fingerprint || !j_origin)
+                                  const JavaParamRef<jstring>& j_origin,
+                                  const JavaParamRef<jstring>& j_relationship) {
+  if (!j_package_name || !j_fingerprint || !j_origin || !j_relationship)
     return false;
 
   std::string package_name = ConvertJavaStringToUTF8(env, j_package_name);
   std::string fingerprint = ConvertJavaStringToUTF8(env, j_fingerprint);
   std::string origin = ConvertJavaStringToUTF8(env, j_origin);
+  std::string relationship = ConvertJavaStringToUTF8(env, j_relationship);
 
   // Multiple calls here will end up resetting the callback on the handler side
   // and cancelling previous requests.
@@ -54,8 +56,7 @@
   return asset_link_handler_->CheckDigitalAssetLinkRelationship(
       base::Bind(&customtabs::OriginVerifier::OnRelationshipCheckComplete,
                  base::Unretained(this)),
-      origin, package_name, fingerprint,
-      "delegate_permission/common.use_as_origin");
+      origin, package_name, fingerprint, relationship);
 }
 
 void OriginVerifier::OnRelationshipCheckComplete(
diff --git a/chrome/browser/android/customtabs/origin_verifier.h b/chrome/browser/android/customtabs/origin_verifier.h
index 517bcf1..86dfca1 100644
--- a/chrome/browser/android/customtabs/origin_verifier.h
+++ b/chrome/browser/android/customtabs/origin_verifier.h
@@ -34,7 +34,8 @@
                     const base::android::JavaParamRef<jobject>& obj,
                     const base::android::JavaParamRef<jstring>& j_package_name,
                     const base::android::JavaParamRef<jstring>& j_fingerprint,
-                    const base::android::JavaParamRef<jstring>& j_origin);
+                    const base::android::JavaParamRef<jstring>& j_origin,
+                    const base::android::JavaParamRef<jstring>& j_relationship);
 
   void Destroy(JNIEnv* env, const base::android::JavaRef<jobject>& obj);
 
diff --git a/chrome/browser/android/offline_pages/DEPS b/chrome/browser/android/offline_pages/DEPS
deleted file mode 100644
index e58dcb0..0000000
--- a/chrome/browser/android/offline_pages/DEPS
+++ /dev/null
@@ -1,4 +0,0 @@
-include_rules = [
-  "+components/offline_pages/core",
-  "+components/offline_pages/core/background",
-]
diff --git a/chrome/browser/android/offline_pages/OWNERS b/chrome/browser/android/offline_pages/OWNERS
deleted file mode 100644
index 5ccdbe2c..0000000
--- a/chrome/browser/android/offline_pages/OWNERS
+++ /dev/null
@@ -1,8 +0,0 @@
-dewittj@chromium.org
-dimich@chromium.org
-fgorski@chromium.org
-jianli@chromium.org
-petewil@chromium.org
-
-# TEAM: offline-dev@chromium.org
-# COMPONENT: UI>Browser>Offline
diff --git a/chrome/browser/android/vr_shell/vr_shell.cc b/chrome/browser/android/vr_shell/vr_shell.cc
index 621b73c..4d0094e31 100644
--- a/chrome/browser/android/vr_shell/vr_shell.cc
+++ b/chrome/browser/android/vr_shell/vr_shell.cc
@@ -158,6 +158,10 @@
     UMA_HISTOGRAM_BOOLEAN("VRAutopresentedWebVR", !for_web_vr);
 }
 
+void VrShell::RestoreLayer(JNIEnv* env, const JavaParamRef<jobject>& obj) {
+  compositor_->SetLayer(nullptr);
+}
+
 void VrShell::Destroy(JNIEnv* env, const JavaParamRef<jobject>& obj) {
   delete this;
 }
diff --git a/chrome/browser/android/vr_shell/vr_shell.h b/chrome/browser/android/vr_shell/vr_shell.h
index 8fbbc2f..1186a78 100644
--- a/chrome/browser/android/vr_shell/vr_shell.h
+++ b/chrome/browser/android/vr_shell/vr_shell.h
@@ -86,6 +86,8 @@
       const base::android::JavaParamRef<jobject>& obj,
       const base::android::JavaParamRef<jobject>& web_contents,
       const base::android::JavaParamRef<jobject>& android_ui_gesture_target);
+  void RestoreLayer(JNIEnv* env,
+                    const base::android::JavaParamRef<jobject>& obj);
   void Destroy(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj);
   void OnTriggerEvent(JNIEnv* env,
                       const base::android::JavaParamRef<jobject>& obj,
diff --git a/chrome/browser/bookmarks/chrome_bookmark_client.h b/chrome/browser/bookmarks/chrome_bookmark_client.h
index 98b74ce..d435a63 100644
--- a/chrome/browser/bookmarks/chrome_bookmark_client.h
+++ b/chrome/browser/bookmarks/chrome_bookmark_client.h
@@ -11,6 +11,7 @@
 #include "base/deferred_sequenced_task_runner.h"
 #include "base/macros.h"
 #include "components/bookmarks/browser/bookmark_client.h"
+#include "components/offline_pages/features/features.h"
 
 class GURL;
 class Profile;
@@ -22,7 +23,7 @@
 class ManagedBookmarkService;
 }
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(ENABLE_OFFLINE_PAGES)
 namespace offline_pages {
 class OfflinePageBookmarkObserver;
 }
@@ -62,7 +63,7 @@
   // be null during testing.
   bookmarks::ManagedBookmarkService* managed_bookmark_service_;
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(ENABLE_OFFLINE_PAGES)
   // Owns the observer used by Offline Page listening to Bookmark Model events.
   std::unique_ptr<offline_pages::OfflinePageBookmarkObserver>
       offline_page_observer_;
diff --git a/chrome/browser/browser_process_impl.cc b/chrome/browser/browser_process_impl.cc
index 2ef318d..675e9220 100644
--- a/chrome/browser/browser_process_impl.cc
+++ b/chrome/browser/browser_process_impl.cc
@@ -225,18 +225,18 @@
       created_subresource_filter_ruleset_service_(false),
       shutting_down_(false),
       tearing_down_(false),
-      download_status_updater_(new DownloadStatusUpdater),
+      download_status_updater_(base::MakeUnique<DownloadStatusUpdater>()),
       local_state_task_runner_(local_state_task_runner),
       cached_default_web_client_state_(shell_integration::UNKNOWN_DEFAULT),
       pref_service_factory_(
           base::MakeUnique<prefs::InProcessPrefServiceFactory>()) {
   g_browser_process = this;
   rappor::SetDefaultServiceAccessor(&GetBrowserRapporService);
-  platform_part_.reset(new BrowserProcessPlatformPart());
+  platform_part_ = base::MakeUnique<BrowserProcessPlatformPart>();
 
 #if BUILDFLAG(ENABLE_PRINTING)
   // Must be created after the NotificationService.
-  print_job_manager_.reset(new printing::PrintJobManager);
+  print_job_manager_ = base::MakeUnique<printing::PrintJobManager>();
 #endif
 
   net_log_ = base::MakeUnique<net_log::ChromeNetLog>();
@@ -255,18 +255,19 @@
   ui::InitIdleMonitor();
 #endif
 
-  device_client_.reset(new ChromeDeviceClient);
+  device_client_ = base::MakeUnique<ChromeDeviceClient>();
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
   extensions::AppWindowClient::Set(ChromeAppWindowClient::GetInstance());
 
-  extension_event_router_forwarder_ = new extensions::EventRouterForwarder;
+  extension_event_router_forwarder_ =
+      base::MakeRefCounted<extensions::EventRouterForwarder>();
 
   extensions::ExtensionsClient::Set(
       extensions::ChromeExtensionsClient::GetInstance());
 
-  extensions_browser_client_.reset(
-      new extensions::ChromeExtensionsBrowserClient);
+  extensions_browser_client_ =
+      base::MakeUnique<extensions::ChromeExtensionsBrowserClient>();
   extensions::ExtensionsBrowserClient::Set(extensions_browser_client_.get());
 #endif
 
@@ -483,7 +484,8 @@
   // Mark all the profiles as clean.
   ProfileManager* pm = profile_manager();
   std::vector<Profile*> profiles(pm->GetLoadedProfiles());
-  scoped_refptr<RundownTaskCounter> rundown_counter(new RundownTaskCounter());
+  scoped_refptr<RundownTaskCounter> rundown_counter =
+      base::MakeRefCounted<RundownTaskCounter>();
   for (size_t i = 0; i < profiles.size(); ++i) {
     Profile* profile = profiles[i];
     profile->SetExitType(Profile::EXIT_SESSION_ENDED);
@@ -541,10 +543,11 @@
 BrowserProcessImpl::GetMetricsServicesManager() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!metrics_services_manager_) {
-    metrics_services_manager_.reset(
-        new metrics_services_manager::MetricsServicesManager(
-            base::MakeUnique<ChromeMetricsServicesManagerClient>(
-                local_state())));
+    auto client =
+        base::MakeUnique<ChromeMetricsServicesManagerClient>(local_state());
+    metrics_services_manager_ =
+        base::MakeUnique<metrics_services_manager::MetricsServicesManager>(
+            std::move(client));
   }
   return metrics_services_manager_.get();
 }
@@ -680,8 +683,8 @@
 
 GpuModeManager* BrowserProcessImpl::gpu_mode_manager() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (!gpu_mode_manager_.get())
-    gpu_mode_manager_.reset(new GpuModeManager());
+  if (!gpu_mode_manager_)
+    gpu_mode_manager_ = base::MakeUnique<GpuModeManager>();
   return gpu_mode_manager_.get();
 }
 
@@ -692,8 +695,9 @@
 #if !defined(OS_ANDROID)
   // StartupBrowserCreator::LaunchBrowser can be run multiple times when browser
   // is started with several profiles or existing browser process is reused.
-  if (!remote_debugging_server_.get()) {
-    remote_debugging_server_.reset(new RemoteDebuggingServer(ip, port));
+  if (!remote_debugging_server_) {
+    remote_debugging_server_ =
+        base::MakeUnique<RemoteDebuggingServer>(ip, port);
   }
 #endif
 }
@@ -703,8 +707,8 @@
 #if !defined(OS_ANDROID)
   // StartupBrowserCreator::LaunchBrowser can be run multiple times when browser
   // is started with several profiles or existing browser process is reused.
-  if (!devtools_auto_opener_.get())
-    devtools_auto_opener_.reset(new DevToolsAutoOpener());
+  if (!devtools_auto_opener_)
+    devtools_auto_opener_ = base::MakeUnique<DevToolsAutoOpener>();
 #endif
 }
 
@@ -737,7 +741,7 @@
     BrowserProcessImpl::background_printing_manager() {
 #if BUILDFLAG(ENABLE_PRINT_PREVIEW)
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (!background_printing_manager_.get())
+  if (!background_printing_manager_)
     CreateBackgroundPrintingManager();
   return background_printing_manager_.get();
 #else
@@ -748,7 +752,7 @@
 
 IntranetRedirectDetector* BrowserProcessImpl::intranet_redirect_detector() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (!intranet_redirect_detector_.get())
+  if (!intranet_redirect_detector_)
     CreateIntranetRedirectDetector();
   return intranet_redirect_detector_.get();
 }
@@ -775,7 +779,7 @@
 MediaFileSystemRegistry* BrowserProcessImpl::media_file_system_registry() {
 #if BUILDFLAG(ENABLE_EXTENSIONS)
   if (!media_file_system_registry_)
-    media_file_system_registry_.reset(new MediaFileSystemRegistry());
+    media_file_system_registry_ = base::MakeUnique<MediaFileSystemRegistry>();
   return media_file_system_registry_.get();
 #else
   return NULL;
@@ -784,18 +788,18 @@
 
 #if BUILDFLAG(ENABLE_WEBRTC)
 WebRtcLogUploader* BrowserProcessImpl::webrtc_log_uploader() {
-  if (!webrtc_log_uploader_.get())
-    webrtc_log_uploader_.reset(new WebRtcLogUploader());
+  if (!webrtc_log_uploader_)
+    webrtc_log_uploader_ = base::MakeUnique<WebRtcLogUploader>();
   return webrtc_log_uploader_.get();
 }
 #endif
 
 network_time::NetworkTimeTracker* BrowserProcessImpl::network_time_tracker() {
   if (!network_time_tracker_) {
-    network_time_tracker_.reset(new network_time::NetworkTimeTracker(
+    network_time_tracker_ = base::MakeUnique<network_time::NetworkTimeTracker>(
         base::WrapUnique(new base::DefaultClock()),
         base::WrapUnique(new base::DefaultTickClock()), local_state(),
-        system_request_context()));
+        system_request_context());
   }
   return network_time_tracker_.get();
 }
@@ -810,8 +814,8 @@
 resource_coordinator::TabManager* BrowserProcessImpl::GetTabManager() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX)
-  if (!tab_manager_.get())
-    tab_manager_.reset(new resource_coordinator::TabManager());
+  if (!tab_manager_)
+    tab_manager_ = base::MakeUnique<resource_coordinator::TabManager>();
   return tab_manager_.get();
 #else
   return nullptr;
@@ -881,15 +885,16 @@
 
 DownloadRequestLimiter* BrowserProcessImpl::download_request_limiter() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (!download_request_limiter_.get())
-    download_request_limiter_ = new DownloadRequestLimiter();
+  if (!download_request_limiter_.get()) {
+    download_request_limiter_ = base::MakeRefCounted<DownloadRequestLimiter>();
+  }
   return download_request_limiter_.get();
 }
 
 BackgroundModeManager* BrowserProcessImpl::background_mode_manager() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 #if BUILDFLAG(ENABLE_BACKGROUND)
-  if (!background_mode_manager_.get())
+  if (!background_mode_manager_)
     CreateBackgroundModeManager();
   return background_mode_manager_.get();
 #else
@@ -907,7 +912,7 @@
 
 StatusTray* BrowserProcessImpl::status_tray() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (!status_tray_.get())
+  if (!status_tray_)
     CreateStatusTray();
   return status_tray_.get();
 }
@@ -968,7 +973,7 @@
 
 CRLSetFetcher* BrowserProcessImpl::crl_set_fetcher() {
   if (!crl_set_fetcher_)
-    crl_set_fetcher_ = new CRLSetFetcher();
+    crl_set_fetcher_ = base::MakeRefCounted<CRLSetFetcher>();
   return crl_set_fetcher_.get();
 }
 
@@ -977,7 +982,7 @@
 #if !defined(DISABLE_NACL)
   if (!pnacl_component_installer_) {
     pnacl_component_installer_ =
-        new component_updater::PnaclComponentInstaller();
+        base::MakeRefCounted<component_updater::PnaclComponentInstaller>();
   }
   return pnacl_component_installer_.get();
 #else
@@ -998,8 +1003,8 @@
 }
 
 void BrowserProcessImpl::ResourceDispatcherHostCreated() {
-  resource_dispatcher_host_delegate_.reset(
-      new ChromeResourceDispatcherHostDelegate);
+  resource_dispatcher_host_delegate_ =
+      base::MakeUnique<ChromeResourceDispatcherHostDelegate>();
   ResourceDispatcherHost::Get()->SetDelegate(
       resource_dispatcher_host_delegate_.get());
 
@@ -1023,7 +1028,7 @@
   DCHECK(!created_watchdog_thread_ && !watchdog_thread_);
   created_watchdog_thread_ = true;
 
-  std::unique_ptr<WatchDogThread> thread(new WatchDogThread());
+  auto thread = base::MakeUnique<WatchDogThread>();
   base::Thread::Options options;
   options.timer_slack = base::TIMER_SLACK_MAXIMUM;
   if (!thread->StartWithOptions(options))
@@ -1037,7 +1042,7 @@
 
   base::FilePath user_data_dir;
   PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
-  profile_manager_.reset(new ProfileManager(user_data_dir));
+  profile_manager_ = base::MakeUnique<ProfileManager>(user_data_dir);
 }
 
 void BrowserProcessImpl::CreateLocalState() {
@@ -1045,7 +1050,7 @@
 
   base::FilePath local_state_path;
   CHECK(PathService::Get(chrome::FILE_LOCAL_STATE, &local_state_path));
-  scoped_refptr<PrefRegistrySimple> pref_registry = new PrefRegistrySimple;
+  auto pref_registry = base::MakeRefCounted<PrefRegistrySimple>();
 
   // Register local state preferences.
   chrome::RegisterLocalState(pref_registry.get());
@@ -1138,31 +1143,29 @@
   storage_monitor::StorageMonitor::Create();
 #endif
 
-  child_process_watcher_.reset(new ChromeChildProcessWatcher());
+  child_process_watcher_ = base::MakeUnique<ChromeChildProcessWatcher>();
 
   CacheDefaultWebClientState();
 
   platform_part_->PreMainMessageLoopRun();
 
   if (base::FeatureList::IsEnabled(network_time::kNetworkTimeServiceQuerying)) {
-    network_time_tracker_.reset(new network_time::NetworkTimeTracker(
+    network_time_tracker_ = base::MakeUnique<network_time::NetworkTimeTracker>(
         base::WrapUnique(new base::DefaultClock()),
         base::WrapUnique(new base::DefaultTickClock()), local_state(),
-        system_request_context()));
+        system_request_context());
   }
 }
 
 void BrowserProcessImpl::CreateIconManager() {
   DCHECK(!created_icon_manager_ && !icon_manager_);
   created_icon_manager_ = true;
-  icon_manager_.reset(new IconManager);
+  icon_manager_ = base::MakeUnique<IconManager>();
 }
 
 void BrowserProcessImpl::CreateIntranetRedirectDetector() {
   DCHECK(!intranet_redirect_detector_);
-  std::unique_ptr<IntranetRedirectDetector> intranet_redirect_detector(
-      new IntranetRedirectDetector);
-  intranet_redirect_detector_.swap(intranet_redirect_detector);
+  intranet_redirect_detector_ = base::MakeUnique<IntranetRedirectDetector>();
 }
 
 void BrowserProcessImpl::CreateNotificationPlatformBridge() {
@@ -1186,10 +1189,9 @@
 void BrowserProcessImpl::CreateBackgroundModeManager() {
 #if BUILDFLAG(ENABLE_BACKGROUND)
   DCHECK(!background_mode_manager_);
-  background_mode_manager_.reset(
-      new BackgroundModeManager(
-              *base::CommandLine::ForCurrentProcess(),
-              &profile_manager()->GetProfileAttributesStorage()));
+  background_mode_manager_ = base::MakeUnique<BackgroundModeManager>(
+      *base::CommandLine::ForCurrentProcess(),
+      &profile_manager()->GetProfileAttributesStorage());
 #endif
 }
 
@@ -1202,7 +1204,7 @@
 #if BUILDFLAG(ENABLE_PRINT_PREVIEW)
   DCHECK(!print_preview_dialog_controller_);
   print_preview_dialog_controller_ =
-      new printing::PrintPreviewDialogController();
+      base::MakeRefCounted<printing::PrintPreviewDialogController>();
 #else
   NOTIMPLEMENTED();
 #endif
@@ -1211,7 +1213,8 @@
 void BrowserProcessImpl::CreateBackgroundPrintingManager() {
 #if BUILDFLAG(ENABLE_PRINT_PREVIEW)
   DCHECK(!background_printing_manager_);
-  background_printing_manager_.reset(new printing::BackgroundPrintingManager());
+  background_printing_manager_ =
+      base::MakeUnique<printing::BackgroundPrintingManager>();
 #else
   NOTIMPLEMENTED();
 #endif
@@ -1299,8 +1302,8 @@
     // The worker pointer is reference counted. While it is running, the
     // message loops of the FILE and UI thread will hold references to it
     // and it will be automatically freed once all its tasks have finished.
-    scoped_refptr<shell_integration::DefaultBrowserWorker> set_browser_worker =
-        new shell_integration::DefaultBrowserWorker(
+    auto set_browser_worker =
+        base::MakeRefCounted<shell_integration::DefaultBrowserWorker>(
             shell_integration::DefaultWebClientWorkerCallback());
     // The user interaction must always be disabled when applying the default
     // browser policy since it is done at each browser startup and the result
@@ -1396,8 +1399,7 @@
 
 void BrowserProcessImpl::RestartBackgroundInstance() {
   base::CommandLine* old_cl = base::CommandLine::ForCurrentProcess();
-  std::unique_ptr<base::CommandLine> new_cl(
-      new base::CommandLine(old_cl->GetProgram()));
+  auto new_cl = base::MakeUnique<base::CommandLine>(old_cl->GetProgram());
 
   std::map<std::string, base::CommandLine::StringType> switches =
       old_cl->GetSwitches();
@@ -1406,15 +1408,12 @@
 
   // Append the rest of the switches (along with their values, if any)
   // to the new command line
-  for (std::map<std::string, base::CommandLine::StringType>::const_iterator i =
-           switches.begin();
-       i != switches.end(); ++i) {
-    base::CommandLine::StringType switch_value = i->second;
-      if (switch_value.length() > 0) {
-        new_cl->AppendSwitchNative(i->first, i->second);
-      } else {
-        new_cl->AppendSwitch(i->first);
-      }
+  for (const auto& it : switches) {
+    base::CommandLine::StringType switch_value = it.second;
+    if (switch_value.length() > 0)
+      new_cl->AppendSwitchNative(it.first, it.second);
+    else
+      new_cl->AppendSwitch(it.first);
   }
 
   // Ensure that our desired switches are set on the new process.
diff --git a/chrome/browser/chrome_browser_main_win.cc b/chrome/browser/chrome_browser_main_win.cc
index 65d16cd0..7ff8d5c 100644
--- a/chrome/browser/chrome_browser_main_win.cc
+++ b/chrome/browser/chrome_browser_main_win.cc
@@ -196,9 +196,7 @@
 
 // Used as the callback for ModuleWatcher events in this process. Dispatches
 // them to the ModuleDatabase.
-void OnModuleEvent(uint32_t process_id,
-                   uint64_t creation_time,
-                   const ModuleWatcher::ModuleEvent& event) {
+void OnModuleEvent(const ModuleWatcher::ModuleEvent& event) {
   auto* module_database = ModuleDatabase::GetInstance();
   uintptr_t load_address =
       reinterpret_cast<uintptr_t>(event.module_load_address);
@@ -207,15 +205,10 @@
     case mojom::ModuleEventType::MODULE_ALREADY_LOADED:
     case mojom::ModuleEventType::MODULE_LOADED: {
       module_database->OnModuleLoad(
-          process_id, creation_time, event.module_path, event.module_size,
+          content::PROCESS_TYPE_BROWSER, event.module_path, event.module_size,
           GetModuleTimeDateStamp(event.module_load_address), load_address);
       return;
     }
-
-    case mojom::ModuleEventType::MODULE_UNLOADED: {
-      module_database->OnModuleUnload(process_id, creation_time, load_address);
-      return;
-    }
   }
 }
 
@@ -225,22 +218,11 @@
 void SetupModuleDatabase(std::unique_ptr<ModuleWatcher>* module_watcher) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  uint64_t creation_time = 0;
-  ModuleEventSinkImpl::GetProcessCreationTime(::GetCurrentProcess(),
-                                              &creation_time);
   ModuleDatabase::SetInstance(
       base::MakeUnique<ModuleDatabase>(base::SequencedTaskRunnerHandle::Get()));
   auto* module_database = ModuleDatabase::GetInstance();
-  uint32_t process_id = ::GetCurrentProcessId();
 
-  // The ModuleWatcher will immediately start emitting module events, but the
-  // ModuleDatabase expects an OnProcessStarted event prior to that. For child
-  // processes this is handled via the ModuleEventSinkImpl. For the browser
-  // process a manual notification is sent before wiring up the ModuleWatcher.
-  module_database->OnProcessStarted(process_id, creation_time,
-                                    content::PROCESS_TYPE_BROWSER);
-  *module_watcher = ModuleWatcher::Create(
-      base::BindRepeating(&OnModuleEvent, process_id, creation_time));
+  *module_watcher = ModuleWatcher::Create(base::BindRepeating(&OnModuleEvent));
 
   // Enumerate shell extensions and input method editors. It is safe to use
   // base::Unretained() here because the ModuleDatabase is never freed.
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 91fa5427..fc964547 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -2884,14 +2884,14 @@
 
 #if defined(OS_WIN)
   if (base::FeatureList::IsEnabled(features::kModuleDatabase)) {
-    // Add the ModuleDatabase interface. This is the interface used by renderer
-    // processes to notify the browser of modules in their address space. It
-    // ultimately drives the chrome://conflicts UI. The process handle is not
-    // yet available at this point so pass in a callback to allow it to be
-    // retrieved at the time the interface is actually created. It is safe to
-    // pass a raw pointer to |render_process_host|: the callback will be invoked
-    // in the context of ModuleDatabase::GetInstance, which is invoked by Mojo
-    // initialization, which occurs while the |render_process_host| is alive.
+    // Add the ModuleEventSink interface. This is the interface used by renderer
+    // processes to notify the browser of modules in their address space. The
+    // process handle is not yet available at this point so pass in a callback
+    // to allow it to be retrieved at the time the interface is actually
+    // created. It is safe to pass a raw pointer to |render_process_host|: the
+    // callback will be invoked in the context of ModuleDatabase::GetInstance,
+    // which is invoked by Mojo initialization, which occurs while the
+    // |render_process_host| is alive.
     auto get_process = base::Bind(&content::RenderProcessHost::GetHandle,
                                   base::Unretained(render_process_host));
     // The ModuleDatabase is a global singleton so passing an unretained pointer
diff --git a/chrome/browser/chromeos/app_mode/kiosk_external_updater.cc b/chrome/browser/chromeos/app_mode/kiosk_external_updater.cc
index 47d912b..6d05fbf 100644
--- a/chrome/browser/chromeos/app_mode/kiosk_external_updater.cc
+++ b/chrome/browser/chromeos/app_mode/kiosk_external_updater.cc
@@ -180,6 +180,11 @@
     const std::string& device_path) {
 }
 
+void KioskExternalUpdater::OnRenameEvent(
+    disks::DiskMountManager::RenameEvent event,
+    RenameError error_code,
+    const std::string& device_path) {}
+
 void KioskExternalUpdater::OnExtenalUpdateUnpackSuccess(
     const std::string& app_id,
     const std::string& version,
diff --git a/chrome/browser/chromeos/app_mode/kiosk_external_updater.h b/chrome/browser/chromeos/app_mode/kiosk_external_updater.h
index 5342266..9da95233 100644
--- a/chrome/browser/chromeos/app_mode/kiosk_external_updater.h
+++ b/chrome/browser/chromeos/app_mode/kiosk_external_updater.h
@@ -67,6 +67,9 @@
   void OnFormatEvent(disks::DiskMountManager::FormatEvent event,
                      FormatError error_code,
                      const std::string& device_path) override;
+  void OnRenameEvent(disks::DiskMountManager::RenameEvent event,
+                     RenameError error_code,
+                     const std::string& device_path) override;
 
   // KioskExternalUpdateValidatorDelegate overrides:
   void OnExtenalUpdateUnpackSuccess(const std::string& app_id,
diff --git a/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_framework_service.cc b/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_framework_service.cc
index 932fd18d..4d24665 100644
--- a/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_framework_service.cc
+++ b/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_framework_service.cc
@@ -240,8 +240,6 @@
 
 void ArcVoiceInteractionFrameworkService::OnInstanceClosed() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  CallAndResetMetalayerCallback();
-  metalayer_enabled_ = false;
 }
 
 void ArcVoiceInteractionFrameworkService::CaptureFocusedWindow(
@@ -312,39 +310,23 @@
 
 void ArcVoiceInteractionFrameworkService::OnMetalayerClosed() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  CallAndResetMetalayerCallback();
+  LOG(ERROR) << "Deprecated method called: "
+                "VoiceInteractionFrameworkHost.OnInstanceClosed";
 }
 
 void ArcVoiceInteractionFrameworkService::SetMetalayerEnabled(bool enabled) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  metalayer_enabled_ = enabled;
-  if (!metalayer_enabled_)
-    CallAndResetMetalayerCallback();
+  LOG(ERROR) << "Deprecated method called: "
+                "VoiceInteractionFrameworkHost.SetMetalayerEnabled";
 }
 
-bool ArcVoiceInteractionFrameworkService::IsMetalayerSupported() {
+void ArcVoiceInteractionFrameworkService::ShowMetalayer() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  return metalayer_enabled_;
-}
-
-void ArcVoiceInteractionFrameworkService::ShowMetalayer(
-    const base::Closure& closed) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  if (!metalayer_closed_callback_.is_null()) {
-    LOG(ERROR) << "Metalayer is already enabled";
-    return;
-  }
-  metalayer_closed_callback_ = closed;
   NotifyMetalayerStatusChanged(true);
 }
 
 void ArcVoiceInteractionFrameworkService::HideMetalayer() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  if (metalayer_closed_callback_.is_null()) {
-    LOG(ERROR) << "Metalayer is already hidden";
-    return;
-  }
-  metalayer_closed_callback_ = base::Closure();
   NotifyMetalayerStatusChanged(false);
 }
 
@@ -374,6 +356,10 @@
       prefs::kVoiceInteractionEnabled);
   ash::Shell::Get()->NotifyVoiceInteractionEnabled(enabled);
 
+  bool context = ProfileManager::GetActiveUserProfile()->GetPrefs()->GetBoolean(
+      prefs::kVoiceInteractionContextEnabled);
+  ash::Shell::Get()->NotifyVoiceInteractionContextEnabled(context);
+
   // We only want notify the status change on first user signed in.
   session_manager::SessionManager::Get()->RemoveObserver(this);
 }
@@ -401,13 +387,6 @@
   framework_instance->SetMetalayerVisibility(visible);
 }
 
-void ArcVoiceInteractionFrameworkService::CallAndResetMetalayerCallback() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  if (metalayer_closed_callback_.is_null())
-    return;
-  base::ResetAndReturn(&metalayer_closed_callback_).Run();
-}
-
 void ArcVoiceInteractionFrameworkService::SetVoiceInteractionEnabled(
     bool enable) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
@@ -426,6 +405,8 @@
     bool enable) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
+  ash::Shell::Get()->NotifyVoiceInteractionContextEnabled(enable);
+
   PrefService* prefs = Profile::FromBrowserContext(context_)->GetPrefs();
   prefs->SetBoolean(prefs::kVoiceInteractionContextEnabled, enable);
 
diff --git a/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_framework_service.h b/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_framework_service.h
index f9f7969..c429e26 100644
--- a/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_framework_service.h
+++ b/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_framework_service.h
@@ -66,8 +66,7 @@
   void SetVoiceInteractionRunning(bool running) override;
   void SetVoiceInteractionState(ash::VoiceInteractionState state) override;
 
-  bool IsMetalayerSupported();
-  void ShowMetalayer(const base::Closure& closed);
+  void ShowMetalayer();
   void HideMetalayer();
 
   // ArcSessionManager::Observer overrides.
@@ -114,8 +113,6 @@
   static const char kArcServiceName[];
 
  private:
-  void CallAndResetMetalayerCallback();
-
   void NotifyMetalayerStatusChanged(bool visible);
 
   bool InitiateUserInteraction();
@@ -123,8 +120,6 @@
   content::BrowserContext* context_;
   ArcBridgeService* const arc_bridge_service_;  // Owned by ArcServiceManager
   mojo::Binding<mojom::VoiceInteractionFrameworkHost> binding_;
-  base::Closure metalayer_closed_callback_;
-  bool metalayer_enabled_ = false;
 
   // Whether there is a pending request to start voice interaction.
   bool is_request_pending_ = false;
diff --git a/chrome/browser/chromeos/extensions/file_manager/device_event_router.cc b/chrome/browser/chromeos/extensions/file_manager/device_event_router.cc
index 3c4fac88..ad8161ac 100644
--- a/chrome/browser/chromeos/extensions/file_manager/device_event_router.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/device_event_router.cc
@@ -121,6 +121,24 @@
                 device_path);
 }
 
+void DeviceEventRouter::OnRenameStarted(const std::string& device_path,
+                                        bool success) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  OnDeviceEvent(success ? file_manager_private::DEVICE_EVENT_TYPE_RENAME_START
+                        : file_manager_private::DEVICE_EVENT_TYPE_RENAME_FAIL,
+                device_path);
+}
+
+void DeviceEventRouter::OnRenameCompleted(const std::string& device_path,
+                                          bool success) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  OnDeviceEvent(success ? file_manager_private::DEVICE_EVENT_TYPE_RENAME_SUCCESS
+                        : file_manager_private::DEVICE_EVENT_TYPE_RENAME_FAIL,
+                device_path);
+}
+
 void DeviceEventRouter::SuspendImminent() {
   DCHECK(thread_checker_.CalledOnValidThread());
   is_resuming_ = true;
diff --git a/chrome/browser/chromeos/extensions/file_manager/device_event_router.h b/chrome/browser/chromeos/extensions/file_manager/device_event_router.h
index b0493a9d..cd4136d42 100644
--- a/chrome/browser/chromeos/extensions/file_manager/device_event_router.h
+++ b/chrome/browser/chromeos/extensions/file_manager/device_event_router.h
@@ -54,6 +54,8 @@
                          const Volume& volume) override;
   void OnFormatStarted(const std::string& device_path, bool success) override;
   void OnFormatCompleted(const std::string& device_path, bool success) override;
+  void OnRenameStarted(const std::string& device_path, bool success) override;
+  void OnRenameCompleted(const std::string& device_path, bool success) override;
 
   // PowerManagerClient::Observer overrides.
   void SuspendImminent() override;
diff --git a/chrome/browser/chromeos/extensions/file_manager/device_event_router_unittest.cc b/chrome/browser/chromeos/extensions/file_manager/device_event_router_unittest.cc
index 9151083..d098b1ef 100644
--- a/chrome/browser/chromeos/extensions/file_manager/device_event_router_unittest.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/device_event_router_unittest.cc
@@ -70,27 +70,9 @@
   Disk CreateTestDisk(const std::string& device_path,
                       const std::string& mount_path,
                       bool is_read_only_hardware) {
-    return Disk(device_path,
-                mount_path,
-                false,
-                "",
-                "",
-                "",
-                "",
-                "",
-                "",
-                "",
-                "",
-                "",
-                device_path,
-                chromeos::DEVICE_TYPE_UNKNOWN,
-                0,
-                false,
-                is_read_only_hardware,
-                false,
-                false,
-                false,
-                false);
+    return Disk(device_path, mount_path, false, "", "", "", "", "", "", "", "",
+                "", device_path, chromeos::DEVICE_TYPE_UNKNOWN, 0, false,
+                is_read_only_hardware, false, false, false, false, "vfat", "");
   }
 
   std::unique_ptr<DeviceEventRouterImpl> device_event_router;
diff --git a/chrome/browser/chromeos/extensions/file_manager/event_router.cc b/chrome/browser/chromeos/extensions/file_manager/event_router.cc
index 8b6ad37..d9ac66b 100644
--- a/chrome/browser/chromeos/extensions/file_manager/event_router.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/event_router.cc
@@ -1002,6 +1002,18 @@
   // Do nothing.
 }
 
+void EventRouter::OnRenameStarted(const std::string& device_path,
+                                  bool success) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  // Do nothing.
+}
+
+void EventRouter::OnRenameCompleted(const std::string& device_path,
+                                    bool success) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  // Do nothing.
+}
+
 void EventRouter::SetDispatchDirectoryChangeEventImplForTesting(
     const DispatchDirectoryChangeEventImplCallback& callback) {
   dispatch_directory_change_event_impl_ = callback;
diff --git a/chrome/browser/chromeos/extensions/file_manager/event_router.h b/chrome/browser/chromeos/extensions/file_manager/event_router.h
index a6fe75cf..1c1a784 100644
--- a/chrome/browser/chromeos/extensions/file_manager/event_router.h
+++ b/chrome/browser/chromeos/extensions/file_manager/event_router.h
@@ -143,7 +143,8 @@
                          const Volume& volume) override;
   void OnFormatStarted(const std::string& device_path, bool success) override;
   void OnFormatCompleted(const std::string& device_path, bool success) override;
-
+  void OnRenameStarted(const std::string& device_path, bool success) override;
+  void OnRenameCompleted(const std::string& device_path, bool success) override;
   // Set custom dispatch directory change event implementation for testing.
   void SetDispatchDirectoryChangeEventImplForTesting(
       const DispatchDirectoryChangeEventImplCallback& callback);
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_manager_private_apitest.cc b/chrome/browser/chromeos/extensions/file_manager/file_manager_private_apitest.cc
index 3c5c971..c28ef03 100644
--- a/chrome/browser/chromeos/extensions/file_manager/file_manager_private_apitest.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/file_manager_private_apitest.cc
@@ -50,6 +50,8 @@
   bool on_boot_device;
   bool on_removable_device;
   bool is_hidden;
+  const char* file_system_type;
+  const char* base_mount_path;
 };
 
 struct TestMountPoint {
@@ -62,71 +64,69 @@
   int disk_info_index;
 };
 
-TestDiskInfo kTestDisks[] = {
-  {
-    "system_path1",
-    "file_path1",
-    false,
-    "device_label1",
-    "drive_label1",
-    "0123",
-    "vendor1",
-    "abcd",
-    "product1",
-    "FFFF-FFFF",
-    "system_path_prefix1",
-    chromeos::DEVICE_TYPE_USB,
-    1073741824,
-    false,
-    false,
-    false,
-    false,
-    false,
-    false
-  },
-  {
-    "system_path2",
-    "file_path2",
-    false,
-    "device_label2",
-    "drive_label2",
-    "4567",
-    "vendor2",
-    "cdef",
-    "product2",
-    "0FFF-FFFF",
-    "system_path_prefix2",
-    chromeos::DEVICE_TYPE_MOBILE,
-    47723,
-    true,
-    true,
-    true,
-    true,
-    false,
-    false
-  },
-  {
-    "system_path3",
-    "file_path3",
-    true,  // write_disabled_by_policy
-    "device_label3",
-    "drive_label3",
-    "89ab",
-    "vendor3",
-    "ef01",
-    "product3",
-    "00FF-FFFF",
-    "system_path_prefix3",
-    chromeos::DEVICE_TYPE_OPTICAL_DISC,
-    0,
-    true,
-    false,  // is_hardware_read_only
-    false,
-    true,
-    false,
-    false
-  }
-};
+TestDiskInfo kTestDisks[] = {{"system_path1",
+                              "file_path1",
+                              false,
+                              "device_label1",
+                              "drive_label1",
+                              "0123",
+                              "vendor1",
+                              "abcd",
+                              "product1",
+                              "FFFF-FFFF",
+                              "system_path_prefix1",
+                              chromeos::DEVICE_TYPE_USB,
+                              1073741824,
+                              false,
+                              false,
+                              false,
+                              false,
+                              false,
+                              false,
+                              "exfat",
+                              ""},
+                             {"system_path2",
+                              "file_path2",
+                              false,
+                              "device_label2",
+                              "drive_label2",
+                              "4567",
+                              "vendor2",
+                              "cdef",
+                              "product2",
+                              "0FFF-FFFF",
+                              "system_path_prefix2",
+                              chromeos::DEVICE_TYPE_MOBILE,
+                              47723,
+                              true,
+                              true,
+                              true,
+                              true,
+                              false,
+                              false,
+                              "exfat",
+                              ""},
+                             {"system_path3",
+                              "file_path3",
+                              true,  // write_disabled_by_policy
+                              "device_label3",
+                              "drive_label3",
+                              "89ab",
+                              "vendor3",
+                              "ef01",
+                              "product3",
+                              "00FF-FFFF",
+                              "system_path_prefix3",
+                              chromeos::DEVICE_TYPE_OPTICAL_DISC,
+                              0,
+                              true,
+                              false,  // is_hardware_read_only
+                              false,
+                              true,
+                              false,
+                              false,
+                              "exfat",
+                              ""}};
 
 void DispatchDirectoryChangeEventImpl(
     int* counter,
@@ -292,7 +292,9 @@
                 kTestDisks[disk_info_index].has_media,
                 kTestDisks[disk_info_index].on_boot_device,
                 kTestDisks[disk_info_index].on_removable_device,
-                kTestDisks[disk_info_index].is_hidden)));
+                kTestDisks[disk_info_index].is_hidden,
+                kTestDisks[disk_info_index].file_system_type,
+                kTestDisks[disk_info_index].base_mount_path)));
       }
     }
   }
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.cc
index 1cf2498..fe52d25b 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.cc
@@ -625,10 +625,8 @@
   if (!volume)
     return false;
 
-  // TODO(klemenko): Uncomment the code below when RenameMountedDevice is
-  // implemented
-  /*DiskMountManager::GetInstance()->RenameMountedDevice(
-    volume->mount_path().AsUTF8Unsafe(), params->new_name);*/
+  DiskMountManager::GetInstance()->RenameMountedDevice(
+      volume->mount_path().AsUTF8Unsafe(), params->new_name);
   SendResponse(true);
   return true;
 }
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_util.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_util.cc
index 2358ebb6..ff684ff 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_util.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_util.cc
@@ -219,6 +219,8 @@
   }
 
   volume_metadata->volume_label.reset(new std::string(volume.volume_label()));
+  volume_metadata->disk_file_system_type.reset(
+      new std::string(volume.file_system_type()));
 
   switch (volume.type()) {
     case VOLUME_TYPE_GOOGLE_DRIVE:
diff --git a/chrome/browser/chromeos/file_manager/fake_disk_mount_manager.cc b/chrome/browser/chromeos/file_manager/fake_disk_mount_manager.cc
index 8f7fb4d..664bad8 100644
--- a/chrome/browser/chromeos/file_manager/fake_disk_mount_manager.cc
+++ b/chrome/browser/chromeos/file_manager/fake_disk_mount_manager.cc
@@ -131,6 +131,10 @@
 void FakeDiskMountManager::FormatMountedDevice(const std::string& mount_path) {
 }
 
+void FakeDiskMountManager::RenameMountedDevice(const std::string& mount_path,
+                                               const std::string& volume_name) {
+}
+
 void FakeDiskMountManager::UnmountDeviceRecursively(
     const std::string& device_path,
     const UnmountDeviceRecursivelyCallbackType& callback) {
diff --git a/chrome/browser/chromeos/file_manager/fake_disk_mount_manager.h b/chrome/browser/chromeos/file_manager/fake_disk_mount_manager.h
index 3f1e99a..d06fead 100644
--- a/chrome/browser/chromeos/file_manager/fake_disk_mount_manager.h
+++ b/chrome/browser/chromeos/file_manager/fake_disk_mount_manager.h
@@ -90,6 +90,8 @@
   void RemountAllRemovableDrives(
       chromeos::MountAccessMode access_mode) override;
   void FormatMountedDevice(const std::string& mount_path) override;
+  void RenameMountedDevice(const std::string& mount_path,
+                           const std::string& volume_name) override;
   void UnmountDeviceRecursively(
       const std::string& device_path,
       const UnmountDeviceRecursivelyCallbackType& callback) override;
diff --git a/chrome/browser/chromeos/file_manager/volume_manager.cc b/chrome/browser/chromeos/file_manager/volume_manager.cc
index 3dbdf66..5984cf5 100644
--- a/chrome/browser/chromeos/file_manager/volume_manager.cc
+++ b/chrome/browser/chromeos/file_manager/volume_manager.cc
@@ -209,8 +209,10 @@
                         : SOURCE_DEVICE;
   volume->mount_path_ = base::FilePath(mount_point.mount_path);
   volume->mount_condition_ = mount_point.mount_condition;
-  volume->volume_label_ = volume->mount_path().BaseName().AsUTF8Unsafe();
+
   if (disk) {
+    volume->file_system_type_ = disk->file_system_type();
+    volume->volume_label_ = disk->device_label();
     volume->device_type_ = disk->device_type();
     volume->system_path_prefix_ = base::FilePath(disk->system_path_prefix());
     volume->is_parent_ = disk->is_parent();
@@ -218,6 +220,7 @@
     volume->is_read_only_removable_device_ = disk->is_read_only_hardware();
     volume->has_media_ = disk->has_media();
   } else {
+    volume->volume_label_ = volume->mount_path().BaseName().AsUTF8Unsafe();
     volume->device_type_ = chromeos::DEVICE_TYPE_UNKNOWN;
     volume->is_read_only_ =
         (mount_point.mount_type == chromeos::MOUNT_TYPE_ARCHIVE);
@@ -684,6 +687,56 @@
   NOTREACHED();
 }
 
+void VolumeManager::OnRenameEvent(
+    chromeos::disks::DiskMountManager::RenameEvent event,
+    chromeos::RenameError error_code,
+    const std::string& device_path) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  DVLOG(1) << "OnDeviceEvent: " << event << ", " << error_code << ", "
+           << device_path;
+
+  switch (event) {
+    case chromeos::disks::DiskMountManager::RENAME_STARTED:
+      for (auto& observer : observers_) {
+        observer.OnRenameStarted(device_path,
+                                 error_code == chromeos::RENAME_ERROR_NONE);
+      }
+      return;
+    case chromeos::disks::DiskMountManager::RENAME_COMPLETED:
+      if (error_code != chromeos::RENAME_ERROR_NONE) {
+        for (auto& observer : observers_)
+          observer.OnRenameCompleted(device_path, false);
+
+        return;
+      }
+
+      // Find previous mount point label if it exists
+      std::string mount_label = "";
+      auto disk_map_iter = disk_mount_manager_->disks().find(device_path);
+      if (disk_map_iter != disk_mount_manager_->disks().end() &&
+          !disk_map_iter->second->base_mount_path().empty()) {
+        mount_label = base::FilePath(disk_map_iter->second->base_mount_path())
+                          .BaseName()
+                          .AsUTF8Unsafe();
+      }
+
+      // If rename is completed successfully, try to mount the device.
+      // MountPath auto-detects filesystem format if second argument is
+      // empty. Third argument is a mount point name of the disk when it was
+      // first time mounted (to preserve mount point regardless of the volume
+      // name).
+      disk_mount_manager_->MountPath(device_path, std::string(), mount_label,
+                                     chromeos::MOUNT_TYPE_DEVICE,
+                                     GetExternalStorageAccessMode(profile_));
+
+      for (auto& observer : observers_)
+        observer.OnRenameCompleted(device_path, true);
+
+      return;
+  }
+  NOTREACHED();
+}
+
 void VolumeManager::OnProvidedFileSystemMount(
     const chromeos::file_system_provider::ProvidedFileSystemInfo&
         file_system_info,
diff --git a/chrome/browser/chromeos/file_manager/volume_manager.h b/chrome/browser/chromeos/file_manager/volume_manager.h
index 1658d149..b7420ba 100644
--- a/chrome/browser/chromeos/file_manager/volume_manager.h
+++ b/chrome/browser/chromeos/file_manager/volume_manager.h
@@ -136,6 +136,7 @@
   bool has_media() const { return has_media_; }
   bool configurable() const { return configurable_; }
   bool watchable() const { return watchable_; }
+  const std::string& file_system_type() const { return file_system_type_; }
 
  private:
   Volume();
@@ -207,6 +208,9 @@
   // True if the volume notifies about changes via file/directory watchers.
   bool watchable_;
 
+  // Identifier for the file system type
+  std::string file_system_type_;
+
   DISALLOW_COPY_AND_ASSIGN(Volume);
 };
 
@@ -292,6 +296,9 @@
   void OnFormatEvent(chromeos::disks::DiskMountManager::FormatEvent event,
                      chromeos::FormatError error_code,
                      const std::string& device_path) override;
+  void OnRenameEvent(chromeos::disks::DiskMountManager::RenameEvent event,
+                     chromeos::RenameError error_code,
+                     const std::string& device_path) override;
 
   // chromeos::file_system_provider::Observer overrides.
   void OnProvidedFileSystemMount(
diff --git a/chrome/browser/chromeos/file_manager/volume_manager_observer.h b/chrome/browser/chromeos/file_manager/volume_manager_observer.h
index 120bdcf..4aa1ad02 100644
--- a/chrome/browser/chromeos/file_manager/volume_manager_observer.h
+++ b/chrome/browser/chromeos/file_manager/volume_manager_observer.h
@@ -48,6 +48,14 @@
   // Fired when formatting a device is completed (or terminated on error).
   virtual void OnFormatCompleted(
       const std::string& device_path, bool success) = 0;
+
+  // Fired when renaming a device is started (or failed to start).
+  virtual void OnRenameStarted(const std::string& device_path,
+                               bool success) = 0;
+
+  // Fired when renaming a device is completed (or terminated on error).
+  virtual void OnRenameCompleted(const std::string& device_path,
+                                 bool success) = 0;
 };
 
 }  // namespace file_manager
diff --git a/chrome/browser/chromeos/file_manager/volume_manager_unittest.cc b/chrome/browser/chromeos/file_manager/volume_manager_unittest.cc
index bff1e080..b8e0852 100644
--- a/chrome/browser/chromeos/file_manager/volume_manager_unittest.cc
+++ b/chrome/browser/chromeos/file_manager/volume_manager_unittest.cc
@@ -43,6 +43,8 @@
       VOLUME_UNMOUNTED,
       FORMAT_STARTED,
       FORMAT_COMPLETED,
+      RENAME_STARTED,
+      RENAME_COMPLETED
     } type;
 
     // Available on DEVICE_ADDED, DEVICE_REMOVED, VOLUME_MOUNTED,
@@ -131,6 +133,23 @@
     events_.push_back(event);
   }
 
+  void OnRenameStarted(const std::string& device_path, bool success) override {
+    Event event;
+    event.type = Event::RENAME_STARTED;
+    event.device_path = device_path;
+    event.success = success;
+    events_.push_back(event);
+  }
+
+  void OnRenameCompleted(const std::string& device_path,
+                         bool success) override {
+    Event event;
+    event.type = Event::RENAME_COMPLETED;
+    event.device_path = device_path;
+    event.success = success;
+    events_.push_back(event);
+  }
+
  private:
   std::vector<Event> events_;
 
@@ -243,7 +262,7 @@
   const chromeos::disks::DiskMountManager::Disk kDisk(
       "device1", "", false, "", "", "", "", "", "", "", "", "", "",
       chromeos::DEVICE_TYPE_UNKNOWN, 0, false, false, false, false, false,
-      kIsHidden);
+      kIsHidden, "", "");
 
   volume_manager()->OnDiskEvent(
       chromeos::disks::DiskMountManager::DISK_ADDED, &kDisk);
@@ -271,7 +290,7 @@
       "",  // empty device path.
       "", false, "", "", "", "", "", "", "", "", "", "",
       chromeos::DEVICE_TYPE_UNKNOWN, 0, false, false, false, false, false,
-      false);
+      false, "", "");
   volume_manager()->OnDiskEvent(
       chromeos::disks::DiskMountManager::DISK_ADDED, &kEmptyDevicePathDisk);
   EXPECT_EQ(0U, observer.events().size());
@@ -280,7 +299,7 @@
   const chromeos::disks::DiskMountManager::Disk kMediaDisk(
       "device1", "", false, "", "", "", "", "", "", "", "", "", "",
       chromeos::DEVICE_TYPE_UNKNOWN, 0, false, false, kHasMedia, false, false,
-      false);
+      false, "", "");
   volume_manager()->OnDiskEvent(
       chromeos::disks::DiskMountManager::DISK_ADDED, &kMediaDisk);
   ASSERT_EQ(1U, observer.events().size());
@@ -312,8 +331,8 @@
     const bool kHasMedia = true;
     const chromeos::disks::DiskMountManager::Disk kMountedMediaDisk(
         "device1", "mounted", false, "", "", "", "", "", "", "", "", "", "",
-        chromeos::DEVICE_TYPE_UNKNOWN, 0, false, false,
-        kHasMedia, false, false, false);
+        chromeos::DEVICE_TYPE_UNKNOWN, 0, false, false, kHasMedia, false, false,
+        false, "", "");
     volume_manager()->OnDiskEvent(
         chromeos::disks::DiskMountManager::DISK_ADDED, &kMountedMediaDisk);
     ASSERT_EQ(1U, observer.events().size());
@@ -335,8 +354,8 @@
     const bool kWithoutMedia = false;
     const chromeos::disks::DiskMountManager::Disk kNoMediaDisk(
         "device1", "", false, "", "", "", "", "", "", "", "", "", "",
-        chromeos::DEVICE_TYPE_UNKNOWN, 0, false, false,
-        kWithoutMedia, false, false, false);
+        chromeos::DEVICE_TYPE_UNKNOWN, 0, false, false, kWithoutMedia, false,
+        false, false, "", "");
     volume_manager()->OnDiskEvent(
         chromeos::disks::DiskMountManager::DISK_ADDED, &kNoMediaDisk);
     ASSERT_EQ(1U, observer.events().size());
@@ -360,8 +379,8 @@
     const bool kHasMedia = true;
     const chromeos::disks::DiskMountManager::Disk kMediaDisk(
         "device1", "", false, "", "", "", "", "", "", "", "", "", "",
-        chromeos::DEVICE_TYPE_UNKNOWN, 0, false, false,
-        kHasMedia, false, false, false);
+        chromeos::DEVICE_TYPE_UNKNOWN, 0, false, false, kHasMedia, false, false,
+        false, "", "");
     volume_manager()->OnDiskEvent(
         chromeos::disks::DiskMountManager::DISK_ADDED, &kMediaDisk);
     ASSERT_EQ(1U, observer.events().size());
@@ -383,7 +402,7 @@
   const chromeos::disks::DiskMountManager::Disk kMountedDisk(
       "device1", "mount_path", false, "", "", "", "", "", "", "", "", "", "",
       chromeos::DEVICE_TYPE_UNKNOWN, 0, false, false, false, false, false,
-      false);
+      false, "", "");
   volume_manager()->OnDiskEvent(
       chromeos::disks::DiskMountManager::DISK_REMOVED, &kMountedDisk);
 
@@ -408,7 +427,7 @@
   const chromeos::disks::DiskMountManager::Disk kNotMountedDisk(
       "device1", "", false, "", "", "", "", "", "", "", "", "", "",
       chromeos::DEVICE_TYPE_UNKNOWN, 0, false, false, false, false, false,
-      false);
+      false, "", "");
   volume_manager()->OnDiskEvent(
       chromeos::disks::DiskMountManager::DISK_REMOVED, &kNotMountedDisk);
 
@@ -429,8 +448,8 @@
 
   const chromeos::disks::DiskMountManager::Disk kDisk(
       "device1", "", false, "", "", "", "", "", "", "", "", "", "",
-      chromeos::DEVICE_TYPE_UNKNOWN, 0, false, false, true, false, false,
-      false);
+      chromeos::DEVICE_TYPE_UNKNOWN, 0, false, false, true, false, false, false,
+      "", "");
   volume_manager()->OnDiskEvent(
       chromeos::disks::DiskMountManager::DISK_CHANGED, &kDisk);
 
@@ -453,8 +472,8 @@
 
   const chromeos::disks::DiskMountManager::Disk kDisk(
       "device1", "", false, "", "", "", "", "", "", "", "", "", "",
-      chromeos::DEVICE_TYPE_UNKNOWN, 0, false, false, true, false, false,
-      false);
+      chromeos::DEVICE_TYPE_UNKNOWN, 0, false, false, true, false, false, false,
+      "", "");
   volume_manager()->OnDiskEvent(chromeos::disks::DiskMountManager::DISK_CHANGED,
                                 &kDisk);
 
@@ -549,7 +568,7 @@
       new chromeos::disks::DiskMountManager::Disk(
           "device1", "", false, "", "", "", "", "", "", "", "", "uuid1", "",
           chromeos::DEVICE_TYPE_UNKNOWN, 0, false, false, false, false, false,
-          false));
+          false, "", ""));
   disk_mount_manager_->AddDiskForTest(std::move(disk));
   disk_mount_manager_->MountPath("device1", "", "", chromeos::MOUNT_TYPE_DEVICE,
                                  chromeos::MOUNT_ACCESS_MODE_READ_WRITE);
@@ -758,8 +777,8 @@
   // Add 1 disk.
   const chromeos::disks::DiskMountManager::Disk kMediaDisk(
       "device1", "", false, "", "", "", "", "", "", "", "", "", "",
-      chromeos::DEVICE_TYPE_UNKNOWN, 0, false, false, true, false, false,
-      false);
+      chromeos::DEVICE_TYPE_UNKNOWN, 0, false, false, true, false, false, false,
+      "", "");
   volume_manager()->OnDiskEvent(
       chromeos::disks::DiskMountManager::DISK_ADDED, &kMediaDisk);
   secondary.volume_manager()->OnDiskEvent(
diff --git a/chrome/browser/chromeos/printing/synced_printers_manager.cc b/chrome/browser/chromeos/printing/synced_printers_manager.cc
index 23b482e..e9286ac 100644
--- a/chrome/browser/chromeos/printing/synced_printers_manager.cc
+++ b/chrome/browser/chromeos/printing/synced_printers_manager.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 #include <string>
+#include <unordered_map>
 #include <utility>
 #include <vector>
 
@@ -13,7 +14,9 @@
 #include "base/json/json_reader.h"
 #include "base/md5.h"
 #include "base/memory/ptr_util.h"
+#include "base/observer_list_threadsafe.h"
 #include "base/optional.h"
+#include "base/synchronization/lock.h"
 #include "base/values.h"
 #include "chrome/browser/chromeos/printing/printer_configurer.h"
 #include "chrome/browser/chromeos/printing/printers_sync_bridge.h"
@@ -33,7 +36,9 @@
  public:
   SyncedPrintersManagerImpl(Profile* profile,
                             std::unique_ptr<PrintersSyncBridge> sync_bridge)
-      : profile_(profile), sync_bridge_(std::move(sync_bridge)) {
+      : profile_(profile),
+        sync_bridge_(std::move(sync_bridge)),
+        observers_(new base::ObserverListThreadSafe<Observer>()) {
     pref_change_registrar_.Init(profile->GetPrefs());
     pref_change_registrar_.Add(
         prefs::kRecommendedNativePrinters,
@@ -44,6 +49,8 @@
   ~SyncedPrintersManagerImpl() override = default;
 
   std::vector<Printer> GetConfiguredPrinters() const override {
+    // No need to lock here, since sync_bridge_ is thread safe and we don't
+    // touch anything else.
     std::vector<Printer> printers;
     std::vector<sync_pb::PrinterSpecifics> values =
         sync_bridge_->GetAllPrinters();
@@ -54,82 +61,46 @@
   }
 
   std::vector<Printer> GetEnterprisePrinters() const override {
-    std::vector<Printer> printers;
-    for (const std::string& key : enterprise_printer_ids_) {
-      auto printer = enterprise_printers_.find(key);
-      if (printer != enterprise_printers_.end()) {
-        printers.push_back(*printer->second);
-      }
-    }
-    return printers;
+    base::AutoLock l(lock_);
+    return GetEnterprisePrintersLocked();
   }
 
   std::unique_ptr<Printer> GetPrinter(
       const std::string& printer_id) const override {
-    // check for a policy printer first
-    const auto& policy_printers = enterprise_printers_;
-    auto found = policy_printers.find(printer_id);
-    if (found != policy_printers.end()) {
-      // Copy a printer.
-      return base::MakeUnique<Printer>(*(found->second));
-    }
-
-    base::Optional<sync_pb::PrinterSpecifics> printer =
-        sync_bridge_->GetPrinter(printer_id);
-    return printer.has_value() ? SpecificsToPrinter(*printer) : nullptr;
+    base::AutoLock l(lock_);
+    return GetPrinterLocked(printer_id);
   }
 
-  void UpdateConfiguredPrinter(const Printer& printer_arg) override {
-    // Need a local copy since we may set the id.
-    Printer printer = printer_arg;
-    if (printer.id().empty()) {
-      printer.set_id(base::GenerateGUID());
-    }
-
-    DCHECK_EQ(Printer::SRC_USER_PREFS, printer.source());
-    sync_bridge_->UpdatePrinter(PrinterToSpecifics(printer));
-
-    NotifyConfiguredObservers();
+  void UpdateConfiguredPrinter(const Printer& printer) override {
+    base::AutoLock l(lock_);
+    UpdateConfiguredPrinterLocked(printer);
   }
 
   bool RemoveConfiguredPrinter(const std::string& printer_id) override {
-    DCHECK(!printer_id.empty());
-
-    base::Optional<sync_pb::PrinterSpecifics> printer =
-        sync_bridge_->GetPrinter(printer_id);
-    bool success = false;
-    if (printer.has_value()) {
-      std::unique_ptr<Printer> p = SpecificsToPrinter(*printer);
-      success = sync_bridge_->RemovePrinter(p->id());
-      if (success) {
-        NotifyConfiguredObservers();
-      }
-    } else {
-      LOG(WARNING) << "Could not find printer" << printer_id;
-    }
-
-    return success;
+    return sync_bridge_->RemovePrinter(printer_id);
   }
 
   void AddObserver(Observer* observer) override {
-    observers_.AddObserver(observer);
+    observers_->AddObserver(observer);
   }
 
   void RemoveObserver(Observer* observer) override {
-    observers_.RemoveObserver(observer);
+    observers_->RemoveObserver(observer);
   }
 
   void PrinterInstalled(const Printer& printer) override {
+    base::AutoLock l(lock_);
     installed_printer_fingerprints_[printer.id()] =
         PrinterConfigurer::SetupFingerprint(printer);
 
     // Register this printer if it's the first time we're using it.
-    if (GetPrinter(printer.id()) == nullptr) {
-      UpdateConfiguredPrinter(printer);
+    if (GetPrinterLocked(printer.id()) == nullptr) {
+      UpdateConfiguredPrinterLocked(printer);
     }
   }
 
   bool IsConfigurationCurrent(const Printer& printer) const override {
+    base::AutoLock l(lock_);
     auto found = installed_printer_fingerprints_.find(printer.id());
     if (found == installed_printer_fingerprints_.end())
       return false;
@@ -140,17 +111,50 @@
   PrintersSyncBridge* GetSyncBridge() override { return sync_bridge_.get(); }
 
  private:
-  // This method is not thread safe and could interact poorly with readers of
-  // |enterprise_printers_|.
+  std::unique_ptr<Printer> GetPrinterLocked(
+      const std::string& printer_id) const {
+    lock_.AssertAcquired();
+    // check for a policy printer first
+    auto found = enterprise_printers_.find(printer_id);
+    if (found != enterprise_printers_.end()) {
+      // Copy a printer.
+      return base::MakeUnique<Printer>(found->second);
+    }
+
+    base::Optional<sync_pb::PrinterSpecifics> printer =
+        sync_bridge_->GetPrinter(printer_id);
+    return printer.has_value() ? SpecificsToPrinter(*printer) : nullptr;
+  }
+
+  void UpdateConfiguredPrinterLocked(const Printer& printer_arg) {
+    lock_.AssertAcquired();
+    DCHECK_EQ(Printer::SRC_USER_PREFS, printer_arg.source());
+
+    // Need a local copy since we may set the id.
+    Printer printer = printer_arg;
+    if (printer.id().empty()) {
+      printer.set_id(base::GenerateGUID());
+    }
+
+    sync_bridge_->UpdatePrinter(PrinterToSpecifics(printer));
+    observers_->Notify(
+        FROM_HERE,
+        &SyncedPrintersManager::Observer::OnConfiguredPrintersChanged,
+        GetConfiguredPrinters());
+  }
+
   void UpdateRecommendedPrinters() {
     const PrefService* prefs = profile_->GetPrefs();
 
     const base::ListValue* values =
         prefs->GetList(prefs::kRecommendedNativePrinters);
 
-    // Parse the policy JSON into new structures.
+    // Parse the policy JSON into new structures outside the lock.
     std::vector<std::string> new_ids;
-    std::map<std::string, std::unique_ptr<Printer>> new_printers;
+    std::unordered_map<std::string, Printer> new_printers;
+
+    new_ids.reserve(values->GetList().size());
+    new_printers.reserve(values->GetList().size());
     for (const auto& value : *values) {
       std::string printer_json;
       if (!value.GetAsString(&printer_json)) {
@@ -169,8 +173,8 @@
       }
 
       // Policy printers don't have id's but the ids only need to be locally
-      // unique so we'll hash the record.  This will not collide with the UUIDs
-      // generated for user entries.
+      // unique so we'll hash the record.  This will not collide with the
+      // UUIDs generated for user entries.
       std::string id = base::MD5String(printer_json);
       printer_dictionary->SetString(kPrinterId, id);
 
@@ -179,59 +183,51 @@
         LOG(WARNING) << "Duplicate printer ignored.";
         continue;
       }
-
       new_ids.push_back(id);
-      // Move existing printers, create othewise.
-      auto old = enterprise_printers_.find(id);
-      if (old != enterprise_printers_.end()) {
-        new_printers[id] = std::move(old->second);
-      } else {
-        auto printer = RecommendedPrinterToPrinter(*printer_dictionary);
-        printer->set_source(Printer::SRC_POLICY);
-
-        new_printers[id] = std::move(printer);
-      }
+      new_printers.insert(
+          {id, *RecommendedPrinterToPrinter(*printer_dictionary)});
     }
 
-    // Objects not in the most recent update get deallocated after method exit.
+    // Objects not in the most recent update get deallocated after method
+    // exit.
+    base::AutoLock l(lock_);
     enterprise_printer_ids_.swap(new_ids);
     enterprise_printers_.swap(new_printers);
-    NotifyEnterpriseObservers();
+    observers_->Notify(
+        FROM_HERE,
+        &SyncedPrintersManager::Observer::OnEnterprisePrintersChanged,
+        GetEnterprisePrintersLocked());
   }
 
-  // Notify observers of a change in the set of Configured printers.
-  void NotifyConfiguredObservers() {
-    std::vector<Printer> printers = GetConfiguredPrinters();
-    for (Observer& obs : observers_) {
-      obs.OnConfiguredPrintersChanged(printers);
+  std::vector<Printer> GetEnterprisePrintersLocked() const {
+    lock_.AssertAcquired();
+    std::vector<Printer> ret;
+    ret.reserve(enterprise_printers_.size());
+    for (const std::string& id : enterprise_printer_ids_) {
+      ret.push_back(enterprise_printers_.find(id)->second);
     }
+    return ret;
   }
 
-  // Notify observers of a change in the set of Enterprise printers.
-  void NotifyEnterpriseObservers() {
-    std::vector<Printer> printers = GetEnterprisePrinters();
-    for (Observer& obs : observers_) {
-      obs.OnEnterprisePrintersChanged(printers);
-    }
-  }
+  mutable base::Lock lock_;
 
- private:
   Profile* profile_;
   PrefChangeRegistrar pref_change_registrar_;
 
   // The backend for profile printers.
   std::unique_ptr<PrintersSyncBridge> sync_bridge_;
 
-  // Contains the keys for all enterprise printers in order so we can return
-  // the list of enterprise printers in the order they were received.
+  // Enterprise printers as of the last time we got a policy update.  The ids
+  // vector is used to preserve the received ordering.
   std::vector<std::string> enterprise_printer_ids_;
-  std::map<std::string, std::unique_ptr<Printer>> enterprise_printers_;
+  // Map is from id to printer.
+  std::unordered_map<std::string, Printer> enterprise_printers_;
 
   // Map of printer ids to PrinterConfigurer setup fingerprints at the time
   // the printers was last installed with CUPS.
   std::map<std::string, std::string> installed_printer_fingerprints_;
 
-  base::ObserverList<Observer> observers_;
+  scoped_refptr<base::ObserverListThreadSafe<Observer>> observers_;
 };
 
 }  // namespace
diff --git a/chrome/browser/chromeos/printing/synced_printers_manager.h b/chrome/browser/chromeos/printing/synced_printers_manager.h
index e3660c5e..2ec83c2 100644
--- a/chrome/browser/chromeos/printing/synced_printers_manager.h
+++ b/chrome/browser/chromeos/printing/synced_printers_manager.h
@@ -30,6 +30,8 @@
 // and ENTERPRISE).  Provides an interface to a user's printers and
 // printers provided by policy.  User printers are backed by the
 // PrintersSyncBridge.
+//
+// This class is thread-safe.
 class SyncedPrintersManager : public KeyedService {
  public:
   class Observer {
diff --git a/chrome/browser/chromeos/printing/synced_printers_manager_unittest.cc b/chrome/browser/chromeos/printing/synced_printers_manager_unittest.cc
index efb2bc2..cc14059 100644
--- a/chrome/browser/chromeos/printing/synced_printers_manager_unittest.cc
+++ b/chrome/browser/chromeos/printing/synced_printers_manager_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/optional.h"
 #include "base/run_loop.h"
+#include "base/scoped_observer.h"
 #include "base/strings/string_util.h"
 #include "base/time/time.h"
 #include "chrome/browser/chromeos/printing/printers_sync_bridge.h"
@@ -61,6 +62,10 @@
 // Helper class to record observed events.
 class LoggingObserver : public SyncedPrintersManager::Observer {
  public:
+  explicit LoggingObserver(SyncedPrintersManager* source) : observer_(this) {
+    observer_.Add(source);
+  }
+
   void OnConfiguredPrintersChanged(
       const std::vector<Printer>& printers) override {
     configured_printers_ = printers;
@@ -81,6 +86,8 @@
  private:
   std::vector<Printer> configured_printers_;
   std::vector<Printer> enterprise_printers_;
+  ScopedObserver<SyncedPrintersManager, SyncedPrintersManager::Observer>
+      observer_;
 };
 
 class SyncedPrintersManagerTest : public testing::Test {
@@ -107,8 +114,11 @@
 
 // Add a test failure if the ids of printers are not those in expected.  Order
 // is not considered.
-void ExpectPrinterIdsAre(const std::vector<Printer>& printers,
-                         std::vector<std::string> expected_ids) {
+void ExpectObservedPrinterIdsAre(const std::vector<Printer>& printers,
+                                 std::vector<std::string> expected_ids) {
+  // Ensure all callbacks have completed before we check.
+  base::RunLoop().RunUntilIdle();
+
   std::sort(expected_ids.begin(), expected_ids.end());
   std::vector<std::string> printer_ids;
   for (const Printer& printer : printers) {
@@ -123,8 +133,7 @@
 }
 
 TEST_F(SyncedPrintersManagerTest, AddPrinter) {
-  LoggingObserver observer;
-  manager_->AddObserver(&observer);
+  LoggingObserver observer(manager_.get());
   manager_->UpdateConfiguredPrinter(Printer(kTestPrinterId));
 
   auto printers = manager_->GetConfiguredPrinters();
@@ -132,7 +141,7 @@
   EXPECT_EQ(kTestPrinterId, printers[0].id());
   EXPECT_EQ(Printer::Source::SRC_USER_PREFS, printers[0].source());
 
-  ExpectPrinterIdsAre(observer.configured_printers(), {kTestPrinterId});
+  ExpectObservedPrinterIdsAre(observer.configured_printers(), {kTestPrinterId});
 }
 
 TEST_F(SyncedPrintersManagerTest, UpdatePrinterAssignsId) {
@@ -149,8 +158,7 @@
   updated_printer.set_uri(kTestUri);
 
   // Register observer so it only receives the update event.
-  LoggingObserver observer;
-  manager_->AddObserver(&observer);
+  LoggingObserver observer(manager_.get());
 
   manager_->UpdateConfiguredPrinter(updated_printer);
 
@@ -158,7 +166,7 @@
   ASSERT_EQ(1U, printers.size());
   EXPECT_EQ(kTestUri, printers[0].uri());
 
-  ExpectPrinterIdsAre(observer.configured_printers(), {kTestPrinterId});
+  ExpectObservedPrinterIdsAre(observer.configured_printers(), {kTestPrinterId});
 }
 
 TEST_F(SyncedPrintersManagerTest, RemovePrinter) {
@@ -252,7 +260,9 @@
   // Installing the configured printer should *not* update it.
   configured.set_display_name("display name");
   manager_->PrinterInstalled(configured);
-  EXPECT_TRUE(manager_->GetPrinter(kTestPrinterId)->display_name().empty());
+  auto found_printer = manager_->GetPrinter(kTestPrinterId);
+  ASSERT_FALSE(found_printer == nullptr);
+  EXPECT_TRUE(found_printer->display_name().empty());
 
   // Installing the enterprise printer should *not* generate a configuration
   // update.
diff --git a/chrome/browser/conflicts/module_database_win.cc b/chrome/browser/conflicts/module_database_win.cc
index 980281e..542d568 100644
--- a/chrome/browser/conflicts/module_database_win.cc
+++ b/chrome/browser/conflicts/module_database_win.cc
@@ -4,10 +4,12 @@
 
 #include "chrome/browser/conflicts/module_database_win.h"
 
-#include <algorithm>
 #include <tuple>
+#include <utility>
 
 #include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/location.h"
 #include "chrome/browser/conflicts/module_database_observer_win.h"
 
 namespace {
@@ -69,13 +71,6 @@
          !idle_timer_.IsRunning() && module_inspector_.IsIdle();
 }
 
-void ModuleDatabase::OnProcessStarted(uint32_t process_id,
-                                      uint64_t creation_time,
-                                      content::ProcessType process_type) {
-  DCHECK(task_runner_->RunsTasksInCurrentSequence());
-  CreateProcessInfo(process_id, creation_time, process_type);
-}
-
 void ModuleDatabase::OnShellExtensionEnumerated(const base::FilePath& path,
                                                 uint32_t size_of_image,
                                                 uint32_t time_date_stamp) {
@@ -120,8 +115,7 @@
     OnRegisteredModulesEnumerated();
 }
 
-void ModuleDatabase::OnModuleLoad(uint32_t process_id,
-                                  uint64_t creation_time,
+void ModuleDatabase::OnModuleLoad(content::ProcessType process_type,
                                   const base::FilePath& module_path,
                                   uint32_t module_size,
                                   uint32_t module_time_date_stamp,
@@ -130,95 +124,23 @@
   // anywhere at all for calls from ModuleWatcher), so bounce if necessary.
   if (!task_runner_->RunsTasksInCurrentSequence()) {
     task_runner_->PostTask(
-        FROM_HERE, base::Bind(&ModuleDatabase::OnModuleLoad,
-                              weak_ptr_factory_.GetWeakPtr(), process_id,
-                              creation_time, module_path, module_size,
-                              module_time_date_stamp, module_load_address));
+        FROM_HERE,
+        base::Bind(&ModuleDatabase::OnModuleLoad,
+                   weak_ptr_factory_.GetWeakPtr(), process_type, module_path,
+                   module_size, module_time_date_stamp, module_load_address));
     return;
   }
 
   has_started_processing_ = true;
   idle_timer_.Reset();
 
-  // In theory this should always succeed. However, it is possible for a client
-  // to misbehave and send out-of-order messages. It is easy to be tolerant of
-  // this by simply not updating the process info in this case. It's not worth
-  // crashing if this data is slightly out of sync as this is purely
-  // informational.
-  auto* process_info = GetProcessInfo(process_id, creation_time);
-  if (!process_info)
-    return;
-
   auto* module_info =
       FindOrCreateModuleInfo(module_path, module_size, module_time_date_stamp);
 
   module_info->second.module_types |= ModuleInfoData::kTypeLoadedModule;
 
   // Update the list of process types that this module has been seen in.
-  module_info->second.process_types |=
-      ProcessTypeToBit(process_info->first.process_type);
-
-  // Update the load address maps.
-  InsertLoadAddress(module_info->first.module_id, module_load_address,
-                    &process_info->second.loaded_modules);
-  RemoveLoadAddressById(module_info->first.module_id,
-                        &process_info->second.unloaded_modules);
-}
-
-void ModuleDatabase::OnModuleUnload(uint32_t process_id,
-                                    uint64_t creation_time,
-                                    uintptr_t module_load_address) {
-  // Messages can arrive from any thread (UI thread for calls over IPC, and
-  // anywhere at all for calls from ModuleWatcher), so bounce if necessary.
-  if (!task_runner_->RunsTasksInCurrentSequence()) {
-    task_runner_->PostTask(
-        FROM_HERE, base::Bind(&ModuleDatabase::OnModuleUnload,
-                              weak_ptr_factory_.GetWeakPtr(), process_id,
-                              creation_time, module_load_address));
-    return;
-  }
-
-  idle_timer_.Reset();
-
-  // See the long-winded comment in OnModuleLoad about reasons why this can
-  // fail (but shouldn't normally).
-  auto* process_info = GetProcessInfo(process_id, creation_time);
-  if (!process_info)
-    return;
-
-  // Find the module corresponding to this load address. This is O(1) in the
-  // common case of removing a recently removed module, but O(n) worst case.
-  // Thankfully, unload events occur far less often and n is quite small.
-  size_t i = FindLoadAddressIndexByAddress(module_load_address,
-                                           process_info->second.loaded_modules);
-
-  // No such module found. This shouldn't happen either, unless messages are
-  // malformed or out of order. Gracefully fail in this case.
-  if (i == kInvalidIndex)
-    return;
-
-  ModuleId module_id = process_info->second.loaded_modules[i].first;
-
-  // Remove from the loaded module list and insert into the unloaded module
-  // list.
-  RemoveLoadAddressByIndex(i, &process_info->second.loaded_modules);
-  InsertLoadAddress(module_id, module_load_address,
-                    &process_info->second.unloaded_modules);
-}
-
-void ModuleDatabase::OnProcessEnded(uint32_t process_id,
-                                    uint64_t creation_time) {
-  // Messages can arrive from any thread (UI thread for calls over IPC, and
-  // anywhere at all for calls from ModuleWatcher), so bounce if necessary.
-  if (!task_runner_->RunsTasksInCurrentSequence()) {
-    task_runner_->PostTask(
-        FROM_HERE,
-        base::Bind(&ModuleDatabase::OnProcessEnded,
-                   weak_ptr_factory_.GetWeakPtr(), process_id, creation_time));
-    return;
-  }
-
-  DeleteProcessInfo(process_id, creation_time);
+  module_info->second.process_types |= ProcessTypeToBit(process_type);
 }
 
 void ModuleDatabase::AddObserver(ModuleDatabaseObserver* observer) {
@@ -258,128 +180,6 @@
   return static_cast<content::ProcessType>(bit_index + kFirstValidProcessType);
 }
 
-// static
-size_t ModuleDatabase::FindLoadAddressIndexById(
-    ModuleId module_id,
-    const ModuleLoadAddresses& load_addresses) {
-  // Process elements in reverse order so that RemoveLoadAddressById can handle
-  // the more common case of removing the maximum element in O(1).
-  for (size_t i = load_addresses.size() - 1; i < load_addresses.size(); --i) {
-    if (load_addresses[i].first == module_id)
-      return i;
-  }
-  return kInvalidIndex;
-}
-
-// static
-size_t ModuleDatabase::FindLoadAddressIndexByAddress(
-    uintptr_t load_address,
-    const ModuleLoadAddresses& load_addresses) {
-  for (size_t i = 0; i < load_addresses.size(); ++i) {
-    if (load_addresses[i].second == load_address)
-      return i;
-  }
-  return kInvalidIndex;
-}
-
-// static
-void ModuleDatabase::InsertLoadAddress(ModuleId module_id,
-                                       uintptr_t load_address,
-                                       ModuleLoadAddresses* load_addresses) {
-  // A very small optimization: the largest module_id is always placed at the
-  // end of the array. This is the most common case, and allows O(1)
-  // determination that a |module_id| isn't present when it's bigger than the
-  // maximum already in the array. This keeps insertions to O(1) in the usual
-  // case.
-  if (load_addresses->empty() || module_id > load_addresses->back().first) {
-    load_addresses->emplace_back(module_id, load_address);
-    return;
-  }
-
-  // If the module exists in the collection then update the load address and
-  // return. This should never really occur, unless the client is deliberately
-  // misbehaving or a race causes a reload event (at a different address) to be
-  // processed before the corresponding unload. This is very unlikely.
-  size_t i = FindLoadAddressIndexById(module_id, *load_addresses);
-  if (i != kInvalidIndex) {
-    (*load_addresses)[i].second = load_address;
-    return;
-  }
-
-  // The module does not exist, and by definition is smaller in value than
-  // the largest module ID already present. Add it, ensuring that the largest
-  // module ID stays at the end.
-  load_addresses->emplace(--load_addresses->end(), module_id, load_address);
-}
-
-// static
-void ModuleDatabase::RemoveLoadAddressById(
-    ModuleId module_id,
-    ModuleLoadAddresses* load_addresses) {
-  if (load_addresses->empty())
-    return;
-
-  // This handles the special case of removing the max element in O(1), as
-  // FindLoadAddressIndexById processes the elements in reverse order.
-  size_t i = FindLoadAddressIndexById(module_id, *load_addresses);
-  if (i != kInvalidIndex)
-    RemoveLoadAddressByIndex(i, load_addresses);
-}
-
-// static
-void ModuleDatabase::RemoveLoadAddressByIndex(
-    size_t index,
-    ModuleLoadAddresses* load_addresses) {
-  DCHECK_LT(index, load_addresses->size());
-
-  // Special case: removing the only remaining element.
-  if (load_addresses->size() == 1) {
-    load_addresses->clear();
-    return;
-  }
-
-  // Special case: removing the last module (with maximum id). Need to find the
-  // new maximum element and ensure it goes to the end.
-  if (load_addresses->size() > 2 && index + 1 == load_addresses->size()) {
-    // Note that |index| == load_addresses->size() - 1, and is the last
-    // indexable element in the vector.
-
-    // Find the index of the new maximum element.
-    ModuleId max_id = -1;  // These start at zero.
-    size_t max_index = kInvalidIndex;
-    for (size_t i = 0; i < load_addresses->size() - 1; ++i) {
-      if ((*load_addresses)[i].first > max_id) {
-        max_id = (*load_addresses)[i].first;
-        max_index = i;
-      }
-    }
-
-    // Remove the last (max) element.
-    load_addresses->resize(index);
-
-    // If the new max element isn't in the last position, then swap it so it is.
-    size_t last_index = load_addresses->size() - 1;
-    if (max_index != last_index)
-      std::swap((*load_addresses)[max_index], (*load_addresses)[last_index]);
-
-    return;
-  }
-
-  // If the element to be removed is second last then a single copy is
-  // sufficient.
-  if (index + 2 == load_addresses->size()) {
-    (*load_addresses)[index] = (*load_addresses)[index + 1];
-  } else {
-    // In the general case two copies are necessary.
-    int max_index = load_addresses->size() - 1;
-    (*load_addresses)[index] = (*load_addresses)[max_index - 1];
-    (*load_addresses)[max_index - 1] = (*load_addresses)[max_index];
-  }
-
-  // Remove the last element, which is now duplicated.
-  load_addresses->resize(load_addresses->size() - 1);
-}
-
 ModuleDatabase::ModuleInfo* ModuleDatabase::FindOrCreateModuleInfo(
     const base::FilePath& module_path,
     uint32_t module_size,
@@ -397,31 +197,6 @@
   return &(*result.first);
 }
 
-ModuleDatabase::ProcessInfo* ModuleDatabase::GetProcessInfo(
-    uint32_t process_id,
-    uint64_t creation_time) {
-  ProcessInfoKey key(process_id, creation_time, content::PROCESS_TYPE_UNKNOWN);
-  auto it = processes_.find(key);
-  if (it == processes_.end())
-    return nullptr;
-  return &(*it);
-}
-
-void ModuleDatabase::CreateProcessInfo(uint32_t process_id,
-                                       uint64_t creation_time,
-                                       content::ProcessType process_type) {
-  processes_.emplace(std::piecewise_construct,
-                     std::forward_as_tuple(ProcessInfoKey(
-                         process_id, creation_time, process_type)),
-                     std::forward_as_tuple(ProcessInfoData()));
-}
-
-void ModuleDatabase::DeleteProcessInfo(uint32_t process_id,
-                                       uint64_t creation_time) {
-  ProcessInfoKey key(process_id, creation_time, content::PROCESS_TYPE_UNKNOWN);
-  processes_.erase(key);
-}
-
 bool ModuleDatabase::RegisteredModulesEnumerated() {
   return shell_extensions_enumerated_ && ime_enumerated_;
 }
@@ -472,32 +247,3 @@
       observer->OnNewModuleFound(module.first, module.second);
   }
 }
-
-// ModuleDatabase::ProcessInfoKey ----------------------------------------------
-
-ModuleDatabase::ProcessInfoKey::ProcessInfoKey(
-    uint32_t process_id,
-    uint64_t creation_time,
-    content::ProcessType process_type)
-    : process_id(process_id),
-      creation_time(creation_time),
-      process_type(process_type) {}
-
-ModuleDatabase::ProcessInfoKey::~ProcessInfoKey() = default;
-
-bool ModuleDatabase::ProcessInfoKey::operator<(
-    const ProcessInfoKey& pik) const {
-  // The key consists of the pair of (process_id, creation_time).
-  // Use the std::tuple lexicographic comparison operator.
-  return std::make_tuple(process_id, creation_time) <
-         std::make_tuple(pik.process_id, pik.creation_time);
-}
-
-// ModuleDatabase::ProcessInfoData ---------------------------------------------
-
-ModuleDatabase::ProcessInfoData::ProcessInfoData() = default;
-
-ModuleDatabase::ProcessInfoData::ProcessInfoData(const ProcessInfoData& other) =
-    default;
-
-ModuleDatabase::ProcessInfoData::~ProcessInfoData() = default;
diff --git a/chrome/browser/conflicts/module_database_win.h b/chrome/browser/conflicts/module_database_win.h
index cc7a6a9..90f2e6a 100644
--- a/chrome/browser/conflicts/module_database_win.h
+++ b/chrome/browser/conflicts/module_database_win.h
@@ -7,10 +7,7 @@
 
 #include <map>
 #include <memory>
-#include <utility>
-#include <vector>
 
-#include "base/files/file_path.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
@@ -24,8 +21,11 @@
 
 class ModuleDatabaseObserver;
 
+namespace base {
+class FilePath;
+}
+
 // A class that keeps track of all modules loaded across Chrome processes.
-// Drives the chrome://conflicts UI.
 //
 // This is effectively a singleton, but doesn't use base::Singleton. The intent
 // is for the object to be created when Chrome is single-threaded, and for it
@@ -36,16 +36,6 @@
   using ModuleMap = std::map<ModuleInfoKey, ModuleInfoData>;
   using ModuleInfo = ModuleMap::value_type;
 
-  // Used for maintaing a list of modules loaded in a process. Maps module IDs
-  // to load addresses.
-  using ModuleLoadAddresses = std::vector<std::pair<ModuleId, uintptr_t>>;
-
-  // Structures for maintaining information about running processes.
-  struct ProcessInfoKey;
-  struct ProcessInfoData;
-  using ProcessMap = std::map<ProcessInfoKey, ProcessInfoData>;
-  using ProcessInfo = ProcessMap::value_type;
-
   // The Module Database becomes idle after this timeout expires without any
   // module events.
   static constexpr base::TimeDelta kIdleTimeout =
@@ -71,13 +61,6 @@
   // the last 10 seconds.
   bool IsIdle();
 
-  // Indicates that process with the given type has started. This must be called
-  // before any calls to OnModuleEvent or OnModuleUnload. Must be called in the
-  // same sequence as |task_runner_|.
-  void OnProcessStarted(uint32_t process_id,
-                        uint64_t creation_time,
-                        content::ProcessType process_type);
-
   // Indicates that a new registered shell extension was found. Must be called
   // in the same sequence as |task_runner_|.
   void OnShellExtensionEnumerated(const base::FilePath& path,
@@ -100,25 +83,12 @@
   // is taken as gospel, so if it originates from a remote process it should be
   // independently validated first. (In practice, see ModuleEventSinkImpl for
   // details of where this happens.)
-  void OnModuleLoad(uint32_t process_id,
-                    uint64_t creation_time,
+  void OnModuleLoad(content::ProcessType process_type,
                     const base::FilePath& module_path,
                     uint32_t module_size,
                     uint32_t module_time_date_stamp,
                     uintptr_t module_load_address);
 
-  // Indicates that the module at the given |load_address| in the specified
-  // process is being unloaded. This need not be trusted data, as it will be
-  // validated by the ModuleDatabase directly.
-  void OnModuleUnload(uint32_t process_id,
-                      uint64_t creation_time,
-                      uintptr_t module_load_address);
-
-  // Indicates that the given process has ended. This can be called from any
-  // thread and will be bounced to the |task_runner_|. In practice it will be
-  // invoked from the UI thread as the Mojo channel is torn down.
-  void OnProcessEnded(uint32_t process_id, uint64_t creation_time);
-
   // TODO(chrisha): Module analysis code, and various accessors for use by
   // chrome://conflicts.
 
@@ -141,10 +111,6 @@
   friend class ModuleDatabaseTest;
   friend class ModuleEventSinkImplTest;
 
-  // Used by the FindLoadAddress* functions to indicate a load address has not
-  // been found.
-  static constexpr size_t kInvalidIndex = ~0u;
-
   // Converts a valid |process_type| to a bit for use in a bitmask of process
   // values. Exposed in the header for testing.
   static uint32_t ProcessTypeToBit(content::ProcessType process_type);
@@ -153,44 +119,11 @@
   // corresponding process type. Exposed in the header for testing.
   static content::ProcessType BitIndexToProcessType(uint32_t bit_index);
 
-  // Performs a linear scan to find the index of a |module_id| or |load_address|
-  // in a collection of modules. Returns kInvalidIndex if the index is not
-  // found.
-  static size_t FindLoadAddressIndexById(
-      ModuleId module_id,
-      const ModuleLoadAddresses& load_addresses);
-  static size_t FindLoadAddressIndexByAddress(
-      uintptr_t load_address,
-      const ModuleLoadAddresses& load_addresses);
-
-  // Inserts a module into a ModuleLoadAddress object.
-  static void InsertLoadAddress(ModuleId module_id,
-                                uintptr_t load_address,
-                                ModuleLoadAddresses* load_addresses);
-
-  // Removes a module from a ModuleLoadAddress object, either by the
-  // |module_id| or the |index| in the collection.
-  static void RemoveLoadAddressById(ModuleId module_id,
-                                    ModuleLoadAddresses* load_addresses);
-  static void RemoveLoadAddressByIndex(size_t index,
-                                       ModuleLoadAddresses* load_addresses);
-
   // Finds or creates a mutable ModuleInfo entry.
   ModuleInfo* FindOrCreateModuleInfo(const base::FilePath& module_path,
                                      uint32_t module_size,
                                      uint32_t module_time_date_stamp);
 
-  // Finds a process info entry. Returns nullptr if none is found.
-  ProcessInfo* GetProcessInfo(uint32_t process_id, uint64_t creation_time);
-
-  // Creates a process info entry.
-  void CreateProcessInfo(uint32_t process_id,
-                         uint64_t creation_time,
-                         content::ProcessType process_type);
-
-  // Deletes a process info entry.
-  void DeleteProcessInfo(uint32_t process_id, uint64_t creation_time);
-
   // Returns true if the enumeration of the IMEs and the shell extensions is
   // finished.
   //
@@ -235,10 +168,6 @@
   // Inspects new modules on a blocking task runner.
   ModuleInspector module_inspector_;
 
-  // A map of all known running processes, and modules loaded/unloaded in
-  // them.
-  ProcessMap processes_;
-
   base::ObserverList<ModuleDatabaseObserver> observer_list_;
 
   ThirdPartyMetricsRecorder third_party_metrics_;
@@ -255,49 +184,4 @@
   DISALLOW_COPY_AND_ASSIGN(ModuleDatabase);
 };
 
-// Information about a running process. This ties modules in a ModuleSet to
-// processes in which they are (or have been) loaded.
-
-// This is the constant portion of the process information, and acts as the key
-// in a std::map.
-struct ModuleDatabase::ProcessInfoKey {
-  ProcessInfoKey(uint32_t process_id,
-                 uint64_t creation_time,
-                 content::ProcessType process_type);
-  ~ProcessInfoKey();
-
-  // Less-than operator allowing this object to be used in std::map.
-  bool operator<(const ProcessInfoKey& pi) const;
-
-  // The process ID.
-  uint32_t process_id;
-
-  // The process creation time. A raw FILETIME value with full precision.
-  // Combined with |process_id| this uniquely identifies a process on a Windows
-  // system.
-  uint64_t creation_time;
-
-  // The type of the process.
-  content::ProcessType process_type;
-};
-
-// This is the mutable portion of the process information, and is the storage
-// type in a std::map.
-struct ModuleDatabase::ProcessInfoData {
-  ProcessInfoData();
-  ProcessInfoData(const ProcessInfoData& other);
-  ~ProcessInfoData();
-
-  // The sets of modules that are loaded/unloaded in this process, by ID. This
-  // is typically a small list so a linear cost is okay to pay for
-  // lookup/deletion (storage is backed by a vector).
-  //
-  // These are modified by the various static *LoadAddress* helper functions in
-  // ModuleDatabase. The vector maintains the invariant the element with maximum
-  // module ID is always last. This ensures that the usual operation of loading
-  // a module is O(1).
-  ModuleLoadAddresses loaded_modules;
-  ModuleLoadAddresses unloaded_modules;
-};
-
 #endif  // CHROME_BROWSER_CONFLICTS_MODULE_DATABASE_WIN_H_
diff --git a/chrome/browser/conflicts/module_database_win_unittest.cc b/chrome/browser/conflicts/module_database_win_unittest.cc
index 50b4ca5..f390579d 100644
--- a/chrome/browser/conflicts/module_database_win_unittest.cc
+++ b/chrome/browser/conflicts/module_database_win_unittest.cc
@@ -21,11 +21,8 @@
 
 namespace {
 
-constexpr uint32_t kPid1 = 1234u;
-constexpr uint32_t kPid2 = 2345u;
-
-constexpr uint64_t kCreateTime1 = 1234u;
-constexpr uint64_t kCreateTime2 = 2345u;
+constexpr content::ProcessType kProcessType1 = content::PROCESS_TYPE_BROWSER;
+constexpr content::ProcessType kProcessType2 = content::PROCESS_TYPE_RENDERER;
 
 constexpr wchar_t kDll1[] = L"dummy.dll";
 constexpr wchar_t kDll2[] = L"foo.dll";
@@ -41,22 +38,6 @@
 
 }  // namespace
 
-class TestModuleDatabase : ModuleDatabase {
- public:
-  // Types.
-  using ModuleDatabase::ModuleLoadAddresses;
-
-  // Constants.
-  using ModuleDatabase::kInvalidIndex;
-
-  // Functions.
-  using ModuleDatabase::FindLoadAddressIndexById;
-  using ModuleDatabase::FindLoadAddressIndexByAddress;
-  using ModuleDatabase::InsertLoadAddress;
-  using ModuleDatabase::RemoveLoadAddressById;
-  using ModuleDatabase::RemoveLoadAddressByIndex;
-};
-
 class ModuleDatabaseTest : public testing::Test {
  protected:
   ModuleDatabaseTest()
@@ -68,26 +49,12 @@
     return module_database_.modules_;
   }
 
-  const ModuleDatabase::ProcessMap& processes() {
-    return module_database_.processes_;
-  }
-
   ModuleDatabase* module_database() { return &module_database_; }
 
   static uint32_t ProcessTypeToBit(content::ProcessType process_type) {
     return ModuleDatabase::ProcessTypeToBit(process_type);
   }
 
-  // Counts the occurrences of the given |module_id| in the given collection of
-  // |load_addresses|.
-  static size_t ModuleIdCount(
-      ModuleId module_id,
-      const ModuleDatabase::ModuleLoadAddresses& load_addresses) {
-    return std::count_if(
-        load_addresses.begin(), load_addresses.end(),
-        [module_id](const auto& x) { return module_id == x.first; });
-  }
-
   void RunSchedulerUntilIdle() {
     // Call ScopedTaskEnvironment::RunUntilIdle() when it supports mocking time.
     base::TaskScheduler::GetInstance()->FlushForTesting();
@@ -113,415 +80,65 @@
   DISALLOW_COPY_AND_ASSIGN(ModuleDatabaseTest);
 };
 
-TEST_F(ModuleDatabaseTest, LoadAddressVectorOperations) {
-  using TMD = TestModuleDatabase;
-  TMD::ModuleLoadAddresses la;
-
-  // Finds should fail in an empty collection.
-  EXPECT_EQ(TMD::kInvalidIndex, TMD::FindLoadAddressIndexById(0, la));
-  EXPECT_EQ(TMD::kInvalidIndex, TMD::FindLoadAddressIndexById(0x04000000, la));
-
-  // A first insert should work. Don't start with ModuleId 0 so that later
-  // inserts can insert that module.
-  TMD::InsertLoadAddress(10, 0x04000000, &la);
-  EXPECT_EQ(1u, la.size());
-  EXPECT_EQ(10, la[0].first);
-  EXPECT_EQ(0x04000000u, la[0].second);
-
-  // Finds should work.
-  EXPECT_EQ(TMD::kInvalidIndex, TMD::FindLoadAddressIndexById(0, la));
-  EXPECT_EQ(TMD::kInvalidIndex, TMD::FindLoadAddressIndexById(0x03000000, la));
-  EXPECT_EQ(0u, TMD::FindLoadAddressIndexById(10, la));
-  EXPECT_EQ(0u, TMD::FindLoadAddressIndexByAddress(0x04000000, la));
-
-  // A second insert should work. This is the new max so should be at the end
-  // of the collection.
-  TMD::InsertLoadAddress(12, 0x06000000, &la);
-  EXPECT_EQ(2u, la.size());
-  EXPECT_EQ(10, la[0].first);
-  EXPECT_EQ(0x04000000u, la[0].second);
-  EXPECT_EQ(12, la[1].first);
-  EXPECT_EQ(0x06000000u, la[1].second);
-
-  // Finds should work.
-  EXPECT_EQ(TMD::kInvalidIndex, TMD::FindLoadAddressIndexById(0, la));
-  EXPECT_EQ(TMD::kInvalidIndex, TMD::FindLoadAddressIndexById(0x03000000, la));
-  EXPECT_EQ(0u, TMD::FindLoadAddressIndexById(10, la));
-  EXPECT_EQ(0u, TMD::FindLoadAddressIndexByAddress(0x04000000, la));
-  EXPECT_EQ(1u, TMD::FindLoadAddressIndexById(12, la));
-  EXPECT_EQ(1u, TMD::FindLoadAddressIndexByAddress(0x06000000, la));
-
-  // Another insert should work. This is not the new max, so a swap should
-  // happen to keep the maximum element at the end of the collection.
-  TMD::InsertLoadAddress(11, 0x05000000, &la);
-  EXPECT_EQ(3u, la.size());
-  EXPECT_EQ(10, la[0].first);
-  EXPECT_EQ(0x04000000u, la[0].second);
-  EXPECT_EQ(11, la[1].first);
-  EXPECT_EQ(0x05000000u, la[1].second);
-  EXPECT_EQ(12, la[2].first);
-  EXPECT_EQ(0x06000000u, la[2].second);
-
-  // An insert of an existing module should work, but simply overwrite the
-  // load address.
-  TMD::InsertLoadAddress(11, 0x0F000000, &la);
-  EXPECT_EQ(3u, la.size());
-  EXPECT_EQ(11, la[1].first);
-  EXPECT_EQ(0x0F000000u, la[1].second);
-  TMD::InsertLoadAddress(11, 0x05000000, &la);
-  EXPECT_EQ(3u, la.size());
-  EXPECT_EQ(11, la[1].first);
-  EXPECT_EQ(0x05000000u, la[1].second);
-
-  // Finds should work.
-  EXPECT_EQ(TMD::kInvalidIndex, TMD::FindLoadAddressIndexById(0, la));
-  EXPECT_EQ(TMD::kInvalidIndex, TMD::FindLoadAddressIndexById(0x03000000, la));
-  EXPECT_EQ(0u, TMD::FindLoadAddressIndexById(10, la));
-  EXPECT_EQ(0u, TMD::FindLoadAddressIndexByAddress(0x04000000, la));
-  EXPECT_EQ(1u, TMD::FindLoadAddressIndexById(11, la));
-  EXPECT_EQ(1u, TMD::FindLoadAddressIndexByAddress(0x05000000, la));
-  EXPECT_EQ(2u, TMD::FindLoadAddressIndexById(12, la));
-  EXPECT_EQ(2u, TMD::FindLoadAddressIndexByAddress(0x06000000, la));
-
-  // Do some inserts of lower modules IDs. This ensures that we'll have some
-  // higher module IDs in the vector before some lower modules IDs, for testing
-  // the deletion logic.
-  TMD::InsertLoadAddress(3, 0x07000000, &la);
-  TMD::InsertLoadAddress(4, 0x08000000, &la);
-  TMD::InsertLoadAddress(5, 0x09000000, &la);
-  EXPECT_EQ(6u, la.size());
-  EXPECT_EQ(10, la[0].first);
-  EXPECT_EQ(0x04000000u, la[0].second);
-  EXPECT_EQ(11, la[1].first);
-  EXPECT_EQ(0x05000000u, la[1].second);
-  EXPECT_EQ(3, la[2].first);
-  EXPECT_EQ(0x07000000u, la[2].second);
-  EXPECT_EQ(4, la[3].first);
-  EXPECT_EQ(0x08000000u, la[3].second);
-  EXPECT_EQ(5, la[4].first);
-  EXPECT_EQ(0x09000000u, la[4].second);
-  EXPECT_EQ(12, la[5].first);
-  EXPECT_EQ(0x06000000u, la[5].second);
-
-  // Remove an element that isn't in the second last position. The second last
-  // element should be swapped into its position, and the last element moved
-  // to the second last place.
-  TMD::RemoveLoadAddressByIndex(2, &la);
-  EXPECT_EQ(5u, la.size());
-  EXPECT_EQ(10, la[0].first);
-  EXPECT_EQ(0x04000000u, la[0].second);
-  EXPECT_EQ(11, la[1].first);
-  EXPECT_EQ(0x05000000u, la[1].second);
-  EXPECT_EQ(5, la[2].first);
-  EXPECT_EQ(0x09000000u, la[2].second);
-  EXPECT_EQ(4, la[3].first);
-  EXPECT_EQ(0x08000000u, la[3].second);
-  EXPECT_EQ(12, la[4].first);
-  EXPECT_EQ(0x06000000u, la[4].second);
-
-  // Remove the second last element. Only the last element should move.
-  TMD::RemoveLoadAddressByIndex(3, &la);
-  EXPECT_EQ(4u, la.size());
-  EXPECT_EQ(10, la[0].first);
-  EXPECT_EQ(0x04000000u, la[0].second);
-  EXPECT_EQ(11, la[1].first);
-  EXPECT_EQ(0x05000000u, la[1].second);
-  EXPECT_EQ(5, la[2].first);
-  EXPECT_EQ(0x09000000u, la[2].second);
-  EXPECT_EQ(12, la[3].first);
-  EXPECT_EQ(0x06000000u, la[3].second);
-
-  // Remove the last element. The new maximum should be found moved to the
-  // end.
-  TMD::RemoveLoadAddressByIndex(3, &la);
-  EXPECT_EQ(3u, la.size());
-  EXPECT_EQ(10, la[0].first);
-  EXPECT_EQ(0x04000000u, la[0].second);
-  EXPECT_EQ(5, la[1].first);
-  EXPECT_EQ(0x09000000u, la[1].second);
-  EXPECT_EQ(11, la[2].first);
-  EXPECT_EQ(0x05000000u, la[2].second);
-
-  // Remove the last element by ModuleId. The remaining modules should be
-  // swapped.
-  TMD::RemoveLoadAddressById(11, &la);
-  EXPECT_EQ(2u, la.size());
-  EXPECT_EQ(5, la[0].first);
-  EXPECT_EQ(0x09000000u, la[0].second);
-  EXPECT_EQ(10, la[1].first);
-  EXPECT_EQ(0x04000000u, la[1].second);
-
-  // Remove the first element by ModuleId.
-  TMD::RemoveLoadAddressById(5, &la);
-  EXPECT_EQ(1u, la.size());
-  EXPECT_EQ(10, la[0].first);
-  EXPECT_EQ(0x04000000u, la[0].second);
-
-  // Remove the only remaining element.
-  TMD::RemoveLoadAddressByIndex(0, &la);
-  EXPECT_TRUE(la.empty());
-}
-
-TEST_F(ModuleDatabaseTest, LoadAddressVectorStressTest) {
-  using TMD = TestModuleDatabase;
-  TMD::ModuleLoadAddresses la;
-
-  for (size_t n = 1; n < 200; ++n) {
-    // Will keep track of which elements have been inserted.
-    std::vector<bool> inserted(n);
-    size_t inserted_count = 0;
-
-    // Generate a shuffled list of IDs. This will be the insertion order.
-    // More insertions than elements will occur so that rewrites occur.,
-    std::vector<ModuleId> ids(11 * n / 10);
-    for (size_t i = 0; i < 11 * n / 10; ++i)
-      ids[i] = i % n;
-    std::random_shuffle(ids.begin(), ids.end());
-
-    // Do the insertions.
-    for (auto id : ids) {
-      if (!inserted[id]) {
-        inserted[id] = true;
-        ++inserted_count;
-      }
-
-      // Generate a load address. The load address bakes in the index so that
-      // searching by load address is easy.
-      uintptr_t load_address = static_cast<uintptr_t>(id) << 16;
-
-      // Do the insertion.
-      TMD::InsertLoadAddress(id, load_address, &la);
-      EXPECT_EQ(inserted_count, la.size());
-    }
-
-    // Validate that every element is there, via both search mechanisms.
-    for (size_t id = 0; id < n; ++id) {
-      uintptr_t load_address = static_cast<uintptr_t>(id) << 16;
-      size_t index1 = TMD::FindLoadAddressIndexById(id, la);
-      size_t index2 = TMD::FindLoadAddressIndexByAddress(load_address, la);
-      EXPECT_NE(TMD::kInvalidIndex, index1);
-      EXPECT_EQ(index1, index2);
-    }
-
-    // Generate the deletion order.
-    ids.resize(n);
-    for (size_t i = 0; i < ids.size(); ++i)
-      ids[i] = i;
-    std::random_shuffle(ids.begin(), ids.end());
-
-    // Do the deletions.
-    for (auto id : ids) {
-      --inserted_count;
-      TMD::RemoveLoadAddressById(id, &la);
-      EXPECT_EQ(inserted_count, la.size());
-    }
-  }
-}
-
 TEST_F(ModuleDatabaseTest, TasksAreBounced) {
   // Run a task on the current thread. This should not be bounced, so their
   // results should be immediately available.
-  module_database()->OnProcessStarted(kPid1, kCreateTime1,
-                                      content::PROCESS_TYPE_BROWSER);
-  EXPECT_EQ(1u, processes().size());
-  module_database()->OnModuleLoad(kPid1, kCreateTime1, dll1_, kSize1, kTime1,
+  module_database()->OnModuleLoad(kProcessType1, dll1_, kSize1, kTime1,
                                   kGoodAddress1);
   EXPECT_EQ(1u, modules().size());
-  module_database()->OnProcessEnded(kPid1, kCreateTime1);
-  EXPECT_EQ(0u, processes().size());
-
-  // Indicate another process start on this thread. This call can't be
-  // bounced.
-  module_database()->OnProcessStarted(kPid2, kCreateTime2,
-                                      content::PROCESS_TYPE_BROWSER);
-  EXPECT_EQ(1u, processes().size());
 
   // Run similar tasks on another thread with another module. These should be
   // bounced.
-  base::PostTask(FROM_HERE, base::Bind(&ModuleDatabase::OnModuleLoad,
-                                       base::Unretained(module_database()),
-                                       kPid2, kCreateTime2, dll2_, kSize1,
-                                       kTime1, kGoodAddress1));
+  base::PostTask(FROM_HERE,
+                 base::Bind(&ModuleDatabase::OnModuleLoad,
+                            base::Unretained(module_database()), kProcessType2,
+                            dll2_, kSize1, kTime1, kGoodAddress1));
+  EXPECT_EQ(1u, modules().size());
   RunSchedulerUntilIdle();
   EXPECT_EQ(2u, modules().size());
-
-  base::PostTask(FROM_HERE, base::Bind(&ModuleDatabase::OnProcessEnded,
-                                       base::Unretained(module_database()),
-                                       kPid2, kCreateTime2));
-  RunSchedulerUntilIdle();
-  EXPECT_EQ(0u, processes().size());
-}
-
-TEST_F(ModuleDatabaseTest, EventsWithoutProcessIgnore) {
-  EXPECT_EQ(0u, modules().size());
-  EXPECT_EQ(0u, processes().size());
-
-  module_database()->OnModuleLoad(kPid1, kCreateTime1, dll1_, kSize1, kTime1,
-                                  kGoodAddress1);
-
-  EXPECT_EQ(0u, modules().size());
-  EXPECT_EQ(0u, processes().size());
-}
-
-TEST_F(ModuleDatabaseTest, OrphanedUnloadIgnored) {
-  EXPECT_EQ(0u, modules().size());
-  EXPECT_EQ(0u, processes().size());
-
-  // Start a process.
-  module_database()->OnProcessStarted(kPid1, kCreateTime1,
-                                      content::PROCESS_TYPE_BROWSER);
-  EXPECT_EQ(0u, modules().size());
-  EXPECT_EQ(1u, processes().size());
-  auto p1 = processes().begin();
-  EXPECT_EQ(kPid1, p1->first.process_id);
-  EXPECT_EQ(kCreateTime1, p1->first.creation_time);
-  EXPECT_EQ(content::PROCESS_TYPE_BROWSER, p1->first.process_type);
-  EXPECT_EQ(0u, p1->second.loaded_modules.size());
-  EXPECT_EQ(0u, p1->second.unloaded_modules.size());
-
-  // Indicate a module unload. This should do nothing because there's no
-  // corresponding module.
-  module_database()->OnModuleUnload(kPid1, kCreateTime1, kGoodAddress1);
-  EXPECT_EQ(0u, modules().size());
-  EXPECT_EQ(1u, processes().size());
-  EXPECT_EQ(0u, p1->second.loaded_modules.size());
-  EXPECT_EQ(0u, p1->second.unloaded_modules.size());
 }
 
 TEST_F(ModuleDatabaseTest, DatabaseIsConsistent) {
   EXPECT_EQ(0u, modules().size());
-  EXPECT_EQ(0u, processes().size());
-
-  // Start a process.
-  module_database()->OnProcessStarted(kPid1, kCreateTime1,
-                                      content::PROCESS_TYPE_BROWSER);
-  EXPECT_EQ(0u, modules().size());
-  EXPECT_EQ(1u, processes().size());
-  auto p1 = processes().begin();
-  EXPECT_EQ(kPid1, p1->first.process_id);
-  EXPECT_EQ(kCreateTime1, p1->first.creation_time);
-  EXPECT_EQ(content::PROCESS_TYPE_BROWSER, p1->first.process_type);
-  EXPECT_EQ(0u, p1->second.loaded_modules.size());
-  EXPECT_EQ(0u, p1->second.unloaded_modules.size());
 
   // Load a module.
-  module_database()->OnModuleLoad(kPid1, kCreateTime1, dll1_, kSize1, kTime1,
+  module_database()->OnModuleLoad(kProcessType1, dll1_, kSize1, kTime1,
                                   kGoodAddress1);
   EXPECT_EQ(1u, modules().size());
-  EXPECT_EQ(1u, processes().size());
 
   // Ensure that the process and module sets are up to date.
   auto m1 = modules().begin();
   EXPECT_EQ(dll1_, m1->first.module_path);
   EXPECT_EQ(ProcessTypeToBit(content::PROCESS_TYPE_BROWSER),
             m1->second.process_types);
-  EXPECT_EQ(kPid1, p1->first.process_id);
-  EXPECT_EQ(kCreateTime1, p1->first.creation_time);
-  EXPECT_EQ(content::PROCESS_TYPE_BROWSER, p1->first.process_type);
-  EXPECT_EQ(1u, p1->second.loaded_modules.size());
-  EXPECT_EQ(0u, p1->second.unloaded_modules.size());
-  EXPECT_EQ(1u, ModuleIdCount(m1->first.module_id, p1->second.loaded_modules));
 
   // Provide a redundant load message for that module.
-  module_database()->OnModuleLoad(kPid1, kCreateTime1, dll1_, kSize1, kTime1,
+  module_database()->OnModuleLoad(kProcessType1, dll1_, kSize1, kTime1,
                                   kGoodAddress1);
   EXPECT_EQ(1u, modules().size());
-  EXPECT_EQ(1u, processes().size());
 
   // Ensure that the process and module sets haven't changed.
   EXPECT_EQ(dll1_, m1->first.module_path);
   EXPECT_EQ(ProcessTypeToBit(content::PROCESS_TYPE_BROWSER),
             m1->second.process_types);
-  EXPECT_EQ(kPid1, p1->first.process_id);
-  EXPECT_EQ(kCreateTime1, p1->first.creation_time);
-  EXPECT_EQ(content::PROCESS_TYPE_BROWSER, p1->first.process_type);
-  EXPECT_EQ(1u, p1->second.loaded_modules.size());
-  EXPECT_EQ(0u, p1->second.unloaded_modules.size());
-  EXPECT_EQ(1u, ModuleIdCount(m1->first.module_id, p1->second.loaded_modules));
 
   // Load a second module into the process.
-  module_database()->OnModuleLoad(kPid1, kCreateTime1, dll2_, kSize2, kTime2,
+  module_database()->OnModuleLoad(kProcessType1, dll2_, kSize2, kTime2,
                                   kGoodAddress2);
   EXPECT_EQ(2u, modules().size());
-  EXPECT_EQ(1u, processes().size());
 
   // Ensure that the process and module sets are up to date.
   auto m2 = modules().rbegin();
   EXPECT_EQ(dll2_, m2->first.module_path);
   EXPECT_EQ(ProcessTypeToBit(content::PROCESS_TYPE_BROWSER),
             m2->second.process_types);
-  EXPECT_EQ(kPid1, p1->first.process_id);
-  EXPECT_EQ(kCreateTime1, p1->first.creation_time);
-  EXPECT_EQ(content::PROCESS_TYPE_BROWSER, p1->first.process_type);
-  EXPECT_EQ(2u, p1->second.loaded_modules.size());
-  EXPECT_EQ(0u, p1->second.unloaded_modules.size());
-  EXPECT_EQ(1u, ModuleIdCount(m1->first.module_id, p1->second.loaded_modules));
-  EXPECT_EQ(1u, ModuleIdCount(m2->first.module_id, p1->second.loaded_modules));
-
-  // Unload the second module.
-  module_database()->OnModuleUnload(kPid1, kCreateTime1, kGoodAddress2);
-  EXPECT_EQ(2u, modules().size());
-  EXPECT_EQ(1u, processes().size());
-
-  // Ensure that the process and module sets are up to date.
-  EXPECT_EQ(dll2_, m2->first.module_path);
-  EXPECT_EQ(ProcessTypeToBit(content::PROCESS_TYPE_BROWSER),
-            m2->second.process_types);
-  EXPECT_EQ(kPid1, p1->first.process_id);
-  EXPECT_EQ(kCreateTime1, p1->first.creation_time);
-  EXPECT_EQ(content::PROCESS_TYPE_BROWSER, p1->first.process_type);
-  EXPECT_EQ(1u, p1->second.loaded_modules.size());
-  EXPECT_EQ(1u, p1->second.unloaded_modules.size());
-  EXPECT_EQ(1u, ModuleIdCount(m1->first.module_id, p1->second.loaded_modules));
-  EXPECT_EQ(1u,
-            ModuleIdCount(m2->first.module_id, p1->second.unloaded_modules));
-
-  // Start a process.
-  module_database()->OnProcessStarted(kPid2, kCreateTime2,
-                                      content::PROCESS_TYPE_RENDERER);
-  EXPECT_EQ(2u, modules().size());
-  EXPECT_EQ(2u, processes().size());
-  auto p2 = processes().rbegin();
-  EXPECT_EQ(kPid2, p2->first.process_id);
-  EXPECT_EQ(kCreateTime2, p2->first.creation_time);
-  EXPECT_EQ(content::PROCESS_TYPE_RENDERER, p2->first.process_type);
-  EXPECT_EQ(0u, p2->second.loaded_modules.size());
-  EXPECT_EQ(0u, p2->second.unloaded_modules.size());
 
   // Load the dummy.dll in the second process as well.
-  module_database()->OnModuleLoad(kPid2, kCreateTime2, dll1_, kSize1, kTime1,
+  module_database()->OnModuleLoad(kProcessType2, dll1_, kSize1, kTime1,
                                   kGoodAddress1);
   EXPECT_EQ(ProcessTypeToBit(content::PROCESS_TYPE_BROWSER) |
                 ProcessTypeToBit(content::PROCESS_TYPE_RENDERER),
             m1->second.process_types);
-  EXPECT_EQ(kPid2, p2->first.process_id);
-  EXPECT_EQ(kCreateTime2, p2->first.creation_time);
-  EXPECT_EQ(content::PROCESS_TYPE_RENDERER, p2->first.process_type);
-  EXPECT_EQ(1u, p2->second.loaded_modules.size());
-  EXPECT_EQ(0u, p2->second.unloaded_modules.size());
-  EXPECT_EQ(1u, ModuleIdCount(m1->first.module_id, p2->second.loaded_modules));
-
-  // End the second process without an explicit unload. This invalidates |p2|.
-  module_database()->OnProcessEnded(kPid2, kCreateTime2);
-  EXPECT_EQ(2u, modules().size());
-  EXPECT_EQ(1u, processes().size());
-  EXPECT_EQ(kPid1, p1->first.process_id);
-  EXPECT_EQ(kCreateTime1, p1->first.creation_time);
-  EXPECT_EQ(ProcessTypeToBit(content::PROCESS_TYPE_BROWSER) |
-                ProcessTypeToBit(content::PROCESS_TYPE_RENDERER),
-            m1->second.process_types);
-  EXPECT_EQ(ProcessTypeToBit(content::PROCESS_TYPE_BROWSER),
-            m2->second.process_types);
-
-  // End the first process without an explicit unload. This invalidates |p1|.
-  module_database()->OnProcessEnded(kPid1, kCreateTime1);
-  EXPECT_EQ(2u, modules().size());
-  EXPECT_EQ(0u, processes().size());
-  EXPECT_EQ(ProcessTypeToBit(content::PROCESS_TYPE_BROWSER) |
-                ProcessTypeToBit(content::PROCESS_TYPE_RENDERER),
-            m1->second.process_types);
-  EXPECT_EQ(ProcessTypeToBit(content::PROCESS_TYPE_BROWSER),
-            m2->second.process_types);
 }
 
 // A dummy observer that only counts how many notifications it receives.
@@ -556,16 +173,13 @@
   module_database()->OnShellExtensionEnumerationFinished();
   module_database()->OnImeEnumerationFinished();
 
-  module_database()->OnProcessStarted(kPid1, kCreateTime1,
-                                      content::PROCESS_TYPE_BROWSER);
-
   DummyObserver before_load_observer;
   EXPECT_EQ(0, before_load_observer.new_module_count());
 
   module_database()->AddObserver(&before_load_observer);
   EXPECT_EQ(0, before_load_observer.new_module_count());
 
-  module_database()->OnModuleLoad(kPid1, kCreateTime1, dll1_, kSize1, kTime1,
+  module_database()->OnModuleLoad(kProcessType1, dll1_, kSize1, kTime1,
                                   kGoodAddress1);
   RunSchedulerUntilIdle();
 
@@ -588,9 +202,6 @@
   module_database()->OnShellExtensionEnumerationFinished();
   module_database()->OnImeEnumerationFinished();
 
-  module_database()->OnProcessStarted(kPid1, kCreateTime1,
-                                      content::PROCESS_TYPE_BROWSER);
-
   // ModuleDatabase starts busy.
   EXPECT_FALSE(module_database()->IsIdle());
 
@@ -599,7 +210,7 @@
   EXPECT_FALSE(module_database()->IsIdle());
 
   // A load module event starts the timer.
-  module_database()->OnModuleLoad(kPid1, kCreateTime1, dll1_, kSize1, kTime1,
+  module_database()->OnModuleLoad(kProcessType1, dll1_, kSize1, kTime1,
                                   kGoodAddress1);
   EXPECT_FALSE(module_database()->IsIdle());
 
@@ -621,7 +232,7 @@
   module_database()->RemoveObserver(&is_idle_observer);
 
   // Make the ModuleDabatase busy.
-  module_database()->OnModuleLoad(kPid2, kCreateTime2, dll2_, kSize2, kTime2,
+  module_database()->OnModuleLoad(kProcessType2, dll2_, kSize2, kTime2,
                                   kGoodAddress2);
   EXPECT_FALSE(module_database()->IsIdle());
 
@@ -641,15 +252,12 @@
 // The ModuleDatabase waits until shell extensions and IMEs are enumerated
 // before notifying observers or going idle.
 TEST_F(ModuleDatabaseTest, WaitUntilRegisteredModulesEnumerated) {
-  module_database()->OnProcessStarted(kPid1, kCreateTime1,
-                                      content::PROCESS_TYPE_BROWSER);
-
   // This observer is added before the first loaded module.
   DummyObserver before_load_observer;
   module_database()->AddObserver(&before_load_observer);
   EXPECT_EQ(0, before_load_observer.new_module_count());
 
-  module_database()->OnModuleLoad(kPid1, kCreateTime1, dll1_, kSize1, kTime1,
+  module_database()->OnModuleLoad(kProcessType1, dll1_, kSize1, kTime1,
                                   kGoodAddress1);
   FastForwardToIdleTimer();
 
diff --git a/chrome/browser/conflicts/module_event_sink_impl_win.cc b/chrome/browser/conflicts/module_event_sink_impl_win.cc
index 0e0ba8cf..7a3177c 100644
--- a/chrome/browser/conflicts/module_event_sink_impl_win.cc
+++ b/chrome/browser/conflicts/module_event_sink_impl_win.cc
@@ -5,9 +5,11 @@
 #include "chrome/browser/conflicts/module_event_sink_impl_win.h"
 
 #include <windows.h>
+
 #include <psapi.h>
 
 #include <utility>
+#include <vector>
 
 #include "base/bind.h"
 #include "base/callback.h"
@@ -100,18 +102,7 @@
                                          ModuleDatabase* module_database)
     : process_(process),
       module_database_(module_database),
-      process_id_(0),
-      creation_time_(0),
-      in_error_(false) {
-  // Failing to get basic process information means this channel should not
-  // continue to forward data, thus it is marked as being in error.
-  process_id_ = ::GetProcessId(process_);
-  if (!GetProcessCreationTime(process_, &creation_time_)) {
-    in_error_ = true;
-    return;
-  }
-  module_database->OnProcessStarted(process_id_, creation_time_, process_type);
-}
+      process_type_(process_type) {}
 
 ModuleEventSinkImpl::~ModuleEventSinkImpl() = default;
 
@@ -125,61 +116,21 @@
   base::ProcessHandle process = get_process_handle.Run();
   auto module_event_sink_impl = base::MakeUnique<ModuleEventSinkImpl>(
       process, process_type, module_database);
-  base::Closure error_handler = base::Bind(
-      &ModuleDatabase::OnProcessEnded, base::Unretained(module_database),
-      module_event_sink_impl->process_id_,
-      module_event_sink_impl->creation_time_);
-  auto binding = mojo::MakeStrongBinding(std::move(module_event_sink_impl),
-                                         std::move(request));
-  binding->set_connection_error_handler(error_handler);
+  mojo::MakeStrongBinding(std::move(module_event_sink_impl),
+                          std::move(request));
 }
 
 void ModuleEventSinkImpl::OnModuleEvent(mojom::ModuleEventType event_type,
                                         uint64_t load_address) {
-  if (in_error_)
-    return;
-
   // Mojo takes care of validating |event_type|, so only |load_address| needs to
   // be checked. Load addresses must be aligned with the allocation granularity
   // which is at least 64KB on any supported Windows OS.
   if (load_address == 0 || load_address % (64 * 1024) != 0)
     return;
 
-  switch (event_type) {
-    case mojom::ModuleEventType::MODULE_ALREADY_LOADED:
-    case mojom::ModuleEventType::MODULE_LOADED:
-      return OnModuleLoad(load_address);
-
-    case mojom::ModuleEventType::MODULE_UNLOADED:
-      return OnModuleUnload(load_address);
-  }
-}
-
-// static
-bool ModuleEventSinkImpl::GetProcessCreationTime(base::ProcessHandle process,
-                                                 uint64_t* creation_time) {
-  FILETIME creation_ft = {};
-  FILETIME exit_ft = {};
-  FILETIME kernel_ft = {};
-  FILETIME user_ft = {};
-  if (!::GetProcessTimes(process, &creation_ft, &exit_ft, &kernel_ft,
-                         &user_ft)) {
-    return false;
-  }
-  *creation_time = (static_cast<uint64_t>(creation_ft.dwHighDateTime) << 32) |
-                   static_cast<uint64_t>(creation_ft.dwLowDateTime);
-  return true;
-}
-
-void ModuleEventSinkImpl::OnModuleLoad(uint64_t load_address) {
-  if (in_error_)
-    return;
-
   // The |load_address| is a unique key to a module in a remote process. If
   // there is a valid module there then the following queries should all pass.
-  // If any of them fail then the load event is silently swallowed. The entire
-  // channel is not marked as being in an error mode, as later events may be
-  // well formed.
+  // If any of them fail then the load event is silently swallowed.
 
   // Convert the |load_address| to a module handle.
   HMODULE module =
@@ -200,13 +151,6 @@
     return;
 
   // Forward this to the module database.
-  module_database_->OnModuleLoad(process_id_, creation_time_, module_path,
-                                 module_size, module_time_date_stamp,
-                                 load_address);
-}
-
-void ModuleEventSinkImpl::OnModuleUnload(uint64_t load_address) {
-  // Forward this directly to the module database.
-  module_database_->OnModuleUnload(process_id_, creation_time_,
-                                   static_cast<uintptr_t>(load_address));
+  module_database_->OnModuleLoad(process_type_, module_path, module_size,
+                                 module_time_date_stamp, load_address);
 }
diff --git a/chrome/browser/conflicts/module_event_sink_impl_win.h b/chrome/browser/conflicts/module_event_sink_impl_win.h
index 7abe5124..778fc77 100644
--- a/chrome/browser/conflicts/module_event_sink_impl_win.h
+++ b/chrome/browser/conflicts/module_event_sink_impl_win.h
@@ -44,20 +44,9 @@
   void OnModuleEvent(mojom::ModuleEventType event_type,
                      uint64_t load_address) override;
 
-  bool in_error() const { return in_error_; }
-
-  // Gets the process creation time associated with the given process.
-  static bool GetProcessCreationTime(base::ProcessHandle process,
-                                     uint64_t* creation_time);
-
  private:
   friend class ModuleEventSinkImplTest;
 
-  // OnModuleEvent disptaches to these two functions depending on the event
-  // type.
-  void OnModuleLoad(uint64_t load_address);
-  void OnModuleUnload(uint64_t load_address);
-
   // A handle to the process on the other side of the pipe.
   base::ProcessHandle process_;
 
@@ -67,15 +56,7 @@
 
   // The process ID of the remote process on the other end of the pipe. This is
   // forwarded along to the ModuleDatabase for each call.
-  uint32_t process_id_;
-
-  // The creation time of the process. Combined with process_id_ this uniquely
-  // identifies a process.
-  uint64_t creation_time_;
-
-  // Indicates whether or not this connection is in an error mode. If true then
-  // all communication from the remote client is silently dropped.
-  bool in_error_;
+  content::ProcessType process_type_;
 
   DISALLOW_COPY_AND_ASSIGN(ModuleEventSinkImpl);
 };
diff --git a/chrome/browser/conflicts/module_event_sink_impl_win_unittest.cc b/chrome/browser/conflicts/module_event_sink_impl_win_unittest.cc
index 2f55c0e..4f8237d 100644
--- a/chrome/browser/conflicts/module_event_sink_impl_win_unittest.cc
+++ b/chrome/browser/conflicts/module_event_sink_impl_win_unittest.cc
@@ -40,12 +40,6 @@
     return module_database_->modules_;
   }
 
-  const ModuleDatabase::ProcessMap& processes() {
-    return module_database_->processes_;
-  }
-
-  uint32_t process_id() { return module_event_sink_impl_->process_id_; }
-
   // Must be before |module_database_|.
   base::test::ScopedTaskEnvironment scoped_task_environment_;
   std::unique_ptr<ModuleDatabase> module_database_;
@@ -59,24 +53,17 @@
   const uintptr_t kValidLoadAddress = reinterpret_cast<uintptr_t>(&__ImageBase);
 
   EXPECT_EQ(0u, modules().size());
-  EXPECT_EQ(0u, processes().size());
 
-  // Construction should immediately fire off a call to OnProcessStarted and
-  // create a process entry in the module database.
   CreateModuleSinkImpl();
-  EXPECT_EQ(::GetCurrentProcessId(), process_id());
   EXPECT_EQ(0u, modules().size());
-  EXPECT_EQ(1u, processes().size());
 
   // An invalid load event should not cause a module entry.
   module_event_sink_impl_->OnModuleEvent(
       mojom::ModuleEventType::MODULE_ALREADY_LOADED, kInvalidLoadAddress);
   EXPECT_EQ(0u, modules().size());
-  EXPECT_EQ(1u, processes().size());
 
   // A valid load event should cause a module entry.
   module_event_sink_impl_->OnModuleEvent(mojom::ModuleEventType::MODULE_LOADED,
                                          kValidLoadAddress);
   EXPECT_EQ(1u, modules().size());
-  EXPECT_EQ(1u, processes().size());
 }
diff --git a/chrome/browser/extensions/activity_log/activity_log_unittest.cc b/chrome/browser/extensions/activity_log/activity_log_unittest.cc
index a15ded8..77be646c 100644
--- a/chrome/browser/extensions/activity_log/activity_log_unittest.cc
+++ b/chrome/browser/extensions/activity_log/activity_log_unittest.cc
@@ -32,7 +32,6 @@
 #include "extensions/browser/uninstall_reason.h"
 #include "extensions/common/dom_action_types.h"
 #include "extensions/common/extension_builder.h"
-#include "extensions/common/test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
diff --git a/chrome/browser/extensions/api/developer_private/developer_private_api_unittest.cc b/chrome/browser/extensions/api/developer_private/developer_private_api_unittest.cc
index 5adf8cf3..200720c2 100644
--- a/chrome/browser/extensions/api/developer_private/developer_private_api_unittest.cc
+++ b/chrome/browser/extensions/api/developer_private/developer_private_api_unittest.cc
@@ -49,7 +49,6 @@
 #include "extensions/common/extension_set.h"
 #include "extensions/common/feature_switch.h"
 #include "extensions/common/manifest_constants.h"
-#include "extensions/common/test_util.h"
 #include "extensions/common/value_builder.h"
 
 using testing::Return;
diff --git a/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api_unittest.cc b/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api_unittest.cc
index c096c56..fb8b1b6b 100644
--- a/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api_unittest.cc
+++ b/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api_unittest.cc
@@ -36,7 +36,6 @@
 #include "components/signin/core/account_id/account_id.h"
 #include "components/signin/core/browser/signin_manager.h"
 #include "extensions/common/extension_builder.h"
-#include "extensions/common/test_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
diff --git a/chrome/browser/extensions/api/file_system/consent_provider_unittest.cc b/chrome/browser/extensions/api/file_system/consent_provider_unittest.cc
index 24fb3718..2c432f5 100644
--- a/chrome/browser/extensions/api/file_system/consent_provider_unittest.cc
+++ b/chrome/browser/extensions/api/file_system/consent_provider_unittest.cc
@@ -20,7 +20,6 @@
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_builder.h"
 #include "extensions/common/manifest.h"
-#include "extensions/common/test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using extensions::file_system_api::ConsentProvider;
diff --git a/chrome/browser/extensions/api/identity/identity_apitest.cc b/chrome/browser/extensions/api/identity/identity_apitest.cc
index 5b5c099..07d9e82 100644
--- a/chrome/browser/extensions/api/identity/identity_apitest.cc
+++ b/chrome/browser/extensions/api/identity/identity_apitest.cc
@@ -63,7 +63,6 @@
 #include "extensions/browser/api_test_utils.h"
 #include "extensions/common/extension_builder.h"
 #include "extensions/common/manifest_handlers/oauth2_manifest_handler.h"
-#include "extensions/common/test_util.h"
 #include "google_apis/gaia/google_service_auth_error.h"
 #include "google_apis/gaia/oauth2_mint_token_flow.h"
 #include "google_apis/gaia/oauth2_token_service.h"
diff --git a/chrome/browser/extensions/api/image_writer_private/removable_storage_provider_chromeos_unittest.cc b/chrome/browser/extensions/api/image_writer_private/removable_storage_provider_chromeos_unittest.cc
index 70687146..bbbea6d 100644
--- a/chrome/browser/extensions/api/image_writer_private/removable_storage_provider_chromeos_unittest.cc
+++ b/chrome/browser/extensions/api/image_writer_private/removable_storage_provider_chromeos_unittest.cc
@@ -28,6 +28,7 @@
 const char kProductName[] = "Test Product";
 const uint64_t kDeviceSize = 1024 * 1024 * 1024;
 const bool kOnRemovableDevice = true;
+const char kDiskFileSystemType[] = "vfat";
 
 const char kUnknownSDDiskModel[] = "SD Card";
 const char kUnknownUSBDiskModel[] = "USB Drive";
@@ -75,17 +76,10 @@
         kMountPath,
         chromeos::MOUNT_TYPE_DEVICE,
         chromeos::disks::MOUNT_CONDITION_NONE);
-    disk_mount_manager_mock_->CreateDiskEntryForMountDevice(mount_info,
-                                                            kDeviceId,
-                                                            kDeviceName,
-                                                            vendor_name,
-                                                            product_name,
-                                                            device_type,
-                                                            kDeviceSize,
-                                                            is_parent,
-                                                            has_media,
-                                                            on_boot_device,
-                                                            kOnRemovableDevice);
+    disk_mount_manager_mock_->CreateDiskEntryForMountDevice(
+        mount_info, kDeviceId, kDeviceName, vendor_name, product_name,
+        device_type, kDeviceSize, is_parent, has_media, on_boot_device,
+        kOnRemovableDevice, kDiskFileSystemType);
   }
 
   // Checks if the DeviceList has a specific entry.
diff --git a/chrome/browser/extensions/api/image_writer_private/test_utils.cc b/chrome/browser/extensions/api/image_writer_private/test_utils.cc
index 3b77a7d8..a19da48 100644
--- a/chrome/browser/extensions/api/image_writer_private/test_utils.cc
+++ b/chrome/browser/extensions/api/image_writer_private/test_utils.cc
@@ -243,20 +243,11 @@
   // Adds a disk entry for test_device_path_ with the same device and file path.
   disk_manager->CreateDiskEntryForMountDevice(
       chromeos::disks::DiskMountManager::MountPointInfo(
-          test_device_path_.value(),
-          "/dummy/mount",
-          chromeos::MOUNT_TYPE_DEVICE,
-          chromeos::disks::MOUNT_CONDITION_NONE),
-      "device_id",
-      "device_label",
-      "Vendor",
-      "Product",
-      chromeos::DEVICE_TYPE_USB,
-      kTestFileSize,
-      true,
-      true,
-      true,
-      false);
+          test_device_path_.value(), "/dummy/mount",
+          chromeos::MOUNT_TYPE_DEVICE, chromeos::disks::MOUNT_CONDITION_NONE),
+      "device_id", "device_label", "Vendor", "Product",
+      chromeos::DEVICE_TYPE_USB, kTestFileSize, true, true, true, false,
+      kTestFileSystemType);
   disk_manager->SetupDefaultReplies();
 #else
   ImageWriterUtilityClient::SetFactoryForTesting(&utility_client_factory_);
diff --git a/chrome/browser/extensions/api/image_writer_private/test_utils.h b/chrome/browser/extensions/api/image_writer_private/test_utils.h
index b5c4e81..8ad775db 100644
--- a/chrome/browser/extensions/api/image_writer_private/test_utils.h
+++ b/chrome/browser/extensions/api/image_writer_private/test_utils.h
@@ -36,6 +36,8 @@
 const int kImagePattern = 0x55555555; // 01010101
 // Pattern to use in the device file.
 const int kDevicePattern = 0xAAAAAAAA; // 10101010
+// Disk file system type
+const char kTestFileSystemType[] = "vfat";
 
 // A mock around the operation manager for tracking callbacks.  Note that there
 // are non-virtual methods on this class that should not be called in tests.
diff --git a/chrome/browser/extensions/api/management/management_api_browsertest.cc b/chrome/browser/extensions/api/management/management_api_browsertest.cc
index 75c9f026..b6cbae0 100644
--- a/chrome/browser/extensions/api/management/management_api_browsertest.cc
+++ b/chrome/browser/extensions/api/management/management_api_browsertest.cc
@@ -26,7 +26,6 @@
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/extension_system.h"
 #include "extensions/browser/notification_types.h"
-#include "extensions/common/test_util.h"
 #include "extensions/test/extension_test_message_listener.h"
 
 namespace keys = extension_management_api_constants;
diff --git a/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.cc b/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.cc
index bcb040b..a06e208f 100644
--- a/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.cc
+++ b/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.cc
@@ -306,35 +306,4 @@
       render_frame_id, request_id, allowed));
 }
 
-void ChromeWebViewPermissionHelperDelegate::FileSystemAccessedSync(
-    int render_process_id,
-    int render_frame_id,
-    const GURL& url,
-    bool blocked_by_policy,
-    IPC::Message* reply_msg) {
-  RequestFileSystemPermission(
-      url,
-      !blocked_by_policy,
-      base::Bind(&ChromeWebViewPermissionHelperDelegate::
-                     FileSystemAccessedSyncResponse,
-                 weak_factory_.GetWeakPtr(),
-                 render_process_id,
-                 render_frame_id,
-                 url,
-                 reply_msg));
-}
-
-void ChromeWebViewPermissionHelperDelegate::FileSystemAccessedSyncResponse(
-    int render_process_id,
-    int render_frame_id,
-    const GURL& url,
-    IPC::Message* reply_msg,
-    bool allowed) {
-  TabSpecificContentSettings::FileSystemAccessed(
-      render_process_id, render_frame_id, url, !allowed);
-  ChromeViewHostMsg_RequestFileSystemAccessSync::WriteReplyParams(reply_msg,
-                                                                  allowed);
-  Send(reply_msg);
-}
-
 }  // namespace extensions
diff --git a/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.h b/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.h
index 8aa9284a1..f40150c 100644
--- a/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.h
+++ b/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.h
@@ -46,11 +46,6 @@
                                int request_id,
                                const GURL& url,
                                bool blocked_by_policy) override;
-  void FileSystemAccessedSync(int render_process_id,
-                              int render_frame_id,
-                              const GURL& url,
-                              bool blocked_by_policy,
-                              IPC::Message* reply_msg) override;
 #if BUILDFLAG(ENABLE_PLUGINS)
   // content::WebContentsObserver implementation.
   bool OnMessageReceived(const IPC::Message& message,
@@ -107,12 +102,6 @@
                                        const GURL& url,
                                        bool allowed);
 
-  void FileSystemAccessedSyncResponse(int render_process_id,
-                                      int render_frame_id,
-                                      const GURL& url,
-                                      IPC::Message* reply_msg,
-                                      bool allowed);
-
   WebViewGuest* web_view_guest() {
     return web_view_permission_helper()->web_view_guest();
   }
diff --git a/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc b/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc
index 1f5d69cf..7c0132d1 100644
--- a/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc
+++ b/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc
@@ -105,13 +105,13 @@
 #endif
 
 #if BUILDFLAG(ENABLE_OFFLINE_PAGES)
+#include "chrome/browser/offline_pages/downloads/resource_throttle.h"
 #include "chrome/browser/offline_pages/offliner_user_data.h"
 #include "chrome/browser/offline_pages/resource_loading_observer.h"
 #endif
 
 #if defined(OS_ANDROID)
 #include "chrome/browser/android/download/intercept_download_resource_throttle.h"
-#include "chrome/browser/android/offline_pages/downloads/resource_throttle.h"
 #include "chrome/browser/loader/data_reduction_proxy_resource_throttle_android.h"
 #include "components/navigation_interception/intercept_navigation_delegate.h"
 #endif
diff --git a/chrome/browser/media/OWNERS b/chrome/browser/media/OWNERS
index 9c6c9f8..a99e095 100644
--- a/chrome/browser/media/OWNERS
+++ b/chrome/browser/media/OWNERS
@@ -7,6 +7,7 @@
 per-file cast_*=miu@chromium.org
 
 # For Media Engagement
+per-file media_engagement*=beccahughes@chromium.org
 per-file media_engagement*=mlamouri@chromium.org
 
 # For IPC security review
diff --git a/chrome/browser/media/media_engagement_contents_observer.cc b/chrome/browser/media/media_engagement_contents_observer.cc
index 7f1d954..4c883aba 100644
--- a/chrome/browser/media/media_engagement_contents_observer.cc
+++ b/chrome/browser/media/media_engagement_contents_observer.cc
@@ -101,6 +101,7 @@
       .SetVisits_Total(score.visits())
       .SetEngagement_Score(ConvertScoreToPercentage(score.actual_score()))
       .SetPlaybacks_Delta(significant_playback_recorded_)
+      .SetEngagement_IsHigh(score.high_score())
       .Record(ukm_recorder);
 }
 
diff --git a/chrome/browser/media/media_engagement_contents_observer_unittest.cc b/chrome/browser/media/media_engagement_contents_observer_unittest.cc
index bd7afed..e51f2f0 100644
--- a/chrome/browser/media/media_engagement_contents_observer_unittest.cc
+++ b/chrome/browser/media/media_engagement_contents_observer_unittest.cc
@@ -165,7 +165,8 @@
                       int playbacks_total,
                       int visits_total,
                       int score,
-                      int playbacks_delta) {
+                      int playbacks_delta,
+                      bool high_score) {
     using Entry = ukm::builders::Media_Engagement_SessionFinished;
 
     std::vector<std::pair<const char*, int64_t>> metrics = {
@@ -173,6 +174,7 @@
         {Entry::kVisits_TotalName, visits_total},
         {Entry::kEngagement_ScoreName, score},
         {Entry::kPlaybacks_DeltaName, playbacks_delta},
+        {Entry::kEngagement_IsHighName, high_score},
     };
 
     const ukm::UkmSource* source =
@@ -633,7 +635,7 @@
   EXPECT_TRUE(WasSignificantPlaybackRecorded());
 
   SimulateDestroy();
-  ExpectUkmEntry(url, 6, 7, 86, 1);
+  ExpectUkmEntry(url, 6, 7, 86, 1, true);
 }
 
 TEST_F(MediaEngagementContentsObserverTest,
@@ -646,7 +648,7 @@
   ExpectScores(url, 5.0 / 7.0, 7, 5);
 
   SimulateDestroy();
-  ExpectUkmEntry(url, 5, 7, 71, 0);
+  ExpectUkmEntry(url, 5, 7, 71, 0, true);
 }
 
 TEST_F(MediaEngagementContentsObserverTest, RecordUkmMetricsOnNavigate) {
@@ -661,20 +663,20 @@
   EXPECT_TRUE(WasSignificantPlaybackRecorded());
 
   Navigate(GURL("https://www.example.org"));
-  ExpectUkmEntry(url, 6, 7, 86, 1);
+  ExpectUkmEntry(url, 6, 7, 86, 1, true);
 }
 
 TEST_F(MediaEngagementContentsObserverTest,
        RecordUkmMetricsOnNavigate_NoPlaybacks) {
   GURL url("https://www.google.com");
-  SetScores(url, 6, 5);
+  SetScores(url, 9, 2);
   Navigate(url);
 
   EXPECT_FALSE(WasSignificantPlaybackRecorded());
-  ExpectScores(url, 5.0 / 7.0, 7, 5);
+  ExpectScores(url, 2 / 10.0, 10, 2);
 
   Navigate(GURL("https://www.example.org"));
-  ExpectUkmEntry(url, 5, 7, 71, 0);
+  ExpectUkmEntry(url, 2, 10, 20, 0, false);
 }
 
 TEST_F(MediaEngagementContentsObserverTest, DoNotRecordMetricsOnInternalUrl) {
diff --git a/chrome/browser/media/router/mojo/media_router_mojo_impl_unittest.cc b/chrome/browser/media/router/mojo/media_router_mojo_impl_unittest.cc
index 82b9255a9a5..c8fbd73 100644
--- a/chrome/browser/media/router/mojo/media_router_mojo_impl_unittest.cc
+++ b/chrome/browser/media/router/mojo/media_router_mojo_impl_unittest.cc
@@ -38,9 +38,6 @@
 #include "extensions/browser/process_manager.h"
 #include "extensions/browser/process_manager_factory.h"
 #include "extensions/common/extension.h"
-#include "extensions/common/extension_builder.h"
-#include "extensions/common/test_util.h"
-#include "extensions/common/value_builder.h"
 #include "media/base/gmock_callback_support.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/browser/metrics/process_memory_metrics_emitter.cc b/chrome/browser/metrics/process_memory_metrics_emitter.cc
index 13ab0f0e..0169163f 100644
--- a/chrome/browser/metrics/process_memory_metrics_emitter.cc
+++ b/chrome/browser/metrics/process_memory_metrics_emitter.cc
@@ -229,7 +229,7 @@
       // Only count non hosted apps extensions.
       const extensions::Extension* extension =
           registry->enabled_extensions().GetByID(extension_id);
-      if (!extension->is_hosted_app())
+      if (extension && !extension->is_hosted_app())
         number_of_extensions++;
     }
   }
diff --git a/chrome/browser/notifications/notification_platform_bridge_android.cc b/chrome/browser/notifications/notification_platform_bridge_android.cc
index 5c24a155..5d4a332 100644
--- a/chrome/browser/notifications/notification_platform_bridge_android.cc
+++ b/chrome/browser/notifications/notification_platform_bridge_android.cc
@@ -152,6 +152,7 @@
     const JavaParamRef<jobject>& java_object,
     const JavaParamRef<jstring>& java_notification_id,
     const JavaParamRef<jstring>& java_origin,
+    const JavaParamRef<jstring>& java_scope_url,
     const JavaParamRef<jstring>& java_profile_id,
     jboolean incognito,
     const JavaParamRef<jstring>& java_tag,
@@ -171,8 +172,10 @@
           : base::NullableString16();
 
   GURL origin(ConvertJavaStringToUTF8(env, java_origin));
+  GURL scope_url(ConvertJavaStringToUTF8(env, java_scope_url));
   regenerated_notification_infos_[notification_id] =
-      RegeneratedNotificationInfo(origin.spec(), tag, webapk_package);
+      RegeneratedNotificationInfo(origin.spec(), scope_url.spec(), tag,
+                                  webapk_package);
 
   ProfileManager* profile_manager = g_browser_process->profile_manager();
   DCHECK(profile_manager);
@@ -199,7 +202,7 @@
   const RegeneratedNotificationInfo& info = iterator->second;
   regenerated_notification_infos_[notification_id] =
       RegeneratedNotificationInfo(
-          info.origin, info.tag,
+          info.origin, info.service_worker_scope, info.tag,
           ConvertJavaStringToUTF8(env, java_webapk_package));
 }
 
@@ -286,13 +289,14 @@
       ConvertUTF8ToJavaString(env, profile_id);
 
   Java_NotificationPlatformBridge_displayNotification(
-      env, java_object_, j_notification_id, j_origin, j_profile_id, incognito,
-      tag, title, body, image, notification_icon, badge, vibration_pattern,
-      notification.timestamp().ToJavaTime(), notification.renotify(),
-      notification.silent(), actions);
+      env, java_object_, j_notification_id, j_origin, j_scope_url, j_profile_id,
+      incognito, tag, title, body, image, notification_icon, badge,
+      vibration_pattern, notification.timestamp().ToJavaTime(),
+      notification.renotify(), notification.silent(), actions);
 
   regenerated_notification_infos_[notification_id] =
-      RegeneratedNotificationInfo(origin_url.spec(), notification.tag());
+      RegeneratedNotificationInfo(origin_url.spec(), scope_url.spec(),
+                                  notification.tag(), base::nullopt);
 }
 
 void NotificationPlatformBridgeAndroid::Close(
@@ -308,9 +312,14 @@
 
   ScopedJavaLocalRef<jstring> j_notification_id =
       ConvertUTF8ToJavaString(env, notification_id);
-  ScopedJavaLocalRef<jstring> origin =
+  ScopedJavaLocalRef<jstring> j_origin =
       ConvertUTF8ToJavaString(env, notification_info.origin);
-  ScopedJavaLocalRef<jstring> tag =
+
+  GURL scope_url(notification_info.service_worker_scope);
+  ScopedJavaLocalRef<jstring> j_scope_url =
+      ConvertUTF8ToJavaString(env, scope_url.spec());
+
+  ScopedJavaLocalRef<jstring> j_tag =
       ConvertUTF8ToJavaString(env, notification_info.tag);
 
   bool has_queried_webapk_package =
@@ -323,7 +332,7 @@
   regenerated_notification_infos_.erase(iterator);
 
   Java_NotificationPlatformBridge_closeNotification(
-      env, java_object_, j_notification_id, origin, tag,
+      env, java_object_, j_notification_id, j_origin, j_scope_url, j_tag,
       has_queried_webapk_package, j_webapk_package);
 }
 
@@ -353,15 +362,15 @@
     RegeneratedNotificationInfo() {}
 
 NotificationPlatformBridgeAndroid::RegeneratedNotificationInfo::
-    RegeneratedNotificationInfo(const std::string& origin,
-                                const std::string& tag)
-    : origin(origin), tag(tag) {}
-
-NotificationPlatformBridgeAndroid::RegeneratedNotificationInfo::
-    RegeneratedNotificationInfo(const std::string& origin,
-                                const std::string& tag,
-                                const std::string& webapk_package)
-    : origin(origin), tag(tag), webapk_package(webapk_package) {}
+    RegeneratedNotificationInfo(
+        const std::string& origin,
+        const std::string& service_worker_scope,
+        const std::string& tag,
+        const base::Optional<std::string>& webapk_package)
+    : origin(origin),
+      service_worker_scope(service_worker_scope),
+      tag(tag),
+      webapk_package(webapk_package) {}
 
 NotificationPlatformBridgeAndroid::RegeneratedNotificationInfo::
     ~RegeneratedNotificationInfo() {}
diff --git a/chrome/browser/notifications/notification_platform_bridge_android.h b/chrome/browser/notifications/notification_platform_bridge_android.h
index 810f194..dd9cd4c8 100644
--- a/chrome/browser/notifications/notification_platform_bridge_android.h
+++ b/chrome/browser/notifications/notification_platform_bridge_android.h
@@ -44,6 +44,7 @@
       const base::android::JavaParamRef<jobject>& java_object,
       const base::android::JavaParamRef<jstring>& java_notification_id,
       const base::android::JavaParamRef<jstring>& java_origin,
+      const base::android::JavaParamRef<jstring>& java_scope_url,
       const base::android::JavaParamRef<jstring>& java_profile_id,
       jboolean incognito,
       const base::android::JavaParamRef<jstring>& java_tag,
@@ -102,14 +103,15 @@
   // query is done.
   struct RegeneratedNotificationInfo {
     RegeneratedNotificationInfo();
-    RegeneratedNotificationInfo(const std::string& origin,
-                                const std::string& tag);
-    RegeneratedNotificationInfo(const std::string& origin,
-                                const std::string& tag,
-                                const std::string& webapk_package);
+    RegeneratedNotificationInfo(
+        const std::string& origin,
+        const std::string& service_worker_scope,
+        const std::string& tag,
+        const base::Optional<std::string>& webapk_package);
     ~RegeneratedNotificationInfo();
 
     std::string origin;
+    std::string service_worker_scope;
     std::string tag;
     base::Optional<std::string> webapk_package;
   };
diff --git a/chrome/browser/offline_pages/DEPS b/chrome/browser/offline_pages/DEPS
index 37b2e5d7..78ddc63 100644
--- a/chrome/browser/offline_pages/DEPS
+++ b/chrome/browser/offline_pages/DEPS
@@ -5,5 +5,5 @@
   # Android. OS-specific subfolders should have their own DEPS enabling specific
   # dependencies.
   "-base/android",
-  "-chrome/browser/android",
+  "-chrome/browser/offline_pages/android",
 ]
diff --git a/chrome/browser/offline_pages/android/DEPS b/chrome/browser/offline_pages/android/DEPS
index 34f5f6f..ff20cbfd 100644
--- a/chrome/browser/offline_pages/android/DEPS
+++ b/chrome/browser/offline_pages/android/DEPS
@@ -2,7 +2,4 @@
   # This folder contains Android-specific code.
   "+base/android",
   "+chrome/browser/android",
-  # c/b/a/o_p folder will eventually be refactored into
-  # platform-independent code and Android-specific, and will go away.
-  "!chrome/browser/android/offline_pages",
 ]
diff --git a/chrome/browser/android/offline_pages/downloads/offline_page_download_bridge.cc b/chrome/browser/offline_pages/android/downloads/offline_page_download_bridge.cc
similarity index 98%
rename from chrome/browser/android/offline_pages/downloads/offline_page_download_bridge.cc
rename to chrome/browser/offline_pages/android/downloads/offline_page_download_bridge.cc
index 0219a03..3f710c58 100644
--- a/chrome/browser/android/offline_pages/downloads/offline_page_download_bridge.cc
+++ b/chrome/browser/offline_pages/android/downloads/offline_page_download_bridge.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 "chrome/browser/android/offline_pages/downloads/offline_page_download_bridge.h"
+#include "chrome/browser/offline_pages/android/downloads/offline_page_download_bridge.h"
 
 #include <vector>
 
@@ -12,9 +12,9 @@
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "chrome/browser/android/download/download_controller_base.h"
-#include "chrome/browser/android/offline_pages/downloads/offline_page_infobar_delegate.h"
-#include "chrome/browser/android/offline_pages/downloads/offline_page_notification_bridge.h"
 #include "chrome/browser/android/tab_android.h"
+#include "chrome/browser/offline_pages/android/downloads/offline_page_infobar_delegate.h"
+#include "chrome/browser/offline_pages/android/downloads/offline_page_notification_bridge.h"
 #include "chrome/browser/offline_pages/offline_page_mhtml_archiver.h"
 #include "chrome/browser/offline_pages/offline_page_model_factory.h"
 #include "chrome/browser/offline_pages/offline_page_utils.h"
@@ -204,8 +204,7 @@
   return Java_OfflinePageDownloadBridge_createDownloadItem(
       env, ConvertUTF8ToJavaString(env, item.guid),
       ConvertUTF8ToJavaString(env, item.url.spec()), item.download_state,
-      item.download_progress_bytes,
-      ConvertUTF16ToJavaString(env, item.title),
+      item.download_progress_bytes, ConvertUTF16ToJavaString(env, item.title),
       ConvertUTF8ToJavaString(env, item.target_path.value()),
       item.start_time.ToJavaTime(), item.total_bytes);
 }
diff --git a/chrome/browser/android/offline_pages/downloads/offline_page_download_bridge.h b/chrome/browser/offline_pages/android/downloads/offline_page_download_bridge.h
similarity index 76%
rename from chrome/browser/android/offline_pages/downloads/offline_page_download_bridge.h
rename to chrome/browser/offline_pages/android/downloads/offline_page_download_bridge.h
index 6576fca..d58f661 100644
--- a/chrome/browser/android/offline_pages/downloads/offline_page_download_bridge.h
+++ b/chrome/browser/offline_pages/android/downloads/offline_page_download_bridge.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 CHROME_BROWSER_ANDROID_OFFLINE_PAGES_DOWNLOADS_OFFLINE_PAGE_DOWNLOAD_BRIDGE_H_
-#define CHROME_BROWSER_ANDROID_OFFLINE_PAGES_DOWNLOADS_OFFLINE_PAGE_DOWNLOAD_BRIDGE_H_
+#ifndef CHROME_BROWSER_OFFLINE_PAGES_ANDROID_DOWNLOADS_OFFLINE_PAGE_DOWNLOAD_BRIDGE_H_
+#define CHROME_BROWSER_OFFLINE_PAGES_ANDROID_DOWNLOADS_OFFLINE_PAGE_DOWNLOAD_BRIDGE_H_
 
 #include <stdint.h>
 
@@ -31,29 +31,24 @@
                             content::BrowserContext* browser_context);
   ~OfflinePageDownloadBridge() override;
 
-  void Destroy(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& obj);
+  void Destroy(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj);
 
-  void GetAllItems(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& obj,
-      const base::android::JavaParamRef<jobject>& j_result_obj);
+  void GetAllItems(JNIEnv* env,
+                   const base::android::JavaParamRef<jobject>& obj,
+                   const base::android::JavaParamRef<jobject>& j_result_obj);
 
   base::android::ScopedJavaLocalRef<jobject> GetItemByGuid(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& obj,
       const base::android::JavaParamRef<jstring>& j_guid);
 
-  void DeleteItemByGuid(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& obj,
-      const base::android::JavaParamRef<jstring>& j_guid);
+  void DeleteItemByGuid(JNIEnv* env,
+                        const base::android::JavaParamRef<jobject>& obj,
+                        const base::android::JavaParamRef<jstring>& j_guid);
 
-  jlong GetOfflineIdByGuid(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& obj,
-      const base::android::JavaParamRef<jstring>& j_guid);
+  jlong GetOfflineIdByGuid(JNIEnv* env,
+                           const base::android::JavaParamRef<jobject>& obj,
+                           const base::android::JavaParamRef<jstring>& j_guid);
 
   void StartDownload(JNIEnv* env,
                      const base::android::JavaParamRef<jobject>& obj,
@@ -95,4 +90,4 @@
 }  // namespace android
 }  // namespace offline_pages
 
-#endif  // CHROME_BROWSER_ANDROID_OFFLINE_PAGES_DOWNLOADS_OFFLINE_PAGE_DOWNLOAD_BRIDGE_H_
+#endif  // CHROME_BROWSER_OFFLINE_PAGES_ANDROID_DOWNLOADS_OFFLINE_PAGE_DOWNLOAD_BRIDGE_H_
diff --git a/chrome/browser/android/offline_pages/downloads/offline_page_infobar_delegate.cc b/chrome/browser/offline_pages/android/downloads/offline_page_infobar_delegate.cc
similarity index 97%
rename from chrome/browser/android/offline_pages/downloads/offline_page_infobar_delegate.cc
rename to chrome/browser/offline_pages/android/downloads/offline_page_infobar_delegate.cc
index dd8a896..235b3d2 100644
--- a/chrome/browser/android/offline_pages/downloads/offline_page_infobar_delegate.cc
+++ b/chrome/browser/offline_pages/android/downloads/offline_page_infobar_delegate.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 "chrome/browser/android/offline_pages/downloads/offline_page_infobar_delegate.h"
+#include "chrome/browser/offline_pages/android/downloads/offline_page_infobar_delegate.h"
 
 #include "base/memory/ptr_util.h"
 #include "base/strings/utf_string_conversions.h"
diff --git a/chrome/browser/android/offline_pages/downloads/offline_page_infobar_delegate.h b/chrome/browser/offline_pages/android/downloads/offline_page_infobar_delegate.h
similarity index 92%
rename from chrome/browser/android/offline_pages/downloads/offline_page_infobar_delegate.h
rename to chrome/browser/offline_pages/android/downloads/offline_page_infobar_delegate.h
index 3e4a1ac..5db981f1 100644
--- a/chrome/browser/android/offline_pages/downloads/offline_page_infobar_delegate.h
+++ b/chrome/browser/offline_pages/android/downloads/offline_page_infobar_delegate.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 CHROME_BROWSER_ANDROID_OFFLINE_PAGES_DOWNLOADS_OFFLINE_PAGE_INFOBAR_DELEGATE_H_
-#define CHROME_BROWSER_ANDROID_OFFLINE_PAGES_DOWNLOADS_OFFLINE_PAGE_INFOBAR_DELEGATE_H_
+#ifndef CHROME_BROWSER_OFFLINE_PAGES_ANDROID_DOWNLOADS_OFFLINE_PAGE_INFOBAR_DELEGATE_H_
+#define CHROME_BROWSER_OFFLINE_PAGES_ANDROID_DOWNLOADS_OFFLINE_PAGE_INFOBAR_DELEGATE_H_
 
 #include "base/callback.h"
 #include "chrome/browser/android/download/duplicate_download_infobar_delegate.h"
@@ -64,4 +64,4 @@
 
 }  // namespace offline_pages
 
-#endif  // CHROME_BROWSER_ANDROID_OFFLINE_PAGES_DOWNLOADS_OFFLINE_PAGE_INFOBAR_DELEGATE_H_
+#endif  // CHROME_BROWSER_OFFLINE_PAGES_ANDROID_DOWNLOADS_OFFLINE_PAGE_INFOBAR_DELEGATE_H_
diff --git a/chrome/browser/android/offline_pages/downloads/offline_page_notification_bridge.cc b/chrome/browser/offline_pages/android/downloads/offline_page_notification_bridge.cc
similarity index 95%
rename from chrome/browser/android/offline_pages/downloads/offline_page_notification_bridge.cc
rename to chrome/browser/offline_pages/android/downloads/offline_page_notification_bridge.cc
index 5196942f..acc365c 100644
--- a/chrome/browser/android/offline_pages/downloads/offline_page_notification_bridge.cc
+++ b/chrome/browser/offline_pages/android/downloads/offline_page_notification_bridge.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 "chrome/browser/android/offline_pages/downloads/offline_page_notification_bridge.h"
+#include "chrome/browser/offline_pages/android/downloads/offline_page_notification_bridge.h"
 
 #include "base/android/jni_string.h"
-#include "chrome/browser/android/offline_pages/downloads/offline_page_download_bridge.h"
+#include "chrome/browser/offline_pages/android/downloads/offline_page_download_bridge.h"
 #include "jni/OfflinePageNotificationBridge_jni.h"
 
 using base::android::AttachCurrentThread;
diff --git a/chrome/browser/android/offline_pages/downloads/offline_page_notification_bridge.h b/chrome/browser/offline_pages/android/downloads/offline_page_notification_bridge.h
similarity index 85%
rename from chrome/browser/android/offline_pages/downloads/offline_page_notification_bridge.h
rename to chrome/browser/offline_pages/android/downloads/offline_page_notification_bridge.h
index a22ad3d..7f9d8b0 100644
--- a/chrome/browser/android/offline_pages/downloads/offline_page_notification_bridge.h
+++ b/chrome/browser/offline_pages/android/downloads/offline_page_notification_bridge.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 CHROME_BROWSER_ANDROID_OFFLINE_PAGES_DOWNLOADS_OFFLINE_PAGE_NOTIFICATION_BRIDGE_H_
-#define CHROME_BROWSER_ANDROID_OFFLINE_PAGES_DOWNLOADS_OFFLINE_PAGE_NOTIFICATION_BRIDGE_H_
+#ifndef CHROME_BROWSER_OFFLINE_PAGES_ANDROID_DOWNLOADS_OFFLINE_PAGE_NOTIFICATION_BRIDGE_H_
+#define CHROME_BROWSER_OFFLINE_PAGES_ANDROID_DOWNLOADS_OFFLINE_PAGE_NOTIFICATION_BRIDGE_H_
 
 #include <stdint.h>
 
@@ -33,4 +33,4 @@
 }  // namespace android
 }  // namespace offline_pages
 
-#endif  // CHROME_BROWSER_ANDROID_OFFLINE_PAGES_DOWNLOADS_OFFLINE_PAGE_NOTIFICATION_BRIDGE_H_
+#endif  // CHROME_BROWSER_OFFLINE_PAGES_ANDROID_DOWNLOADS_OFFLINE_PAGE_NOTIFICATION_BRIDGE_H_
diff --git a/chrome/browser/android/offline_pages/evaluation/evaluation_test_scheduler.cc b/chrome/browser/offline_pages/android/evaluation/evaluation_test_scheduler.cc
similarity index 98%
rename from chrome/browser/android/offline_pages/evaluation/evaluation_test_scheduler.cc
rename to chrome/browser/offline_pages/android/evaluation/evaluation_test_scheduler.cc
index 0c65168..42786be 100644
--- a/chrome/browser/android/offline_pages/evaluation/evaluation_test_scheduler.cc
+++ b/chrome/browser/offline_pages/android/evaluation/evaluation_test_scheduler.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 "chrome/browser/android/offline_pages/evaluation/evaluation_test_scheduler.h"
+#include "chrome/browser/offline_pages/android/evaluation/evaluation_test_scheduler.h"
 
 #include "base/logging.h"
 #include "base/threading/thread_task_runner_handle.h"
diff --git a/chrome/browser/android/offline_pages/evaluation/evaluation_test_scheduler.h b/chrome/browser/offline_pages/android/evaluation/evaluation_test_scheduler.h
similarity index 84%
rename from chrome/browser/android/offline_pages/evaluation/evaluation_test_scheduler.h
rename to chrome/browser/offline_pages/android/evaluation/evaluation_test_scheduler.h
index 965e7d7..f98d880 100644
--- a/chrome/browser/android/offline_pages/evaluation/evaluation_test_scheduler.h
+++ b/chrome/browser/offline_pages/android/evaluation/evaluation_test_scheduler.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 CHROME_BROWSER_ANDROID_OFFLINE_PAGES_EVALUATION_EVALUATION_TEST_SCHEDULER_H_
-#define CHROME_BROWSER_ANDROID_OFFLINE_PAGES_EVALUATION_EVALUATION_TEST_SCHEDULER_H_
+#ifndef CHROME_BROWSER_OFFLINE_PAGES_ANDROID_EVALUATION_EVALUATION_TEST_SCHEDULER_H_
+#define CHROME_BROWSER_OFFLINE_PAGES_ANDROID_EVALUATION_EVALUATION_TEST_SCHEDULER_H_
 
 #include "components/offline_pages/core/background/scheduler.h"
 
@@ -36,4 +36,4 @@
 }  // namespace android
 }  // namespace offline_pages
 
-#endif  // CHROME_BROWSER_ANDROID_OFFLINE_PAGES_EVALUATION_EVALUATION_TEST_SCHEDULER_H_
+#endif  // CHROME_BROWSER_OFFLINE_PAGES_ANDROID_EVALUATION_EVALUATION_TEST_SCHEDULER_H_
diff --git a/chrome/browser/android/offline_pages/evaluation/offline_page_evaluation_bridge.cc b/chrome/browser/offline_pages/android/evaluation/offline_page_evaluation_bridge.cc
similarity index 98%
rename from chrome/browser/android/offline_pages/evaluation/offline_page_evaluation_bridge.cc
rename to chrome/browser/offline_pages/android/evaluation/offline_page_evaluation_bridge.cc
index ac7bf4f8..150f193d 100644
--- a/chrome/browser/android/offline_pages/evaluation/offline_page_evaluation_bridge.cc
+++ b/chrome/browser/offline_pages/android/evaluation/offline_page_evaluation_bridge.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 "chrome/browser/android/offline_pages/evaluation/offline_page_evaluation_bridge.h"
+#include "chrome/browser/offline_pages/android/evaluation/offline_page_evaluation_bridge.h"
 
 #include "base/android/callback_android.h"
 #include "base/android/jni_android.h"
@@ -12,11 +12,11 @@
 #include "base/memory/ptr_util.h"
 #include "base/sequenced_task_runner.h"
 #include "base/task_scheduler/post_task.h"
-#include "chrome/browser/android/offline_pages/downloads/offline_page_notification_bridge.h"
-#include "chrome/browser/android/offline_pages/evaluation/evaluation_test_scheduler.h"
 #include "chrome/browser/net/nqe/ui_network_quality_estimator_service.h"
 #include "chrome/browser/net/nqe/ui_network_quality_estimator_service_factory.h"
 #include "chrome/browser/offline_pages/android/background_scheduler_bridge.h"
+#include "chrome/browser/offline_pages/android/downloads/offline_page_notification_bridge.h"
+#include "chrome/browser/offline_pages/android/evaluation/evaluation_test_scheduler.h"
 #include "chrome/browser/offline_pages/android/prerendering_offliner.h"
 #include "chrome/browser/offline_pages/background_loader_offliner.h"
 #include "chrome/browser/offline_pages/offline_page_model_factory.h"
diff --git a/chrome/browser/android/offline_pages/evaluation/offline_page_evaluation_bridge.h b/chrome/browser/offline_pages/android/evaluation/offline_page_evaluation_bridge.h
similarity index 95%
rename from chrome/browser/android/offline_pages/evaluation/offline_page_evaluation_bridge.h
rename to chrome/browser/offline_pages/android/evaluation/offline_page_evaluation_bridge.h
index fafff2a..4660a31 100644
--- a/chrome/browser/android/offline_pages/evaluation/offline_page_evaluation_bridge.h
+++ b/chrome/browser/offline_pages/android/evaluation/offline_page_evaluation_bridge.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 CHROME_BROWSER_ANDROID_OFFLINE_PAGES_EVALUATION_OFFLINE_PAGE_EVALUATION_BRIDGE_H_
-#define CHROME_BROWSER_ANDROID_OFFLINE_PAGES_EVALUATION_OFFLINE_PAGE_EVALUATION_BRIDGE_H_
+#ifndef CHROME_BROWSER_OFFLINE_PAGES_ANDROID_EVALUATION_OFFLINE_PAGE_EVALUATION_BRIDGE_H_
+#define CHROME_BROWSER_OFFLINE_PAGES_ANDROID_EVALUATION_OFFLINE_PAGE_EVALUATION_BRIDGE_H_
 
 #include "base/android/jni_weak_ref.h"
 #include "base/android/scoped_java_ref.h"
@@ -109,4 +109,4 @@
 }  // namespace android
 }  // namespace offline_pages
 
-#endif  // CHROME_BROWSER_ANDROID_OFFLINE_PAGES_EVALUATION_OFFLINE_PAGE_EVALUATION_BRIDGE_H_
+#endif  // CHROME_BROWSER_OFFLINE_PAGES_ANDROID_EVALUATION_OFFLINE_PAGE_EVALUATION_BRIDGE_H_
diff --git a/chrome/browser/android/offline_pages/evaluation/run_offline_page_evaluation_test.py b/chrome/browser/offline_pages/android/evaluation/run_offline_page_evaluation_test.py
similarity index 100%
rename from chrome/browser/android/offline_pages/evaluation/run_offline_page_evaluation_test.py
rename to chrome/browser/offline_pages/android/evaluation/run_offline_page_evaluation_test.py
diff --git a/chrome/browser/offline_pages/android/offline_page_model_factory.cc b/chrome/browser/offline_pages/android/offline_page_model_factory.cc
index b3358e9..1c085e5 100644
--- a/chrome/browser/offline_pages/android/offline_page_model_factory.cc
+++ b/chrome/browser/offline_pages/android/offline_page_model_factory.cc
@@ -57,7 +57,9 @@
   OfflinePageModelImpl* model = new OfflinePageModelImpl(
       std::move(metadata_store), std::move(archive_manager),
       background_task_runner);
+
   CctOriginObserver::AttachToOfflinePageModel(model);
+
   return model;
 }
 
diff --git a/chrome/browser/offline_pages/android/offline_page_utils_android.cc b/chrome/browser/offline_pages/android/offline_page_utils_android.cc
index 89eff5d..cba2a71 100644
--- a/chrome/browser/offline_pages/android/offline_page_utils_android.cc
+++ b/chrome/browser/offline_pages/android/offline_page_utils_android.cc
@@ -6,8 +6,8 @@
 
 #include "base/logging.h"
 #include "chrome/browser/android/tab_android.h"
-#include "chrome/browser/android/offline_pages/downloads/offline_page_infobar_delegate.h"
-#include "chrome/browser/android/offline_pages/downloads/offline_page_notification_bridge.h"
+#include "chrome/browser/offline_pages/android/downloads/offline_page_infobar_delegate.h"
+#include "chrome/browser/offline_pages/android/downloads/offline_page_notification_bridge.h"
 #include "content/public/browser/web_contents.h"
 
 // Android-specific part of OfflinePageUtils.
diff --git a/chrome/browser/offline_pages/android/request_coordinator_factory.cc b/chrome/browser/offline_pages/android/request_coordinator_factory.cc
index 7b3de6c..124b72bd 100644
--- a/chrome/browser/offline_pages/android/request_coordinator_factory.cc
+++ b/chrome/browser/offline_pages/android/request_coordinator_factory.cc
@@ -10,11 +10,11 @@
 #include "base/memory/singleton.h"
 #include "base/sequenced_task_runner.h"
 #include "base/task_scheduler/post_task.h"
-#include "chrome/browser/android/offline_pages/downloads/offline_page_notification_bridge.h"
 #include "chrome/browser/net/nqe/ui_network_quality_estimator_service.h"
 #include "chrome/browser/net/nqe/ui_network_quality_estimator_service_factory.h"
 #include "chrome/browser/offline_pages/android/background_scheduler_bridge.h"
 #include "chrome/browser/offline_pages/android/cct_request_observer.h"
+#include "chrome/browser/offline_pages/android/downloads/offline_page_notification_bridge.h"
 #include "chrome/browser/offline_pages/android/load_termination_listener_impl.h"
 #include "chrome/browser/offline_pages/android/prerendering_offliner.h"
 #include "chrome/browser/offline_pages/background_loader_offliner.h"
diff --git a/chrome/browser/android/offline_pages/downloads/resource_throttle.cc b/chrome/browser/offline_pages/downloads/resource_throttle.cc
similarity index 96%
rename from chrome/browser/android/offline_pages/downloads/resource_throttle.cc
rename to chrome/browser/offline_pages/downloads/resource_throttle.cc
index 126145e..ce3411a 100644
--- a/chrome/browser/android/offline_pages/downloads/resource_throttle.cc
+++ b/chrome/browser/offline_pages/downloads/resource_throttle.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 "chrome/browser/android/offline_pages/downloads/resource_throttle.h"
+#include "chrome/browser/offline_pages/downloads/resource_throttle.h"
 
 #include "base/logging.h"
 #include "chrome/browser/offline_pages/offline_page_utils.h"
diff --git a/chrome/browser/android/offline_pages/downloads/resource_throttle.h b/chrome/browser/offline_pages/downloads/resource_throttle.h
similarity index 83%
rename from chrome/browser/android/offline_pages/downloads/resource_throttle.h
rename to chrome/browser/offline_pages/downloads/resource_throttle.h
index 3bf5fd5..85d4f5b 100644
--- a/chrome/browser/android/offline_pages/downloads/resource_throttle.h
+++ b/chrome/browser/offline_pages/downloads/resource_throttle.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 CHROME_BROWSER_ANDROID_OFFLINE_PAGES_DOWNLOADS_RESOURCE_THROTTLE_H_
-#define CHROME_BROWSER_ANDROID_OFFLINE_PAGES_DOWNLOADS_RESOURCE_THROTTLE_H_
+#ifndef CHROME_BROWSER_OFFLINE_PAGES_DOWNLOADS_RESOURCE_THROTTLE_H_
+#define CHROME_BROWSER_OFFLINE_PAGES_DOWNLOADS_RESOURCE_THROTTLE_H_
 
 #include "content/public/browser/resource_throttle.h"
 #include "net/url_request/url_request.h"
@@ -35,4 +35,4 @@
 }  // namespace downloads
 }  // namespace offline_pages
 
-#endif  // CHROME_BROWSER_ANDROID_OFFLINE_PAGES_DOWNLOADS_RESOURCE_THROTTLE_H_
+#endif  // CHROME_BROWSER_OFFLINE_PAGES_DOWNLOADS_RESOURCE_THROTTLE_H_
diff --git a/chrome/browser/profiles/profile_manager_browsertest.cc b/chrome/browser/profiles/profile_manager_browsertest.cc
index 799e406..4e00ce1 100644
--- a/chrome/browser/profiles/profile_manager_browsertest.cc
+++ b/chrome/browser/profiles/profile_manager_browsertest.cc
@@ -447,8 +447,7 @@
 }
 
 // Flakes on ChromiumOS: http://crbug.com/758930
-IN_PROC_BROWSER_TEST_F(ProfileManagerBrowserTest,
-                       DISABLE_SwitchToProfile) {
+IN_PROC_BROWSER_TEST_F(ProfileManagerBrowserTest, DISABLED_SwitchToProfile) {
   // If multiprofile mode is not enabled, you can't switch between profiles.
   if (!profiles::IsMultipleProfilesEnabled())
     return;
@@ -504,7 +503,7 @@
 
 // Flakes on Windows: http://crbug.com/314905
 // Flakes on ChromiumOS: http://crbug.com/758930
-IN_PROC_BROWSER_TEST_F(ProfileManagerBrowserTest, DISABLE_EphemeralProfile) {
+IN_PROC_BROWSER_TEST_F(ProfileManagerBrowserTest, DISABLED_EphemeralProfile) {
   // If multiprofile mode is not enabled, you can't switch between profiles.
   if (!profiles::IsMultipleProfilesEnabled())
     return;
diff --git a/chrome/browser/renderer_host/chrome_render_message_filter.cc b/chrome/browser/renderer_host/chrome_render_message_filter.cc
index 08edbfd..c57e6d1 100644
--- a/chrome/browser/renderer_host/chrome_render_message_filter.cc
+++ b/chrome/browser/renderer_host/chrome_render_message_filter.cc
@@ -191,26 +191,6 @@
   Send(reply_msg);
 }
 
-#if BUILDFLAG(ENABLE_EXTENSIONS)
-void ChromeRenderMessageFilter::FileSystemAccessedSyncOnUIThread(
-    int render_process_id,
-    int render_frame_id,
-    const GURL& url,
-    bool blocked_by_policy,
-    IPC::Message* reply_msg) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  extensions::WebViewPermissionHelper* web_view_permission_helper =
-      extensions::WebViewPermissionHelper::FromFrameID(
-          render_process_id, render_frame_id);
-  // Between the time the permission request is made and the time it is handled
-  // by the UI thread, the extensions::WebViewPermissionHelper might be gone.
-  if (!web_view_permission_helper)
-    return;
-  web_view_permission_helper->FileSystemAccessedSync(
-      render_process_id, render_frame_id, url, blocked_by_policy, reply_msg);
-}
-#endif
-
 void ChromeRenderMessageFilter::OnRequestFileSystemAccessAsync(
     int render_frame_id,
     int request_id,
diff --git a/chrome/browser/renderer_host/chrome_render_message_filter.h b/chrome/browser/renderer_host/chrome_render_message_filter.h
index dbefcb3..6b92d89 100644
--- a/chrome/browser/renderer_host/chrome_render_message_filter.h
+++ b/chrome/browser/renderer_host/chrome_render_message_filter.h
@@ -69,14 +69,6 @@
                                      const GURL& origin_url,
                                      const GURL& top_origin_url,
                                      IPC::Message* message);
-#if BUILDFLAG(ENABLE_EXTENSIONS)
-  static void FileSystemAccessedSyncOnUIThread(
-      int render_process_id,
-      int render_frame_id,
-      const GURL& url,
-      bool blocked_by_policy,
-      IPC::Message* reply_msg);
-#endif
   void OnRequestFileSystemAccessAsync(int render_frame_id,
                                       int request_id,
                                       const GURL& origin_url,
diff --git a/chrome/browser/resources/chromeos/arc_support/main.css b/chrome/browser/resources/chromeos/arc_support/main.css
index d1ebb8f..9185f6e 100644
--- a/chrome/browser/resources/chromeos/arc_support/main.css
+++ b/chrome/browser/resources/chromeos/arc_support/main.css
@@ -217,7 +217,7 @@
   border: 1px solid #d9d9d9;
   box-sizing: border-box;
   flex: auto;
-  margin: 40px 0 0 0;
+  margin: 40px 0 14px 0;
   padding: 0;
 }
 
diff --git a/chrome/browser/resources/md_extensions/detail_view.html b/chrome/browser/resources/md_extensions/detail_view.html
index b7776be..f52d9b6 100644
--- a/chrome/browser/resources/md_extensions/detail_view.html
+++ b/chrome/browser/resources/md_extensions/detail_view.html
@@ -42,7 +42,7 @@
 
       #top-bar {
         align-items: center;
-        color: #5a5a5a;
+        color: var(--paper-grey-700);
         display: flex;
         font-size: 14px;
         height: 40px;
@@ -69,11 +69,11 @@
       }
 
       .control-line span {
-        color: #333;
+        color: var(--paper-grey-900);
       }
 
       .section {
-        @apply(--section);
+        @apply(--cr-section);
       }
 
       .section.block {
@@ -91,12 +91,12 @@
       }
 
       .section-title {
-        color: #333;
+        color: var(--paper-grey-900);
         margin-bottom: 12px;
       }
 
       .section-content {
-        color: #646464;
+        color: var(--paper-grey-600);
       }
 
       .actionable {
diff --git a/chrome/browser/resources/settings/device_page/stylus.html b/chrome/browser/resources/settings/device_page/stylus.html
index 919bb181..4c50c212 100644
--- a/chrome/browser/resources/settings/device_page/stylus.html
+++ b/chrome/browser/resources/settings/device_page/stylus.html
@@ -15,10 +15,6 @@
         margin-top: 0;
       }
 
-      settings-toggle-button {
-        width: 100%;
-      }
-
       paper-spinner {
         margin-left: 12px;
         @apply(--cr-icon-height-width);
diff --git a/chrome/browser/safe_browsing/chrome_password_protection_service.cc b/chrome/browser/safe_browsing/chrome_password_protection_service.cc
index f136cd6..e7bd01c 100644
--- a/chrome/browser/safe_browsing/chrome_password_protection_service.cc
+++ b/chrome/browser/safe_browsing/chrome_password_protection_service.cc
@@ -37,10 +37,10 @@
 
 using content::BrowserThread;
 using sync_pb::UserEventSpecifics;
-using SyncPasswordReuseEvent = UserEventSpecifics::SyncPasswordReuseEvent;
-using PasswordReuseLookup = SyncPasswordReuseEvent::PasswordReuseLookup;
+using GaiaPasswordReuse = UserEventSpecifics::GaiaPasswordReuse;
+using PasswordReuseLookup = GaiaPasswordReuse::PasswordReuseLookup;
 using SafeBrowsingStatus =
-    SyncPasswordReuseEvent::PasswordReuseDetected::SafeBrowsingStatus;
+    GaiaPasswordReuse::PasswordReuseDetected::SafeBrowsingStatus;
 
 namespace safe_browsing {
 
@@ -217,7 +217,7 @@
     content::WebContents* web_contents) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-  if (!base::FeatureList::IsEnabled(safe_browsing::kSyncPasswordReuseEvent))
+  if (!base::FeatureList::IsEnabled(kGaiaPasswordReuseReporting))
     return;
 
   syncer::UserEventService* user_event_service =
@@ -230,22 +230,21 @@
   if (!specifics)
     return;
 
-  auto* const status = specifics->mutable_sync_password_reuse_event()
+  auto* const status = specifics->mutable_gaia_password_reuse_event()
                            ->mutable_reuse_detected()
                            ->mutable_status();
   status->set_enabled(IsSafeBrowsingEnabled());
 
-  safe_browsing::ExtendedReportingLevel erl =
-      safe_browsing::GetExtendedReportingLevel(*GetPrefs());
+  ExtendedReportingLevel erl = GetExtendedReportingLevel(*GetPrefs());
   switch (erl) {
-    case safe_browsing::SBER_LEVEL_OFF:
+    case SBER_LEVEL_OFF:
       status->set_safe_browsing_reporting_population(SafeBrowsingStatus::NONE);
       break;
-    case safe_browsing::SBER_LEVEL_LEGACY:
+    case SBER_LEVEL_LEGACY:
       status->set_safe_browsing_reporting_population(
           SafeBrowsingStatus::EXTENDED_REPORTING);
       break;
-    case safe_browsing::SBER_LEVEL_SCOUT:
+    case SBER_LEVEL_SCOUT:
       status->set_safe_browsing_reporting_population(SafeBrowsingStatus::SCOUT);
       break;
   }
@@ -307,7 +306,7 @@
     return;
 
   auto* const reuse_lookup =
-      specifics->mutable_sync_password_reuse_event()->mutable_reuse_lookup();
+      specifics->mutable_gaia_password_reuse_event()->mutable_reuse_lookup();
   reuse_lookup->set_lookup_result(result);
   user_event_service->RecordUserEvent(std::move(specifics));
 }
@@ -330,7 +329,7 @@
     return;
 
   PasswordReuseLookup* const reuse_lookup =
-      specifics->mutable_sync_password_reuse_event()->mutable_reuse_lookup();
+      specifics->mutable_gaia_password_reuse_event()->mutable_reuse_lookup();
   reuse_lookup->set_lookup_result(result);
   reuse_lookup->set_verdict(verdict);
   reuse_lookup->set_verdict_token(verdict_token);
@@ -341,7 +340,7 @@
     content::WebContents* web_contents,
     PasswordProtectionService::RequestOutcome outcome,
     const LoginReputationClientResponse* response) {
-  if (!base::FeatureList::IsEnabled(safe_browsing::kSyncPasswordReuseEvent))
+  if (!base::FeatureList::IsEnabled(kGaiaPasswordReuseReporting))
     return;
 
   switch (outcome) {
@@ -397,12 +396,11 @@
   resource.original_url = phishing_url;
   resource.is_subresource = false;
   resource.threat_type = SB_THREAT_TYPE_URL_PASSWORD_PROTECTION_PHISHING;
-  resource.threat_source =
-      safe_browsing::ThreatSource::PASSWORD_PROTECTION_SERVICE;
+  resource.threat_source = ThreatSource::PASSWORD_PROTECTION_SERVICE;
   resource.web_contents_getter =
-      safe_browsing::SafeBrowsingUIManager::UnsafeResource::
-          GetWebContentsGetter(web_contents->GetRenderProcessHost()->GetID(),
-                               web_contents->GetMainFrame()->GetRoutingID());
+      SafeBrowsingUIManager::UnsafeResource::GetWebContentsGetter(
+          web_contents->GetRenderProcessHost()->GetID(),
+          web_contents->GetMainFrame()->GetRoutingID());
   resource.token = token;
   if (!ui_manager_->IsWhitelisted(resource)) {
     web_contents->GetController().DiscardNonCommittedEntries();
@@ -411,7 +409,7 @@
 }
 
 void ChromePasswordProtectionService::UpdateSecurityState(
-    safe_browsing::SBThreatType threat_type,
+    SBThreatType threat_type,
     content::WebContents* web_contents) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   content::NavigationEntry* entry =
@@ -435,7 +433,7 @@
     return;
   }
 
-  safe_browsing::SBThreatType current_threat_type = SB_THREAT_TYPE_UNUSED;
+  SBThreatType current_threat_type = SB_THREAT_TYPE_UNUSED;
   // If user already click-through interstitial warning, or if there's already
   // a dangerous security state showing, we'll override it.
   if (ui_manager_->IsUrlWhitelistedOrPendingForWebContents(
diff --git a/chrome/browser/safe_browsing/chrome_password_protection_service.h b/chrome/browser/safe_browsing/chrome_password_protection_service.h
index ec2d16bc..092fd1d0 100644
--- a/chrome/browser/safe_browsing/chrome_password_protection_service.h
+++ b/chrome/browser/safe_browsing/chrome_password_protection_service.h
@@ -117,14 +117,14 @@
 
   void LogPasswordReuseLookupResult(
       content::WebContents* web_contents,
-      sync_pb::UserEventSpecifics::SyncPasswordReuseEvent::PasswordReuseLookup::
+      sync_pb::UserEventSpecifics::GaiaPasswordReuse::PasswordReuseLookup::
           LookupResult result);
 
   void LogPasswordReuseLookupResultWithVerdict(
       content::WebContents* web_contents,
-      sync_pb::UserEventSpecifics::SyncPasswordReuseEvent::PasswordReuseLookup::
+      sync_pb::UserEventSpecifics::GaiaPasswordReuse::PasswordReuseLookup::
           LookupResult result,
-      sync_pb::UserEventSpecifics::SyncPasswordReuseEvent::PasswordReuseLookup::
+      sync_pb::UserEventSpecifics::GaiaPasswordReuse::PasswordReuseLookup::
           ReputationVerdict verdict,
       const std::string& verdict_token);
 
diff --git a/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc b/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc
index 0af2512..d8d4404 100644
--- a/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc
+++ b/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc
@@ -38,8 +38,8 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 using sync_pb::UserEventSpecifics;
-using SyncPasswordReuseEvent = UserEventSpecifics::SyncPasswordReuseEvent;
-using PasswordReuseLookup = SyncPasswordReuseEvent::PasswordReuseLookup;
+using GaiaPasswordReuse = UserEventSpecifics::GaiaPasswordReuse;
+using PasswordReuseLookup = GaiaPasswordReuse::PasswordReuseLookup;
 
 namespace safe_browsing {
 
@@ -161,8 +161,8 @@
     return builder.Build().release();
   }
 
-  void EnableSyncPasswordReuseEvent() {
-    scoped_feature_list_.InitAndEnableFeature(kSyncPasswordReuseEvent);
+  void EnableGaiaPasswordReuseReporting() {
+    scoped_feature_list_.InitAndEnableFeature(kGaiaPasswordReuseReporting);
   }
 
   syncer::FakeUserEventService* GetUserEventService() {
@@ -375,7 +375,7 @@
       web_contents(), PasswordProtectionService::MATCHED_WHITELIST, nullptr);
   EXPECT_TRUE(GetUserEventService()->GetRecordedUserEvents().empty());
 
-  EnableSyncPasswordReuseEvent();
+  EnableGaiaPasswordReuseReporting();
   // Feature enabled but no committed navigation entry.
   service_->MaybeLogPasswordReuseDetectedEvent(web_contents());
   EXPECT_TRUE(GetUserEventService()->GetRecordedUserEvents().empty());
@@ -386,16 +386,16 @@
 
 TEST_F(ChromePasswordProtectionServiceTest,
        VerifyPasswordReuseDetectedUserEventRecorded) {
-  EnableSyncPasswordReuseEvent();
+  EnableGaiaPasswordReuseReporting();
   NavigateAndCommit(GURL("https://www.example.com/"));
 
   // Case 1: safe_browsing_enabled = true
   profile()->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnabled, true);
   service_->MaybeLogPasswordReuseDetectedEvent(web_contents());
   ASSERT_EQ(1ul, GetUserEventService()->GetRecordedUserEvents().size());
-  SyncPasswordReuseEvent event = GetUserEventService()
-                                     ->GetRecordedUserEvents()[0]
-                                     .sync_password_reuse_event();
+  GaiaPasswordReuse event = GetUserEventService()
+                                ->GetRecordedUserEvents()[0]
+                                .gaia_password_reuse_event();
   EXPECT_TRUE(event.reuse_detected().status().enabled());
 
   // Case 2: safe_browsing_enabled = false
@@ -404,7 +404,7 @@
   ASSERT_EQ(2ul, GetUserEventService()->GetRecordedUserEvents().size());
   event = GetUserEventService()
               ->GetRecordedUserEvents()[1]
-              .sync_password_reuse_event();
+              .gaia_password_reuse_event();
   EXPECT_FALSE(event.reuse_detected().status().enabled());
 
   // Not checking for the extended_reporting_level since that requires setting
@@ -413,7 +413,7 @@
 
 TEST_F(ChromePasswordProtectionServiceTest,
        VerifyPasswordReuseLookupUserEventRecorded) {
-  EnableSyncPasswordReuseEvent();
+  EnableGaiaPasswordReuseReporting();
   NavigateAndCommit(GURL("https://www.example.com/"));
 
   std::vector<std::pair<PasswordProtectionService::RequestOutcome,
@@ -452,7 +452,7 @@
     ASSERT_EQ(t + 1, GetUserEventService()->GetRecordedUserEvents().size());
     PasswordReuseLookup reuse_lookup = GetUserEventService()
                                            ->GetRecordedUserEvents()[t]
-                                           .sync_password_reuse_event()
+                                           .gaia_password_reuse_event()
                                            .reuse_lookup();
     EXPECT_EQ(it.second, reuse_lookup.lookup_result());
     t++;
@@ -469,7 +469,7 @@
     ASSERT_EQ(t + 1, GetUserEventService()->GetRecordedUserEvents().size());
     PasswordReuseLookup reuse_lookup = GetUserEventService()
                                            ->GetRecordedUserEvents()[t]
-                                           .sync_password_reuse_event()
+                                           .gaia_password_reuse_event()
                                            .reuse_lookup();
     EXPECT_EQ(PasswordReuseLookup::CACHE_HIT, reuse_lookup.lookup_result());
     EXPECT_EQ(PasswordReuseLookup::LOW_REPUTATION, reuse_lookup.verdict());
@@ -487,7 +487,7 @@
     ASSERT_EQ(t + 1, GetUserEventService()->GetRecordedUserEvents().size());
     PasswordReuseLookup reuse_lookup = GetUserEventService()
                                            ->GetRecordedUserEvents()[t]
-                                           .sync_password_reuse_event()
+                                           .gaia_password_reuse_event()
                                            .reuse_lookup();
     EXPECT_EQ(PasswordReuseLookup::REQUEST_SUCCESS,
               reuse_lookup.lookup_result());
diff --git a/chrome/browser/search/instant_service.h b/chrome/browser/search/instant_service.h
index b63769ad..8cab4a4 100644
--- a/chrome/browser/search/instant_service.h
+++ b/chrome/browser/search/instant_service.h
@@ -98,11 +98,8 @@
  private:
   friend class InstantExtendedTest;
   friend class InstantServiceTest;
-  friend class InstantTestBase;
   friend class InstantUnitTestBase;
 
-  FRIEND_TEST_ALL_PREFIXES(InstantExtendedManualTest,
-                           MANUAL_SearchesFromFakebox);
   FRIEND_TEST_ALL_PREFIXES(InstantExtendedTest, ProcessIsolation);
   FRIEND_TEST_ALL_PREFIXES(InstantServiceEnabledTest,
                            SendsSearchURLsToRenderer);
diff --git a/chrome/browser/search_engines/OWNERS b/chrome/browser/search_engines/OWNERS
index b09c45cd..ba5be80 100644
--- a/chrome/browser/search_engines/OWNERS
+++ b/chrome/browser/search_engines/OWNERS
@@ -1,5 +1,7 @@
-pkasting@chromium.org
-stevet@chromium.org
+file://components/search_engines/OWNERS
+
+bauerb@chromium.org
+treib@chromium.org
 
 per-file *_android.*=mariakhomenko@chromium.org
 
diff --git a/chrome/browser/subresource_filter/subresource_filter_browsertest.cc b/chrome/browser/subresource_filter/subresource_filter_browsertest.cc
index a449b82..fe4b2f2f 100644
--- a/chrome/browser/subresource_filter/subresource_filter_browsertest.cc
+++ b/chrome/browser/subresource_filter/subresource_filter_browsertest.cc
@@ -121,13 +121,10 @@
 #if defined(GOOGLE_CHROME_BUILD)
 // Names of navigation chain patterns histogram.
 const char kMatchesPatternHistogramName[] =
-    "SubresourceFilter.PageLoad.FinalURLMatch";
+    "SubresourceFilter.PageLoad.ActivationList";
 const char kNavigationChainSize[] =
     "SubresourceFilter.PageLoad.RedirectChainLength";
 const char kSubresourceFilterOnlySuffix[] = ".SubresourceFilterOnly";
-const char kSocialEngineeringAdsInterstitialSuffix[] =
-    ".SocialEngineeringAdsInterstitial";
-const char kPhishingInterstitialSuffix[] = ".PhishingInterstitial";
 #endif
 
 // Other histograms.
@@ -1096,16 +1093,8 @@
   ui_test_utils::NavigateToURL(browser(), url);
 
   tester.ExpectUniqueSample(
-      std::string(kMatchesPatternHistogramName) +
-          std::string(kSocialEngineeringAdsInterstitialSuffix),
-      false, 1);
-  tester.ExpectUniqueSample(std::string(kMatchesPatternHistogramName) +
-                                std::string(kSubresourceFilterOnlySuffix),
-                            true, 1);
-
-  tester.ExpectUniqueSample(std::string(kMatchesPatternHistogramName) +
-                                std::string(kPhishingInterstitialSuffix),
-                            false, 1);
+      kMatchesPatternHistogramName,
+      static_cast<int>(ActivationList::SUBRESOURCE_FILTER), 1);
   EXPECT_THAT(tester.GetAllSamples(std::string(kNavigationChainSize) +
                                    std::string(kSubresourceFilterOnlySuffix)),
               ::testing::ElementsAre(base::Bucket(1, 1)));
@@ -1127,17 +1116,8 @@
   ConfigureAsSubresourceFilterOnlyURL(url.GetOrigin());
   base::HistogramTester tester;
   ui_test_utils::NavigateToURL(browser(), url);
-  tester.ExpectUniqueSample(
-      std::string(kMatchesPatternHistogramName) +
-          std::string(kSocialEngineeringAdsInterstitialSuffix),
-      false, 1);
-  tester.ExpectUniqueSample(std::string(kMatchesPatternHistogramName) +
-                                std::string(kSubresourceFilterOnlySuffix),
-                            false, 1);
-
-  tester.ExpectUniqueSample(std::string(kMatchesPatternHistogramName) +
-                                std::string(kPhishingInterstitialSuffix),
-                            false, 1);
+  tester.ExpectUniqueSample(kMatchesPatternHistogramName,
+                            static_cast<int>(ActivationList::NONE), 1);
 }
 #endif
 
diff --git a/chrome/browser/ui/ash/palette_delegate_chromeos.cc b/chrome/browser/ui/ash/palette_delegate_chromeos.cc
index 0fb10d94e4..78b4623 100644
--- a/chrome/browser/ui/ash/palette_delegate_chromeos.cc
+++ b/chrome/browser/ui/ash/palette_delegate_chromeos.cc
@@ -200,23 +200,12 @@
   ash::Shell::Get()->screenshot_controller()->CancelScreenshotSession();
 }
 
-bool PaletteDelegateChromeOS::IsMetalayerSupported() {
-  if (!profile_)
-    return false;
+void PaletteDelegateChromeOS::ShowMetalayer() {
   auto* service =
       arc::ArcVoiceInteractionFrameworkService::GetForBrowserContext(profile_);
-  return service && service->IsMetalayerSupported();
-}
-
-void PaletteDelegateChromeOS::ShowMetalayer(const base::Closure& closed) {
-  auto* service =
-      arc::ArcVoiceInteractionFrameworkService::GetForBrowserContext(profile_);
-  if (!service) {
-    if (!closed.is_null())
-      closed.Run();
+  if (!service)
     return;
-  }
-  service->ShowMetalayer(closed);
+  service->ShowMetalayer();
 
   if (!highlighter_selection_observer_) {
     highlighter_selection_observer_ =
diff --git a/chrome/browser/ui/ash/palette_delegate_chromeos.h b/chrome/browser/ui/ash/palette_delegate_chromeos.h
index 75c4417..2790213 100644
--- a/chrome/browser/ui/ash/palette_delegate_chromeos.h
+++ b/chrome/browser/ui/ash/palette_delegate_chromeos.h
@@ -45,8 +45,7 @@
   void TakeScreenshot() override;
   void TakePartialScreenshot(const base::Closure& done) override;
   void CancelPartialScreenshot() override;
-  bool IsMetalayerSupported() override;
-  void ShowMetalayer(const base::Closure& closed) override;
+  void ShowMetalayer() override;
   void HideMetalayer() override;
 
   // user_manager::UserManager::UserSessionStateObserver:
diff --git a/chrome/browser/ui/chrome_pages.cc b/chrome/browser/ui/chrome_pages.cc
index f8ca4ec..6cb833a 100644
--- a/chrome/browser/ui/chrome_pages.cc
+++ b/chrome/browser/ui/chrome_pages.cc
@@ -150,6 +150,7 @@
         {CONTENT_SETTINGS_TYPE_BACKGROUND_SYNC, "backgroundSync"},
         {CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC, "microphone"},
         {CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA, "camera"},
+        {CONTENT_SETTINGS_TYPE_MIDI_SYSEX, "midiDevices"},
         {CONTENT_SETTINGS_TYPE_PLUGINS, "flash"},
         {CONTENT_SETTINGS_TYPE_ADS, "ads"},
         {CONTENT_SETTINGS_TYPE_PPAPI_BROKER, "unsandboxedPlugins"}}));
diff --git a/chrome/browser/ui/cocoa/content_settings/content_setting_bubble_cocoa.mm b/chrome/browser/ui/cocoa/content_settings/content_setting_bubble_cocoa.mm
index b2cfb98..932bdcbd 100644
--- a/chrome/browser/ui/cocoa/content_settings/content_setting_bubble_cocoa.mm
+++ b/chrome/browser/ui/cocoa/content_settings/content_setting_bubble_cocoa.mm
@@ -1089,7 +1089,7 @@
 }
 
 - (IBAction)manageBlocking:(id)sender {
-  [self model]->OnManageLinkClicked();
+  [self model]->OnManageButtonClicked();
 }
 
 - (IBAction)closeBubble:(id)sender {
diff --git a/chrome/browser/ui/cocoa/extensions/media_galleries_dialog_cocoa_unittest.mm b/chrome/browser/ui/cocoa/extensions/media_galleries_dialog_cocoa_unittest.mm
index 1bad2c8..f8b99945 100644
--- a/chrome/browser/ui/cocoa/extensions/media_galleries_dialog_cocoa_unittest.mm
+++ b/chrome/browser/ui/cocoa/extensions/media_galleries_dialog_cocoa_unittest.mm
@@ -9,7 +9,6 @@
 #include "chrome/browser/ui/cocoa/extensions/media_galleries_dialog_cocoa.h"
 #include "components/storage_monitor/storage_info.h"
 #include "extensions/common/extension.h"
-#include "extensions/common/test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using ::testing::_;
diff --git a/chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.mm b/chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.mm
index ab670a8ae..51a4b2d5 100644
--- a/chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.mm
+++ b/chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.mm
@@ -88,11 +88,6 @@
   return in_dark_mode ? skia::SkColorToSRGBNSColor(SK_ColorWHITE)
                       : skia::SkColorToSRGBNSColor(gfx::kGoogleGreen700);
 }
-NSColor* SecurityWarningSchemeColor(bool in_dark_mode) {
-  return in_dark_mode
-      ? skia::SkColorToSRGBNSColor(SkColorSetA(SK_ColorWHITE, 0x7F))
-      : skia::SkColorToSRGBNSColor(gfx::kGoogleYellow700);
-}
 NSColor* SecurityErrorSchemeColor(bool in_dark_mode) {
   return in_dark_mode
       ? skia::SkColorToSRGBNSColor(SkColorSetA(SK_ColorWHITE, 0x7F))
@@ -161,11 +156,8 @@
     return SecureSchemeColor(in_dark_mode);
   }
 
-  if (security_level == security_state::DANGEROUS)
-    return SecurityErrorSchemeColor(in_dark_mode);
-
-  DCHECK_EQ(security_state::SECURITY_WARNING, security_level);
-  return SecurityWarningSchemeColor(in_dark_mode);
+  DCHECK_EQ(security_state::DANGEROUS, security_level);
+  return SecurityErrorSchemeColor(in_dark_mode);
 }
 
 OmniboxViewMac::OmniboxViewMac(OmniboxEditController* controller,
diff --git a/chrome/browser/ui/content_settings/content_setting_bubble_model.cc b/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
index 22f52d6..01294c7 100644
--- a/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
+++ b/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
@@ -172,23 +172,10 @@
 }
 
 void ContentSettingSimpleBubbleModel::SetManageText() {
-  static const ContentSettingsTypeIdEntry kLinkIDs[] = {
-    {CONTENT_SETTINGS_TYPE_COOKIES, IDS_BLOCKED_COOKIES_LINK},
-    {CONTENT_SETTINGS_TYPE_IMAGES, IDS_BLOCKED_IMAGES_LINK},
-    {CONTENT_SETTINGS_TYPE_JAVASCRIPT, IDS_BLOCKED_JAVASCRIPT_LINK},
-    {CONTENT_SETTINGS_TYPE_PLUGINS, IDS_BLOCKED_PLUGINS_LINK},
-    {CONTENT_SETTINGS_TYPE_POPUPS, IDS_BLOCKED_POPUPS_LINK},
-    {CONTENT_SETTINGS_TYPE_GEOLOCATION, IDS_GEOLOCATION_BUBBLE_MANAGE_LINK},
-    {CONTENT_SETTINGS_TYPE_MIXEDSCRIPT, IDS_LEARN_MORE},
-    {CONTENT_SETTINGS_TYPE_PROTOCOL_HANDLERS, IDS_HANDLERS_BUBBLE_MANAGE_LINK},
-    {CONTENT_SETTINGS_TYPE_PPAPI_BROKER, IDS_PPAPI_BROKER_BUBBLE_MANAGE_LINK},
-    {CONTENT_SETTINGS_TYPE_MIDI_SYSEX, IDS_MIDI_SYSEX_BUBBLE_MANAGE_LINK},
-  };
-  set_manage_text(l10n_util::GetStringUTF16(
-      GetIdForContentType(kLinkIDs, arraysize(kLinkIDs), content_type())));
+  set_manage_text(l10n_util::GetStringUTF16(IDS_MANAGE));
 }
 
-void ContentSettingSimpleBubbleModel::OnManageLinkClicked() {
+void ContentSettingSimpleBubbleModel::OnManageButtonClicked() {
   if (delegate())
     delegate()->ShowContentSettingsPage(content_type());
 
@@ -468,7 +455,7 @@
       map->GetWebsiteSetting(url, url, content_type(), std::string(), &info);
   ContentSetting setting = content_settings::ValueToContentSetting(value.get());
 
-  // If the setting is not managed by the user, hide the "Manage..." link.
+  // If the setting is not managed by the user, hide the "Manage" button.
   if (info.source != SETTING_SOURCE_USER)
     set_manage_text(base::string16());
 
@@ -707,7 +694,7 @@
   return this;
 }
 
-void ContentSettingMediaStreamBubbleModel::OnManageLinkClicked() {
+void ContentSettingMediaStreamBubbleModel::OnManageButtonClicked() {
   if (!delegate())
     return;
 
@@ -919,21 +906,8 @@
 }
 
 void ContentSettingMediaStreamBubbleModel::SetManageText() {
-  // By default, the manage link refers to both media types. We only need
-  // to change the link text if only one media type was accessed.
-  int link_id;
-  if (CameraAccessed() && MicrophoneAccessed()) {
-    link_id = IDS_MEDIASTREAM_BUBBLE_MANAGE_LINK;
-  } else if (CameraAccessed()) {
-    link_id = IDS_MEDIASTREAM_CAMERA_BUBBLE_MANAGE_LINK;
-  } else if (MicrophoneAccessed()) {
-    link_id = IDS_MEDIASTREAM_MICROPHONE_BUBBLE_MANAGE_LINK;
-  } else {
-    NOTREACHED();
-    return;
-  }
-
-  set_manage_text(l10n_util::GetStringUTF16(link_id));
+  DCHECK(CameraAccessed() || MicrophoneAccessed());
+  set_manage_text(l10n_util::GetStringUTF16(IDS_MANAGE));
 }
 
 void ContentSettingMediaStreamBubbleModel::SetCustomLink() {
@@ -1526,10 +1500,10 @@
 }
 
 void ContentSettingDownloadsBubbleModel::SetManageText() {
-  set_manage_text(l10n_util::GetStringUTF16(IDS_BLOCKED_DOWNLOADS_LINK));
+  set_manage_text(l10n_util::GetStringUTF16(IDS_MANAGE));
 }
 
-void ContentSettingDownloadsBubbleModel::OnManageLinkClicked() {
+void ContentSettingDownloadsBubbleModel::OnManageButtonClicked() {
   if (delegate())
     delegate()->ShowContentSettingsPage(
         CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS);
diff --git a/chrome/browser/ui/content_settings/content_setting_bubble_model.h b/chrome/browser/ui/content_settings/content_setting_bubble_model.h
index 8d501da..a487d10f 100644
--- a/chrome/browser/ui/content_settings/content_setting_bubble_model.h
+++ b/chrome/browser/ui/content_settings/content_setting_bubble_model.h
@@ -166,7 +166,7 @@
   virtual void OnRadioClicked(int radio_index) {}
   virtual void OnListItemClicked(int index, int event_flags) {}
   virtual void OnCustomLinkClicked() {}
-  virtual void OnManageLinkClicked() {}
+  virtual void OnManageButtonClicked() {}
   virtual void OnManageCheckboxChecked(bool is_checked) {}
   virtual void OnLearnMoreClicked() {}
   virtual void OnMediaMenuClicked(content::MediaStreamType type,
@@ -287,7 +287,7 @@
   // ContentSettingBubbleModel implementation.
   void SetTitle() override;
   void SetManageText() override;
-  void OnManageLinkClicked() override;
+  void OnManageButtonClicked() override;
   void SetCustomLink();
   void OnCustomLinkClicked() override;
 
@@ -363,7 +363,7 @@
 
   // ContentSettingBubbleModel:
   ContentSettingMediaStreamBubbleModel* AsMediaStreamBubbleModel() override;
-  void OnManageLinkClicked() override;
+  void OnManageButtonClicked() override;
 
  private:
   // Helper functions to check if this bubble was invoked for microphone,
@@ -426,7 +426,7 @@
   void OnRadioClicked(int radio_index) override;
   void SetTitle() override;
   void SetManageText() override;
-  void OnManageLinkClicked() override;
+  void OnManageButtonClicked() override;
 
   int selected_item_ = 0;
 
diff --git a/chrome/browser/ui/content_settings/content_setting_bubble_model_browsertest.cc b/chrome/browser/ui/content_settings/content_setting_bubble_model_browsertest.cc
index b8d1841a..e00b8c59 100644
--- a/chrome/browser/ui/content_settings/content_setting_bubble_model_browsertest.cc
+++ b/chrome/browser/ui/content_settings/content_setting_bubble_model_browsertest.cc
@@ -188,9 +188,9 @@
             browser()->content_setting_bubble_model_delegate(), original_tab,
             browser()->profile()));
 
-    // Click the management link, which opens in a new tab or window.
-    // Wait until it loads.
-    bubble->OnManageLinkClicked();
+    // Click the manage button, which opens in a new tab or window. Wait until
+    // it loads.
+    bubble->OnManageButtonClicked();
     ASSERT_NE(GetActiveTab(), original_tab);
     content::TestNavigationObserver observer(GetActiveTab());
     observer.Wait();
@@ -204,14 +204,14 @@
   }
 };
 
-// Tests that clicking on the management link in the media bubble opens
-// the correct section of the settings UI.
-// This test sometimes leaks memory, detected by linux_chromium_asan_rel_ng. See
-// http://crbug/668693 for more info.
+// Tests that clicking on the manage button in the media bubble opens the
+// correct section of the settings UI. This test sometimes leaks memory,
+// detected by linux_chromium_asan_rel_ng. See http://crbug/668693 for more
+// info.
 IN_PROC_BROWSER_TEST_F(ContentSettingBubbleModelMediaStreamTest,
                        DISABLED_ManageLink) {
-  // For each of the three options, we click the management link and check if
-  // the active tab loads the correct internal url.
+  // For each of the three options, we click the manage button and check if the
+  // active tab loads the correct internal url.
 
   // The microphone bubble links to microphone exceptions.
   ManageMediaStreamSettings(TabSpecificContentSettings::MICROPHONE_ACCESSED);
@@ -271,7 +271,7 @@
         "ContentSettings.Popups",
         content_settings::POPUPS_ACTION_CLICKED_LIST_ITEM_CLICKED, 1);
 
-  model->OnManageLinkClicked();
+  model->OnManageButtonClicked();
   histograms.ExpectBucketCount(
         "ContentSettings.Popups",
         content_settings::POPUPS_ACTION_CLICKED_MANAGE_POPUPS_BLOCKING, 1);
diff --git a/chrome/browser/ui/libgtkui/gtk_ui.cc b/chrome/browser/ui/libgtkui/gtk_ui.cc
index f1c3a3d..992574b 100644
--- a/chrome/browser/ui/libgtkui/gtk_ui.cc
+++ b/chrome/browser/ui/libgtkui/gtk_ui.cc
@@ -718,9 +718,8 @@
 
 void GtkUi::AddWindowButtonOrderObserver(
     views::WindowButtonOrderObserver* observer) {
-  if (!leading_buttons_.empty() || !trailing_buttons_.empty()) {
+  if (nav_buttons_set_)
     observer->OnWindowButtonOrderingChange(leading_buttons_, trailing_buttons_);
-  }
 
   window_button_order_observer_list_.AddObserver(observer);
 }
@@ -735,6 +734,7 @@
     const std::vector<views::FrameButton>& trailing_buttons) {
   leading_buttons_ = leading_buttons;
   trailing_buttons_ = trailing_buttons;
+  nav_buttons_set_ = true;
 
   for (views::WindowButtonOrderObserver& observer :
        window_button_order_observer_list_) {
diff --git a/chrome/browser/ui/libgtkui/gtk_ui.h b/chrome/browser/ui/libgtkui/gtk_ui.h
index c3e7721..3048cf8 100644
--- a/chrome/browser/ui/libgtkui/gtk_ui.h
+++ b/chrome/browser/ui/libgtkui/gtk_ui.h
@@ -173,8 +173,9 @@
 
   std::unique_ptr<NavButtonLayoutManager> nav_button_layout_manager_;
 
-  // If either of these vectors are non-empty, they represent the current
-  // window button configuration.
+  // Frame button layout state.  If |nav_buttons_set_| is false, then
+  // |leading_buttons_| and |trailing_buttons_| are meaningless.
+  bool nav_buttons_set_ = false;
   std::vector<views::FrameButton> leading_buttons_;
   std::vector<views::FrameButton> trailing_buttons_;
 
diff --git a/chrome/browser/ui/search/instant_controller.h b/chrome/browser/ui/search/instant_controller.h
index cf9354b..b5edc38 100644
--- a/chrome/browser/ui/search/instant_controller.h
+++ b/chrome/browser/ui/search/instant_controller.h
@@ -52,9 +52,6 @@
   }
 
  private:
-  friend class InstantExtendedManualTest;
-  friend class InstantTestBase;
-
   FRIEND_TEST_ALL_PREFIXES(InstantExtendedTest,
                            SearchDoesntReuseInstantTabWithoutSupport);
   FRIEND_TEST_ALL_PREFIXES(InstantExtendedTest,
diff --git a/chrome/browser/ui/search/instant_test_base.h b/chrome/browser/ui/search/instant_test_base.h
index 07437a5..15c05e0 100644
--- a/chrome/browser/ui/search/instant_test_base.h
+++ b/chrome/browser/ui/search/instant_test_base.h
@@ -15,13 +15,13 @@
 
 // This utility class is meant to be used in a "mix-in" fashion, giving the
 // derived test class additional Instant-related functionality.
+// TODO(treib): Merge this class into InstantUITestBase.
 class InstantTestBase {
  protected:
   InstantTestBase();
   virtual ~InstantTestBase();
 
  protected:
-  void set_browser(Browser* browser) { browser_ = browser; }
   Browser* instant_browser() { return browser_; }
 
   void SetupInstant(Browser* browser);
diff --git a/chrome/browser/ui/search/local_ntp_browsertest.cc b/chrome/browser/ui/search/local_ntp_browsertest.cc
index c9ee653c..40d4463 100644
--- a/chrome/browser/ui/search/local_ntp_browsertest.cc
+++ b/chrome/browser/ui/search/local_ntp_browsertest.cc
@@ -12,6 +12,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/optional.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/threading/thread_restrictions.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile.h"
@@ -24,9 +25,9 @@
 #include "chrome/browser/signin/gaia_cookie_manager_service_factory.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
-#include "chrome/browser/ui/search/instant_test_base.h"
 #include "chrome/browser/ui/search/instant_test_utils.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/common/chrome_features.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/test/base/in_process_browser_test.h"
@@ -38,7 +39,6 @@
 #include "components/search_engines/template_url_service.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/web_contents.h"
-#include "content/public/common/content_switches.h"
 #include "content/public/test/test_navigation_observer.h"
 #include "content/public/test/test_utils.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
@@ -74,50 +74,45 @@
 
 }  // namespace
 
-// A test class that sets up local_ntp_browsertest.html (which is mostly a copy
-// of the real local_ntp.html) as the NTP URL.
-class LocalNTPTest : public InProcessBrowserTest, public InstantTestBase {
+class LocalNTPTest : public InProcessBrowserTest {
  public:
   LocalNTPTest() {}
 
-  GURL other_url() { return https_test_server().GetURL("/simple.html"); }
+  void SetUserSelectedDefaultSearchProvider(const std::string& base_url,
+                                            const std::string& ntp_url) {
+    base::ThreadRestrictions::ScopedAllowIO allow_io;
+    TemplateURLData data;
+    data.SetShortName(base::UTF8ToUTF16(base_url));
+    data.SetKeyword(base::UTF8ToUTF16(base_url));
+    data.SetURL(base_url + "url?bar={searchTerms}");
+    data.new_tab_url = ntp_url;
 
- protected:
-  void SetUpInProcessBrowserTestFixture() override {
-    ASSERT_TRUE(https_test_server().Start());
-    GURL instant_url =
-        https_test_server().GetURL("/instant_extended.html?strk=1&");
-    GURL ntp_url =
-        https_test_server().GetURL("/local_ntp_browsertest.html?strk=1&");
-    InstantTestBase::Init(instant_url, ntp_url, false);
+    TemplateURLService* template_url_service =
+        TemplateURLServiceFactory::GetForProfile(browser()->profile());
+    TemplateURL* template_url =
+        template_url_service->Add(base::MakeUnique<TemplateURL>(data));
+    template_url_service->SetUserSelectedDefaultSearchProvider(template_url);
   }
+
+ private:
+  void SetUp() override {
+    feature_list_.InitAndEnableFeature(features::kUseGoogleLocalNtp);
+    InProcessBrowserTest::SetUp();
+  }
+
+  base::test::ScopedFeatureList feature_list_;
 };
 
-// This runs a bunch of pure JS-side tests, i.e. those that don't require any
-// interaction from the native side.
-IN_PROC_BROWSER_TEST_F(LocalNTPTest, SimpleJavascriptTests) {
-  if (content::AreAllSitesIsolatedForTesting()) {
-    LOG(ERROR) << "LocalNTPTest.SimpleJavascriptTests doesn't work in "
-                  "--site-per-process mode yet, see crbug.com/695221.";
-    return;
-  }
-
-  ASSERT_NO_FATAL_FAILURE(SetupInstant(browser()));
-
-  content::WebContents* active_tab = OpenNewTab(browser(), ntp_url());
-  ASSERT_TRUE(search::IsInstantNTP(active_tab));
-
-  bool success = false;
-  ASSERT_TRUE(instant_test_utils::GetBoolFromJS(
-      active_tab, "!!runSimpleTests()", &success));
-  EXPECT_TRUE(success);
-}
-
 IN_PROC_BROWSER_TEST_F(LocalNTPTest, EmbeddedSearchAPIOnlyAvailableOnNTP) {
-  ASSERT_NO_FATAL_FAILURE(SetupInstant(browser()));
+  // Set up a test server, so we have some arbitrary non-NTP URL to navigate to.
+  net::EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS);
+  test_server.ServeFilesFromSourceDirectory("chrome/test/data");
+  ASSERT_TRUE(test_server.Start());
+  const GURL other_url = test_server.GetURL("/simple.html");
 
   // Open an NTP.
-  content::WebContents* active_tab = OpenNewTab(browser(), ntp_url());
+  content::WebContents* active_tab =
+      OpenNewTab(browser(), GURL(chrome::kChromeUINewTabURL));
   ASSERT_TRUE(search::IsInstantNTP(active_tab));
   // Check that the embeddedSearch API is available.
   bool result = false;
@@ -127,7 +122,7 @@
 
   // Navigate somewhere else in the same tab.
   ui_test_utils::NavigateToURLWithDisposition(
-      browser(), other_url(), WindowOpenDisposition::CURRENT_TAB,
+      browser(), other_url, WindowOpenDisposition::CURRENT_TAB,
       ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
   ASSERT_FALSE(search::IsInstantNTP(active_tab));
   // Now the embeddedSearch API should have gone away.
@@ -155,7 +150,8 @@
 
   // Navigate to a new NTP instance.
   ui_test_utils::NavigateToURLWithDisposition(
-      browser(), ntp_url(), WindowOpenDisposition::CURRENT_TAB,
+      browser(), GURL(chrome::kChromeUINewTabURL),
+      WindowOpenDisposition::CURRENT_TAB,
       ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
   ASSERT_TRUE(search::IsInstantNTP(active_tab));
   // Now the API should be available again.
@@ -164,6 +160,164 @@
   EXPECT_TRUE(result);
 }
 
+IN_PROC_BROWSER_TEST_F(LocalNTPTest, NTPRespectsBrowserLanguageSetting) {
+  // If the platform cannot load the French locale (GetApplicationLocale() is
+  // platform specific, and has been observed to fail on a small number of
+  // platforms), abort the test.
+  if (!SwitchToFrench()) {
+    LOG(ERROR) << "Failed switching to French language, aborting test.";
+    return;
+  }
+
+  // Open a new tab.
+  content::WebContents* active_tab =
+      OpenNewTab(browser(), GURL(chrome::kChromeUINewTabURL));
+
+  // Verify that the NTP is in French.
+  EXPECT_EQ(base::ASCIIToUTF16("Nouvel onglet"), active_tab->GetTitle());
+}
+
+IN_PROC_BROWSER_TEST_F(LocalNTPTest, GoogleNTPLoadsWithoutError) {
+  // Open a new blank tab.
+  content::WebContents* active_tab = OpenNewTab(browser(), GURL("about:blank"));
+  ASSERT_FALSE(search::IsInstantNTP(active_tab));
+
+  // Attach a console observer, listening for any message ("*" pattern).
+  content::ConsoleObserverDelegate console_observer(active_tab, "*");
+  active_tab->SetDelegate(&console_observer);
+
+  // Navigate to the NTP.
+  ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUINewTabURL));
+  ASSERT_TRUE(search::IsInstantNTP(active_tab));
+  ASSERT_EQ(GURL(chrome::kChromeSearchLocalNtpUrl),
+            active_tab->GetController().GetVisibleEntry()->GetURL());
+
+  bool is_google = false;
+  ASSERT_TRUE(instant_test_utils::GetBoolFromJS(
+      active_tab, "!!window.configData && !!window.configData.isGooglePage",
+      &is_google));
+  EXPECT_TRUE(is_google);
+
+  // We shouldn't have gotten any console error messages.
+  EXPECT_TRUE(console_observer.message().empty()) << console_observer.message();
+}
+
+IN_PROC_BROWSER_TEST_F(LocalNTPTest, NonGoogleNTPLoadsWithoutError) {
+  SetUserSelectedDefaultSearchProvider("https://www.example.com",
+                                       /*ntp_url=*/"");
+
+  // Open a new blank tab.
+  content::WebContents* active_tab = OpenNewTab(browser(), GURL("about:blank"));
+  ASSERT_FALSE(search::IsInstantNTP(active_tab));
+
+  // Attach a console observer, listening for any message ("*" pattern).
+  content::ConsoleObserverDelegate console_observer(active_tab, "*");
+  active_tab->SetDelegate(&console_observer);
+
+  // Navigate to the NTP.
+  ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUINewTabURL));
+  ASSERT_TRUE(search::IsInstantNTP(active_tab));
+  ASSERT_EQ(GURL(chrome::kChromeSearchLocalNtpUrl),
+            active_tab->GetController().GetVisibleEntry()->GetURL());
+
+  bool is_google = false;
+  ASSERT_TRUE(instant_test_utils::GetBoolFromJS(
+      active_tab, "!!window.configData && !!window.configData.isGooglePage",
+      &is_google));
+  EXPECT_FALSE(is_google);
+
+  // We shouldn't have gotten any console error messages.
+  EXPECT_TRUE(console_observer.message().empty()) << console_observer.message();
+}
+
+IN_PROC_BROWSER_TEST_F(LocalNTPTest, FrenchGoogleNTPLoadsWithoutError) {
+  if (!SwitchToFrench()) {
+    LOG(ERROR) << "Failed switching to French language, aborting test.";
+    return;
+  }
+
+  // Open a new blank tab.
+  content::WebContents* active_tab = OpenNewTab(browser(), GURL("about:blank"));
+  ASSERT_FALSE(search::IsInstantNTP(active_tab));
+
+  // Attach a console observer, listening for any message ("*" pattern).
+  content::ConsoleObserverDelegate console_observer(active_tab, "*");
+  active_tab->SetDelegate(&console_observer);
+
+  // Navigate to the NTP.
+  ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUINewTabURL));
+  ASSERT_TRUE(search::IsInstantNTP(active_tab));
+  ASSERT_EQ(GURL(chrome::kChromeSearchLocalNtpUrl),
+            active_tab->GetController().GetVisibleEntry()->GetURL());
+  // Make sure it's actually in French.
+  ASSERT_EQ(base::ASCIIToUTF16("Nouvel onglet"), active_tab->GetTitle());
+
+  // We shouldn't have gotten any console error messages.
+  EXPECT_TRUE(console_observer.message().empty()) << console_observer.message();
+}
+
+class LocalNTPRTLTest : public LocalNTPTest {
+ public:
+  LocalNTPRTLTest() {}
+
+ private:
+  void SetUpCommandLine(base::CommandLine* cmdline) override {
+    cmdline->AppendSwitchASCII(switches::kForceUIDirection,
+                               switches::kForceDirectionRTL);
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(LocalNTPRTLTest, RightToLeft) {
+  // Open an NTP.
+  content::WebContents* active_tab =
+      OpenNewTab(browser(), GURL(chrome::kChromeUINewTabURL));
+  ASSERT_TRUE(search::IsInstantNTP(active_tab));
+  // Check that the "dir" attribute on the main "html" element says "rtl".
+  std::string dir;
+  ASSERT_TRUE(instant_test_utils::GetStringFromJS(
+      active_tab, "document.documentElement.dir", &dir));
+  EXPECT_EQ("rtl", dir);
+}
+
+// A test class that sets up local_ntp_browsertest.html as the NTP URL. It's
+// mostly a copy of the real local_ntp.html, but it adds some testing JS.
+class LocalNTPJavascriptTest : public LocalNTPTest {
+ public:
+  LocalNTPJavascriptTest()
+      : https_test_server_(net::EmbeddedTestServer::TYPE_HTTPS) {
+    https_test_server_.ServeFilesFromSourceDirectory("chrome/test/data");
+  }
+
+ private:
+  void SetUpOnMainThread() override {
+    ASSERT_TRUE(https_test_server_.Start());
+    GURL ntp_url = https_test_server_.GetURL("/local_ntp_browsertest.html");
+    SetUserSelectedDefaultSearchProvider(https_test_server_.base_url().spec(),
+                                         ntp_url.spec());
+  }
+
+  net::EmbeddedTestServer https_test_server_;
+};
+
+// This runs a bunch of pure JS-side tests, i.e. those that don't require any
+// interaction from the native side.
+IN_PROC_BROWSER_TEST_F(LocalNTPJavascriptTest, SimpleJavascriptTests) {
+  if (content::AreAllSitesIsolatedForTesting()) {
+    LOG(ERROR) << "LocalNTPJavascriptTest.SimpleJavascriptTests doesn't work "
+                  "in --site-per-process mode yet, see crbug.com/695221.";
+    return;
+  }
+
+  content::WebContents* active_tab =
+      OpenNewTab(browser(), GURL(chrome::kChromeUINewTabURL));
+  ASSERT_TRUE(search::IsInstantNTP(active_tab));
+
+  bool success = false;
+  ASSERT_TRUE(instant_test_utils::GetBoolFromJS(
+      active_tab, "!!runSimpleTests()", &success));
+  EXPECT_TRUE(success);
+}
+
 namespace {
 
 // Returns the RenderFrameHost corresponding to the most visited iframe in the
@@ -181,16 +335,15 @@
 
 }  // namespace
 
-IN_PROC_BROWSER_TEST_F(LocalNTPTest, LoadsIframe) {
+IN_PROC_BROWSER_TEST_F(LocalNTPJavascriptTest, LoadsIframe) {
   if (content::AreAllSitesIsolatedForTesting()) {
-    LOG(ERROR) << "LocalNTPTest.LoadsIframe doesn't work in "
+    LOG(ERROR) << "LocalNTPJavascriptTest.LoadsIframe doesn't work in "
                   "--site-per-process mode yet, see crbug.com/695221.";
     return;
   }
 
-  ASSERT_NO_FATAL_FAILURE(SetupInstant(browser()));
-
-  content::WebContents* active_tab = OpenNewTab(browser(), ntp_url());
+  content::WebContents* active_tab =
+      OpenNewTab(browser(), GURL(chrome::kChromeUINewTabURL));
   ASSERT_TRUE(search::IsInstantNTP(active_tab));
 
   content::DOMMessageQueue msg_queue;
@@ -240,145 +393,6 @@
   EXPECT_EQ(0, failed_imgs);
 }
 
-IN_PROC_BROWSER_TEST_F(LocalNTPTest,
-                       NTPRespectsBrowserLanguageSetting) {
-  // If the platform cannot load the French locale (GetApplicationLocale() is
-  // platform specific, and has been observed to fail on a small number of
-  // platforms), abort the test.
-  if (!SwitchToFrench()) {
-    LOG(ERROR) << "Failed switching to French language, aborting test.";
-    return;
-  }
-
-  // Setup Instant.
-  ASSERT_NO_FATAL_FAILURE(SetupInstant(browser()));
-
-  // Open a new tab.
-  content::WebContents* active_tab =
-      OpenNewTab(browser(), GURL(chrome::kChromeUINewTabURL));
-
-  // Verify that the NTP is in French.
-  EXPECT_EQ(base::ASCIIToUTF16("Nouvel onglet"), active_tab->GetTitle());
-}
-
-// In contrast to LocalNTPTest, this one doesn't set up any special NTP
-// wrangling. It just turns on the local NTP and forces the UI to RTL.
-class LocalNTPRTLTest : public InProcessBrowserTest {
- public:
-  LocalNTPRTLTest() {}
-
- protected:
-  void SetUpCommandLine(base::CommandLine* cmdline) override {
-    cmdline->AppendSwitchASCII(switches::kEnableFeatures, "UseGoogleLocalNtp");
-    cmdline->AppendSwitchASCII(switches::kForceUIDirection,
-                               switches::kForceDirectionRTL);
-  }
-};
-
-IN_PROC_BROWSER_TEST_F(LocalNTPRTLTest, RightToLeft) {
-  // Open an NTP.
-  content::WebContents* active_tab =
-      OpenNewTab(browser(), GURL(chrome::kChromeUINewTabURL));
-  ASSERT_TRUE(search::IsInstantNTP(active_tab));
-  // Check that the "dir" attribute on the main "html" element says "rtl".
-  std::string dir;
-  ASSERT_TRUE(instant_test_utils::GetStringFromJS(
-      active_tab, "document.documentElement.dir", &dir));
-  EXPECT_EQ("rtl", dir);
-}
-
-// In contrast to LocalNTPTest, this one doesn't set up any special NTP
-// wrangling. It just turns on the local NTP.
-class LocalNTPSmokeTest : public InProcessBrowserTest {
- public:
-  LocalNTPSmokeTest() {}
-
- protected:
-  void SetUpCommandLine(base::CommandLine* cmdline) override {
-    cmdline->AppendSwitchASCII(switches::kEnableFeatures, "UseGoogleLocalNtp");
-  }
-
-  void SetUserSelectedDefaultSearchProvider(const std::string& base_url) {
-    base::ThreadRestrictions::ScopedAllowIO allow_io;
-    TemplateURLData data;
-    data.SetShortName(base::UTF8ToUTF16(base_url));
-    data.SetKeyword(base::UTF8ToUTF16(base_url));
-    data.SetURL(base_url + "url?bar={searchTerms}");
-
-    TemplateURLService* template_url_service =
-        TemplateURLServiceFactory::GetForProfile(browser()->profile());
-    TemplateURL* template_url =
-        template_url_service->Add(base::MakeUnique<TemplateURL>(data));
-    template_url_service->SetUserSelectedDefaultSearchProvider(template_url);
-  }
-};
-
-IN_PROC_BROWSER_TEST_F(LocalNTPSmokeTest, GoogleNTPLoadsWithoutError) {
-  // Open a new blank tab.
-  content::WebContents* active_tab = OpenNewTab(browser(), GURL("about:blank"));
-  ASSERT_FALSE(search::IsInstantNTP(active_tab));
-
-  // Attach a console observer, listening for any message ("*" pattern).
-  content::ConsoleObserverDelegate console_observer(active_tab, "*");
-  active_tab->SetDelegate(&console_observer);
-
-  // Navigate to the NTP.
-  ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUINewTabURL));
-  ASSERT_TRUE(search::IsInstantNTP(active_tab));
-  ASSERT_EQ(GURL(chrome::kChromeSearchLocalNtpUrl),
-            active_tab->GetController().GetVisibleEntry()->GetURL());
-
-  // We shouldn't have gotten any console error messages.
-  EXPECT_TRUE(console_observer.message().empty()) << console_observer.message();
-}
-
-IN_PROC_BROWSER_TEST_F(LocalNTPSmokeTest, NonGoogleNTPLoadsWithoutError) {
-  SetUserSelectedDefaultSearchProvider("https://www.example.com");
-
-  // Open a new blank tab.
-  content::WebContents* active_tab = OpenNewTab(browser(), GURL("about:blank"));
-  ASSERT_FALSE(search::IsInstantNTP(active_tab));
-
-  // Attach a console observer, listening for any message ("*" pattern).
-  content::ConsoleObserverDelegate console_observer(active_tab, "*");
-  active_tab->SetDelegate(&console_observer);
-
-  // Navigate to the NTP.
-  ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUINewTabURL));
-  ASSERT_TRUE(search::IsInstantNTP(active_tab));
-  ASSERT_EQ(GURL(chrome::kChromeSearchLocalNtpUrl),
-            active_tab->GetController().GetVisibleEntry()->GetURL());
-
-  // We shouldn't have gotten any console error messages.
-  EXPECT_TRUE(console_observer.message().empty()) << console_observer.message();
-}
-
-IN_PROC_BROWSER_TEST_F(LocalNTPSmokeTest, FrenchGoogleNTPLoadsWithoutError) {
-  if (!SwitchToFrench()) {
-    LOG(ERROR) << "Failed switching to French language, aborting test.";
-    return;
-  }
-
-  // Open a new blank tab.
-  content::WebContents* active_tab = OpenNewTab(browser(), GURL("about:blank"));
-  ASSERT_FALSE(search::IsInstantNTP(active_tab));
-
-  // Attach a console observer, listening for any message ("*" pattern).
-  content::ConsoleObserverDelegate console_observer(active_tab, "*");
-  active_tab->SetDelegate(&console_observer);
-
-  // Navigate to the NTP.
-  ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUINewTabURL));
-  ASSERT_TRUE(search::IsInstantNTP(active_tab));
-  ASSERT_EQ(GURL(chrome::kChromeSearchLocalNtpUrl),
-            active_tab->GetController().GetVisibleEntry()->GetURL());
-  // Make sure it's actually in French.
-  ASSERT_EQ(base::ASCIIToUTF16("Nouvel onglet"), active_tab->GetTitle());
-
-  // We shouldn't have gotten any console error messages.
-  EXPECT_TRUE(console_observer.message().empty()) << console_observer.message();
-}
-
 // A simple fake implementation of OneGoogleBarFetcher that immediately returns
 // a pre-configured OneGoogleBarData, which is null by default.
 class FakeOneGoogleBarFetcher : public OneGoogleBarFetcher {
@@ -400,10 +414,17 @@
  public:
   LocalNTPOneGoogleBarSmokeTest() {}
 
- protected:
-  void SetUpCommandLine(base::CommandLine* cmdline) override {
-    cmdline->AppendSwitchASCII(switches::kEnableFeatures,
-                               "UseGoogleLocalNtp,OneGoogleBarOnLocalNtp");
+  FakeOneGoogleBarFetcher* one_google_bar_fetcher() {
+    return static_cast<FakeOneGoogleBarFetcher*>(
+        OneGoogleBarServiceFactory::GetForProfile(browser()->profile())
+            ->fetcher_for_testing());
+  }
+
+ private:
+  void SetUp() override {
+    feature_list_.InitWithFeatures(
+        {features::kUseGoogleLocalNtp, features::kOneGoogleBarOnLocalNtp}, {});
+    InProcessBrowserTest::SetUp();
   }
 
   void SetUpInProcessBrowserTestFixture() override {
@@ -415,13 +436,6 @@
                            base::Unretained(this)));
   }
 
-  FakeOneGoogleBarFetcher* one_google_bar_fetcher() {
-    return static_cast<FakeOneGoogleBarFetcher*>(
-        OneGoogleBarServiceFactory::GetForProfile(browser()->profile())
-            ->fetcher_for_testing());
-  }
-
- private:
   static std::unique_ptr<KeyedService> CreateOneGoogleBarService(
       content::BrowserContext* context) {
     GaiaCookieManagerService* cookie_service =
@@ -436,6 +450,8 @@
         context, &LocalNTPOneGoogleBarSmokeTest::CreateOneGoogleBarService);
   }
 
+  base::test::ScopedFeatureList feature_list_;
+
   std::unique_ptr<
       base::CallbackList<void(content::BrowserContext*)>::Subscription>
       will_create_browser_context_services_subscription_;
@@ -503,11 +519,14 @@
  public:
   LocalNTPVoiceSearchSmokeTest() {}
 
- protected:
-  void SetUpCommandLine(base::CommandLine* cmdline) override {
-    cmdline->AppendSwitchASCII(switches::kEnableFeatures,
-                               "UseGoogleLocalNtp,VoiceSearchOnLocalNtp");
+ private:
+  void SetUp() override {
+    feature_list_.InitWithFeatures(
+        {features::kUseGoogleLocalNtp, features::kVoiceSearchOnLocalNtp}, {});
+    InProcessBrowserTest::SetUp();
   }
+
+  base::test::ScopedFeatureList feature_list_;
 };
 
 IN_PROC_BROWSER_TEST_F(LocalNTPVoiceSearchSmokeTest,
diff --git a/chrome/browser/ui/views/content_setting_bubble_contents.cc b/chrome/browser/ui/views/content_setting_bubble_contents.cc
index 833573d..3bc6f4bb1 100644
--- a/chrome/browser/ui/views/content_setting_bubble_contents.cc
+++ b/chrome/browser/ui/views/content_setting_bubble_contents.cc
@@ -290,7 +290,6 @@
       content_setting_bubble_model_(content_setting_bubble_model),
       list_item_container_(nullptr),
       custom_link_(nullptr),
-      manage_link_(nullptr),
       manage_checkbox_(nullptr),
       learn_more_button_(nullptr) {
   // Compensate for built-in vertical padding in the anchor view's image.
@@ -524,11 +523,6 @@
     manage_checkbox_ = new views::Checkbox(bubble_content.manage_text);
     manage_checkbox_->set_listener(this);
     layout->AddView(manage_checkbox_);
-  } else {
-    layout->StartRow(0, kSingleColumnSetId);
-    manage_link_ = new views::Link(bubble_content.manage_text);
-    manage_link_->set_listener(this);
-    layout->AddView(manage_link_);
   }
 
   if (!bubble_content_empty) {
@@ -557,6 +551,12 @@
   return learn_more_button_;
 }
 
+// Note: The cancel button is really the "Manage" button.
+bool ContentSettingBubbleContents::Cancel() {
+  content_setting_bubble_model_->OnManageButtonClicked();
+  return true;
+}
+
 bool ContentSettingBubbleContents::Accept() {
   content_setting_bubble_model_->OnDoneClicked();
   return true;
@@ -567,16 +567,31 @@
 }
 
 int ContentSettingBubbleContents::GetDialogButtons() const {
-  return ui::DIALOG_BUTTON_OK;
+  int buttons = ui::DIALOG_BUTTON_OK;
+
+  // Use the CANCEL button as a manage button.
+  const ContentSettingBubbleModel::BubbleContent& bubble_content =
+      content_setting_bubble_model_->bubble_content();
+  if (!bubble_content.manage_text.empty() &&
+      !bubble_content.show_manage_text_as_checkbox) {
+    buttons |= ui::DIALOG_BUTTON_CANCEL;
+  }
+  return buttons;
 }
 
 base::string16 ContentSettingBubbleContents::GetDialogButtonLabel(
     ui::DialogButton button) const {
-  const base::string16& done_text =
-      content_setting_bubble_model_->bubble_content().done_button_text;
-  if (!done_text.empty())
-    return done_text;
-  return l10n_util::GetStringUTF16(IDS_DONE);
+  const ContentSettingBubbleModel::BubbleContent& bubble_content =
+      content_setting_bubble_model_->bubble_content();
+  if (button == ui::DIALOG_BUTTON_OK) {
+    return bubble_content.done_button_text.empty()
+               ? l10n_util::GetStringUTF16(IDS_DONE)
+               : bubble_content.done_button_text;
+  }
+  DCHECK_EQ(ui::DIALOG_BUTTON_CANCEL, button);
+  DCHECK(!bubble_content.show_manage_text_as_checkbox);
+  DCHECK(!bubble_content.manage_text.empty());
+  return bubble_content.manage_text;
 }
 
 void ContentSettingBubbleContents::StyleLearnMoreButton(
@@ -628,14 +643,6 @@
     GetWidget()->Close();
     return;
   }
-  if (source == manage_link_) {
-    GetWidget()->Close();
-    content_setting_bubble_model_->OnManageLinkClicked();
-    // CAREFUL: Showing the settings window activates it, which deactivates the
-    // info bubble, which causes it to close, which deletes us.
-    return;
-  }
-
   int row = list_item_container_->GetRowIndexOf(source);
   DCHECK_NE(row, -1);
   content_setting_bubble_model_->OnListItemClicked(row, event_flags);
diff --git a/chrome/browser/ui/views/content_setting_bubble_contents.h b/chrome/browser/ui/views/content_setting_bubble_contents.h
index fa0e294..12b7ba7 100644
--- a/chrome/browser/ui/views/content_setting_bubble_contents.h
+++ b/chrome/browser/ui/views/content_setting_bubble_contents.h
@@ -38,7 +38,7 @@
 // were blocked, and the user can click one to get a bubble hosting a few
 // controls.  This class provides the content of that bubble.  In general,
 // these bubbles typically have a title, a pair of radio buttons for toggling
-// the blocking settings for the current site, a close button, and a link to
+// the blocking settings for the current site, a close button, and a button to
 // get to a more comprehensive settings management dialog.  A few types have
 // more or fewer controls than this.
 class ContentSettingBubbleContents : public content::WebContentsObserver,
@@ -67,6 +67,7 @@
   // views::BubbleDialogDelegateView:
   void Init() override;
   View* CreateExtraView() override;
+  bool Cancel() override;
   bool Accept() override;
   bool Close() override;
   int GetDialogButtons() const override;
@@ -125,7 +126,6 @@
   typedef std::vector<views::RadioButton*> RadioGroup;
   RadioGroup radio_group_;
   views::Link* custom_link_;
-  views::Link* manage_link_;
   views::LabelButton* manage_button_;
   views::Checkbox* manage_checkbox_;
   views::ImageButton* learn_more_button_;
diff --git a/chrome/browser/ui/views/extensions/extension_install_dialog_view_browsertest.cc b/chrome/browser/ui/views/extensions/extension_install_dialog_view_browsertest.cc
index 230696a5..e88fa2c9 100644
--- a/chrome/browser/ui/views/extensions/extension_install_dialog_view_browsertest.cc
+++ b/chrome/browser/ui/views/extensions/extension_install_dialog_view_browsertest.cc
@@ -25,7 +25,6 @@
 #include "extensions/common/extension.h"
 #include "extensions/common/permissions/permission_message_provider.h"
 #include "extensions/common/permissions/permissions_data.h"
-#include "extensions/common/test_util.h"
 #include "ui/views/controls/scroll_view.h"
 #include "ui/views/view.h"
 #include "ui/views/widget/widget.h"
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
index 6c83699..9a13f0d 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
@@ -333,13 +333,35 @@
 
 void BrowserNonClientFrameViewAsh::OnTabletModeStarted() {
   caption_button_container_->UpdateSizeButtonVisibility();
+
+  // Enter immersive mode if the feature is enabled and the widget is not
+  // already in fullscreen mode. Popups that are not activated but not
+  // minimized are still put in immersive mode, since they may still be visible
+  // but not activated due to something transparent and/or not fullscreen (ie.
+  // fullscreen launcher).
+  if (ash::Shell::Get()->tablet_mode_controller()->ShouldAutoHideTitlebars() &&
+      !frame()->IsFullscreen() && !browser_view()->IsBrowserTypeNormal() &&
+      !frame()->IsMinimized()) {
+    browser_view()->immersive_mode_controller()->SetEnabled(true);
+    return;
+  }
   InvalidateLayout();
   frame()->client_view()->InvalidateLayout();
   frame()->GetRootView()->Layout();
 }
 
 void BrowserNonClientFrameViewAsh::OnTabletModeEnded() {
-  OnTabletModeStarted();
+  caption_button_container_->UpdateSizeButtonVisibility();
+
+  // Exit immersive mode if the feature is enabled and the widget is not in
+  // fullscreen mode.
+  if (!frame()->IsFullscreen() && !browser_view()->IsBrowserTypeNormal()) {
+    browser_view()->immersive_mode_controller()->SetEnabled(false);
+    return;
+  }
+  InvalidateLayout();
+  frame()->client_view()->InvalidateLayout();
+  frame()->GetRootView()->Layout();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index 6850f856..8bf3e9f 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -1793,6 +1793,8 @@
              extension_keybinding_registry_.get()) {
     registry->set_registry_for_active_window(nullptr);
   }
+
+  immersive_mode_controller()->OnWidgetActivationChanged(widget, active);
 }
 
 void BrowserView::OnWindowBeginUserBoundsChange() {
@@ -2353,7 +2355,10 @@
   frame_->SetFullscreen(fullscreen);
 
   // Enable immersive before the browser refreshes its list of enabled commands.
-  if (ShouldUseImmersiveFullscreenForUrl(url))
+  const bool should_stay_in_immersive =
+      !fullscreen &&
+      immersive_mode_controller_->ShouldStayImmersiveAfterExitingFullscreen();
+  if (ShouldUseImmersiveFullscreenForUrl(url) && !should_stay_in_immersive)
     immersive_mode_controller_->SetEnabled(fullscreen);
 
   browser_->WindowFullscreenStateWillChange();
diff --git a/chrome/browser/ui/views/frame/browser_view_layout_unittest.cc b/chrome/browser/ui/views/frame/browser_view_layout_unittest.cc
index 9efa7ec8..c5789a1 100644
--- a/chrome/browser/ui/views/frame/browser_view_layout_unittest.cc
+++ b/chrome/browser/ui/views/frame/browser_view_layout_unittest.cc
@@ -100,7 +100,9 @@
   }
   void OnFindBarVisibleBoundsChanged(
       const gfx::Rect& new_visible_bounds) override {}
+  bool ShouldStayImmersiveAfterExitingFullscreen() override { return true; }
   views::Widget* GetRevealWidget() override { return nullptr; }
+  void OnWidgetActivationChanged(views::Widget* widget, bool active) override {}
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockImmersiveModeController);
diff --git a/chrome/browser/ui/views/frame/immersive_mode_controller.h b/chrome/browser/ui/views/frame/immersive_mode_controller.h
index 0ff37ff..8ed7afbe 100644
--- a/chrome/browser/ui/views/frame/immersive_mode_controller.h
+++ b/chrome/browser/ui/views/frame/immersive_mode_controller.h
@@ -109,12 +109,24 @@
   virtual void OnFindBarVisibleBoundsChanged(
       const gfx::Rect& new_visible_bounds_in_screen) = 0;
 
+  // Returns true if we should stay in immersive mode after exiting fullscreen.
+  // This should be true unless we are leaving fullscreen while in tablet mode,
+  // in which case we should stay in immersive mode.
+  virtual bool ShouldStayImmersiveAfterExitingFullscreen() = 0;
+
   Type type() const { return type_; }
 
   // Returns the widget hosting the reveal, null if a widget isn't used to
   // host the reveal, or not currently revealed.
   virtual views::Widget* GetRevealWidget() = 0;
 
+  // Called by browser view to indicate the widget activation has changed.
+  // Immersive mode should be enabled/disabled if the widget is
+  // active/nonactive when the auto hide title bars in tablet mode feature is
+  // on.
+  virtual void OnWidgetActivationChanged(views::Widget* widget,
+                                         bool active) = 0;
+
   virtual void AddObserver(Observer* observer);
   virtual void RemoveObserver(Observer* observer);
 
diff --git a/chrome/browser/ui/views/frame/immersive_mode_controller_ash.cc b/chrome/browser/ui/views/frame/immersive_mode_controller_ash.cc
index 0794ef22..0d6d6008 100644
--- a/chrome/browser/ui/views/frame/immersive_mode_controller_ash.cc
+++ b/chrome/browser/ui/views/frame/immersive_mode_controller_ash.cc
@@ -6,6 +6,7 @@
 
 #include "ash/public/cpp/immersive/immersive_revealed_lock.h"
 #include "ash/shell.h"
+#include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "ash/wm/window_state.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
@@ -164,10 +165,33 @@
   find_bar_visible_bounds_in_screen_ = new_visible_bounds_in_screen;
 }
 
+bool ImmersiveModeControllerAsh::ShouldStayImmersiveAfterExitingFullscreen() {
+  return !browser_view_->IsBrowserTypeNormal() &&
+         ash::Shell::Get()->tablet_mode_controller()->ShouldAutoHideTitlebars();
+}
+
 views::Widget* ImmersiveModeControllerAsh::GetRevealWidget() {
   return mash_reveal_widget_.get();
 }
 
+void ImmersiveModeControllerAsh::OnWidgetActivationChanged(
+    views::Widget* widget,
+    bool active) {
+  if (browser_view_->IsBrowserTypeNormal())
+    return;
+
+  if (!ash::Shell::Get()->tablet_mode_controller()->ShouldAutoHideTitlebars())
+    return;
+
+  // Enable immersive mode if the widget is activated. Do not disable immersive
+  // mode if the widget deactivates, but is not minimized.
+  controller_->SetEnabled(
+      browser_view_->browser()->is_app()
+          ? ash::ImmersiveFullscreenController::WINDOW_TYPE_HOSTED_APP
+          : ash::ImmersiveFullscreenController::WINDOW_TYPE_BROWSER,
+      active || !widget->IsMinimized());
+}
+
 void ImmersiveModeControllerAsh::EnableWindowObservers(bool enable) {
   if (observers_enabled_ == enable)
     return;
diff --git a/chrome/browser/ui/views/frame/immersive_mode_controller_ash.h b/chrome/browser/ui/views/frame/immersive_mode_controller_ash.h
index e5ad2c0..ea01a0ac 100644
--- a/chrome/browser/ui/views/frame/immersive_mode_controller_ash.h
+++ b/chrome/browser/ui/views/frame/immersive_mode_controller_ash.h
@@ -47,7 +47,9 @@
       WARN_UNUSED_RESULT;
   void OnFindBarVisibleBoundsChanged(
       const gfx::Rect& new_visible_bounds_in_screen) override;
+  bool ShouldStayImmersiveAfterExitingFullscreen() override;
   views::Widget* GetRevealWidget() override;
+  void OnWidgetActivationChanged(views::Widget* widget, bool active) override;
 
  private:
   // Enables or disables observers for window restore and entering / exiting
diff --git a/chrome/browser/ui/views/frame/immersive_mode_controller_ash_unittest.cc b/chrome/browser/ui/views/frame/immersive_mode_controller_ash_unittest.cc
index 2880f02..9424224 100644
--- a/chrome/browser/ui/views/frame/immersive_mode_controller_ash_unittest.cc
+++ b/chrome/browser/ui/views/frame/immersive_mode_controller_ash_unittest.cc
@@ -10,8 +10,10 @@
 #include "ash/shelf/shelf_layout_manager.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
+#include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "base/command_line.h"
 #include "base/macros.h"
+#include "base/test/scoped_feature_list.h"
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/exclusive_access/fullscreen_controller.h"
@@ -347,3 +349,63 @@
   EXPECT_FALSE(toolbar->visible());
   EXPECT_EQ(header_height, GetBoundsInWidget(contents_web_view).y());
 }
+
+class ImmersiveModeControllerAshTestTabletMode
+    : public ImmersiveModeControllerAshTest {
+ public:
+  ImmersiveModeControllerAshTestTabletMode()
+      : ImmersiveModeControllerAshTest(Browser::TYPE_POPUP, false) {}
+  ~ImmersiveModeControllerAshTestTabletMode() override {}
+
+  void SetUp() override {
+    scoped_feature_list.InitAndEnableFeature(
+        ash::kAutoHideTitleBarsInTabletMode);
+    ImmersiveModeControllerAshTest::SetUp();
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list;
+
+  DISALLOW_COPY_AND_ASSIGN(ImmersiveModeControllerAshTestTabletMode);
+};
+
+// Verify the immersive mode status is as expected in tablet mode when the auto
+// hide title bars in tablet mode feature is enabled.
+TEST_F(ImmersiveModeControllerAshTestTabletMode, ImmersiveModeStatus) {
+  ASSERT_FALSE(controller()->IsEnabled());
+
+  // Verify that after entering tablet mode, immersive mode is enabled.
+  ash::Shell::Get()->tablet_mode_controller()->EnableTabletModeWindowManager(
+      true);
+  EXPECT_TRUE(controller()->IsEnabled());
+
+  // Verify that after minimizing, immersive mode is disabled.
+  browser()->window()->Minimize();
+  EXPECT_FALSE(controller()->IsEnabled());
+
+  // Verify that after showing the browser, immersive mode is reenabled.
+  browser()->window()->Show();
+  EXPECT_TRUE(controller()->IsEnabled());
+
+  // Verify that immersive mode remains if fullscreen is toggled while in tablet
+  // mode.
+  ToggleFullscreen();
+  EXPECT_TRUE(controller()->IsEnabled());
+  ash::Shell::Get()->tablet_mode_controller()->EnableTabletModeWindowManager(
+      false);
+  EXPECT_TRUE(controller()->IsEnabled());
+
+  // Verify that immersive mode remains if the browser was fullscreened when
+  // entering tablet mode.
+  ash::Shell::Get()->tablet_mode_controller()->EnableTabletModeWindowManager(
+      true);
+  EXPECT_TRUE(controller()->IsEnabled());
+
+  // Verify that if the browser is not fullscreened, upon exiting tablet mode,
+  // immersive mode is not enabled.
+  ToggleFullscreen();
+  EXPECT_TRUE(controller()->IsEnabled());
+  ash::Shell::Get()->tablet_mode_controller()->EnableTabletModeWindowManager(
+      false);
+  EXPECT_FALSE(controller()->IsEnabled());
+}
diff --git a/chrome/browser/ui/views/frame/immersive_mode_controller_stub.cc b/chrome/browser/ui/views/frame/immersive_mode_controller_stub.cc
index 02a57be..90547cc6 100644
--- a/chrome/browser/ui/views/frame/immersive_mode_controller_stub.cc
+++ b/chrome/browser/ui/views/frame/immersive_mode_controller_stub.cc
@@ -45,6 +45,14 @@
     const gfx::Rect& new_visible_bounds_in_screen) {
 }
 
+bool ImmersiveModeControllerStub::ShouldStayImmersiveAfterExitingFullscreen() {
+  return false;
+}
+
 views::Widget* ImmersiveModeControllerStub::GetRevealWidget() {
   return nullptr;
 }
+
+void ImmersiveModeControllerStub::OnWidgetActivationChanged(
+    views::Widget* widget,
+    bool active) {}
diff --git a/chrome/browser/ui/views/frame/immersive_mode_controller_stub.h b/chrome/browser/ui/views/frame/immersive_mode_controller_stub.h
index 500c262..ca944e9 100644
--- a/chrome/browser/ui/views/frame/immersive_mode_controller_stub.h
+++ b/chrome/browser/ui/views/frame/immersive_mode_controller_stub.h
@@ -29,7 +29,9 @@
       WARN_UNUSED_RESULT;
   void OnFindBarVisibleBoundsChanged(
       const gfx::Rect& new_visible_bounds_in_screen) override;
+  bool ShouldStayImmersiveAfterExitingFullscreen() override;
   views::Widget* GetRevealWidget() override;
+  void OnWidgetActivationChanged(views::Widget* widget, bool active) override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(ImmersiveModeControllerStub);
diff --git a/chrome/browser/ui/views/toolbar/app_menu_button.cc b/chrome/browser/ui/views/toolbar/app_menu_button.cc
index 75eb9aa..23b1aff 100644
--- a/chrome/browser/ui/views/toolbar/app_menu_button.cc
+++ b/chrome/browser/ui/views/toolbar/app_menu_button.cc
@@ -183,11 +183,12 @@
     // Only show a special color for severity when using the classic Chrome
     // theme. Otherwise, we can't be sure that it contrasts with the toolbar
     // background.
-    new_icon_->SetColor(
-        ThemeServiceFactory::GetForProfile(toolbar_view_->browser()->profile())
-                ->UsingDefaultTheme()
-            ? severity_color
-            : toolbar_icon_color);
+    ThemeService* theme_service =
+        ThemeServiceFactory::GetForProfile(toolbar_view_->browser()->profile());
+    new_icon_->SetColor(theme_service->UsingSystemTheme() ||
+                                theme_service->UsingDefaultTheme()
+                            ? severity_color
+                            : toolbar_icon_color);
 
     if (should_animate)
       AnimateIconIfPossible();
diff --git a/chrome/browser/vr/BUILD.gn b/chrome/browser/vr/BUILD.gn
index edb7bb3..f99019f 100644
--- a/chrome/browser/vr/BUILD.gn
+++ b/chrome/browser/vr/BUILD.gn
@@ -18,6 +18,9 @@
     "animation_player.h",
     "color_scheme.cc",
     "color_scheme.h",
+    "databinding/binding.h",
+    "databinding/binding_base.cc",
+    "databinding/binding_base.h",
     "elements/button.cc",
     "elements/button.h",
     "elements/button_texture.cc",
@@ -143,6 +146,7 @@
 test("vr_common_unittests") {
   sources = [
     "animation_player_unittest.cc",
+    "databinding/binding_unittest.cc",
     "elements/close_button_texture_unittest.cc",
     "elements/exit_prompt_unittest.cc",
     "elements/linear_layout_unittest.cc",
diff --git a/chrome/browser/vr/databinding/binding.h b/chrome/browser/vr/databinding/binding.h
new file mode 100644
index 0000000..e5a10a0
--- /dev/null
+++ b/chrome/browser/vr/databinding/binding.h
@@ -0,0 +1,98 @@
+// 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_VR_DATABINDING_BINDING_H_
+#define CHROME_BROWSER_VR_DATABINDING_BINDING_H_
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/optional.h"
+#include "chrome/browser/vr/databinding/binding_base.h"
+
+namespace vr {
+
+// This class represents a one-way binding that propagates a change from a
+// source/model property to a sink/view. This is inappropriate for use in the
+// case of, say, an editor view like a text field where changes must alse be
+// propegated back to the model.
+//
+// IMPORTANT: it is assumed that a Binding instance will outlive the model to
+// which it is bound. This class in not appropriate for use with models that
+// come and go in the lifetime of the application.
+template <typename T>
+class Binding : public BindingBase {
+ public:
+  Binding(const base::Callback<T()>& getter,
+          const base::Callback<void(const T&)>& setter)
+      : getter_(getter), setter_(setter) {}
+  ~Binding() override {}
+
+  // This function will check if the getter is producing a different value than
+  // when it was last polled. If so, it will pass that value to the provided
+  // setter. NB: this assumes that T is copyable.
+  void Update() override {
+    T current_value = getter_.Run();
+    if (!last_value_ || current_value != last_value_.value()) {
+      last_value_ = current_value;
+      setter_.Run(current_value);
+    }
+  }
+
+ private:
+  base::Callback<T()> getter_;
+  base::Callback<void(const T&)> setter_;
+  base::Optional<T> last_value_;
+
+  DISALLOW_COPY_AND_ASSIGN(Binding);
+};
+
+// These macros are sugar for constructing a simple binding. It is meant to make
+// setting up bindings a little less painful, but it is not meant to handle all
+// cases. If you need to do something more complex (eg, convert type T before
+// it is propagated), you should use the constructor directly.
+//
+// For example:
+//
+// struct MyModel { int source; };
+// struct MyView {
+//   int sink;
+//   int awesomeness;
+//   void SetAwesomeness(int new_awesomeness) {
+//     awesomeness = new_awesomeness;
+//   }
+// };
+//
+// MyModel m;
+// m.source = 20;
+//
+// MyView v;
+// v.sink = 10;
+// v.awesomeness = 30;
+//
+// auto binding = VR_BIND(int, MyModel, &m, source, MyView, &v, sink = value);
+//
+// Or, equivalently:
+//
+// auto binding = VR_BIND_FIELD(int, MyModel, &m, source, MyView, &v, sink);
+//
+// If your view has a setter, you may find VR_BIND_FUNC handy:
+//
+// auto binding =
+//     VR_BIND_FUNC(int, MyModel, &m, source, MyView, &v, SetAwesomeness);
+//
+#define VR_BIND(T, M, m, Get, V, v, Set)                                    \
+  base::MakeUnique<Binding<T>>(                                             \
+      base::Bind([](M* model) { return model->Get; }, base::Unretained(m)), \
+      base::Bind([](V* view, const T& value) { view->Set; },                \
+                 base::Unretained(v)))
+
+#define VR_BIND_FUNC(T, M, m, Get, V, v, f) \
+  VR_BIND(T, M, m, Get, V, v, f(value))
+
+#define VR_BIND_FIELD(T, M, m, Get, V, v, f) \
+  VR_BIND(T, M, m, Get, V, v, f = value)
+
+}  // namespace vr
+
+#endif  // CHROME_BROWSER_VR_DATABINDING_BINDING_H_
diff --git a/chrome/browser/vr/databinding/binding_base.cc b/chrome/browser/vr/databinding/binding_base.cc
new file mode 100644
index 0000000..e8065bb
--- /dev/null
+++ b/chrome/browser/vr/databinding/binding_base.cc
@@ -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.
+
+#include "chrome/browser/vr/databinding/binding_base.h"
+
+namespace vr {
+
+BindingBase::BindingBase() : weak_ptr_factory_(this) {}
+BindingBase::~BindingBase() {}
+
+base::WeakPtr<BindingBase> BindingBase::GetWeakPtr() {
+  return weak_ptr_factory_.GetWeakPtr();
+}
+
+}  // namespace vr
diff --git a/chrome/browser/vr/databinding/binding_base.h b/chrome/browser/vr/databinding/binding_base.h
new file mode 100644
index 0000000..a6c7981
--- /dev/null
+++ b/chrome/browser/vr/databinding/binding_base.h
@@ -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.
+
+#ifndef CHROME_BROWSER_VR_DATABINDING_BINDING_BASE_H_
+#define CHROME_BROWSER_VR_DATABINDING_BINDING_BASE_H_
+
+#include "base/memory/weak_ptr.h"
+
+namespace vr {
+
+// Bindings are used to tie models to views. You may, for example, want to bind
+// the visibility of an error indicator to a boolean that signals that the
+// application is exhibiting the error condition.
+class BindingBase {
+ public:
+  BindingBase();
+  virtual ~BindingBase();
+
+  // This function updates the binding. The exact behavior depends on the
+  // subclass. Please see comments on the overridden functions for details.
+  virtual void Update() = 0;
+
+  base::WeakPtr<BindingBase> GetWeakPtr();
+
+ private:
+  base::WeakPtrFactory<BindingBase> weak_ptr_factory_;
+};
+
+}  // namespace vr
+
+#endif  // CHROME_BROWSER_VR_DATABINDING_BINDING_BASE_H_
diff --git a/chrome/browser/vr/databinding/binding_unittest.cc b/chrome/browser/vr/databinding/binding_unittest.cc
new file mode 100644
index 0000000..8adea0c
--- /dev/null
+++ b/chrome/browser/vr/databinding/binding_unittest.cc
@@ -0,0 +1,72 @@
+// 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/vr/databinding/binding.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/memory/weak_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace vr {
+
+namespace {
+
+struct TestModel {
+  bool value;
+};
+
+struct TestView {
+  bool value;
+  std::unique_ptr<Binding<bool>> binding;
+};
+
+}  // namespace
+
+TEST(Binding, BoundBool) {
+  TestModel a;
+  a.value = true;
+
+  TestView b;
+  b.value = false;
+
+  b.binding = VR_BIND_FIELD(bool, TestModel, &a, value, TestView, &b, value);
+
+  EXPECT_NE(a.value, b.value);
+  b.binding->Update();
+
+  EXPECT_EQ(true, a.value);
+  EXPECT_EQ(true, b.value);
+
+  a.value = false;
+  EXPECT_EQ(true, b.value);
+
+  b.binding->Update();
+  EXPECT_EQ(false, a.value);
+  EXPECT_EQ(false, b.value);
+
+  b.value = true;
+  // Since this is a one way binding, Update will not detect a change in value
+  // of b. Since a's value has not changed, a new value should not be pushed
+  // to b.
+  b.binding->Update();
+  EXPECT_EQ(false, a.value);
+  EXPECT_EQ(true, b.value);
+}
+
+TEST(Binding, Lifetime) {
+  base::WeakPtr<BindingBase> binding;
+  {
+    TestModel a;
+    TestView b;
+    b.binding = VR_BIND_FIELD(bool, TestModel, &a, value, TestView, &b, value);
+    binding = b.binding->GetWeakPtr();
+  }
+  // This test is not particularly useful, since we're just testing the behavior
+  // of base::WeakPtr, but it confirms that when an object owning a binding
+  // falls out of scope, weak pointers to its bindings are correctly
+  // invalidated.
+  EXPECT_FALSE(!!binding);
+}
+
+}  // namespace vr
diff --git a/chrome/browser/vr/elements/url_bar_texture.cc b/chrome/browser/vr/elements/url_bar_texture.cc
index be0ada28d..ac08e4f 100644
--- a/chrome/browser/vr/elements/url_bar_texture.cc
+++ b/chrome/browser/vr/elements/url_bar_texture.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/vr/elements/url_bar_texture.h"
 
+#include <utility>
+
 #include "base/strings/utf_string_conversions.h"
 #include "cc/paint/skia_paint_canvas.h"
 #include "chrome/browser/vr/color_scheme.h"
@@ -46,8 +48,6 @@
     case SecurityLevel::EV_SECURE:
     case SecurityLevel::SECURE:
       return color_scheme.secure;
-    case SecurityLevel::SECURITY_WARNING:
-      return color_scheme.url_deemphasized;
     case SecurityLevel::SECURE_WITH_POLICY_INSTALLED_CERT:  // ChromeOS only.
       return color_scheme.insecure;
     case SecurityLevel::DANGEROUS:
diff --git a/chrome/common/BUILD.gn b/chrome/common/BUILD.gn
index 5fbb36a..5c4d97e 100644
--- a/chrome/common/BUILD.gn
+++ b/chrome/common/BUILD.gn
@@ -234,6 +234,7 @@
     "//components/network_session_configurator/common",
     "//components/ntp_tiles",
     "//components/offline_pages/core:switches",
+    "//components/offline_pages/features:features",
     "//components/omnibox/common",
     "//components/password_manager/core/common",
     "//components/policy:generated",
diff --git a/chrome/common/conflicts/module_event_sink_win.mojom b/chrome/common/conflicts/module_event_sink_win.mojom
index 3d2fa936..58fcb89e8 100644
--- a/chrome/common/conflicts/module_event_sink_win.mojom
+++ b/chrome/common/conflicts/module_event_sink_win.mojom
@@ -10,8 +10,6 @@
   MODULE_ALREADY_LOADED,
   // A module is in the process of being loaded.
   MODULE_LOADED,
-  // A module is in the process of being unloaded.
-  MODULE_UNLOADED,
 };
 
 // Interface for a remote consumer of module events.
diff --git a/chrome/common/conflicts/module_watcher_win.cc b/chrome/common/conflicts/module_watcher_win.cc
index fca97a3..f9bbe2e 100644
--- a/chrome/common/conflicts/module_watcher_win.cc
+++ b/chrome/common/conflicts/module_watcher_win.cc
@@ -209,8 +209,7 @@
       break;
 
     case LDR_DLL_NOTIFICATION_REASON_UNLOADED:
-      OnModuleEvent(mojom::ModuleEventType::MODULE_UNLOADED,
-                    notification_data->Unloaded, callback);
+      // Intentionally ignored.
       break;
 
     default:
diff --git a/chrome/common/conflicts/module_watcher_win.h b/chrome/common/conflicts/module_watcher_win.h
index a7c00d1..4140474 100644
--- a/chrome/common/conflicts/module_watcher_win.h
+++ b/chrome/common/conflicts/module_watcher_win.h
@@ -15,16 +15,15 @@
 
 union LDR_DLL_NOTIFICATION_DATA;
 
-// This class observes modules as they are loaded and unloaded into a process's
-// address space.
+// This class observes modules as they are loaded into a process's address
+// space.
 //
 // This class is safe to be created on any thread. Similarly, it is safe to be
 // destroyed on any thread, independent of the thread on which the instance was
 // created.
 class ModuleWatcher {
  public:
-  // Houses information about a module load/unload event, and some module
-  // metadata.
+  // Houses information about a module event, and some module metadata.
   struct ModuleEvent {
     ModuleEvent() = default;
     ModuleEvent(const ModuleEvent& other) = default;
@@ -57,11 +56,11 @@
 
   // Creates and starts a watcher. This enumerates all loaded modules
   // synchronously on the current thread during construction, and provides
-  // synchronous notifications as modules are loaded and unloaded. The callback
-  // is invoked in the context of the thread that is loading a module, and as
-  // such may be invoked on any thread in the process. Note that it is possible
-  // to receive two notifications for some modules as the initial loaded module
-  // enumeration races briefly with the callback mechanism. In this case both a
+  // synchronous notifications as modules are loaded. The callback is invoked in
+  // the context of the thread that is loading a module, and as such may be
+  // invoked on any thread in the process. Note that it is possible to receive
+  // two notifications for some modules as the initial loaded module enumeration
+  // races briefly with the callback mechanism. In this case both a
   // MODULE_LOADED and a MODULE_ALREADY_LOADED event will be received for the
   // same module. Since the callback is installed first no modules can be
   // missed, however. This factory function may be called on any thread.
diff --git a/chrome/common/conflicts/module_watcher_win_unittest.cc b/chrome/common/conflicts/module_watcher_win_unittest.cc
index dd698874..d9e3b1c7 100644
--- a/chrome/common/conflicts/module_watcher_win_unittest.cc
+++ b/chrome/common/conflicts/module_watcher_win_unittest.cc
@@ -15,8 +15,7 @@
       : module_(nullptr),
         module_event_count_(0),
         module_already_loaded_event_count_(0),
-        module_loaded_event_count_(0),
-        module_unloaded_event_count_(0) {}
+        module_loaded_event_count_(0) {}
 
   void OnModuleEvent(const ModuleWatcher::ModuleEvent& event) {
     ++module_event_count_;
@@ -27,9 +26,6 @@
       case mojom::ModuleEventType::MODULE_LOADED:
         ++module_loaded_event_count_;
         break;
-      case mojom::ModuleEventType::MODULE_UNLOADED:
-        ++module_unloaded_event_count_;
-        break;
     }
   }
 
@@ -69,8 +65,6 @@
   int module_already_loaded_event_count_;
   // Total number of MODULE_LOADED events seen.
   int module_loaded_event_count_;
-  // Total number of MODULE_UNLOADED events seen.
-  int module_unloaded_event_count_;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(ModuleWatcherTest);
@@ -91,28 +85,26 @@
   EXPECT_LT(0, module_event_count_);
   EXPECT_LT(0, module_already_loaded_event_count_);
   EXPECT_EQ(0, module_loaded_event_count_);
-  EXPECT_EQ(0, module_unloaded_event_count_);
 
   // Dynamically load a module and ensure a notification is received for it.
   int previous_module_loaded_event_count = module_loaded_event_count_;
   LoadModule();
   EXPECT_LT(previous_module_loaded_event_count, module_loaded_event_count_);
 
-  // Unload the module and ensure another notification is received.
-  int previous_module_unloaded_event_count = module_unloaded_event_count_;
   UnloadModule();
-  EXPECT_LT(previous_module_unloaded_event_count, module_unloaded_event_count_);
 
   // Dynamically load a module and ensure a notification is received for it.
   previous_module_loaded_event_count = module_loaded_event_count_;
   LoadModule();
   EXPECT_LT(previous_module_loaded_event_count, module_loaded_event_count_);
 
+  UnloadModule();
+
   // Destroy the module watcher.
   mw.reset();
 
-  // Unload the module and ensure no notification is received this time.
-  previous_module_unloaded_event_count = module_unloaded_event_count_;
-  UnloadModule();
-  EXPECT_EQ(previous_module_unloaded_event_count, module_unloaded_event_count_);
+  // Load the module and ensure no notification is received this time.
+  previous_module_loaded_event_count = module_loaded_event_count_;
+  LoadModule();
+  EXPECT_EQ(previous_module_loaded_event_count, module_loaded_event_count_);
 }
diff --git a/chrome/common/extensions/api/file_manager_private.idl b/chrome/common/extensions/api/file_manager_private.idl
index 6822900..14ea9f7e 100644
--- a/chrome/common/extensions/api/file_manager_private.idl
+++ b/chrome/common/extensions/api/file_manager_private.idl
@@ -112,7 +112,13 @@
   // Format succeeded.
   format_success,
   // Format failed.
-  format_fail
+  format_fail,
+  // Rename started.
+  rename_start,
+  // Rename succeeded.
+  rename_success,
+  // Rename failed.
+  rename_fail
 };
 
 // Drive sync error type.
@@ -378,6 +384,9 @@
 
   // Context in which the volume has been mounted.
   MountContext? mountContext;
+
+  // File system type indentifier.
+  DOMString? diskFileSystemType;
 };
 
 // Payload data for mount event.
diff --git a/chrome/common/extensions/extension_test_util.h b/chrome/common/extensions/extension_test_util.h
index f536a70..c21f1c0 100644
--- a/chrome/common/extensions/extension_test_util.h
+++ b/chrome/common/extensions/extension_test_util.h
@@ -14,7 +14,6 @@
 class Extension;
 }
 
-// Newer functions go in extensions/common/test_util.h.
 namespace extension_test_util {
 
 // Helpers for loading manifests, |dir| is relative to chrome::DIR_TEST_DATA
diff --git a/chrome/common/extensions/media_parser_struct_traits.h b/chrome/common/extensions/media_parser_struct_traits.h
index 0edd0d1..e87d6b4 100644
--- a/chrome/common/extensions/media_parser_struct_traits.h
+++ b/chrome/common/extensions/media_parser_struct_traits.h
@@ -7,6 +7,7 @@
 
 #include <string>
 
+#include "base/containers/span.h"
 #include "chrome/common/extensions/media_parser.mojom.h"
 #include "chrome/common/media_galleries/metadata_types.h"
 #include "mojo/public/cpp/bindings/array_traits_span.h"
@@ -20,11 +21,12 @@
     return image.type;
   }
 
-  static ConstCArray<uint8_t> data(const ::metadata::AttachedImage& image) {
+  static base::span<const uint8_t> data(
+      const ::metadata::AttachedImage& image) {
     // TODO(dcheng): perhaps metadata::AttachedImage should consider passing the
     // image data around in a std::vector<uint8_t>.
-    return ConstCArray<uint8_t>(
-        reinterpret_cast<const uint8_t*>(image.data.data()), image.data.size());
+    return base::make_span(reinterpret_cast<const uint8_t*>(image.data.data()),
+                           image.data.size());
   }
 
   static bool Read(extensions::mojom::AttachedImageDataView view,
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index 82a4a3c6d..df1af01 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -61,8 +61,9 @@
     "arc.voice_interaction_value_prop.accepted";
 // A preference that indicates the user has enabled voice interaction services.
 const char kVoiceInteractionEnabled[] = "settings.voice_interaction.enabled";
-// A preference that indicates the user has enabled providing context to
-// voice interaction services.
+// A preference that indicates the user has allowed voice interaction services
+// to access the "context" (text and graphic content that is currently on
+// screen).
 const char kVoiceInteractionContextEnabled[] =
     "settings.voice_interaction.context.enabled";
 // A preference indicating whether voice interaction settings have been read
diff --git a/chrome/common/render_messages.h b/chrome/common/render_messages.h
index eb67c9b..c5a35860 100644
--- a/chrome/common/render_messages.h
+++ b/chrome/common/render_messages.h
@@ -19,6 +19,7 @@
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/content_settings/core/common/content_settings_pattern.h"
 #include "components/content_settings/core/common/content_settings_types.h"
+#include "components/offline_pages/features/features.h"
 #include "components/omnibox/common/omnibox_focus_state.h"
 #include "content/public/common/browser_controls_state.h"
 #include "content/public/common/webplugininfo.h"
@@ -139,7 +140,7 @@
 // Tells the frame it is displaying an interstitial page.
 IPC_MESSAGE_ROUTED0(ChromeViewMsg_SetAsInterstitial)
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(ENABLE_OFFLINE_PAGES)
 // Message sent from the renderer to the browser to schedule to download the
 // page at a later time.
 IPC_MESSAGE_ROUTED0(ChromeViewHostMsg_DownloadPageLater)
@@ -148,7 +149,9 @@
 // is being shown in error page.
 IPC_MESSAGE_ROUTED1(ChromeViewHostMsg_SetIsShowingDownloadButtonInErrorPage,
                     bool /* showing download button */)
+#endif
 
+#if defined(OS_ANDROID)
 // Sent when navigating to chrome://sandbox to install bindings onto the WebUI.
 IPC_MESSAGE_ROUTED0(ChromeViewMsg_AddSandboxStatusExtension)
 #endif  // defined(OS_ANDROID)
diff --git a/chrome/installer/zucchini/BUILD.gn b/chrome/installer/zucchini/BUILD.gn
index 508bd4b..952e5bb6 100644
--- a/chrome/installer/zucchini/BUILD.gn
+++ b/chrome/installer/zucchini/BUILD.gn
@@ -8,6 +8,7 @@
 
 static_library("zucchini_lib") {
   sources = [
+    "address_translator.cc",
     "address_translator.h",
     "algorithm.h",
     "binary_data_histogram.cc",
@@ -119,6 +120,7 @@
 
 test("zucchini_unittests") {
   sources = [
+    "address_translator_unittest.cc",
     "algorithm_unittest.cc",
     "binary_data_histogram_unittest.cc",
     "buffer_sink_unittest.cc",
diff --git a/chrome/installer/zucchini/address_translator.cc b/chrome/installer/zucchini/address_translator.cc
new file mode 100644
index 0000000..a892096
--- /dev/null
+++ b/chrome/installer/zucchini/address_translator.cc
@@ -0,0 +1,258 @@
+// 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/installer/zucchini/address_translator.h"
+
+#include <algorithm>
+#include <utility>
+
+namespace zucchini {
+
+/******** AddressTranslator::OffsetToRvaCache ********/
+
+AddressTranslator::OffsetToRvaCache::OffsetToRvaCache(
+    const AddressTranslator& translator)
+    : translator_(translator) {}
+
+rva_t AddressTranslator::OffsetToRvaCache::Convert(offset_t offset) const {
+  if (offset >= translator_.fake_offset_begin_) {
+    // Rely on |translator_| to handle this special case.
+    return translator_.OffsetToRva(offset);
+  }
+  if (cached_unit_ && cached_unit_->CoversOffset(offset))
+    return cached_unit_->OffsetToRvaUnsafe(offset);
+  const AddressTranslator::Unit* unit = translator_.OffsetToUnit(offset);
+  if (!unit)
+    return kInvalidRva;
+  cached_unit_ = unit;
+  return unit->OffsetToRvaUnsafe(offset);
+}
+
+/******** AddressTranslator::RvaToOffsetCache ********/
+
+AddressTranslator::RvaToOffsetCache::RvaToOffsetCache(
+    const AddressTranslator& translator)
+    : translator_(translator) {}
+
+bool AddressTranslator::RvaToOffsetCache::IsValid(rva_t rva) const {
+  if (!cached_unit_ || !cached_unit_->CoversRva(rva)) {
+    const AddressTranslator::Unit* unit = translator_.RvaToUnit(rva);
+    if (!unit)
+      return false;
+    cached_unit_ = unit;
+  }
+  return true;
+}
+
+offset_t AddressTranslator::RvaToOffsetCache::Convert(rva_t rva) const {
+  if (!cached_unit_ || !cached_unit_->CoversRva(rva)) {
+    const AddressTranslator::Unit* unit = translator_.RvaToUnit(rva);
+    if (!unit)
+      return kInvalidOffset;
+    cached_unit_ = unit;
+  }
+  return cached_unit_->RvaToOffsetUnsafe(rva, translator_.fake_offset_begin_);
+}
+
+/******** AddressTranslator ********/
+
+AddressTranslator::AddressTranslator() = default;
+
+AddressTranslator::~AddressTranslator() = default;
+
+AddressTranslator::Status AddressTranslator::Initialize(
+    std::vector<Unit>&& units) {
+  for (Unit& unit : units) {
+    // Check for overflows and fail if found.
+    if (!RangeIsBounded<offset_t>(unit.offset_begin, unit.offset_size,
+                                  kOffsetBound) ||
+        !RangeIsBounded<rva_t>(unit.rva_begin, unit.rva_size, kRvaBound)) {
+      return kErrorOverflow;
+    }
+    // If |rva_size < offset_size|: Just shrink |offset_size| to accommodate.
+    unit.offset_size = std::min(unit.offset_size, unit.rva_size);
+    // Now |rva_size >= offset_size|. Note that |rva_size > offset_size| is
+    // allowed; these lead to dangling RVA.
+  }
+
+  // Remove all empty units.
+  units.erase(std::remove_if(units.begin(), units.end(),
+                             [](const Unit& unit) { return unit.IsEmpty(); }),
+              units.end());
+
+  // Sort |units| by RVA, then uniquefy.
+  std::sort(units.begin(), units.end(), [](const Unit& a, const Unit& b) {
+    return std::tie(a.rva_begin, a.rva_size) <
+           std::tie(b.rva_begin, b.rva_size);
+  });
+  units.erase(std::unique(units.begin(), units.end()), units.end());
+
+  // Scan for RVA range overlaps, validate, and merge wherever possible.
+  if (units.size() > 1) {
+    // Traverse with two iterators: |slow| stays behind and modifies Units that
+    // absorb all overlapping (or tangent if suitable) Units; |fast| explores
+    // new Units as candidates for consistency checks and potential merge into
+    // |slow|.
+    auto slow = units.begin();
+
+    // All |it| with |slow| < |it| < |fast| contain garbage.
+    for (auto fast = slow + 1; fast != units.end(); ++fast) {
+      // Comment notation: S = slow offset, F = fast offset, O = overlap offset,
+      // s = slow RVA, f = fast RVA, o = overlap RVA.
+      DCHECK_GE(fast->rva_begin, slow->rva_begin);
+      if (slow->rva_end() < fast->rva_begin) {
+        // ..ssssss..ffffff..: Disjoint: Can advance |slow|.
+        *(++slow) = *fast;
+        continue;
+      }
+
+      // ..ssssffff..: Tangent: Merge is optional.
+      // ..sssooofff.. / ..sssooosss..: Overlap: Merge is required.
+      bool merge_is_optional = slow->rva_end() == fast->rva_begin;
+
+      // Check whether |fast| and |slow| have identical RVA -> offset shift.
+      // If not, then merge cannot be resolved. Examples:
+      // ..ssssffff.. -> ..SSSSFFFF..: Good, can merge.
+      // ..ssssffff.. -> ..SSSS..FFFF..: Non-fatal: don't merge.
+      // ..ssssffff.. -> ..FFFF..SSSS..: Non-fatal: don't merge.
+      // ..ssssffff.. -> ..SSOOFF..: Fatal: Ignore for now (handled later).
+      // ..sssooofff.. -> ..SSSOOOFFF..: Good, can merge.
+      // ..sssooofff.. -> ..SSSSSOFFFFF..: Fatal.
+      // ..sssooofff.. -> ..FFOOOOSS..: Fatal.
+      // ..sssooofff.. -> ..SSSOOOF..: Good, notice |fast| has dangling RVAs.
+      // ..oooooo.. -> ..OOOOOO..: Good, can merge.
+      if (fast->offset_begin < slow->offset_begin ||
+          fast->offset_begin - slow->offset_begin !=
+              fast->rva_begin - slow->rva_begin) {
+        if (merge_is_optional) {
+          *(++slow) = *fast;
+          continue;
+        }
+        return kErrorBadOverlap;
+      }
+
+      // Check whether dangling RVAs (if they exist) are consistent. Examples:
+      // ..sssooofff.. -> ..SSSOOOF..: Good, can merge.
+      // ..sssooosss.. -> ..SSSOOOS..: Good, can merge.
+      // ..sssooofff.. -> ..SSSOO..: Good, can merge.
+      // ..sssooofff.. -> ..SSSOFFF..: Fatal.
+      // ..sssooosss.. -> ..SSSOOFFFF..: Fatal.
+      // ..oooooo.. -> ..OOO..: Good, can merge.
+      // Idea of check: Suppose |fast| has dangling RVA, then
+      // |[fast->rva_start, fast->rva_start + fast->offset_start)| ->
+      // |[fast->offset_start, **fast->offset_end()**)|, with remaining RVA
+      // mapping to fake offsets. This means |fast->offset_end()| must be >=
+      // |slow->offset_end()|, and failure to do so resluts in error. The
+      // argument for |slow| havng dangling RVA is symmetric.
+      if ((fast->HasDanglingRva() && fast->offset_end() < slow->offset_end()) ||
+          (slow->HasDanglingRva() && slow->offset_end() < fast->offset_end())) {
+        if (merge_is_optional) {
+          *(++slow) = *fast;
+          continue;
+        }
+        return kErrorBadOverlapDanglingRva;
+      }
+
+      // Merge |fast| into |slow|.
+      slow->rva_size =
+          std::max(slow->rva_size, fast->rva_end() - slow->rva_begin);
+      slow->offset_size =
+          std::max(slow->offset_size, fast->offset_end() - slow->offset_begin);
+    }
+    ++slow;
+    units.erase(slow, units.end());
+  }
+
+  // After resolving RVA overlaps, any offset overlap would imply error.
+  std::sort(units.begin(), units.end(), [](const Unit& a, const Unit& b) {
+    return a.offset_begin < b.offset_begin;
+  });
+
+  if (units.size() > 1) {
+    auto previous = units.begin();
+    for (auto current = previous + 1; current != units.end(); ++current) {
+      if (previous->offset_end() > current->offset_begin)
+        return kErrorBadOverlap;
+      previous = current;
+    }
+  }
+
+  // For to fake offset heuristics: Compute exclusive upper bounds for offsets
+  // and RVAs.
+  offset_t offset_bound = 0;
+  rva_t rva_bound = 0;
+  for (const Unit& unit : units) {
+    offset_bound = std::max(offset_bound, unit.offset_end());
+    rva_bound = std::max(rva_bound, unit.rva_end());
+  }
+
+  // Compute pessimistic range and see if it still fits within space of valid
+  // offsets. This limits image size to one half of |kOffsetBound|, and is a
+  // main drawback for the current heuristic to convert dangling RVA to fake
+  // offsets.
+  if (!RangeIsBounded(offset_bound, rva_bound, kOffsetBound))
+    return kErrorFakeOffsetBeginTooLarge;
+
+  // Success. Store results. |units| is currently sorted by offset, so assign.
+  units_sorted_by_offset_.assign(units.begin(), units.end());
+
+  // Sort |units| by RVA, and just store it directly
+  std::sort(units.begin(), units.end(), [](const Unit& a, const Unit& b) {
+    return a.rva_begin < b.rva_begin;
+  });
+  units_sorted_by_rva_ = std::move(units);
+
+  fake_offset_begin_ = offset_bound;
+  return kSuccess;
+}
+
+rva_t AddressTranslator::OffsetToRva(offset_t offset) const {
+  if (offset >= fake_offset_begin_) {
+    // Handle dangling RVA: First shift it to regular RVA space.
+    rva_t rva = offset - fake_offset_begin_;
+    // If result is indeed a dangling RVA, return it; else return |kInvalidRva|.
+    const Unit* unit = RvaToUnit(rva);
+    return (unit && unit->HasDanglingRva() && unit->CoversDanglingRva(rva))
+               ? rva
+               : kInvalidRva;
+  }
+  const Unit* unit = OffsetToUnit(offset);
+  return unit ? unit->OffsetToRvaUnsafe(offset) : kInvalidRva;
+}
+
+offset_t AddressTranslator::RvaToOffset(rva_t rva) const {
+  const Unit* unit = RvaToUnit(rva);
+  // This also handles dangling RVA.
+  return unit ? unit->RvaToOffsetUnsafe(rva, fake_offset_begin_)
+              : kInvalidOffset;
+}
+
+bool AddressTranslator::IsValidRva(rva_t rva) const {
+  return RvaToUnit(rva) != nullptr;
+}
+
+const AddressTranslator::Unit* AddressTranslator::OffsetToUnit(
+    offset_t offset) const {
+  // Finds first Unit with |offset_begin| > |offset|, rewind by 1 to find the
+  // last Unit with |offset_begin| >= |offset| (if it exists).
+  auto it = std::upper_bound(
+      units_sorted_by_offset_.begin(), units_sorted_by_offset_.end(), offset,
+      [](offset_t a, const Unit& b) { return a < b.offset_begin; });
+  if (it == units_sorted_by_offset_.begin())
+    return nullptr;
+  --it;
+  return it->CoversOffset(offset) ? &(*it) : nullptr;
+}
+
+const AddressTranslator::Unit* AddressTranslator::RvaToUnit(rva_t rva) const {
+  auto it = std::upper_bound(
+      units_sorted_by_rva_.begin(), units_sorted_by_rva_.end(), rva,
+      [](rva_t a, const Unit& b) { return a < b.rva_begin; });
+  if (it == units_sorted_by_rva_.begin())
+    return nullptr;
+  --it;
+  return it->CoversRva(rva) ? &(*it) : nullptr;
+}
+
+}  // namespace zucchini
diff --git a/chrome/installer/zucchini/address_translator.h b/chrome/installer/zucchini/address_translator.h
index 6c87b43..28a4dc6c 100644
--- a/chrome/installer/zucchini/address_translator.h
+++ b/chrome/installer/zucchini/address_translator.h
@@ -5,49 +5,193 @@
 #ifndef CHROME_INSTALLER_ZUCCHINI_ADDRESS_TRANSLATOR_H_
 #define CHROME_INSTALLER_ZUCCHINI_ADDRESS_TRANSLATOR_H_
 
-#include <memory>
+#include <stdint.h>
 
+#include <tuple>
+#include <vector>
+
+#include "base/macros.h"
+#include "chrome/installer/zucchini/algorithm.h"
 #include "chrome/installer/zucchini/image_utils.h"
 
 namespace zucchini {
 
+// There are several ways to reason about addresses in an image:
+// - Offset: Position relative to start of image.
+// - VA (Virtual Address): Virtual memory address of a loaded image. This is
+//   subject to relocation by the OS.
+// - RVA (Relative Virtual Address): VA relative to some base address. This is
+//   the preferred way to specify pointers in an image.
+//
+// Zucchini is primarily concerned with offsets and RVAs. Executable images like
+// PE and ELF are organized into sections. Each section specifies offset and RVA
+// ranges as:
+//   {Offset start, offset size, RVA start, RVA size}.
+// This constitutes a basic unit to translate between offsets and RVAs. Note:
+// |offset size| < |RVA size| is possible. For example, the .bss section can can
+// have zero-filled statically-allocated data that have no corresponding bytes
+// on image (to save space). This poses a problem for Zucchini, which stores
+// addresses as offsets: now we'd have "dangling RVAs" that don't map to
+// offsets! Some ways to handling this are:
+// 1. Ignore all dangling RVAs. This simplifies the algorithm, but also means
+//    some reference targets would escape detection and processing.
+// 2. Create distinct "fake offsets" to accommodate dangling RVAs. Image data
+//    must not be read on these fake offsets, which are only valid as target
+//    addresses for reference matching.
+// As for |RVA size| < |offset size|, the extra portion just gets ignored.
+//
+// Status: Zucchini implements (2) in a simple way: dangling RVAs are mapped to
+// fake offsets by adding a large value. This value can be chosen as an
+// exclusive upper bound of all offsets (i.e., image size). This allows them to
+// be easily detected and processed as a special-case.
+// TODO(huangs): Investigate option (1), now that the refactored code makes
+// experimentation easier.
+// TODO(huangs): Make AddressTranslator smarter: Allocate unused |offset_t|
+// ranges and create "fake" units to accommodate dangling RVAs. Then
+// AddressTranslator can be simplified.
+
 // Virtual Address relative to some base address (RVA).
 using rva_t = uint32_t;
-constexpr rva_t kRVABound = static_cast<rva_t>(-1);
+// Divide by 2 to match |kOffsetBound|.
+constexpr rva_t kRvaBound = static_cast<rva_t>(-1) / 2;
+constexpr rva_t kInvalidRva = static_cast<rva_t>(-1);
 
-// The following interfaces are used to convert between RVAs and offsets.
-// Caveat: "Offsets" sounds like a value that is confined by image size, but
-// sometimes a valid RVA may have no matching offset (e.g., RVA in sections with
-// allocated data)! To accommodate these cases, an RVAToOffsetTranslator may
-// convert RVAs to offsets with values >= image size. This is okay as long as
-// OffsetToRVATranslator properly inverts the conversion.
-
-// Interface for converting a file offset to an RVA.
-class RVAToOffsetTranslator {
+// A utility to translate between offsets and RVAs in an image.
+class AddressTranslator {
  public:
-  virtual ~RVAToOffsetTranslator() = default;
+  // A basic unit for address translation, roughly maps to a section, but may
+  // be processed (e.g., merged) as an optimization.
+  struct Unit {
+    offset_t offset_end() const { return offset_begin + offset_size; }
+    rva_t rva_end() const { return rva_begin + rva_size; }
+    bool IsEmpty() const {
+      // |rva_size == 0| and |offset_size > 0| means Unit hasn't been trimmed
+      // yet, and once it is then it's empty.
+      // |rva_size > 0| and |offset_size == 0| means Unit has dangling RVA, but
+      // is not empty.
+      return rva_size == 0;
+    }
+    bool CoversOffset(offset_t offset) const {
+      return RangeCovers(offset_begin, offset_size, offset);
+    }
+    bool CoversRva(rva_t rva) const {
+      return RangeCovers(rva_begin, rva_size, rva);
+    }
+    bool CoversDanglingRva(rva_t rva) const {
+      return CoversRva(rva) && rva - rva_begin >= offset_size;
+    }
+    // Assumes valid |offset| (*cannot* be fake offset).
+    rva_t OffsetToRvaUnsafe(offset_t offset) const {
+      return offset - offset_begin + rva_begin;
+    }
+    // Assumes valid |rva| (*can* be danging RVA).
+    offset_t RvaToOffsetUnsafe(rva_t rva, offset_t fake_offset_begin) const {
+      rva_t delta = rva - rva_begin;
+      return delta < offset_size ? delta + offset_begin
+                                 : fake_offset_begin + rva;
+    }
+    bool HasDanglingRva() const { return rva_size > offset_size; }
+    friend bool operator==(const Unit& a, const Unit& b) {
+      return std::tie(a.offset_begin, a.offset_size, a.rva_begin, a.rva_size) ==
+             std::tie(b.offset_begin, b.offset_size, b.rva_begin, b.rva_size);
+    }
 
-  virtual offset_t Convert(rva_t rva) = 0;
-};
+    offset_t offset_begin;
+    offset_t offset_size;
+    rva_t rva_begin;
+    rva_t rva_size;
+  };
 
-// Interface for converting an RVA to a file offset.
-class OffsetToRVATranslator {
- public:
-  virtual ~OffsetToRVATranslator() = default;
+  // An adaptor for AddressTranslator::OffsetToRva() that caches the last Unit
+  // found, to reduce the number of OffsetToUnit() calls for clustered queries.
+  class OffsetToRvaCache {
+   public:
+    // Embeds |translator| for use. Now object lifetime is tied to |translator|
+    // lifetime.
+    explicit OffsetToRvaCache(const AddressTranslator& translator);
 
-  virtual rva_t Convert(offset_t offset) = 0;
-};
+    rva_t Convert(offset_t offset) const;
 
-// Interface for creating translators of both direction.
-class AddressTranslatorFactory {
- public:
-  virtual ~AddressTranslatorFactory() = default;
+   private:
+    const AddressTranslator& translator_;
+    mutable const AddressTranslator::Unit* cached_unit_ = nullptr;
 
-  virtual std::unique_ptr<RVAToOffsetTranslator> MakeRVAToOffsetTranslator()
-      const = 0;
+    DISALLOW_COPY_AND_ASSIGN(OffsetToRvaCache);
+  };
 
-  virtual std::unique_ptr<OffsetToRVATranslator> MakeOffsetToRVATranslator()
-      const = 0;
+  // An adaptor for AddressTranslator::RvaToOffset() that caches the last Unit
+  // found, to reduce the number of RvaToUnit() calls for clustered queries.
+  class RvaToOffsetCache {
+   public:
+    // Embeds |translator| for use. Now object lifetime is tied to |translator|
+    // lifetime.
+    explicit RvaToOffsetCache(const AddressTranslator& translator);
+
+    bool IsValid(rva_t rva) const;
+    offset_t Convert(rva_t rva) const;
+
+   private:
+    const AddressTranslator& translator_;
+    mutable const AddressTranslator::Unit* cached_unit_ = nullptr;
+
+    DISALLOW_COPY_AND_ASSIGN(RvaToOffsetCache);
+  };
+
+  enum Status {
+    kSuccess = 0,
+    kErrorOverflow,
+    kErrorBadOverlap,
+    kErrorBadOverlapDanglingRva,
+    kErrorFakeOffsetBeginTooLarge,
+  };
+
+  AddressTranslator();
+  ~AddressTranslator();
+
+  // Consumes |units| to populate data in this class. Performs consistency
+  // checks and overlapping Units. Returns Status to indicate success.
+  Status Initialize(std::vector<Unit>&& units);
+
+  // Returns the (possibly dangling) RVA corresponding to |offset|, or
+  // kInvalidRva if not found.
+  rva_t OffsetToRva(offset_t offset) const;
+
+  // Returns the (possibly fake) offset corresponding to |rva|, or
+  // kInvalidOffset if not found.
+  offset_t RvaToOffset(rva_t rva) const;
+
+  // Returns whether a given |rva| is represented.
+  bool IsValidRva(rva_t rva) const;
+
+  // There is no IsValidOffset(): Check for regular offsets is merely comparing
+  // against image size; and fake offset check is not needed outside this class.
+
+  // For testing.
+  offset_t fake_offset_begin() const { return fake_offset_begin_; }
+
+  const std::vector<Unit>& units_sorted_by_offset() const {
+    return units_sorted_by_offset_;
+  }
+
+  const std::vector<Unit>& units_sorted_by_rva() const {
+    return units_sorted_by_rva_;
+  }
+
+ private:
+  // Helper to find the Unit that contains given |offset| or |rva|. Returns null
+  // if not found.
+  const Unit* OffsetToUnit(offset_t offset) const;
+  const Unit* RvaToUnit(rva_t rva) const;
+
+  // Storage of Units. All offset ranges are non-empty and disjoint. Likewise
+  // for all RVA ranges.
+  std::vector<Unit> units_sorted_by_offset_;
+  std::vector<Unit> units_sorted_by_rva_;
+
+  // Conversion factor to translate between dangling RVAs and fake offsets.
+  offset_t fake_offset_begin_;
+
+  DISALLOW_COPY_AND_ASSIGN(AddressTranslator);
 };
 
 }  // namespace zucchini
diff --git a/chrome/installer/zucchini/address_translator_unittest.cc b/chrome/installer/zucchini/address_translator_unittest.cc
new file mode 100644
index 0000000..baacf347
--- /dev/null
+++ b/chrome/installer/zucchini/address_translator_unittest.cc
@@ -0,0 +1,556 @@
+// 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/installer/zucchini/address_translator.h"
+
+#include <algorithm>
+#include <sstream>
+#include <string>
+#include <utility>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace zucchini {
+
+namespace {
+
+// Test case structs. The convention of EXPECT() specifies "expectd" value
+// before ""actual". However, AddressTranslator interfaces explicitly state "X
+// to Y". So it is clearer in test cases to specify "input" before "expect".
+struct OffsetToRvaTestCase {
+  offset_t input;
+  rva_t expect;
+};
+
+struct RvaToOffsetTestCase {
+  rva_t input;
+  offset_t expect;
+};
+
+class TestAddressTranslator : public AddressTranslator {
+ public:
+  using AddressTranslator::AddressTranslator;
+
+  // Initialize() alternative that parses a visual representation of offset and
+  // RVA ranges. Illustrative example ("special" means '.' or '!'):
+  // "..AAA...|....aaaa" => "..AAA..." for offsets, and "....aaaa" for RVAs:
+  // - "..AAA...": First non-period character is at 2, so |offset_begin| = 2.
+  // - "..AAA...": There are 3 non-special characters, so |offset_size| = +3.
+  // - "....aaaa": First non-period character is at 4, so |rva_begin| = 4.
+  // - "....aaaa": There are 4 non-special characters, so |rva_size| = +4.
+  // For the special case of length-0 range, '!' can be used. For example,
+  // "...!...." specifies |begin| = 3 and |size| = +0.
+  AddressTranslator::Status InitializeWithStrings(
+      const std::vector<std::string>& specs) {
+    std::vector<Unit> units;
+    units.reserve(specs.size());
+    for (const std::string& s : specs) {
+      size_t sep = s.find('|');
+      CHECK_NE(sep, std::string::npos);
+      std::string s1 = s.substr(0, sep);
+      std::string s2 = s.substr(sep + 1);
+
+      auto first_non_blank = [](const std::string& t) {
+        auto is_blank = [](char ch) { return ch == '.'; };
+        return std::find_if_not(t.begin(), t.end(), is_blank) - t.begin();
+      };
+      auto count_non_special = [](const std::string& t) {
+        auto is_special = [](char ch) { return ch == '.' || ch == '!'; };
+        return t.size() - std::count_if(t.begin(), t.end(), is_special);
+      };
+      units.push_back({static_cast<offset_t>(first_non_blank(s1)),
+                       static_cast<offset_t>(count_non_special(s1)),
+                       static_cast<rva_t>(first_non_blank(s2)),
+                       static_cast<rva_t>(count_non_special(s2))});
+    }
+    return Initialize(std::move(units));
+  }
+};
+
+// Simple test: Initialize TestAddressTranslator using |specs|, and match
+// |expected| results re. success or failure.
+void SimpleTest(const std::vector<std::string>& specs,
+                AddressTranslator::Status expected,
+                const std::string& case_name) {
+  TestAddressTranslator translator;
+  auto result = translator.InitializeWithStrings(specs);
+  EXPECT_EQ(expected, result) << case_name;
+}
+
+// Test AddressTranslator::Initialize's Unit overlap and error checks over
+// multiple test cases, each case consists of a fixed unit (specified as
+// string), and a variable string taken from an list.
+class TwoUnitOverlapTester {
+ public:
+  struct TestCase {
+    std::string unit_str;
+    AddressTranslator::Status expected;
+  };
+
+  static void RunTest(const std::string& unit_str1,
+                      const std::vector<TestCase>& test_cases) {
+    for (size_t i = 0; i < test_cases.size(); ++i) {
+      const auto& test_case = test_cases[i];
+      const std::string& unit_str2 = test_case.unit_str;
+      std::ostringstream oss;
+      oss << "Case #" << i << ": " << unit_str2;
+      SimpleTest({unit_str1, unit_str2}, test_case.expected, oss.str());
+      // Switch order. Expect same results.
+      SimpleTest({unit_str2, unit_str1}, test_case.expected, oss.str());
+    }
+  }
+};
+
+}  // namespace
+
+TEST(AddressTranslatorTest, Empty) {
+  using AT = AddressTranslator;
+  TestAddressTranslator translator;
+  EXPECT_EQ(AT::kSuccess,
+            translator.Initialize(std::vector<AddressTranslator::Unit>()));
+  offset_t fake_offset_begin = translator.fake_offset_begin();
+
+  // Optimized versions.
+  AddressTranslator::OffsetToRvaCache offset_to_rva(translator);
+  AddressTranslator::RvaToOffsetCache rva_to_offset(translator);
+
+  EXPECT_EQ(kInvalidRva, translator.OffsetToRva(0U));
+  EXPECT_EQ(kInvalidRva, translator.OffsetToRva(100U));
+  EXPECT_EQ(kInvalidRva, offset_to_rva.Convert(0U));
+  EXPECT_EQ(kInvalidRva, offset_to_rva.Convert(100U));
+
+  EXPECT_EQ(kInvalidOffset, translator.RvaToOffset(0U));
+  EXPECT_EQ(kInvalidOffset, translator.RvaToOffset(100U));
+  EXPECT_EQ(kInvalidOffset, rva_to_offset.Convert(0U));
+  EXPECT_EQ(kInvalidOffset, rva_to_offset.Convert(100U));
+
+  EXPECT_EQ(kInvalidRva, translator.OffsetToRva(fake_offset_begin));
+  EXPECT_EQ(kInvalidRva, offset_to_rva.Convert(fake_offset_begin));
+}
+
+TEST(AddressTranslatorTest, Single) {
+  using AT = AddressTranslator;
+  TestAddressTranslator translator;
+  // Offsets to RVA: [10, 30) -> [100, 120).
+  EXPECT_EQ(AT::kSuccess, translator.Initialize({{10U, +20U, 100U, +20U}}));
+  offset_t fake_offset_begin = translator.fake_offset_begin();
+
+  // Optimized versions.
+  AddressTranslator::OffsetToRvaCache offset_to_rva(translator);
+  AddressTranslator::RvaToOffsetCache rva_to_offset(translator);
+  EXPECT_EQ(30U, fake_offset_begin);  // Test implementation detail.
+
+  // Offsets to RVAs.
+  OffsetToRvaTestCase test_cases1[] = {
+      {0U, kInvalidRva}, {9U, kInvalidRva}, {10U, 100U},
+      {20U, 110U},       {29U, 119U},       {30U, kInvalidRva},
+  };
+  for (auto& test_case : test_cases1) {
+    EXPECT_EQ(test_case.expect, translator.OffsetToRva(test_case.input));
+    EXPECT_EQ(test_case.expect, offset_to_rva.Convert(test_case.input));
+  }
+
+  // RVAs to offsets.
+  RvaToOffsetTestCase test_cases2[] = {
+      {0U, kInvalidOffset}, {99U, kInvalidOffset}, {100U, 10U},
+      {110U, 20U},          {119U, 29U},           {120U, kInvalidOffset},
+  };
+  for (auto& test_case : test_cases2) {
+    EXPECT_EQ(test_case.expect, translator.RvaToOffset(test_case.input));
+    EXPECT_EQ(test_case.expect, rva_to_offset.Convert(test_case.input));
+  }
+}
+
+TEST(AddressTranslatorTest, SingleDanglingRva) {
+  using AT = AddressTranslator;
+  TestAddressTranslator translator;
+  // Offsets to RVA: [10, 30) -> [100, 120 + 7), so has dangling RVAs.
+  EXPECT_EQ(AT::kSuccess,
+            translator.Initialize({{10U, +20U, 100U, +20U + 7U}}));
+  offset_t fake_offset_begin = translator.fake_offset_begin();
+
+  EXPECT_EQ(30U, fake_offset_begin);  // Test implementation detail.
+
+  // Optimized versions.
+  AddressTranslator::OffsetToRvaCache offset_to_rva(translator);
+  AddressTranslator::RvaToOffsetCache rva_to_offset(translator);
+
+  // Offsets to RVAs.
+  OffsetToRvaTestCase test_cases1[] = {
+      {0U, kInvalidRva},
+      {9U, kInvalidRva},
+      {10U, 100U},
+      {20U, 110U},
+      {29U, 119U},
+      {30U, kInvalidRva},
+      // Fake offsets to dangling RVAs.
+      {fake_offset_begin + 100U, kInvalidRva},
+      {fake_offset_begin + 119U, kInvalidRva},
+      {fake_offset_begin + 120U, 120U},
+      {fake_offset_begin + 126U, 126U},
+      {fake_offset_begin + 127U, kInvalidRva},
+  };
+  for (auto& test_case : test_cases1) {
+    EXPECT_EQ(test_case.expect, translator.OffsetToRva(test_case.input));
+    EXPECT_EQ(test_case.expect, offset_to_rva.Convert(test_case.input));
+  }
+
+  // RVAs to offsets.
+  RvaToOffsetTestCase test_cases2[] = {
+      {0U, kInvalidOffset},
+      {99U, kInvalidOffset},
+      {100U, 10U},
+      {110U, 20U},
+      {119U, 29U},
+      // Dangling RVAs to fake offsets.
+      {120U, fake_offset_begin + 120U},
+      {126U, fake_offset_begin + 126U},
+      {127U, kInvalidOffset},
+  };
+  for (auto& test_case : test_cases2) {
+    EXPECT_EQ(test_case.expect, translator.RvaToOffset(test_case.input));
+    EXPECT_EQ(test_case.expect, rva_to_offset.Convert(test_case.input));
+  }
+}
+
+TEST(AddressTranslatorTest, BasicUsage) {
+  using AT = AddressTranslator;
+  TestAddressTranslator translator;
+  // Offsets covered: [10, 30), [40, 70), [70, 110).
+  // Map to RVAs: [200, 220 + 5), [300, 330), [100, 140), so has dangling RVAs.
+  auto result = translator.Initialize({
+      {10U, +20U, 200U, +20U + 5U},  // Has dangling RVAs.
+      {40U, +30U, 300U, +20U},       // Extra offset truncated and ignored.
+      {50U, +20U, 310U, +20U},       // Overlap with previous: Merged.
+      {70U, +40U, 100U, +20U},  // Tangent with previous but inconsistent; extra
+                                // offset truncated and ignored.
+      {90U, +20U, 120U, +20U},  // Tangent with previous and consistent: Merged.
+  });
+  EXPECT_EQ(AT::kSuccess, result);
+  offset_t fake_offset_begin = translator.fake_offset_begin();
+  EXPECT_EQ(110U, fake_offset_begin);  // Test implementation detail.
+
+  // Optimized versions.
+  AddressTranslator::OffsetToRvaCache offset_to_rva(translator);
+  AddressTranslator::RvaToOffsetCache rva_to_offset(translator);
+
+  // Offsets to RVAs.
+  OffsetToRvaTestCase test_cases1[] = {
+      {0U, kInvalidRva},
+      {9U, kInvalidRva},
+      {10U, 200U},
+      {20U, 210U},
+      {29U, 219U},
+      {30U, kInvalidRva},
+      {39U, kInvalidRva},
+      {40U, 300U},
+      {55U, 315U},
+      {69U, 329U},
+      {70U, 100U},
+      {90U, 120U},
+      {109U, 139U},
+      {110U, kInvalidRva},
+      // Fake offsets to dangling RVAs.
+      {fake_offset_begin + 220U, 220U},
+      {fake_offset_begin + 224U, 224U},
+      {fake_offset_begin + 225U, kInvalidRva},
+  };
+  for (auto& test_case : test_cases1) {
+    EXPECT_EQ(test_case.expect, translator.OffsetToRva(test_case.input));
+    EXPECT_EQ(test_case.expect, offset_to_rva.Convert(test_case.input));
+  }
+
+  // RVAs to offsets.
+  RvaToOffsetTestCase test_cases2[] = {
+      {0U, kInvalidOffset},
+      {99U, kInvalidOffset},
+      {100U, 70U},
+      {120U, 90U},
+      {139U, 109U},
+      {140U, kInvalidOffset},
+      {199U, kInvalidOffset},
+      {200U, 10U},
+      {210U, 20U},
+      {219U, 29U},
+      {225U, kInvalidOffset},
+      {299U, kInvalidOffset},
+      {300U, 40U},
+      {315U, 55U},
+      {329U, 69U},
+      {330U, kInvalidOffset},
+      // Dangling RVAs to fake offsets.
+      {220U, fake_offset_begin + 220U},
+      {224U, fake_offset_begin + 224U},
+      {225U, kInvalidOffset},
+  };
+  for (auto& test_case : test_cases2) {
+    EXPECT_EQ(test_case.expect, translator.RvaToOffset(test_case.input));
+    EXPECT_EQ(test_case.expect, rva_to_offset.Convert(test_case.input));
+  }
+}
+
+TEST(AddressTranslatorTest, Overflow) {
+  using AT = AddressTranslator;
+  // Test assumes that offset_t and rva_t to be 32-bit.
+  static_assert(sizeof(offset_t) == 4 && sizeof(rva_t) == 4,
+                "Needs to update test.");
+  {
+    AddressTranslator translator1;
+    EXPECT_EQ(AT::kErrorOverflow,
+              translator1.Initialize({{0, +0xC0000000U, 0, +0xC0000000U}}));
+  }
+  {
+    AddressTranslator translator2;
+    EXPECT_EQ(AT::kErrorOverflow,
+              translator2.Initialize({{0, +0, 0, +0xC0000000U}}));
+  }
+  {
+    // Units are okay, owing to but limitations of the heuristic to convert
+    // dangling RVA to fake offset, AddressTranslator::Initialize() fails.
+    AddressTranslator translator3;
+    EXPECT_EQ(AT::kErrorFakeOffsetBeginTooLarge,
+              translator3.Initialize(
+                  {{32, +0, 32, +0x50000000U}, {0x50000000U, +16, 0, +16}}));
+  }
+}
+
+// Sanity test for TestAddressTranslator::InitializeWithStrings();
+TEST(AddressTranslatorTest, AddUnitAsString) {
+  using AT = AddressTranslator;
+  {
+    TestAddressTranslator translator1;
+    EXPECT_EQ(AT::kSuccess, translator1.InitializeWithStrings({"..A..|.aaa."}));
+    AddressTranslator::Unit unit1 = translator1.units_sorted_by_offset()[0];
+    EXPECT_EQ(2U, unit1.offset_begin);
+    EXPECT_EQ(+1U, unit1.offset_size);
+    EXPECT_EQ(1U, unit1.rva_begin);
+    EXPECT_EQ(+3U, unit1.rva_size);
+  }
+  {
+    TestAddressTranslator translator2;
+    EXPECT_EQ(AT::kSuccess,
+              translator2.InitializeWithStrings({".....!...|.bbbbbb..."}));
+    AddressTranslator::Unit unit2 = translator2.units_sorted_by_offset()[0];
+    EXPECT_EQ(5U, unit2.offset_begin);
+    EXPECT_EQ(+0U, unit2.offset_size);
+    EXPECT_EQ(1U, unit2.rva_begin);
+    EXPECT_EQ(+6U, unit2.rva_size);
+  }
+}
+
+// AddressTranslator::Initialize() lists Unit merging examples in comments. The
+// format is different from that used by InitializeWithStrings(), but adapting
+// them is easy, so we may as well do so.
+TEST(AddressTranslatorTest, OverlapFromComment) {
+  using AT = AddressTranslator;
+  constexpr auto OK = AT::kSuccess;
+  struct {
+    const char* rva_str;  // RVA comes first in this case.
+    const char* offset_str;
+    AT::Status expected;
+  } test_cases[] = {
+      {"..ssssffff..", "..SSSSFFFF..", OK},
+      {"..ssssffff..", "..SSSS..FFFF..", OK},
+      {"..ssssffff..", "..FFFF..SSSS..", OK},
+      {"..ssssffff..", "..SSOOFF..", AT::kErrorBadOverlap},
+      {"..sssooofff..", "..SSSOOOFFF..", OK},
+      {"..sssooofff..", "..SSSSSOFFFFF..", AT::kErrorBadOverlap},
+      {"..sssooofff..", "..FFOOOOSS..", AT::kErrorBadOverlap},
+      {"..sssooofff..", "..SSSOOOF..", OK},
+      {"..sssooofff..", "..SSSOOOF..", OK},
+      {"..sssooosss..", "..SSSOOOS..", OK},
+      {"..sssooofff..", "..SSSOO..", OK},
+      {"..sssooofff..", "..SSSOFFF..", AT::kErrorBadOverlapDanglingRva},
+      {"..sssooosss..", "..SSSOOSSSS..", AT::kErrorBadOverlapDanglingRva},
+      {"..oooooo..", "..OOO..", OK},
+  };
+
+  auto to_period = [](std::string s, char ch) {  // |s| passed by value.
+    std::replace(s.begin(), s.end(), ch, '.');
+    return s;
+  };
+
+  size_t idx = 0;
+  for (const auto& test_case : test_cases) {
+    std::string base_str =
+        std::string(test_case.offset_str) + "|" + test_case.rva_str;
+    std::string unit_str1 = to_period(to_period(base_str, 'S'), 's');
+    std::string unit_str2 = to_period(to_period(base_str, 'F'), 'f');
+    std::ostringstream oss;
+    oss << "Case #" << idx;
+    SimpleTest({unit_str1, unit_str2}, test_case.expected, oss.str());
+    ++idx;
+  }
+}
+
+TEST(AddressTranslatorTest, Overlap) {
+  using AT = AddressTranslator;
+  constexpr auto OK = AT::kSuccess;
+  constexpr const char* unit_str1 = "....AAA.......|.....aaa......";
+
+  std::vector<TwoUnitOverlapTester::TestCase> test_cases = {
+      //....AAA.......|.....aaa......   The first Unit. NOLINT
+      {"....BBB.......|.....bbb......", OK},
+      {"..BBB.........|...bbb........", OK},
+      {"......BBB.....|.......bbb....", OK},
+      {"..BBBBBBBBB...|...bbb........", OK},  // Extra offset get truncated.
+      {"......BBBBBBBB|.......bbb....", OK},
+      {"....BBB.......|.......bbb....", AT::kErrorBadOverlap},
+      {"..BBB.........|.......bbb....", AT::kErrorBadOverlap},
+      {".......BBB....|.......bbb....", AT::kErrorBadOverlap},
+      //....AAA.......|.....aaa......   The first Unit. NOLINT
+      {"....BBB.......|..........bbb.", AT::kErrorBadOverlap},
+      {"..........BBB.|.......bbb....", AT::kErrorBadOverlap},
+      {"......BBB.....|.....bbb......", AT::kErrorBadOverlap},
+      {"......BBB.....|..bbb.........", AT::kErrorBadOverlap},
+      {"......BBB.....|bbb...........", AT::kErrorBadOverlap},
+      {"BBB...........|bbb...........", OK},  // Disjoint.
+      {"........BBB...|.........bbb..", OK},  // Disjoint.
+      {"BBB...........|..........bbb.", OK},  // Disjoint, offset elsewhere.
+      //....AAA.......|.....aaa......   The first Unit. NOLINT
+      {".BBB..........|..bbb.........", OK},  // Tangent.
+      {".......BBB....|........bbb...", OK},  // Tangent.
+      {".BBB..........|........bbb...", OK},  // Tangent, offset elsewhere.
+      {"BBBBBB........|bbb...........", OK},  // Repeat, with extra offsets.
+      {"........BBBB..|.........bbb..", OK},
+      {"BBBBBB........|..........bbb.", OK},
+      {".BBBBBB.......|..bbb.........", OK},
+      {".......BBBBB..|........bbb...", OK},
+      //....AAA.......|.....aaa......   The first Unit. NOLINT
+      {".BBB..........|........bbb...", OK},  // Tangent, offset elsewhere.
+      {"..BBB.........|........bbb...", AT::kErrorBadOverlap},
+      {"...BB.........|....bb........", OK},
+      {"....BB........|.....bb.......", OK},
+      {".......BB.....|........bb....", OK},
+      {"...BBBBBB.....|....bbbbbb....", OK},
+      {"..BBBBBB......|...bbbbbb.....", OK},
+      {"......BBBBBB..|.......bbbbbb.", OK},
+      //....AAA.......|.....aaa......   The first Unit. NOLINT
+      {"BBBBBBBBBBBBBB|bbbbbbbbbbbbbb", AT::kErrorBadOverlap},
+      {"B.............|b.............", OK},
+      {"B.............|.............b", OK},
+      {"....B.........|.....b........", OK},
+      {"....B.........|......b.......", AT::kErrorBadOverlap},
+      {"....B.........|......b.......", AT::kErrorBadOverlap},
+      {"....BBB.......|.....bb.......", OK},
+      {"....BBBB......|.....bbb......", OK},
+      //....AAA.......|.....aaa......   The first Unit. NOLINT
+      {".........BBBBB|.b............", OK},
+      {"....AAA.......|.....!........", OK},
+      {"....!.........|.....!........", OK},  // Empty units gets deleted early.
+      {"....!.........|..........!...", OK},  // Forgiving!
+  };
+
+  TwoUnitOverlapTester::RunTest(unit_str1, test_cases);
+}
+
+TEST(AddressTranslatorTest, OverlapOffsetMultiple) {
+  using AT = AddressTranslator;
+  // Simple case. Note that RVA ranges don't get merged.
+  SimpleTest({"A..|a....",  //
+              ".A.|..a..",  //
+              "..A|....a"},
+             AT::kSuccess, "Case #0");
+
+  // Offset range 1 overlaps 2 and 3, but truncation takes place to trim down
+  // offset ranges, so still successful.
+  SimpleTest({"..A|a....",  //
+              ".AA|..a..",  //
+              "AAA|....a"},
+             AT::kSuccess, "Case #1");
+
+  // Offset range 2 and 3 overlap, so fail.
+  SimpleTest({"A..|a....",  //
+              ".A.|..a..",  //
+              ".A.|....a"},
+             AT::kErrorBadOverlap, "Case #2");
+}
+
+TEST(AddressTranslatorTest, OverlapDangling) {
+  using AT = AddressTranslator;
+  constexpr auto OK = AT::kSuccess;
+  // First Unit has dangling offsets at
+  constexpr const char* unit_str1 = "....AAA.......|.....aaaaaa...";
+
+  std::vector<TwoUnitOverlapTester::TestCase> test_cases = {
+      //....AAA.......|.....aaaaaa...   The first Unit. NOLINT
+      {"....BBB.......|.....bbbbbb...", OK},
+      {"....BBB.......|.....bbbbb....", OK},
+      {"....BBB.......|.....bbbb.....", OK},
+      {"....BBB.......|.....bbb......", OK},
+      {".....BBB......|......bbb.....", AT::kErrorBadOverlapDanglingRva},
+      {".....BB.......|......bbb.....", OK},
+      {"....BBB.......|.....bbbbbbbb.", OK},
+      {"..BBBBB.......|...bbbbbbbb...", OK},
+      //....AAA.......|.....aaaaaa...   The first Unit. NOLINT
+      {"......!.......|.bbb..........", AT::kErrorBadOverlap},
+      {"..BBBBB.......|...bbbbb......", OK},
+      {".......BBB....|.bbb..........", OK},  // Just tangent: Can go elsewhere.
+      {".......BBB....|.bbbb.........", OK},  // Can be another dangling RVA.
+      {".......!......|.bbbb.........", OK},  // Same with empty.
+      {"......!.......|.......!......", OK},  // Okay, but gets deleted.
+      {"......!.......|.......b......", AT::kErrorBadOverlapDanglingRva},
+      {"......B.......|.......b......", OK},
+      //....AAA.......|.....aaaaaa...   The first Unit. NOLINT
+      {"......BBBB....|.......bbbb...", AT::kErrorBadOverlapDanglingRva},
+      {"......BB......|.......bb.....", AT::kErrorBadOverlapDanglingRva},
+      {"......BB......|bb............", AT::kErrorBadOverlap},
+  };
+
+  TwoUnitOverlapTester::RunTest(unit_str1, test_cases);
+}
+
+// Tests implementation since algorithm is tricky.
+TEST(AddressTranslatorTest, Merge) {
+  using AT = AddressTranslator;
+  // Merge a bunch of overlapping Units into one big Unit.
+  std::vector<std::string> test_case1 = {
+      "AAA.......|.aaa......",  // Comment to prevent wrap by formatter.
+      "AA........|.aa.......",  //
+      "..AAA.....|...aaa....",  //
+      "....A.....|.....a....",  //
+      ".....AAA..|......aaa.",  //
+      "........A.|.........a",  //
+  };
+  // Try all 6! permutations.
+  std::sort(test_case1.begin(), test_case1.end());
+  do {
+    TestAddressTranslator translator1;
+    EXPECT_EQ(AT::kSuccess, translator1.InitializeWithStrings(test_case1));
+    EXPECT_EQ(9U, translator1.fake_offset_begin());
+
+    AT::Unit expected{0U, +9U, 1U, +9U};
+    EXPECT_EQ(1U, translator1.units_sorted_by_offset().size());
+    EXPECT_EQ(expected, translator1.units_sorted_by_offset()[0]);
+    EXPECT_EQ(1U, translator1.units_sorted_by_rva().size());
+    EXPECT_EQ(expected, translator1.units_sorted_by_rva()[0]);
+  } while (std::next_permutation(test_case1.begin(), test_case1.end()));
+
+  // Merge RVA-adjacent Units into two Units.
+  std::vector<std::string> test_case2 = {
+      ".....A..|.a......",  // First Unit.
+      "......A.|..a.....",  //
+      "A.......|...a....",  // Second Unit: RVA-adjacent to first Unit, but
+      ".A......|....a...",  // offset would become inconsistent, so a new
+      "..A.....|.....a..",  // Unit gets created.
+  };
+  // Try all 5! permutations.
+  std::sort(test_case2.begin(), test_case2.end());
+  do {
+    TestAddressTranslator translator2;
+    EXPECT_EQ(AT::kSuccess, translator2.InitializeWithStrings(test_case2));
+    EXPECT_EQ(7U, translator2.fake_offset_begin());
+
+    AT::Unit expected1{0U, +3U, 3U, +3U};
+    AT::Unit expected2{5U, +2U, 1U, +2U};
+    EXPECT_EQ(2U, translator2.units_sorted_by_offset().size());
+    EXPECT_EQ(expected1, translator2.units_sorted_by_offset()[0]);
+    EXPECT_EQ(expected2, translator2.units_sorted_by_offset()[1]);
+    EXPECT_EQ(2U, translator2.units_sorted_by_rva().size());
+    EXPECT_EQ(expected2, translator2.units_sorted_by_rva()[0]);
+    EXPECT_EQ(expected1, translator2.units_sorted_by_rva()[1]);
+  } while (std::next_permutation(test_case2.begin(), test_case2.end()));
+}
+
+}  // namespace zucchini
diff --git a/chrome/installer/zucchini/algorithm.h b/chrome/installer/zucchini/algorithm.h
index 62f0f28..83be8596 100644
--- a/chrome/installer/zucchini/algorithm.h
+++ b/chrome/installer/zucchini/algorithm.h
@@ -5,22 +5,31 @@
 #ifndef CHROME_INSTALLER_ZUCCHINI_ALGORITHM_H_
 #define CHROME_INSTALLER_ZUCCHINI_ALGORITHM_H_
 
-#include <type_traits>
-
 #include <stddef.h>
 
+#include <type_traits>
+
 // Collection of simple utilities used in for low-level computation.
 
 namespace zucchini {
 
-// Safely determines whether [begin, begin + size) is in [0, bound). Note that
-// the special case [bound, bound) is not considered to be in [0, bound).
+// Safely determines whether |[begin, begin + size)| is in |[0, bound)|. Note:
+// The special case |[bound, bound)| is not considered to be in |[0, bound)|.
 template <typename T>
 bool RangeIsBounded(T begin, T size, size_t bound) {
   static_assert(std::is_unsigned<T>::value, "Value type must be unsigned.");
   return begin < bound && size <= bound - begin;
 }
 
+// Safely determines whether |value| lies in |[begin, begin + size)|. Works
+// properly even if |begin + size| overflows -- although such ranges are
+// considered pathological, and should fail validation elsewhere.
+template <typename T>
+bool RangeCovers(T begin, T size, T value) {
+  static_assert(std::is_unsigned<T>::value, "Value type must be unsigned.");
+  return begin <= value && value - begin < size;
+}
+
 }  // namespace zucchini
 
 #endif  // CHROME_INSTALLER_ZUCCHINI_ALGORITHM_H_
diff --git a/chrome/installer/zucchini/algorithm_unittest.cc b/chrome/installer/zucchini/algorithm_unittest.cc
index d3efd5a..a30c21d 100644
--- a/chrome/installer/zucchini/algorithm_unittest.cc
+++ b/chrome/installer/zucchini/algorithm_unittest.cc
@@ -57,4 +57,47 @@
       RangeIsBounded<uint32_t>(0xFFFFFFFFU, +0xFFFFFFFFU, 0xFFFFFFFFU));
 }
 
+TEST(Algorithm, RangeCovers) {
+  // Basic tests.
+  EXPECT_TRUE(RangeCovers<uint8_t>(0U, +10U, 0U));
+  EXPECT_TRUE(RangeCovers<uint8_t>(0U, +10U, 5U));
+  EXPECT_TRUE(RangeCovers<uint8_t>(0U, +10U, 9U));
+  EXPECT_FALSE(RangeCovers<uint8_t>(0U, +10U, 10U));
+  EXPECT_FALSE(RangeCovers<uint8_t>(0U, +10U, 100U));
+  EXPECT_FALSE(RangeCovers<uint8_t>(0U, +10U, 255U));
+
+  EXPECT_FALSE(RangeCovers<uint8_t>(42U, +137U, 0U));
+  EXPECT_FALSE(RangeCovers<uint8_t>(42U, +137U, 41U));
+  EXPECT_TRUE(RangeCovers<uint8_t>(42U, +137U, 42U));
+  EXPECT_TRUE(RangeCovers<uint8_t>(42U, +137U, 100U));
+  EXPECT_TRUE(RangeCovers<uint8_t>(42U, +137U, 178U));
+  EXPECT_FALSE(RangeCovers<uint8_t>(42U, +137U, 179U));
+  EXPECT_FALSE(RangeCovers<uint8_t>(42U, +137U, 255U));
+
+  // 0-size ranges.
+  EXPECT_FALSE(RangeCovers<uint8_t>(42U, +0U, 41U));
+  EXPECT_FALSE(RangeCovers<uint8_t>(42U, +0U, 42U));
+  EXPECT_FALSE(RangeCovers<uint8_t>(42U, +0U, 43U));
+
+  // Test at boundary of overflow.
+  EXPECT_TRUE(RangeCovers<uint8_t>(254U, +1U, 254U));
+  EXPECT_FALSE(RangeCovers<uint8_t>(254U, +1U, 255U));
+  EXPECT_FALSE(RangeCovers<uint8_t>(255U, +0U, 255U));
+  EXPECT_TRUE(RangeCovers<uint8_t>(255U, +1U, 255U));
+  EXPECT_FALSE(RangeCovers<uint8_t>(255U, +5U, 0U));
+
+  // Test with unit32_t.
+  EXPECT_FALSE(RangeCovers<uint32_t>(1234567U, +7654321U, 0U));
+  EXPECT_FALSE(RangeCovers<uint32_t>(1234567U, +7654321U, 1234566U));
+  EXPECT_TRUE(RangeCovers<uint32_t>(1234567U, +7654321U, 1234567U));
+  EXPECT_TRUE(RangeCovers<uint32_t>(1234567U, +7654321U, 4444444U));
+  EXPECT_TRUE(RangeCovers<uint32_t>(1234567U, +7654321U, 8888887U));
+  EXPECT_FALSE(RangeCovers<uint32_t>(1234567U, +7654321U, 8888888U));
+  EXPECT_FALSE(RangeCovers<uint32_t>(1234567U, +7654321U, 0x80000000U));
+  EXPECT_FALSE(RangeCovers<uint32_t>(1234567U, +7654321U, 0xFFFFFFFFU));
+  EXPECT_FALSE(RangeCovers<uint32_t>(0xFFFFFFFFU, +0, 0xFFFFFFFFU));
+  EXPECT_TRUE(RangeCovers<uint32_t>(0xFFFFFFFFU, +1, 0xFFFFFFFFU));
+  EXPECT_FALSE(RangeCovers<uint32_t>(0xFFFFFFFFU, +2, 0));
+}
+
 }  // namespace zucchini
diff --git a/chrome/installer/zucchini/disassembler_win32.cc b/chrome/installer/zucchini/disassembler_win32.cc
index f14b1542..415ebd9 100644
--- a/chrome/installer/zucchini/disassembler_win32.cc
+++ b/chrome/installer/zucchini/disassembler_win32.cc
@@ -6,7 +6,6 @@
 
 #include <algorithm>
 #include <cstddef>
-#include <functional>
 
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
@@ -113,83 +112,13 @@
     offset_t upper) {
   ParseAndStoreRel32();
   return base::MakeUnique<Rel32ReaderX86>(image_, lower, upper,
-                                          &rel32_locations_, *this);
+                                          &rel32_locations_, translator_);
 }
 
 template <class Traits>
 std::unique_ptr<ReferenceWriter> DisassemblerWin32<Traits>::MakeWriteRel32(
     MutableBufferView image) {
-  return base::MakeUnique<Rel32WriterX86>(image, *this);
-}
-
-template <class Traits>
-std::unique_ptr<RVAToOffsetTranslator>
-DisassemblerWin32<Traits>::MakeRVAToOffsetTranslator() const {
-  // Use enclosed class to wrap RVAToSection(). Intermediate results |section_|
-  // and |adjust_| are cached, as optimization.
-  class RVAToOffset : public RVAToOffsetTranslator {
-   public:
-    explicit RVAToOffset(const DisassemblerWin32* self) : self_(self) {}
-
-    // RVAToOffsetTranslator:
-    offset_t Convert(rva_t rva) override {
-      if (section_ == nullptr || rva < section_->virtual_address ||
-          rva >=
-              section_->virtual_address + std::min(section_->size_of_raw_data,
-                                                   section_->virtual_size)) {
-        section_ = self_->RVAToSection(rva);
-        if (section_ != nullptr &&
-            rva < section_->virtual_address + section_->virtual_size) {
-          // |rva| is in |section_|, so |adjust_| can be computed normall.
-          adjust_ =
-              section_->file_offset_of_raw_data - section_->virtual_address;
-        } else {
-          // |rva| has no offset, so use |self_->fake_offset_adjust_|.
-          adjust_ = self_->fake_offset_adjust_;
-        }
-      }
-      return rva_t(rva + adjust_);
-    }
-
-   private:
-    const DisassemblerWin32* self_;
-    const pe::ImageSectionHeader* section_ = nullptr;
-    std::ptrdiff_t adjust_ = 0;
-  };
-  return base::MakeUnique<RVAToOffset>(this);
-}
-
-template <class Traits>
-std::unique_ptr<OffsetToRVATranslator>
-DisassemblerWin32<Traits>::MakeOffsetToRVATranslator() const {
-  // Use enclosed class to wrap SectionToRVA(). Intermediate results |section_|
-  // and |adjust_| are cached, as optimization.
-  class OffsetToRVA : public OffsetToRVATranslator {
-   public:
-    explicit OffsetToRVA(const DisassemblerWin32* self) : self_(self) {}
-
-    // OffsetToRVATranslator:
-    rva_t Convert(offset_t offset) override {
-      if (offset >= self_->fake_offset_adjust_) {
-        // |offset| is an untranslated RVA that was shifted outside the image.
-        return offset_t(offset - self_->fake_offset_adjust_);
-      }
-      if (section_ == nullptr || offset < section_->file_offset_of_raw_data ||
-          offset >=
-              section_->file_offset_of_raw_data + section_->size_of_raw_data) {
-        section_ = self_->OffsetToSection(offset);
-        DCHECK_NE(section_, nullptr);
-        adjust_ = section_->virtual_address - section_->file_offset_of_raw_data;
-      }
-      return offset_t(offset + adjust_);
-    }
-
-   private:
-    const DisassemblerWin32* self_;
-    const pe::ImageSectionHeader* section_ = nullptr;
-    std::ptrdiff_t adjust_ = 0;
-  };
-  return base::MakeUnique<OffsetToRVA>(this);
+  return base::MakeUnique<Rel32WriterX86>(image, translator_);
 }
 
 template <class Traits>
@@ -234,8 +163,8 @@
 
   // |optional_header->size_of_image| is the size of the image when loaded into
   // memory, and not the actual size on disk.
-  rva_bound_ = optional_header->size_of_image;
-  if (rva_bound_ >= kRVABound)
+  rva_t rva_bound = optional_header->size_of_image;
+  if (rva_bound >= kRvaBound)
     return false;
 
   // An exclusive upper bound of all offsets used in the image. This gets
@@ -243,17 +172,20 @@
   offset_t offset_bound =
       base::checked_cast<offset_t>(source.begin() - image_.begin());
 
-  // Extract and validate all sections.
+  // Extract |sections_|. .
   size_t sections_count = coff_header->number_of_sections;
   auto* sections_array =
       source.GetArray<pe::ImageSectionHeader>(sections_count);
   if (!sections_array)
     return false;
   sections_.assign(sections_array, sections_array + sections_count);
-  section_rva_map_.resize(sections_count);
-  section_offset_map_.resize(sections_count);
+
+  // Visit each section, validate, and add data to |trans_builder|.
+  std::vector<AddressTranslator::Unit> units;
+  units.reserve(sections_count);
   bool has_text_section = false;
   decltype(pe::ImageSectionHeader::virtual_address) prev_virtual_address = 0;
+
   for (size_t i = 0; i < sections_count; ++i) {
     const pe::ImageSectionHeader& section = sections_[i];
     // Apply strict checks on section bounds.
@@ -262,7 +194,7 @@
       return false;
     }
     if (!RangeIsBounded(section.virtual_address, section.virtual_size,
-                        rva_bound_)) {
+                        rva_bound)) {
       return false;
     }
 
@@ -272,8 +204,10 @@
       LOG(WARNING) << "RVA anomaly found for Section " << i;
     prev_virtual_address = section.virtual_address;
 
-    section_rva_map_[i] = std::make_pair(section.virtual_address, i);
-    section_offset_map_[i] = std::make_pair(section.file_offset_of_raw_data, i);
+    // Add |section| data for offset-RVA translation.
+    units.push_back({section.file_offset_of_raw_data, section.size_of_raw_data,
+                     section.virtual_address, section.virtual_size});
+
     offset_t end_offset =
         section.file_offset_of_raw_data + section.size_of_raw_data;
     offset_bound = std::max(end_offset, offset_bound);
@@ -285,15 +219,16 @@
   if (!has_text_section)
     return false;
 
+  // Initialize |translator_| for offset-RVA translations. Any inconsistency
+  // (e.g., 2 offsets correspond to the same RVA) would invalidate the PE file.
+  if (translator_.Initialize(std::move(units)) != AddressTranslator::kSuccess)
+    return false;
+
   // Resize |image_| to include only contents claimed by sections. Note that
   // this may miss digital signatures at end of PE files, but for patching this
   // is of minor concern.
   image_.shrink(offset_bound);
 
-  fake_offset_adjust_ = std::max(offset_bound, rva_bound_);
-
-  std::sort(section_rva_map_.begin(), section_rva_map_.end());
-  std::sort(section_offset_map_.begin(), section_offset_map_.end());
   return true;
 }
 
@@ -305,12 +240,13 @@
 
   // TODO(huangs): ParseAndStoreAbs32() once it's available.
 
+  AddressTranslator::OffsetToRvaCache location_offset_to_rva(translator_);
+  AddressTranslator::RvaToOffsetCache target_rva_checker(translator_);
+
   for (const pe::ImageSectionHeader& section : sections_) {
     if (!IsWin32CodeSection<Traits>(section))
       continue;
 
-    std::ptrdiff_t from_offset_to_rva =
-        section.virtual_address - section.file_offset_of_raw_data;
     rva_t start_rva = section.virtual_address;
     rva_t end_rva = start_rva + section.virtual_size;
 
@@ -329,10 +265,10 @@
       for (auto rel32 = finder.GetNext(); rel32.has_value();
            rel32 = finder.GetNext()) {
         offset_t rel32_offset = offset_t(rel32->location - image_.begin());
-        rva_t rel32_rva = rva_t(rel32_offset + from_offset_to_rva);
+        rva_t rel32_rva = location_offset_to_rva.Convert(rel32_offset);
         rva_t target_rva =
             rel32_rva + 4 + *reinterpret_cast<const uint32_t*>(rel32->location);
-        if (target_rva < rva_bound_ &&  // Subsumes rva != kUnassignedRVA.
+        if (target_rva_checker.IsValid(target_rva) &&
             (rel32->can_point_outside_section ||
              (start_rva <= target_rva && target_rva < end_rva))) {
           finder.Accept();
@@ -349,62 +285,13 @@
 }
 
 template <class Traits>
-const pe::ImageSectionHeader* DisassemblerWin32<Traits>::RVAToSection(
-    rva_t rva) const {
-  auto it = std::upper_bound(
-      section_rva_map_.begin(), section_rva_map_.end(), rva,
-      [](rva_t a, const std::pair<rva_t, int>& b) { return a < b.first; });
-  if (it == section_rva_map_.begin())
-    return nullptr;
-  --it;
-  const pe::ImageSectionHeader* section = &sections_[it->second];
-  return rva - it->first < section->size_of_raw_data ? section : nullptr;
-}
-
-template <class Traits>
-const pe::ImageSectionHeader* DisassemblerWin32<Traits>::OffsetToSection(
-    offset_t offset) const {
-  auto it = std::upper_bound(section_offset_map_.begin(),
-                             section_offset_map_.end(), offset,
-                             [](offset_t a, const std::pair<offset_t, int>& b) {
-                               return a < b.first;
-                             });
-  if (it == section_offset_map_.begin())
-    return nullptr;
-  --it;
-  const pe::ImageSectionHeader* section = &sections_[it->second];
-  return offset - it->first < section->size_of_raw_data ? section : nullptr;
-}
-
-template <class Traits>
-offset_t DisassemblerWin32<Traits>::RVAToOffset(rva_t rva) const {
-  const pe::ImageSectionHeader* section = RVAToSection(rva);
-  if (section == nullptr ||
-      rva >= section->virtual_address + section->virtual_size) {
-    return rva + fake_offset_adjust_;
-  }
-  return offset_t(rva - section->virtual_address) +
-         section->file_offset_of_raw_data;
-}
-
-template <class Traits>
-rva_t DisassemblerWin32<Traits>::OffsetToRVA(offset_t offset) const {
-  if (offset >= fake_offset_adjust_)
-    return rva_t(offset - fake_offset_adjust_);
-  const pe::ImageSectionHeader* section = OffsetToSection(offset);
-  DCHECK_NE(section, nullptr);
-  return rva_t(offset - section->file_offset_of_raw_data) +
-         section->virtual_address;
-}
-
-template <class Traits>
-rva_t DisassemblerWin32<Traits>::AddressToRVA(
+rva_t DisassemblerWin32<Traits>::AddressToRva(
     typename Traits::Address address) const {
   return rva_t(address - image_base_);
 }
 
 template <class Traits>
-typename Traits::Address DisassemblerWin32<Traits>::RVAToAddress(
+typename Traits::Address DisassemblerWin32<Traits>::RvaToAddress(
     rva_t rva) const {
   return rva + image_base_;
 }
diff --git a/chrome/installer/zucchini/disassembler_win32.h b/chrome/installer/zucchini/disassembler_win32.h
index b336467..0241257 100644
--- a/chrome/installer/zucchini/disassembler_win32.h
+++ b/chrome/installer/zucchini/disassembler_win32.h
@@ -50,7 +50,7 @@
 };
 
 template <class Traits>
-class DisassemblerWin32 : public Disassembler, public AddressTranslatorFactory {
+class DisassemblerWin32 : public Disassembler {
  public:
   enum ReferenceType : uint8_t { kRel32, kTypeCount };
 
@@ -69,13 +69,6 @@
   std::string GetExeTypeString() const override;
   std::vector<ReferenceGroup> MakeReferenceGroups() const override;
 
-  // AddressTranslatorFactory:
-  // The returned objects must not outlive |this|, since it uses the pointer.
-  std::unique_ptr<RVAToOffsetTranslator> MakeRVAToOffsetTranslator()
-      const override;
-  std::unique_ptr<OffsetToRVATranslator> MakeOffsetToRVATranslator()
-      const override;
-
   // Functions that return reader / writer for references.
   std::unique_ptr<ReferenceReader> MakeReadRel32(offset_t lower,
                                                  offset_t upper);
@@ -92,13 +85,9 @@
   // parsing was successful (failures are non-fatal).
   bool ParseAndStoreRel32();
 
-  // Translation utilities.
-  const pe::ImageSectionHeader* RVAToSection(rva_t rva) const;
-  const pe::ImageSectionHeader* OffsetToSection(offset_t offset) const;
-  offset_t RVAToOffset(rva_t rva) const;
-  rva_t OffsetToRVA(offset_t offset) const;
-  rva_t AddressToRVA(typename Traits::Address address) const;
-  typename Traits::Address RVAToAddress(rva_t rva) const;
+  // PE-specific translation utilities.
+  rva_t AddressToRva(typename Traits::Address address) const;
+  typename Traits::Address RvaToAddress(rva_t rva) const;
 
   // In-memory copy of sections.
   std::vector<pe::ImageSectionHeader> sections_;
@@ -106,21 +95,8 @@
   // Image base address to translate between RVA and VA.
   typename Traits::Address image_base_ = 0;
 
-  // An exclusive upper bound on all RVAs used in the image.
-  rva_t rva_bound_ = 0;
-
-  // Some RVAs have no matching offset on disk (e.g., RVAs to .bss sections).
-  // However, Zucchini reprsents all references using offsets. To accommodate
-  // these RVAs, Zucchini assigns "fake offsets" to these RVAs so they can be
-  // processed and restored. |fake_offset_adjust_| is the adjustment factor.
-  // This is made into a large value (e.g., max of |rva_bound_| and image size)
-  // to prevent collision with regular offsets.
-  uint32_t fake_offset_adjust_ = 0;
-
-  // Maps for address translation, sorted by the first element. The second
-  // element of each std::pair is an index in into |sections_|.
-  std::vector<std::pair<rva_t, int>> section_rva_map_;
-  std::vector<std::pair<offset_t, int>> section_offset_map_;
+  // Translator between offsets and RVAs.
+  AddressTranslator translator_;
 
   // Reference storage.
   std::vector<offset_t> rel32_locations_;
diff --git a/chrome/installer/zucchini/image_utils.h b/chrome/installer/zucchini/image_utils.h
index c5c93f23..61f3b8a 100644
--- a/chrome/installer/zucchini/image_utils.h
+++ b/chrome/installer/zucchini/image_utils.h
@@ -18,6 +18,9 @@
 // offset_t is used to describe an offset in an image.
 // Files bigger than 4GB are not supported.
 using offset_t = uint32_t;
+// Divide by 2 since label marking uses the most significant bit.
+constexpr offset_t kOffsetBound = static_cast<offset_t>(-1) / 2;
+constexpr offset_t kInvalidOffset = static_cast<offset_t>(-1);
 
 // Used to uniquely identify a reference type.
 // Strongly typed objects are used to avoid ambiguitees with PoolTag.
diff --git a/chrome/installer/zucchini/rel32_utils.cc b/chrome/installer/zucchini/rel32_utils.cc
index fdc1302..bdde8aec 100644
--- a/chrome/installer/zucchini/rel32_utils.cc
+++ b/chrome/installer/zucchini/rel32_utils.cc
@@ -17,10 +17,10 @@
                                offset_t lo,
                                offset_t hi,
                                const std::vector<offset_t>* locations,
-                               const AddressTranslatorFactory& factory)
+                               const AddressTranslator& translator)
     : image_(image),
-      target_rva_to_offset_(factory.MakeRVAToOffsetTranslator()),
-      location_offset_to_rva_(factory.MakeOffsetToRVATranslator()),
+      target_rva_to_offset_(translator),
+      location_offset_to_rva_(translator),
       hi_(hi),
       last_(locations->end()) {
   DCHECK_LE(lo, image.size());
@@ -34,9 +34,9 @@
   while (current_ < last_ && *current_ < hi_) {
     offset_t loc_offset = *(current_++);
     DCHECK_LE(loc_offset + 4, image_.size());  // Sanity check.
-    rva_t loc_rva = location_offset_to_rva_->Convert(loc_offset);
+    rva_t loc_rva = location_offset_to_rva_.Convert(loc_offset);
     rva_t target_rva = loc_rva + 4 + image_.read<int32_t>(loc_offset);
-    offset_t target_offset = target_rva_to_offset_->Convert(target_rva);
+    offset_t target_offset = target_rva_to_offset_.Convert(target_rva);
     // In rare cases, the most significant bit of |target| is set. This
     // interferes with label marking. We expect these to already be filtered out
     // from |locations|.
@@ -49,16 +49,16 @@
 /******** Rel32ReceptorX86 ********/
 
 Rel32WriterX86::Rel32WriterX86(MutableBufferView image,
-                               const AddressTranslatorFactory& factory)
+                               const AddressTranslator& translator)
     : image_(image),
-      target_offset_to_rva_(factory.MakeOffsetToRVATranslator()),
-      location_offset_to_rva_(factory.MakeOffsetToRVATranslator()) {}
+      target_offset_to_rva_(translator),
+      location_offset_to_rva_(translator) {}
 
 Rel32WriterX86::~Rel32WriterX86() = default;
 
 void Rel32WriterX86::PutNext(Reference ref) {
-  rva_t target_rva = target_offset_to_rva_->Convert(ref.target);
-  rva_t loc_rva = location_offset_to_rva_->Convert(ref.location);
+  rva_t target_rva = target_offset_to_rva_.Convert(ref.target);
+  rva_t loc_rva = location_offset_to_rva_.Convert(ref.location);
 
   // Subtraction underflow is okay
   uint32_t code =
diff --git a/chrome/installer/zucchini/rel32_utils.h b/chrome/installer/zucchini/rel32_utils.h
index da89dc7..c1048a1 100644
--- a/chrome/installer/zucchini/rel32_utils.h
+++ b/chrome/installer/zucchini/rel32_utils.h
@@ -5,7 +5,6 @@
 #ifndef CHROME_INSTALLER_ZUCCHINI_REL32_UTILS_H_
 #define CHROME_INSTALLER_ZUCCHINI_REL32_UTILS_H_
 
-#include <memory>
 #include <vector>
 
 #include "base/macros.h"
@@ -20,14 +19,16 @@
 // portion of an x86 / x64 image, given a list of valid locations.
 class Rel32ReaderX86 : public ReferenceReader {
  public:
-  // |image| is an image containing  x86 / x64 code in [|lo|, |hi|).
+  // |image| is an image containing x86 / x64 code in [|lo|, |hi|).
   // |locations| is a sorted list of offsets of rel32 reference locations.
-  // |factory| is a factory of offset / RVA translators for |image|.
+  // |translator| (for |image|) is embedded into |target_rva_to_offset_| and
+  // |location_offset_to_rva_| for address translation, and therefore must
+  // outlive |*this|.
   Rel32ReaderX86(ConstBufferView image,
                  offset_t lo,
                  offset_t hi,
                  const std::vector<offset_t>* locations,
-                 const AddressTranslatorFactory& factory);
+                 const AddressTranslator& translator);
   ~Rel32ReaderX86() override;
 
   // Returns the next reference, or base::nullopt if exhausted.
@@ -35,8 +36,8 @@
 
  private:
   ConstBufferView image_;
-  std::unique_ptr<RVAToOffsetTranslator> target_rva_to_offset_;
-  std::unique_ptr<OffsetToRVATranslator> location_offset_to_rva_;
+  AddressTranslator::RvaToOffsetCache target_rva_to_offset_;
+  AddressTranslator::OffsetToRvaCache location_offset_to_rva_;
   const offset_t hi_;
   const std::vector<offset_t>::const_iterator last_;
   std::vector<offset_t>::const_iterator current_;
@@ -48,18 +49,18 @@
 class Rel32WriterX86 : public ReferenceWriter {
  public:
   // |image| wraps the raw bytes of a binary in which rel32 references will be
-  // written. |translator| is a suitable address translator for the binary
-  // |image|.
-  Rel32WriterX86(MutableBufferView image,
-                 const AddressTranslatorFactory& factory);
+  // written. |translator| (for |image|) is embedded into
+  // |target_offset_to_rva_| and |location_offset_to_rva_| for address
+  // translation, and therefore must outlive |*this|.
+  Rel32WriterX86(MutableBufferView image, const AddressTranslator& translator);
   ~Rel32WriterX86() override;
 
   void PutNext(Reference ref) override;
 
  private:
   MutableBufferView image_;
-  std::unique_ptr<OffsetToRVATranslator> target_offset_to_rva_;
-  std::unique_ptr<OffsetToRVATranslator> location_offset_to_rva_;
+  AddressTranslator::OffsetToRvaCache target_offset_to_rva_;
+  AddressTranslator::OffsetToRvaCache location_offset_to_rva_;
 
   DISALLOW_COPY_AND_ASSIGN(Rel32WriterX86);
 };
diff --git a/chrome/installer/zucchini/rel32_utils_unittest.cc b/chrome/installer/zucchini/rel32_utils_unittest.cc
index 299270b..916a4d2 100644
--- a/chrome/installer/zucchini/rel32_utils_unittest.cc
+++ b/chrome/installer/zucchini/rel32_utils_unittest.cc
@@ -4,7 +4,6 @@
 
 #include "chrome/installer/zucchini/rel32_utils.h"
 
-#include <stddef.h>
 #include <stdint.h>
 
 #include <memory>
@@ -21,51 +20,14 @@
 
 namespace {
 
-// A trivial AddressTranslatorFactory that applies constant shift, and does not
-// filter.
-class TestAddressTranslatorFactory : public AddressTranslatorFactory {
+// A trivial AddressTranslator that applies constant shift.
+class TestAddressTranslator : public AddressTranslator {
  public:
-  explicit TestAddressTranslatorFactory(ptrdiff_t offset_to_rva_adjust)
-      : offset_to_rva_adjust_(offset_to_rva_adjust) {}
-
-  // AddressTranslatorFactory:
-  std::unique_ptr<RVAToOffsetTranslator> MakeRVAToOffsetTranslator()
-      const override {
-    class RVAToOffset : public RVAToOffsetTranslator {
-      const ptrdiff_t offset_to_rva_adjust_;
-
-     public:
-      explicit RVAToOffset(ptrdiff_t offset_to_rva_adjust)
-          : offset_to_rva_adjust_(offset_to_rva_adjust) {}
-
-      offset_t Convert(rva_t rva) override {
-        return offset_t(rva - offset_to_rva_adjust_);
-      }
-    };
-
-    return base::MakeUnique<RVAToOffset>(offset_to_rva_adjust_);
+  explicit TestAddressTranslator(offset_t image_size, rva_t rva_begin) {
+    DCHECK_GE(rva_begin, 0U);
+    CHECK_EQ(AddressTranslator::kSuccess,
+             Initialize({{0, image_size, rva_begin, image_size}}));
   }
-
-  // AddressTranslatorFactory:
-  std::unique_ptr<OffsetToRVATranslator> MakeOffsetToRVATranslator()
-      const override {
-    class OffsetToRVA : public OffsetToRVATranslator {
-      const ptrdiff_t offset_to_rva_adjust_;
-
-     public:
-      explicit OffsetToRVA(ptrdiff_t offset_to_rva_adjust)
-          : offset_to_rva_adjust_(offset_to_rva_adjust) {}
-
-      rva_t Convert(offset_t offset) override {
-        return rva_t(offset + offset_to_rva_adjust_);
-      }
-    };
-
-    return base::MakeUnique<OffsetToRVA>(offset_to_rva_adjust_);
-  }
-
- private:
-  const ptrdiff_t offset_to_rva_adjust_;
 };
 
 // Checks that |reader| emits and only emits |expected_refs|, in order.
@@ -82,8 +44,9 @@
 }  // namespace
 
 TEST(Rel32UtilsTest, Rel32ReaderX86) {
-  constexpr ptrdiff_t kTestOffsetToRVAAdjust = 0x00030000U;
-  TestAddressTranslatorFactory factory(kTestOffsetToRVAAdjust);
+  constexpr offset_t kTestImageSize = 0x00100000U;
+  constexpr rva_t kRvaBegin = 0x00030000U;
+  TestAddressTranslator translator(kTestImageSize, kRvaBegin);
 
   // For simplicity, test data is not real X86 machine code. We are only
   // including rel32 targets, without the full instructions.
@@ -102,7 +65,8 @@
   std::vector<offset_t> rel32_locations = {0x0008U, 0x0010U, 0x0018U, 0x001CU};
 
   // Generate everything.
-  Rel32ReaderX86 reader1(buffer, 0x0000U, 0x0020U, &rel32_locations, factory);
+  Rel32ReaderX86 reader1(buffer, 0x0000U, 0x0020U, &rel32_locations,
+                         translator);
   CheckReader({{0x0008U, 0x0010U},
                {0x0010U, 0x0014U},
                {0x0018U, 0x0010U},
@@ -110,28 +74,32 @@
               &reader1);
 
   // Exclude last.
-  Rel32ReaderX86 reader2(buffer, 0x0000U, 0x001CU, &rel32_locations, factory);
+  Rel32ReaderX86 reader2(buffer, 0x0000U, 0x001CU, &rel32_locations,
+                         translator);
   CheckReader({{0x0008U, 0x0010U}, {0x0010U, 0x0014U}, {0x0018U, 0x0010U}},
               &reader2);
 
   // Only find one.
-  Rel32ReaderX86 reader3(buffer, 0x000CU, 0x0018U, &rel32_locations, factory);
+  Rel32ReaderX86 reader3(buffer, 0x000CU, 0x0018U, &rel32_locations,
+                         translator);
   CheckReader({{0x0010U, 0x0014U}}, &reader3);
 
   // Marked target encountered (error).
   std::vector<offset_t> rel32_marked_locations = {0x00004U};
   Rel32ReaderX86 reader4(buffer, 0x0000U, 0x0020U, &rel32_marked_locations,
-                         factory);
+                         translator);
   EXPECT_DCHECK_DEATH(reader4.GetNext());
 }
 
 TEST(Rel32UtilsTest, Rel32WriterX86) {
-  constexpr ptrdiff_t kTestOffsetToRVAAdjust = 0x00030000U;
-  TestAddressTranslatorFactory factory(kTestOffsetToRVAAdjust);
+  constexpr offset_t kTestImageSize = 0x00100000U;
+  constexpr rva_t kRvaBegin = 0x00030000U;
+  TestAddressTranslator translator(kTestImageSize, kRvaBegin);
+
   std::vector<uint8_t> bytes(32, 0xFF);
   MutableBufferView buffer(bytes.data(), bytes.size());
 
-  Rel32WriterX86 writer(buffer, factory);
+  Rel32WriterX86 writer(buffer, translator);
   writer.PutNext({0x0008U, 0x0010U});
   EXPECT_EQ(0x00000004U, buffer.read<uint32_t>(0x08));  // 00030008: 00030010
 
diff --git a/chrome/test/chromedriver/test/run_py_tests.py b/chrome/test/chromedriver/test/run_py_tests.py
index 4c20ec5..6c75951c 100755
--- a/chrome/test/chromedriver/test/run_py_tests.py
+++ b/chrome/test/chromedriver/test/run_py_tests.py
@@ -106,6 +106,8 @@
     'ChromeLogPathCapabilityTest.testChromeLogPath',
     # https://bugs.chromium.org/p/chromedriver/issues/detail?id=992
     'ChromeDownloadDirTest.testDownloadDirectoryOverridesExistingPreferences',
+    # https://bugs.chromium.org/p/chromedriver/issues/detail?id=1945
+    'ChromeDriverTest.testWindowFullScreen',
 ]
 _OS_SPECIFIC_FILTER['linux'] = [
     # Xvfb doesn't support maximization.
@@ -153,6 +155,7 @@
         # https://crbug.com/274650
         'ChromeDriverTest.testCloseWindow',
         # https://bugs.chromium.org/p/chromedriver/issues/detail?id=298
+        'ChromeDriverTest.testWindowFullScreen',
         'ChromeDriverTest.testWindowPosition',
         'ChromeDriverTest.testWindowSize',
         'ChromeDriverTest.testWindowMaximize',
diff --git a/chrome/test/data/extensions/api_test/file_browser/mount_test/test.js b/chrome/test/data/extensions/api_test/file_browser/mount_test/test.js
index 631cf5f0..634498a 100644
--- a/chrome/test/data/extensions/api_test/file_browser/mount_test/test.js
+++ b/chrome/test/data/extensions/api_test/file_browser/mount_test/test.js
@@ -5,7 +5,7 @@
 // These have to be sync'd with file_manager_private_apitest.cc
 var expectedVolume1 = {
   volumeId: 'removable:mount_path1',
-  volumeLabel: 'mount_path1',
+  volumeLabel: 'device_label1',
   sourcePath: 'device_path1',
   volumeType: 'removable',
   deviceType: 'usb',
@@ -18,11 +18,12 @@
   watchable: true,
   source: 'device',
   profile: {profileId: '', displayName: '', isCurrentProfile: true},
+  diskFileSystemType: 'exfat'
 };
 
 var expectedVolume2 = {
   volumeId: 'removable:mount_path2',
-  volumeLabel: 'mount_path2',
+  volumeLabel: 'device_label2',
   sourcePath: 'device_path2',
   volumeType: 'removable',
   deviceType: 'mobile',
@@ -36,12 +37,13 @@
   // TODO(mtomasz): Add a test for a real MTP device.
   watchable: true,
   source: 'device',
-  profile: {profileId: '', displayName: '', isCurrentProfile: true}
+  profile: {profileId: '', displayName: '', isCurrentProfile: true},
+  diskFileSystemType: 'exfat'
 };
 
 var expectedVolume3 = {
   volumeId: 'removable:mount_path3',
-  volumeLabel: 'mount_path3',
+  volumeLabel: 'device_label3',
   sourcePath: 'device_path3',
   volumeType: 'removable',
   deviceType: 'optical',
@@ -53,7 +55,8 @@
   configurable: false,
   watchable: true,
   source: 'device',
-  profile: {profileId: '', displayName: '', isCurrentProfile: true}
+  profile: {profileId: '', displayName: '', isCurrentProfile: true},
+  diskFileSystemType: 'exfat'
 };
 
 var expectedDownloadsVolume = {
@@ -66,7 +69,8 @@
   configurable: false,
   watchable: true,
   source: 'system',
-  profile: {profileId: '', displayName: '', isCurrentProfile: true}
+  profile: {profileId: '', displayName: '', isCurrentProfile: true},
+  diskFileSystemType: ''
 };
 
 var expectedDriveVolume = {
@@ -80,7 +84,8 @@
   configurable: false,
   watchable: true,
   source: 'network',
-  profile: {profileId: '', displayName: '', isCurrentProfile: true}
+  profile: {profileId: '', displayName: '', isCurrentProfile: true},
+  diskFileSystemType: ''
 };
 
 var expectedArchiveVolume = {
@@ -94,7 +99,8 @@
   configurable: false,
   watchable: true,
   source: 'file',
-  profile: {profileId: '', displayName: '', isCurrentProfile: true}
+  profile: {profileId: '', displayName: '', isCurrentProfile: true},
+  diskFileSystemType: ''
 };
 
 var expectedProvidedVolume = {
@@ -110,7 +116,8 @@
   source: 'network',
   mountContext: 'auto',
   fileSystemId: '',
-  profile: {profileId: '', displayName: '', isCurrentProfile: true}
+  profile: {profileId: '', displayName: '', isCurrentProfile: true},
+  diskFileSystemType: ''
 };
 
 // List of expected mount points.
diff --git a/chromecast/media/cma/base/decoder_config_adapter.cc b/chromecast/media/cma/base/decoder_config_adapter.cc
index c69341c5..4006715 100644
--- a/chromecast/media/cma/base/decoder_config_adapter.cc
+++ b/chromecast/media/cma/base/decoder_config_adapter.cc
@@ -74,6 +74,8 @@
       return ::media::ChannelLayout::CHANNEL_LAYOUT_MONO;
     case 2:
       return ::media::ChannelLayout::CHANNEL_LAYOUT_STEREO;
+    case 6:
+      return ::media::ChannelLayout::CHANNEL_LAYOUT_5_1;
     default:
       return ::media::ChannelLayout::CHANNEL_LAYOUT_UNSUPPORTED;
   }
diff --git a/chromecast/media/cma/decoder/cast_audio_decoder.cc b/chromecast/media/cma/decoder/cast_audio_decoder.cc
index 2201380..07e4ac04 100644
--- a/chromecast/media/cma/decoder/cast_audio_decoder.cc
+++ b/chromecast/media/cma/decoder/cast_audio_decoder.cc
@@ -33,8 +33,7 @@
 
 namespace {
 
-const int kOutputChannelCount = 2;  // Always output stereo audio.
-const int kMaxChannelInput = 2;
+const int kStereoChannelCount = 2;  // Number of channel for stereo
 
 class CastAudioDecoderImpl : public CastAudioDecoder {
  public:
@@ -53,7 +52,6 @@
 
   void Initialize(const media::AudioConfig& config) {
     DCHECK(!initialized_);
-    DCHECK_LE(config_.channel_number, kMaxChannelInput);
     config_ = config;
 
     // Media pipeline should already decrypt the stream with the selected key
@@ -204,9 +202,9 @@
     }
 
     if (mixer_) {
-      // Convert to stereo if necessary.
+      // Convert mono to stereo if necessary.
       std::unique_ptr<::media::AudioBus> converted_to_stereo =
-          ::media::AudioBus::Create(kOutputChannelCount, num_frames);
+          ::media::AudioBus::Create(kStereoChannelCount, num_frames);
       mixer_->Transform(decoded.get(), converted_to_stereo.get());
       decoded.swap(converted_to_stereo);
     }
@@ -217,8 +215,7 @@
 
   scoped_refptr<media::DecoderBufferBase> FinishConversion(
       ::media::AudioBus* bus) {
-    DCHECK_EQ(kOutputChannelCount, bus->channels());
-    int size = bus->frames() * kOutputChannelCount *
+    int size = bus->frames() * bus->channels() *
                OutputFormatSizeInBytes(output_format_);
     scoped_refptr<::media::DecoderBuffer> result(
         new ::media::DecoderBuffer(size));
diff --git a/chromecast/media/cma/pipeline/audio_decoder_software_wrapper.cc b/chromecast/media/cma/pipeline/audio_decoder_software_wrapper.cc
index f20331b..d4a3dd8 100644
--- a/chromecast/media/cma/pipeline/audio_decoder_software_wrapper.cc
+++ b/chromecast/media/cma/pipeline/audio_decoder_software_wrapper.cc
@@ -15,6 +15,26 @@
 namespace chromecast {
 namespace media {
 
+namespace {
+const int kMonoChannelCount = 1;
+const int kStereoChannelCount = 2;
+const int k5_1ChannelCount = 6;
+
+bool IsChannelLayoutSupported(AudioConfig config) {
+  if (config.channel_number == kMonoChannelCount ||
+      config.channel_number == kStereoChannelCount)
+    return true;
+
+  // Only supports 5.1 for Opus.
+  if (config.channel_number == k5_1ChannelCount &&
+      config.codec == AudioCodec::kCodecOpus)
+    return true;
+
+  return false;
+}
+
+} // namespace
+
 AudioDecoderSoftwareWrapper::AudioDecoderSoftwareWrapper(
     MediaPipelineBackend::AudioDecoder* backend_decoder)
     : backend_decoder_(backend_decoder),
@@ -72,7 +92,7 @@
 
   output_config_.codec = media::kCodecPCM;
   output_config_.sample_format = media::kSampleFormatS16;
-  output_config_.channel_number = 2;
+  output_config_.channel_number = config.channel_number;
   output_config_.bytes_per_channel = 2;
   output_config_.samples_per_second = config.samples_per_second;
   output_config_.encryption_scheme = Unencrypted();
@@ -90,8 +110,9 @@
 
 bool AudioDecoderSoftwareWrapper::CreateSoftwareDecoder(
     const AudioConfig& config) {
-  if (config.channel_number > 2) {
-    LOG(ERROR) << "Multi-channel software audio decoding is not supported";
+  if (!IsChannelLayoutSupported(config)) {
+    LOG(ERROR) << "Software audio decoding is not supported for channel: "
+               << config.channel_number << " with codec: " << config.codec;
     return false;
   }
   // TODO(kmackay) Consider using planar float instead.
diff --git a/chromeos/components/tether/ble_scanner.cc b/chromeos/components/tether/ble_scanner.cc
index 0a604076..586a417 100644
--- a/chromeos/components/tether/ble_scanner.cc
+++ b/chromeos/components/tether/ble_scanner.cc
@@ -21,11 +21,6 @@
 
 namespace {
 
-// Minimum RSSI value to use for discovery. The -90 value was determined
-// empirically and is borrowed from
-// |proximity_auth::BluetoothLowEnergyConnectionFinder|.
-const int kMinDiscoveryRSSI = -90;
-
 // Valid service data must include at least 4 bytes: 2 bytes associated with the
 // scanning device (used as a scan filter) and 2 bytes which identify the
 // advertising device to the scanning device.
@@ -179,15 +174,15 @@
   DCHECK(adapter_);
   is_initializing_discovery_session_ = true;
 
-  // Discover only low energy (LE) devices with strong enough signal.
-  std::unique_ptr<device::BluetoothDiscoveryFilter> filter =
-      base::MakeUnique<device::BluetoothDiscoveryFilter>(
-          device::BLUETOOTH_TRANSPORT_LE);
-  filter->SetRSSI(kMinDiscoveryRSSI);
-
-  adapter_->StartDiscoverySessionWithFilter(
-      std::move(filter), base::Bind(&BleScanner::OnDiscoverySessionStarted,
-                                    weak_ptr_factory_.GetWeakPtr()),
+  // Note: Ideally, we would use a filter for only LE devices here. However,
+  // using a filter here triggers a bug in some kernel implementations which
+  // causes LE scanning to toggle rapidly on and off. This can cause race
+  // conditions which result in Bluetooth bugs. See crbug.com/759090.
+  // TODO(mcchou): Once these issues have been resolved, add the filter back.
+  // See crbug.com/759091.
+  adapter_->StartDiscoverySession(
+      base::Bind(&BleScanner::OnDiscoverySessionStarted,
+                 weak_ptr_factory_.GetWeakPtr()),
       base::Bind(&BleScanner::OnStartDiscoverySessionError,
                  weak_ptr_factory_.GetWeakPtr()));
 }
diff --git a/chromeos/components/tether/ble_scanner_unittest.cc b/chromeos/components/tether/ble_scanner_unittest.cc
index abb7b3b..aed5218 100644
--- a/chromeos/components/tether/ble_scanner_unittest.cc
+++ b/chromeos/components/tether/ble_scanner_unittest.cc
@@ -76,7 +76,6 @@
   std::vector<uint8_t> service_data_;
 };
 
-const int kExpectedDiscoveryRSSI = -90;
 const size_t kMinNumBytesInServiceData = 4;
 
 const std::string kDefaultBluetoothAddress = "11:22:33:44:55:66";
@@ -168,12 +167,11 @@
 
     mock_adapter_ =
         make_scoped_refptr(new NiceMock<device::MockBluetoothAdapter>());
-    stored_discovery_filter_.reset();
     stored_discovery_callback_.Reset();
     stored_discovery_errback_.Reset();
-    ON_CALL(*mock_adapter_, StartDiscoverySessionWithFilterRaw(_, _, _))
-        .WillByDefault(Invoke(
-            this, &BleScannerTest::SaveStartDiscoverySessionWithFilterArgs));
+    ON_CALL(*mock_adapter_, StartDiscoverySession(_, _))
+        .WillByDefault(
+            Invoke(this, &BleScannerTest::SaveStartDiscoverySessionArgs));
     ON_CALL(*mock_adapter_, IsPowered()).WillByDefault(Return(true));
 
     mock_discovery_session_ = nullptr;
@@ -186,28 +184,14 @@
     ble_scanner_->AddObserver(mock_observer_.get());
   }
 
-  void SaveStartDiscoverySessionWithFilterArgs(
-      const device::BluetoothDiscoveryFilter* discovery_filter,
+  void SaveStartDiscoverySessionArgs(
       const device::BluetoothAdapter::DiscoverySessionCallback& callback,
       const device::BluetoothAdapter::ErrorCallback& errback) {
-    stored_discovery_filter_ =
-        base::MakeUnique<device::BluetoothDiscoveryFilter>(
-            device::BluetoothTransport::BLUETOOTH_TRANSPORT_LE);
-    stored_discovery_filter_->CopyFrom(*discovery_filter);
     stored_discovery_callback_ = callback;
     stored_discovery_errback_ = errback;
   }
 
   void AssertDiscoverySessionRequested() {
-    // First, ensure that the correct discovery filter was passed.
-    EXPECT_TRUE(stored_discovery_filter_);
-    EXPECT_EQ(device::BluetoothTransport::BLUETOOTH_TRANSPORT_LE,
-              stored_discovery_filter_->GetTransport());
-    int16_t observed_rssi;
-    ASSERT_TRUE(stored_discovery_filter_->GetRSSI(&observed_rssi));
-    EXPECT_EQ(kExpectedDiscoveryRSSI, observed_rssi);
-
-    // Now, ensure that both a callback and errback were passed.
     EXPECT_FALSE(stored_discovery_callback_.is_null());
     EXPECT_FALSE(stored_discovery_errback_.is_null());
   }
@@ -232,7 +216,6 @@
   scoped_refptr<NiceMock<device::MockBluetoothAdapter>> mock_adapter_;
   device::MockBluetoothDiscoverySession* mock_discovery_session_;
 
-  std::unique_ptr<device::BluetoothDiscoveryFilter> stored_discovery_filter_;
   device::BluetoothAdapter::DiscoverySessionCallback stored_discovery_callback_;
   device::BluetoothAdapter::ErrorCallback stored_discovery_errback_;
 
@@ -529,7 +512,6 @@
   AssertDiscoverySessionRequested();
 
   // The discovery session should have been requested but not yet initialized.
-  EXPECT_TRUE(stored_discovery_filter_.get());
   EXPECT_FALSE(mock_discovery_session_);
 
   // Turn the adapter off before the discovery session starts.
diff --git a/chromeos/dbus/cros_disks_client.cc b/chromeos/dbus/cros_disks_client.cc
index 06ecf12..eb598dd9 100644
--- a/chromeos/dbus/cros_disks_client.cc
+++ b/chromeos/dbus/cros_disks_client.cc
@@ -188,6 +188,21 @@
                                   error_callback));
   }
 
+  void Rename(const std::string& device_path,
+              const std::string& volume_name,
+              const base::Closure& callback,
+              const base::Closure& error_callback) override {
+    dbus::MethodCall method_call(cros_disks::kCrosDisksInterface,
+                                 cros_disks::kRename);
+    dbus::MessageWriter writer(&method_call);
+    writer.AppendString(device_path);
+    writer.AppendString(volume_name);
+    proxy_->CallMethod(
+        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+        base::Bind(&CrosDisksClientImpl::OnRename,
+                   weak_ptr_factory_.GetWeakPtr(), callback, error_callback));
+  }
+
   // CrosDisksClient override.
   void GetDeviceProperties(const std::string& device_path,
                            const GetDevicePropertiesCallback& callback,
@@ -257,6 +272,17 @@
                    weak_ptr_factory_.GetWeakPtr()));
   }
 
+  // CrosDisksClient override.
+  void SetRenameCompletedHandler(
+      const RenameCompletedHandler& rename_completed_handler) override {
+    proxy_->ConnectToSignal(
+        cros_disks::kCrosDisksInterface, cros_disks::kRenameCompleted,
+        base::Bind(&CrosDisksClientImpl::OnRenameCompleted,
+                   weak_ptr_factory_.GetWeakPtr(), rename_completed_handler),
+        base::Bind(&CrosDisksClientImpl::OnSignalConnected,
+                   weak_ptr_factory_.GetWeakPtr()));
+  }
+
  protected:
   void Init(dbus::Bus* bus) override {
     proxy_ = bus->GetObjectProxy(
@@ -376,6 +402,17 @@
     callback.Run();
   }
 
+  // Handles the result of Rename and calls |callback| or |error_callback|.
+  void OnRename(const base::Closure& callback,
+                const base::Closure& error_callback,
+                dbus::Response* response) {
+    if (!response) {
+      error_callback.Run();
+      return;
+    }
+    callback.Run();
+  }
+
   // Handles the result of GetDeviceProperties and calls |callback| or
   // |error_callback|.
   void OnGetDeviceProperties(const std::string& device_path,
@@ -426,6 +463,18 @@
     handler.Run(static_cast<FormatError>(error_code), device_path);
   }
 
+  // Handles RenameCompleted signal and calls |handler|.
+  void OnRenameCompleted(RenameCompletedHandler handler, dbus::Signal* signal) {
+    dbus::MessageReader reader(signal);
+    uint32_t error_code = 0;
+    std::string device_path;
+    if (!reader.PopUint32(&error_code) || !reader.PopString(&device_path)) {
+      LOG(ERROR) << "Invalid signal: " << signal->ToString();
+      return;
+    }
+    handler.Run(static_cast<RenameError>(error_code), device_path);
+  }
+
   // Handles the result of signal connection setup.
   void OnSignalConnected(const std::string& interface,
                          const std::string& signal,
@@ -561,6 +610,10 @@
 //     string "NativePath"
 //     variant       string "/sys/devices/pci0000:00/0000:00:1d.7/usb1/1-4/...
 //   }
+//   dict entry {
+//     string "FileSystemType"
+//     variant       string "vfat"
+//   }
 // ]
 void DiskInfo::InitializeFromResponse(dbus::Response* response) {
   dbus::MessageReader reader(response);
@@ -596,6 +649,8 @@
       cros_disks::kDriveModel, &drive_model_);
   properties->GetStringWithoutPathExpansion(cros_disks::kIdLabel, &label_);
   properties->GetStringWithoutPathExpansion(cros_disks::kIdUuid, &uuid_);
+  properties->GetStringWithoutPathExpansion(cros_disks::kFileSystemType,
+                                            &file_system_type_);
 
   // dbus::PopDataAsValue() pops uint64_t as double.
   // The top 11 bits of uint64_t are dropped by the use of double. But, this
diff --git a/chromeos/dbus/cros_disks_client.h b/chromeos/dbus/cros_disks_client.h
index f6d9ce2..8b80ec6 100644
--- a/chromeos/dbus/cros_disks_client.h
+++ b/chromeos/dbus/cros_disks_client.h
@@ -72,6 +72,21 @@
   // consider doing explicit translation from cros-disks error_types.
 };
 
+// Rename error reported by cros-disks.
+enum RenameError {
+  RENAME_ERROR_NONE,
+  RENAME_ERROR_UNKNOWN,
+  RENAME_ERROR_INTERNAL,
+  RENAME_ERROR_INVALID_DEVICE_PATH,
+  RENAME_ERROR_DEVICE_BEING_RENAMED,
+  RENAME_ERROR_UNSUPPORTED_FILESYSTEM,
+  RENAME_ERROR_RENAME_PROGRAM_NOT_FOUND,
+  RENAME_ERROR_RENAME_PROGRAM_FAILED,
+  RENAME_ERROR_DEVICE_NOT_ALLOWED,
+  RENAME_ERROR_LONG_NAME,
+  RENAME_ERROR_INVALID_CHARACTER,
+};
+
 // Format error reported by cros-disks.
 enum FormatError {
   FORMAT_ERROR_NONE,
@@ -183,6 +198,9 @@
   // Returns file system uuid.
   const std::string& uuid() const { return uuid_; }
 
+  // Returns file system type identifier.
+  const std::string& file_system_type() const { return file_system_type_; }
+
  private:
   void InitializeFromResponse(dbus::Response* response);
 
@@ -206,6 +224,7 @@
   bool is_read_only_;
   bool is_hidden_;
   std::string uuid_;
+  std::string file_system_type_;
 };
 
 // A struct to represent information about a mount point sent from cros-disks.
@@ -267,6 +286,13 @@
                               const std::string& device_path)>
       FormatCompletedHandler;
 
+  // A callback to handle RenameCompleted signal.
+  // The first argument is the error code.
+  // The second argument is the device path.
+  typedef base::Callback<void(RenameError error_code,
+                              const std::string& device_path)>
+      RenameCompletedHandler;
+
   // A callback to handle mount events.
   // The first argument is the event type.
   // The second argument is the device path.
@@ -319,6 +345,13 @@
                       const base::Closure& callback,
                       const base::Closure& error_callback) = 0;
 
+  // Calls Rename method. |callback| is called after the method call succeeds,
+  // otherwise, |error_callback| is called.
+  virtual void Rename(const std::string& device_path,
+                      const std::string& volume_name,
+                      const base::Closure& callback,
+                      const base::Closure& error_callback) = 0;
+
   // Calls GetDeviceProperties method.  |callback| is called after the method
   // call succeeds, otherwise, |error_callback| is called.
   virtual void GetDeviceProperties(const std::string& device_path,
@@ -340,6 +373,11 @@
   virtual void SetFormatCompletedHandler(
       const FormatCompletedHandler& format_completed_handler) = 0;
 
+  // Registers |rename_completed_handler| as a callback to be invoked when a
+  // RenameCompleted signal is received.
+  virtual void SetRenameCompletedHandler(
+      const RenameCompletedHandler& rename_completed_handler) = 0;
+
   // Factory function, creates a new instance and returns ownership.
   // For normal usage, access the singleton via DBusThreadManager::Get().
   static CrosDisksClient* Create(DBusClientImplementationType type);
diff --git a/chromeos/dbus/cros_disks_client_unittest.cc b/chromeos/dbus/cros_disks_client_unittest.cc
index 84aeb4b..11c18e0 100644
--- a/chromeos/dbus/cros_disks_client_unittest.cc
+++ b/chromeos/dbus/cros_disks_client_unittest.cc
@@ -62,6 +62,7 @@
   const std::string kProductName = "Product Name";
   const std::string kVendorId = "0000";
   const std::string kVendorName = "Vendor Name";
+  const std::string kFileSystemType = "exfat";
 
   // Construct a fake response of GetDeviceProperties().
   std::unique_ptr<dbus::Response> response(dbus::Response::CreateEmpty());
@@ -121,6 +122,8 @@
                           kProductName);
     AppendStringDictEntry(&array_writer, cros_disks::kVendorId, kVendorId);
     AppendStringDictEntry(&array_writer, cros_disks::kVendorName, kVendorName);
+    AppendStringDictEntry(&array_writer, cros_disks::kFileSystemType,
+                          kFileSystemType);
 
     writer.CloseContainer(&array_writer);
   }
@@ -153,6 +156,7 @@
   EXPECT_EQ(kDeviceSize, result.total_size_in_bytes());
   EXPECT_EQ(DEVICE_TYPE_SD, result.device_type());
   EXPECT_EQ(kMountPath, result.mount_path());
+  EXPECT_EQ(kFileSystemType, result.file_system_type());
 }
 
 TEST(CrosDisksClientTest, ComposeMountOptions) {
diff --git a/chromeos/dbus/fake_cros_disks_client.cc b/chromeos/dbus/fake_cros_disks_client.cc
index 846c7f7..0aaa82a2 100644
--- a/chromeos/dbus/fake_cros_disks_client.cc
+++ b/chromeos/dbus/fake_cros_disks_client.cc
@@ -66,8 +66,9 @@
     : unmount_call_count_(0),
       unmount_success_(true),
       format_call_count_(0),
-      format_success_(true) {
-}
+      format_success_(true),
+      rename_call_count_(0),
+      rename_success_(true) {}
 
 FakeCrosDisksClient::~FakeCrosDisksClient() {
 }
@@ -167,6 +168,23 @@
   }
 }
 
+void FakeCrosDisksClient::Rename(const std::string& device_path,
+                                 const std::string& volume_name,
+                                 const base::Closure& callback,
+                                 const base::Closure& error_callback) {
+  DCHECK(!callback.is_null());
+  DCHECK(!error_callback.is_null());
+
+  rename_call_count_++;
+  last_rename_device_path_ = device_path;
+  last_rename_volume_name_ = volume_name;
+  if (rename_success_) {
+    base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback);
+  } else {
+    base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, error_callback);
+  }
+}
+
 void FakeCrosDisksClient::GetDeviceProperties(
     const std::string& device_path,
     const GetDevicePropertiesCallback& callback,
@@ -188,6 +206,11 @@
   format_completed_handler_ = format_completed_handler;
 }
 
+void FakeCrosDisksClient::SetRenameCompletedHandler(
+    const RenameCompletedHandler& rename_completed_handler) {
+  rename_completed_handler_ = rename_completed_handler;
+}
+
 bool FakeCrosDisksClient::SendMountEvent(MountEventType event,
                                          const std::string& path) {
   if (mount_event_handler_.is_null())
@@ -217,4 +240,13 @@
   return true;
 }
 
+bool FakeCrosDisksClient::SendRenameCompletedEvent(
+    RenameError error_code,
+    const std::string& device_path) {
+  if (rename_completed_handler_.is_null())
+    return false;
+  rename_completed_handler_.Run(error_code, device_path);
+  return true;
+}
+
 }  // namespace chromeos
diff --git a/chromeos/dbus/fake_cros_disks_client.h b/chromeos/dbus/fake_cros_disks_client.h
index ef89b84..10e3fed1 100644
--- a/chromeos/dbus/fake_cros_disks_client.h
+++ b/chromeos/dbus/fake_cros_disks_client.h
@@ -49,6 +49,10 @@
               const std::string& filesystem,
               const base::Closure& callback,
               const base::Closure& error_callback) override;
+  void Rename(const std::string& device_path,
+              const std::string& volume_name,
+              const base::Closure& callback,
+              const base::Closure& error_callback) override;
   void GetDeviceProperties(const std::string& device_path,
                            const GetDevicePropertiesCallback& callback,
                            const base::Closure& error_callback) override;
@@ -58,6 +62,8 @@
       const MountCompletedHandler& mount_completed_handler) override;
   void SetFormatCompletedHandler(
       const FormatCompletedHandler& format_completed_handler) override;
+  void SetRenameCompletedHandler(
+      const RenameCompletedHandler& rename_completed_handler) override;
 
   // Used in tests to simulate signals sent by cros disks layer.
   // Invokes handlers set in |SetMountEventHandler|, |SetMountCompletedHandler|,
@@ -69,6 +75,8 @@
                                const std::string& mount_path);
   bool SendFormatCompletedEvent(FormatError error_code,
                                 const std::string& device_path);
+  bool SendRenameCompletedEvent(RenameError error_code,
+                                const std::string& device_path);
 
   // Returns how many times Unmount() was called.
   int unmount_call_count() const {
@@ -116,10 +124,27 @@
     format_success_ = false;
   }
 
+  // Returns how many times Rename() was called.
+  int rename_call_count() const { return rename_call_count_; }
+
+  // Returns the |device_path| parameter from the last invocation of Rename().
+  const std::string& last_rename_device_path() const {
+    return last_rename_device_path_;
+  }
+
+  // Returns the |volume_name| parameter from the last invocation of Rename().
+  const std::string& last_rename_volume_name() const {
+    return last_rename_volume_name_;
+  }
+
+  // Makes the subsequent Rename() calls fail. Rename() succeeds by default.
+  void MakeRenameFail() { rename_success_ = false; }
+
  private:
   MountEventHandler mount_event_handler_;
   MountCompletedHandler mount_completed_handler_;
   FormatCompletedHandler format_completed_handler_;
+  RenameCompletedHandler rename_completed_handler_;
 
   int unmount_call_count_;
   std::string last_unmount_device_path_;
@@ -130,6 +155,10 @@
   std::string last_format_device_path_;
   std::string last_format_filesystem_;
   bool format_success_;
+  int rename_call_count_;
+  std::string last_rename_device_path_;
+  std::string last_rename_volume_name_;
+  bool rename_success_;
   std::set<base::FilePath> mounted_paths_;
 };
 
diff --git a/chromeos/disks/disk_mount_manager.cc b/chromeos/disks/disk_mount_manager.cc
index d7dab5e..052a9407 100644
--- a/chromeos/disks/disk_mount_manager.cc
+++ b/chromeos/disks/disk_mount_manager.cc
@@ -7,7 +7,9 @@
 #include <stddef.h>
 #include <stdint.h>
 
+#include <map>
 #include <set>
+#include <string>
 
 #include "base/bind.h"
 #include "base/macros.h"
@@ -23,6 +25,8 @@
 
 namespace {
 
+const char kDefaultFormattedDeviceName[] = "UNTITLED";
+const char kDefaultFormatVFAT[] = "vfat";
 const char kDeviceNotFound[] = "Device could not be found";
 
 DiskMountManager* g_disk_mount_manager = NULL;
@@ -48,6 +52,9 @@
     cros_disks_client_->SetFormatCompletedHandler(
         base::Bind(&DiskMountManagerImpl::OnFormatCompleted,
                    weak_ptr_factory_.GetWeakPtr()));
+    cros_disks_client_->SetRenameCompletedHandler(
+        base::Bind(&DiskMountManagerImpl::OnRenameCompleted,
+                   weak_ptr_factory_.GetWeakPtr()));
   }
 
   ~DiskMountManagerImpl() override {
@@ -157,6 +164,35 @@
                            device_path));
   }
 
+  void RenameMountedDevice(const std::string& mount_path,
+                           const std::string& volume_name) override {
+    MountPointMap::const_iterator mount_point = mount_points_.find(mount_path);
+    if (mount_point == mount_points_.end()) {
+      LOG(ERROR) << "Mount point with path '" << mount_path << "' not found.";
+      OnRenameCompleted(RENAME_ERROR_UNKNOWN, mount_path);
+      return;
+    }
+
+    std::string device_path = mount_point->second.source_path;
+    DiskMap::const_iterator iter = disks_.find(device_path);
+    if (iter == disks_.end()) {
+      LOG(ERROR) << "Device with path '" << device_path << "' not found.";
+      OnRenameCompleted(RENAME_ERROR_UNKNOWN, device_path);
+      return;
+    }
+    if (iter->second->is_read_only()) {
+      LOG(ERROR) << "Mount point with path '" << mount_path
+                 << "' is read-only.";
+      OnRenameCompleted(RENAME_ERROR_DEVICE_NOT_ALLOWED, mount_path);
+      return;
+    }
+
+    UnmountPath(
+        iter->second->mount_path(), UNMOUNT_OPTIONS_NONE,
+        base::Bind(&DiskMountManagerImpl::OnUnmountPathForRename,
+                   weak_ptr_factory_.GetWeakPtr(), device_path, volume_name));
+  }
+
   // DiskMountManager override.
   void UnmountDeviceRecursively(
       const std::string& device_path,
@@ -283,6 +319,24 @@
   }
 
  private:
+  // A struct to represent information about a format changes.
+  struct FormatChange {
+    // new file system type
+    std::string file_system_type;
+    // New volume name
+    std::string volume_name;
+  };
+
+  // Stores new volume name and file system type for a device on which
+  // formatting is invoked on, so that OnFormatCompleted can set it back to
+  // |disks_|. The key is a device_path and the value is a FormatChange.
+  std::map<std::string, FormatChange> pending_format_changes_;
+
+  // Stores new volume name for a device on which renaming is invoked on, so
+  // that OnRenameCompleted can set it back to |disks_|. The key is a
+  // device_path and the value is new volume_name.
+  std::map<std::string, std::string> pending_rename_changes_;
+
   struct UnmountDeviceRecursivelyCallbackData {
     UnmountDeviceRecursivelyCallbackData(
         const UnmountDeviceRecursivelyCallbackType& in_callback,
@@ -418,7 +472,7 @@
         disk->set_write_disabled_by_policy(
             it != access_modes_.end() && !disk->is_read_only_hardware()
                 && it->second == MOUNT_ACCESS_MODE_READ_ONLY);
-        disk->set_mount_path(mount_info.mount_path);
+        disk->SetMountPath(mount_info.mount_path);
       }
     }
     // Observers may read the values of disks_. So notify them after tweaking
@@ -477,16 +531,15 @@
     DiskMap::const_iterator disk = disks_.find(device_path);
     DCHECK(disk != disks_.end() && disk->second->mount_path().empty());
 
-    const char kFormatVFAT[] = "vfat";
+    pending_format_changes_[device_path] = {kDefaultFormatVFAT,
+                                            kDefaultFormattedDeviceName};
+
     cros_disks_client_->Format(
-        device_path,
-        kFormatVFAT,
+        device_path, kDefaultFormatVFAT,
         base::Bind(&DiskMountManagerImpl::OnFormatStarted,
-                   weak_ptr_factory_.GetWeakPtr(),
-                   device_path),
+                   weak_ptr_factory_.GetWeakPtr(), device_path),
         base::Bind(&DiskMountManagerImpl::OnFormatCompleted,
-                   weak_ptr_factory_.GetWeakPtr(),
-                   FORMAT_ERROR_UNKNOWN,
+                   weak_ptr_factory_.GetWeakPtr(), FORMAT_ERROR_UNKNOWN,
                    device_path));
   }
 
@@ -498,9 +551,80 @@
   // Callback to handle FormatCompleted signal and Format method call failure.
   void OnFormatCompleted(FormatError error_code,
                          const std::string& device_path) {
+    auto iter = disks_.find(device_path);
+
+    // disk might have been removed by now?
+    if (iter != disks_.end()) {
+      Disk* disk = iter->second.get();
+      DCHECK(disk);
+
+      auto pending_change = pending_format_changes_.find(device_path);
+      if (pending_change != pending_format_changes_.end() &&
+          error_code == FORMAT_ERROR_NONE) {
+        disk->set_device_label(pending_change->second.volume_name);
+        disk->set_file_system_type(pending_change->second.file_system_type);
+      }
+    }
+
+    pending_format_changes_.erase(device_path);
+
     NotifyFormatStatusUpdate(FORMAT_COMPLETED, error_code, device_path);
   }
 
+  void OnUnmountPathForRename(const std::string& device_path,
+                              const std::string& volume_name,
+                              MountError error_code) {
+    if (error_code != MOUNT_ERROR_NONE ||
+        disks_.find(device_path) == disks_.end()) {
+      OnRenameCompleted(RENAME_ERROR_UNKNOWN, device_path);
+      return;
+    }
+
+    RenameUnmountedDevice(device_path, volume_name);
+  }
+
+  // Start device renaming
+  void RenameUnmountedDevice(const std::string& device_path,
+                             const std::string& volume_name) {
+    DiskMap::const_iterator disk = disks_.find(device_path);
+    DCHECK(disk != disks_.end() && disk->second->mount_path().empty());
+
+    pending_rename_changes_[device_path] = volume_name;
+    cros_disks_client_->Rename(
+        device_path, volume_name,
+        base::Bind(&DiskMountManagerImpl::OnRenameStarted,
+                   weak_ptr_factory_.GetWeakPtr(), device_path),
+        base::Bind(&DiskMountManagerImpl::OnRenameCompleted,
+                   weak_ptr_factory_.GetWeakPtr(), RENAME_ERROR_UNKNOWN,
+                   device_path));
+  }
+
+  // Callback for Rename.
+  void OnRenameStarted(const std::string& device_path) {
+    NotifyRenameStatusUpdate(RENAME_STARTED, RENAME_ERROR_NONE, device_path);
+  }
+
+  // Callback to handle RenameCompleted signal and Rename method call failure.
+  void OnRenameCompleted(RenameError error_code,
+                         const std::string& device_path) {
+    auto iter = disks_.find(device_path);
+
+    // disk might have been removed by now?
+    if (iter != disks_.end()) {
+      Disk* disk = iter->second.get();
+      DCHECK(disk);
+
+      auto pending_change = pending_rename_changes_.find(device_path);
+      if (pending_change != pending_rename_changes_.end() &&
+          error_code == RENAME_ERROR_NONE)
+        disk->set_device_label(pending_change->second);
+    }
+
+    pending_rename_changes_.erase(device_path);
+
+    NotifyRenameStatusUpdate(RENAME_COMPLETED, error_code, device_path);
+  }
+
   // Callback for GetDeviceProperties.
   void OnGetDeviceProperties(const DiskInfo& disk_info) {
     // TODO(zelidrag): Find a better way to filter these out before we
@@ -512,8 +636,10 @@
     LOG(WARNING) << "Found disk " << disk_info.device_path();
     // Delete previous disk info for this path:
     bool is_new = true;
+    std::string base_mount_path = std::string();
     DiskMap::iterator iter = disks_.find(disk_info.device_path());
     if (iter != disks_.end()) {
+      base_mount_path = iter->second->base_mount_path();
       disks_.erase(iter);
       is_new = false;
     }
@@ -525,27 +651,17 @@
     auto access_mode = access_modes_.find(disk_info.device_path());
     bool write_disabled_by_policy = access_mode != access_modes_.end()
         && access_mode->second == chromeos::MOUNT_ACCESS_MODE_READ_ONLY;
-    Disk* disk = new Disk(disk_info.device_path(),
-                          disk_info.mount_path(),
-                          write_disabled_by_policy,
-                          disk_info.system_path(),
-                          disk_info.file_path(),
-                          disk_info.label(),
-                          disk_info.drive_label(),
-                          disk_info.vendor_id(),
-                          disk_info.vendor_name(),
-                          disk_info.product_id(),
-                          disk_info.product_name(),
-                          disk_info.uuid(),
-                          FindSystemPathPrefix(disk_info.system_path()),
-                          disk_info.device_type(),
-                          disk_info.total_size_in_bytes(),
-                          disk_info.is_drive(),
-                          disk_info.is_read_only(),
-                          disk_info.has_media(),
-                          disk_info.on_boot_device(),
-                          disk_info.on_removable_device(),
-                          disk_info.is_hidden());
+    Disk* disk = new Disk(
+        disk_info.device_path(), disk_info.mount_path(),
+        write_disabled_by_policy, disk_info.system_path(),
+        disk_info.file_path(), disk_info.label(), disk_info.drive_label(),
+        disk_info.vendor_id(), disk_info.vendor_name(), disk_info.product_id(),
+        disk_info.product_name(), disk_info.uuid(),
+        FindSystemPathPrefix(disk_info.system_path()), disk_info.device_type(),
+        disk_info.total_size_in_bytes(), disk_info.is_drive(),
+        disk_info.is_read_only(), disk_info.has_media(),
+        disk_info.on_boot_device(), disk_info.on_removable_device(),
+        disk_info.is_hidden(), disk_info.file_system_type(), base_mount_path);
     disks_.insert(
         std::make_pair(disk_info.device_path(), base::WrapUnique(disk)));
     NotifyDiskStatusUpdate(is_new ? DISK_ADDED : DISK_CHANGED, disk);
@@ -682,6 +798,13 @@
       observer.OnFormatEvent(event, error_code, device_path);
   }
 
+  void NotifyRenameStatusUpdate(RenameEvent event,
+                                RenameError error_code,
+                                const std::string& device_path) {
+    for (auto& observer : observers_)
+      observer.OnRenameEvent(event, error_code, device_path);
+  }
+
   // Finds system path prefix from |system_path|.
   const std::string& FindSystemPathPrefix(const std::string& system_path) {
     if (system_path.empty())
@@ -746,7 +869,9 @@
                              bool has_media,
                              bool on_boot_device,
                              bool on_removable_device,
-                             bool is_hidden)
+                             bool is_hidden,
+                             const std::string& file_system_type,
+                             const std::string& base_mount_path)
     : device_path_(device_path),
       mount_path_(mount_path),
       write_disabled_by_policy_(write_disabled_by_policy),
@@ -767,12 +892,21 @@
       has_media_(has_media),
       on_boot_device_(on_boot_device),
       on_removable_device_(on_removable_device),
-      is_hidden_(is_hidden) {}
+      is_hidden_(is_hidden),
+      file_system_type_(file_system_type),
+      base_mount_path_(base_mount_path) {}
 
 DiskMountManager::Disk::Disk(const Disk& other) = default;
 
 DiskMountManager::Disk::~Disk() {}
 
+void DiskMountManager::Disk::SetMountPath(const std::string& mount_path) {
+  mount_path_ = mount_path;
+
+  if (base_mount_path_.empty())
+    base_mount_path_ = mount_path;
+}
+
 bool DiskMountManager::AddDiskForTest(std::unique_ptr<Disk> disk) {
   return false;
 }
diff --git a/chromeos/disks/disk_mount_manager.h b/chromeos/disks/disk_mount_manager.h
index cceebc09..cb8a927 100644
--- a/chromeos/disks/disk_mount_manager.h
+++ b/chromeos/disks/disk_mount_manager.h
@@ -11,6 +11,7 @@
 #include <memory>
 
 #include "base/callback_forward.h"
+#include "base/files/file_path.h"
 #include "chromeos/chromeos_export.h"
 #include "chromeos/dbus/cros_disks_client.h"
 
@@ -51,6 +52,8 @@
     FORMAT_COMPLETED
   };
 
+  enum RenameEvent { RENAME_STARTED, RENAME_COMPLETED };
+
   // Used to house an instance of each found mount device.
   class Disk {
    public:
@@ -78,7 +81,9 @@
          bool has_media,
          bool on_boot_device,
          bool on_removable_device,
-         bool is_hidden);
+         bool is_hidden,
+         const std::string& file_system_type,
+         const std::string& base_mount_path);
     Disk(const Disk& other);
     ~Disk();
 
@@ -101,6 +106,10 @@
     // Device's label.
     const std::string& device_label() const { return device_label_; }
 
+    void set_device_label(const std::string& device_label) {
+      device_label_ = device_label;
+    }
+
     // If disk is a parent, then its label, else parents label.
     // (e.g. "TransMemory")
     const std::string& drive_label() const { return drive_label_; }
@@ -155,10 +164,6 @@
     // Shoud the device be shown in the UI, or automounted.
     bool is_hidden() const { return is_hidden_; }
 
-    void set_mount_path(const std::string& mount_path) {
-      mount_path_ = mount_path;
-    }
-
     void set_write_disabled_by_policy(bool disable) {
       write_disabled_by_policy_ = disable;
     }
@@ -167,6 +172,16 @@
 
     bool is_mounted() const { return !mount_path_.empty(); }
 
+    const std::string& file_system_type() const { return file_system_type_; }
+
+    void set_file_system_type(const std::string& file_system_type) {
+      file_system_type_ = file_system_type;
+    }
+    // Name of the first mount path of the disk.
+    const std::string& base_mount_path() const { return base_mount_path_; }
+
+    void SetMountPath(const std::string& mount_path);
+
    private:
     std::string device_path_;
     std::string mount_path_;
@@ -189,6 +204,8 @@
     bool on_boot_device_;
     bool on_removable_device_;
     bool is_hidden_;
+    std::string file_system_type_;
+    std::string base_mount_path_;
   };
   typedef std::map<std::string, std::unique_ptr<Disk>> DiskMap;
 
@@ -245,6 +262,10 @@
     virtual void OnFormatEvent(FormatEvent event,
                                FormatError error_code,
                                const std::string& device_path) = 0;
+    // Called on rename process events.
+    virtual void OnRenameEvent(RenameEvent event,
+                               RenameError error_code,
+                               const std::string& device_path) = 0;
   };
 
   virtual ~DiskMountManager() {}
@@ -304,6 +325,12 @@
   // Example: mount_path: /media/VOLUME_LABEL
   virtual void FormatMountedDevice(const std::string& mount_path) = 0;
 
+  // Renames Device given its mount path.
+  // Example: mount_path: /media/VOLUME_LABEL
+  //          volume_name: MYUSB
+  virtual void RenameMountedDevice(const std::string& mount_path,
+                                   const std::string& volume_name) = 0;
+
   // Unmounts device_path and all of its known children.
   virtual void UnmountDeviceRecursively(
       const std::string& device_path,
diff --git a/chromeos/disks/disk_mount_manager_unittest.cc b/chromeos/disks/disk_mount_manager_unittest.cc
index adb9725..e60702d 100644
--- a/chromeos/disks/disk_mount_manager_unittest.cc
+++ b/chromeos/disks/disk_mount_manager_unittest.cc
@@ -31,6 +31,8 @@
 const char kDevice2SourcePath[] = "/device/source_path2";
 const char kReadOnlyDeviceMountPath[] = "/device/read_only_mount_path";
 const char kReadOnlyDeviceSourcePath[] = "/device/read_only_source_path";
+const char kFileSystemType1[] = "ntfs";
+const char kFileSystemType2[] = "exfat";
 
 // Holds information needed to create a DiskMountManager::Disk instance.
 struct TestDiskInfo {
@@ -55,6 +57,8 @@
   bool on_boot_device;
   bool on_removable_device;
   bool is_hidden;
+  const char* file_system_type;
+  const char* base_mount_path;
 };
 
 // Holds information to create a DiskMOuntManager::MountPointInfo instance.
@@ -67,75 +71,81 @@
 
 // List of disks held in DiskMountManager at the begining of the test.
 const TestDiskInfo kTestDisks[] = {
-  {
-    kDevice1SourcePath,
-    kDevice1MountPath,
-    false,  // write_disabled_by_policy
-    "/device/prefix/system_path",
-    "/device/file_path",
-    "/device/device_label",
-    "/device/drive_label",
-    "/device/vendor_id",
-    "/device/vendor_name",
-    "/device/product_id",
-    "/device/product_name",
-    "/device/fs_uuid",
-    "/device/prefix",
-    chromeos::DEVICE_TYPE_USB,
-    1073741824,  // size in bytes
-    false,       // is parent
-    false,       // is read only
-    true,        // has media
-    false,       // is on boot device
-    true,        // is on removable device
-    false        // is hidden
-  },
-  {
-    kDevice2SourcePath,
-    "",  // not mounted initially
-    false,  // write_disabled_by_policy
-    "/device/prefix/system_path2",
-    "/device/file_path2",
-    "/device/device_label2",
-    "/device/drive_label2",
-    "/device/vendor_id2",
-    "/device/vendor_name2",
-    "/device/product_id2",
-    "/device/product_name2",
-    "/device/fs_uuid2",
-    "/device/prefix2",
-    chromeos::DEVICE_TYPE_SD,
-    1073741824,  // size in bytes
-    false,       // is parent
-    false,       // is read only
-    true,        // has media
-    false,       // is on boot device
-    true,        // is on removable device
-    false        // is hidden
-  },
-  {
-    kReadOnlyDeviceSourcePath,
-    kReadOnlyDeviceMountPath,
-    false,  // write_disabled_by_policy
-    "/device/prefix/system_path_3",
-    "/device/file_path_3",
-    "/device/device_label_3",
-    "/device/drive_label_3",
-    "/device/vendor_id_3",
-    "/device/vendor_name_3",
-    "/device/product_id_3",
-    "/device/product_name_3",
-    "/device/fs_uuid_3",
-    "/device/prefix",
-    chromeos::DEVICE_TYPE_USB,
-    1073741824,  // size in bytes
-    false,       // is parent
-    true,        // is read only
-    true,        // has media
-    false,       // is on boot device
-    true,        // is on removable device
-    false        // is hidden
-  },
+    {
+        kDevice1SourcePath,
+        kDevice1MountPath,
+        false,  // write_disabled_by_policy
+        "/device/prefix/system_path",
+        "/device/file_path",
+        "/device/device_label",
+        "/device/drive_label",
+        "/device/vendor_id",
+        "/device/vendor_name",
+        "/device/product_id",
+        "/device/product_name",
+        "/device/fs_uuid",
+        "/device/prefix",
+        chromeos::DEVICE_TYPE_USB,
+        1073741824,  // size in bytes
+        false,       // is parent
+        false,       // is read only
+        true,        // has media
+        false,       // is on boot device
+        true,        // is on removable device
+        false,       // is hidden
+        kFileSystemType1,
+        ""  // base mount path
+    },
+    {
+        kDevice2SourcePath,
+        "",     // not mounted initially
+        false,  // write_disabled_by_policy
+        "/device/prefix/system_path2",
+        "/device/file_path2",
+        "/device/device_label2",
+        "/device/drive_label2",
+        "/device/vendor_id2",
+        "/device/vendor_name2",
+        "/device/product_id2",
+        "/device/product_name2",
+        "/device/fs_uuid2",
+        "/device/prefix2",
+        chromeos::DEVICE_TYPE_SD,
+        1073741824,  // size in bytes
+        false,       // is parent
+        false,       // is read only
+        true,        // has media
+        false,       // is on boot device
+        true,        // is on removable device
+        false,       // is hidden
+        kFileSystemType2,
+        ""  // base mount path
+    },
+    {
+        kReadOnlyDeviceSourcePath,
+        kReadOnlyDeviceMountPath,
+        false,  // write_disabled_by_policy
+        "/device/prefix/system_path_3",
+        "/device/file_path_3",
+        "/device/device_label_3",
+        "/device/drive_label_3",
+        "/device/vendor_id_3",
+        "/device/vendor_name_3",
+        "/device/product_id_3",
+        "/device/product_name_3",
+        "/device/fs_uuid_3",
+        "/device/prefix",
+        chromeos::DEVICE_TYPE_USB,
+        1073741824,  // size in bytes
+        false,       // is parent
+        true,        // is read only
+        true,        // has media
+        false,       // is on boot device
+        true,        // is on removable device
+        false,       // is hidden
+        kFileSystemType2,
+        ""  // base mount path
+    },
 };
 
 // List of mount points  held in DiskMountManager at the begining of the test.
@@ -312,6 +322,13 @@
     events_.push_back(MakeUnique<FormatEvent>(event, error_code, device_path));
   }
 
+  void OnRenameEvent(DiskMountManager::RenameEvent event,
+                     chromeos::RenameError error_code,
+                     const std::string& device_path) override {
+    // TODO(klemenko): Add implementation when you will add unit tests for
+    // rename
+  }
+
   void OnMountEvent(
       DiskMountManager::MountEvent event,
       chromeos::MountError error_code,
@@ -454,7 +471,8 @@
             disk.product_name, disk.fs_uuid, disk.system_path_prefix,
             disk.device_type, disk.size_in_bytes, disk.is_parent,
             disk.is_read_only, disk.has_media, disk.on_boot_device,
-            disk.on_removable_device, disk.is_hidden)));
+            disk.on_removable_device, disk.is_hidden, disk.file_system_type,
+            disk.base_mount_path)));
   }
 
   // Adds a new mount point to the disk mount manager.
@@ -706,6 +724,9 @@
 
 // Tests the case when formatting completes successfully.
 TEST_F(DiskMountManagerTest, Format_FormatSuccess) {
+  DiskMountManager* manager = DiskMountManager::GetInstance();
+  const DiskMountManager::DiskMap& disks = manager->disks();
+
   // Set up cros disks client mocks.
   // Both unmount and format device cals are successful in this test.
 
@@ -743,6 +764,10 @@
   EXPECT_EQ(FormatEvent(DiskMountManager::FORMAT_COMPLETED,
                         chromeos::FORMAT_ERROR_NONE, kDevice1SourcePath),
             observer_->GetFormatEvent(2));
+
+  // Disk should have new values for file system type and device label name
+  EXPECT_EQ("vfat", disks.find(kDevice1SourcePath)->second->file_system_type());
+  EXPECT_EQ("UNTITLED", disks.find(kDevice1SourcePath)->second->device_label());
 }
 
 // Tests that it's possible to format the device twice in a row (this may not be
diff --git a/chromeos/disks/mock_disk_mount_manager.cc b/chromeos/disks/mock_disk_mount_manager.cc
index e2d38ab..33a6828 100644
--- a/chromeos/disks/mock_disk_mount_manager.cc
+++ b/chromeos/disks/mock_disk_mount_manager.cc
@@ -35,6 +35,7 @@
 const char kTestProductId[] = "abcd";
 const char kTestProductName[] = "A product";
 const char kTestUuid[] = "FFFF-FFFF";
+const char kTestFileSystemType[] = "vfat";
 
 }  // namespace
 
@@ -80,12 +81,13 @@
           std::string(kTestProductId), std::string(kTestProductName),
           std::string(kTestUuid), std::string(kTestSystemPathPrefix),
           DEVICE_TYPE_USB, 4294967295U,
-          false,   // is_parent
-          false,   // is_read_only
-          true,    // has_media
-          false,   // on_boot_device
-          true,    // on_removable_device
-          false);  // is_hidden
+          false,  // is_parent
+          false,  // is_read_only
+          true,   // has_media
+          false,  // on_boot_device
+          true,   // on_removable_device
+          false,  // is_hidden
+          std::string(kTestFileSystemType), std::string());
   DiskMountManager::Disk* disk1 = disk1_ptr.get();
 
   disks_.clear();
@@ -108,12 +110,13 @@
           std::string(kTestProductId), std::string(kTestProductName),
           std::string(kTestUuid), std::string(kTestSystemPathPrefix),
           DEVICE_TYPE_MOBILE, 1073741824,
-          false,   // is_parent
-          false,   // is_read_only
-          true,    // has_media
-          false,   // on_boot_device
-          true,    // on_removable_device
-          false);  // is_hidden
+          false,  // is_parent
+          false,  // is_read_only
+          true,   // has_media
+          false,  // on_boot_device
+          true,   // on_removable_device
+          false,  // is_hidden
+          std::string(kTestFileSystemType), std::string());
   DiskMountManager::Disk* disk2 = disk2_ptr.get();
   disks_.clear();
   disks_[std::string(kTestDevicePath)] = std::move(disk2_ptr);
@@ -131,12 +134,13 @@
           std::string(kTestProductId), std::string(kTestProductName),
           std::string(kTestUuid), std::string(kTestSystemPathPrefix),
           DEVICE_TYPE_SD, 1073741824,
-          false,   // is_parent
-          false,   // is_read_only
-          true,    // has_media
-          false,   // on_boot_device
-          true,    // on_removable_device
-          false);  // is_hidden
+          false,  // is_parent
+          false,  // is_read_only
+          true,   // has_media
+          false,  // on_boot_device
+          true,   // on_removable_device
+          false,  // is_hidden
+          std::string(kTestFileSystemType), std::string());
   DiskMountManager::Disk* disk = disk_ptr.get();
   disks_.clear();
   disks_[std::string(kTestDevicePath)] = std::move(disk_ptr);
@@ -176,7 +180,8 @@
     bool is_parent,
     bool has_media,
     bool on_boot_device,
-    bool on_removable_device) {
+    bool on_removable_device,
+    const std::string& file_system_type) {
   std::unique_ptr<DiskMountManager::Disk> disk_ptr =
       base::MakeUnique<DiskMountManager::Disk>(
           mount_info.source_path, mount_info.mount_path,
@@ -193,7 +198,8 @@
           device_type, total_size_in_bytes, is_parent,
           false,  // is_read_only
           has_media, on_boot_device, on_removable_device,
-          false);  // is_hidden
+          false,  // is_hidden
+          file_system_type, std::string());
   disks_[std::string(mount_info.source_path)] = std::move(disk_ptr);
 }
 
diff --git a/chromeos/disks/mock_disk_mount_manager.h b/chromeos/disks/mock_disk_mount_manager.h
index 52e73d6..598d920e 100644
--- a/chromeos/disks/mock_disk_mount_manager.h
+++ b/chromeos/disks/mock_disk_mount_manager.h
@@ -48,6 +48,8 @@
                                  const DiskMountManager::UnmountPathCallback&));
   MOCK_METHOD1(RemountAllRemovableDrives, void(MountAccessMode));
   MOCK_METHOD1(FormatMountedDevice, void(const std::string&));
+  MOCK_METHOD2(RenameMountedDevice,
+               void(const std::string&, const std::string&));
   MOCK_METHOD2(
       UnmountDeviceRecursively,
       void(const std::string&,
@@ -75,7 +77,8 @@
       bool is_parent,
       bool has_media,
       bool on_boot_device,
-      bool on_removable_device);
+      bool on_removable_device,
+      const std::string& file_system_type);
 
   // Removes the fake disk entry associated with the mounted device. This
   // function is primarily for StorageMonitorTest.
diff --git a/chromeos/disks/suspend_unmount_manager_unittest.cc b/chromeos/disks/suspend_unmount_manager_unittest.cc
index 5beb153..c41e88c 100644
--- a/chromeos/disks/suspend_unmount_manager_unittest.cc
+++ b/chromeos/disks/suspend_unmount_manager_unittest.cc
@@ -20,6 +20,7 @@
 const char kDeviceLabel[] = "device_label";
 const char kVendor[] = "vendor";
 const char kProduct[] = "product";
+const char kFileSystemType[] = "exfat";
 
 class FakeDiskMountManager : public MockDiskMountManager {
  public:
@@ -66,21 +67,24 @@
           chromeos::disks::MOUNT_CONDITION_NONE),
       kDeviceId, kDeviceLabel, kVendor, kProduct, chromeos::DEVICE_TYPE_USB,
       1024 * 1024, false /* is_parent */, false /* has_media */,
-      false /* on_boot_device */, true /* on_removable_device */);
+      false /* on_boot_device */, true /* on_removable_device */,
+      kFileSystemType);
   disk_mount_manager_.CreateDiskEntryForMountDevice(
       chromeos::disks::DiskMountManager::MountPointInfo(
           "/dummy/device/sd", kDummyMountPathSd, chromeos::MOUNT_TYPE_DEVICE,
           chromeos::disks::MOUNT_CONDITION_NONE),
       kDeviceId, kDeviceLabel, kVendor, kProduct, chromeos::DEVICE_TYPE_SD,
       1024 * 1024, false /* is_parent */, false /* has_media */,
-      false /* on_boot_device */, true /* on_removable_device */);
+      false /* on_boot_device */, true /* on_removable_device */,
+      kFileSystemType);
   disk_mount_manager_.CreateDiskEntryForMountDevice(
       chromeos::disks::DiskMountManager::MountPointInfo(
           "/dummy/device/unknown", kDummyMountPathUnknown,
           chromeos::MOUNT_TYPE_DEVICE, chromeos::disks::MOUNT_CONDITION_NONE),
       kDeviceId, kDeviceLabel, kVendor, kProduct, chromeos::DEVICE_TYPE_UNKNOWN,
       1024 * 1024, false /* is_parent */, false /* has_media */,
-      false /* on_boot_device */, true /* on_removable_device */);
+      false /* on_boot_device */, true /* on_removable_device */,
+      kFileSystemType);
   disk_mount_manager_.SetupDefaultReplies();
   fake_power_client_.SendSuspendImminent();
 
@@ -107,7 +111,8 @@
           chromeos::disks::MOUNT_CONDITION_NONE),
       kDeviceId, kDeviceLabel, kVendor, kProduct, chromeos::DEVICE_TYPE_USB,
       1024 * 1024, false /* is_parent */, false /* has_media */,
-      false /* on_boot_device */, true /* on_removable_device */);
+      false /* on_boot_device */, true /* on_removable_device */,
+      kFileSystemType);
   disk_mount_manager_.SetupDefaultReplies();
   fake_power_client_.SendSuspendImminent();
   EXPECT_EQ(1, fake_power_client_.GetNumPendingSuspendReadinessCallbacks());
diff --git a/components/arc/bitmap/bitmap_struct_traits.h b/components/arc/bitmap/bitmap_struct_traits.h
index e8aacf3..141251d 100644
--- a/components/arc/bitmap/bitmap_struct_traits.h
+++ b/components/arc/bitmap/bitmap_struct_traits.h
@@ -5,6 +5,7 @@
 #ifndef COMPONENTS_ARC_BITMAP_BITMAP_STRUCT_TRAITS_H_
 #define COMPONENTS_ARC_BITMAP_BITMAP_STRUCT_TRAITS_H_
 
+#include "base/containers/span.h"
 #include "components/arc/common/bitmap.mojom.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 
@@ -12,12 +13,11 @@
 
 template <>
 struct StructTraits<arc::mojom::ArcBitmapDataView, SkBitmap> {
-  static const mojo::CArray<uint8_t> pixel_data(const SkBitmap& r) {
+  static const base::span<const uint8_t> pixel_data(const SkBitmap& r) {
     const SkImageInfo& info = r.info();
     DCHECK_EQ(info.colorType(), kRGBA_8888_SkColorType);
 
-    return mojo::CArray<uint8_t>(static_cast<uint8_t*>(r.getPixels()),
-                                 r.getSize());
+    return base::make_span(static_cast<uint8_t*>(r.getPixels()), r.getSize());
   }
   static uint32_t width(const SkBitmap& r) { return r.width(); }
   static uint32_t height(const SkBitmap& r) { return r.height(); }
diff --git a/components/arc/intent_helper/intent_filter_struct_traits.h b/components/arc/intent_helper/intent_filter_struct_traits.h
index d6cb0cf8..a76ba40 100644
--- a/components/arc/intent_helper/intent_filter_struct_traits.h
+++ b/components/arc/intent_helper/intent_filter_struct_traits.h
@@ -8,6 +8,7 @@
 #include <string>
 #include <vector>
 
+#include "base/containers/span.h"
 #include "components/arc/common/intent_helper.mojom.h"
 #include "components/arc/intent_helper/intent_filter.h"
 
@@ -15,19 +16,18 @@
 
 template <>
 struct StructTraits<arc::mojom::IntentFilterDataView, arc::IntentFilter> {
-  static const mojo::CArray<std::string> actions(const arc::IntentFilter& r) {
+  static const base::span<std::string> actions(const arc::IntentFilter& r) {
     // Returns an empty array.
-    return mojo::CArray<std::string>();
+    return base::span<std::string>();
   }
-  static const mojo::CArray<std::string> categories(
+  static const base::span<std::string> categories(const arc::IntentFilter& r) {
+    // Returns an empty array.
+    return base::span<std::string>();
+  }
+  static const base::span<std::string> data_schemes(
       const arc::IntentFilter& r) {
     // Returns an empty array.
-    return mojo::CArray<std::string>();
-  }
-  static const mojo::CArray<std::string> data_schemes(
-      const arc::IntentFilter& r) {
-    // Returns an empty array.
-    return mojo::CArray<std::string>();
+    return base::span<std::string>();
   }
   static const std::vector<arc::IntentFilter::AuthorityEntry>& data_authorities(
       const arc::IntentFilter& r) {
@@ -37,10 +37,10 @@
       const arc::IntentFilter& r) {
     return r.paths();
   }
-  static const mojo::CArray<arc::IntentFilter::PatternMatcher>
+  static const base::span<arc::IntentFilter::PatternMatcher>
   deprecated_data_scheme_specific_parts(const arc::IntentFilter& r) {
     // Returns an empty array.
-    return mojo::CArray<arc::IntentFilter::PatternMatcher>();
+    return base::span<arc::IntentFilter::PatternMatcher>();
   }
 
   static bool Read(arc::mojom::IntentFilterDataView data,
diff --git a/components/arc/volume_mounter/arc_volume_mounter_bridge.cc b/components/arc/volume_mounter/arc_volume_mounter_bridge.cc
index b6553738..ce6107a 100644
--- a/components/arc/volume_mounter/arc_volume_mounter_bridge.cc
+++ b/components/arc/volume_mounter/arc_volume_mounter_bridge.cc
@@ -93,6 +93,13 @@
   // Ignored. ARC doesn't care about events other than Disk and Mount events.
 }
 
+void ArcVolumeMounterBridge::OnRenameEvent(
+    chromeos::disks::DiskMountManager::RenameEvent event,
+    chromeos::RenameError error_code,
+    const std::string& device_path) {
+  // Ignored. ARC doesn't care about events other than Disk and Mount events.
+}
+
 void ArcVolumeMounterBridge::OnMountEvent(
     DiskMountManager::MountEvent event,
     chromeos::MountError error_code,
diff --git a/components/arc/volume_mounter/arc_volume_mounter_bridge.h b/components/arc/volume_mounter/arc_volume_mounter_bridge.h
index ae57e7a..2e54fe6 100644
--- a/components/arc/volume_mounter/arc_volume_mounter_bridge.h
+++ b/components/arc/volume_mounter/arc_volume_mounter_bridge.h
@@ -54,6 +54,9 @@
   void OnFormatEvent(chromeos::disks::DiskMountManager::FormatEvent event,
                      chromeos::FormatError error_code,
                      const std::string& device_path) override;
+  void OnRenameEvent(chromeos::disks::DiskMountManager::RenameEvent event,
+                     chromeos::RenameError error_code,
+                     const std::string& device_path) override;
 
  private:
   ArcBridgeService* const arc_bridge_service_;  // Owned by ArcServiceManager.
diff --git a/components/autofill/core/browser/autofill_profile.h b/components/autofill/core/browser/autofill_profile.h
index b3b066e..c3d9225 100644
--- a/components/autofill/core/browser/autofill_profile.h
+++ b/components/autofill/core/browser/autofill_profile.h
@@ -44,12 +44,15 @@
     // The field has not been validated.
     UNVALIDATED,
 
-    // The field is invalid.
-    INVALID,
+    // The field is empty.
+    EMPTY,
 
     // The field is valid.
     VALID,
 
+    // The field is invalid.
+    INVALID,
+
     // The validation for the field is unsupported.
     UNSUPPORTED,
   };
diff --git a/components/autofill/core/browser/autofill_profile_unittest.cc b/components/autofill/core/browser/autofill_profile_unittest.cc
index 5bcdea965..2cbcf82 100644
--- a/components/autofill/core/browser/autofill_profile_unittest.cc
+++ b/components/autofill/core/browser/autofill_profile_unittest.cc
@@ -1100,10 +1100,13 @@
   // Make sure setting the validity state works.
   profile.SetValidityState(ADDRESS_HOME_COUNTRY, AutofillProfile::VALID);
   profile.SetValidityState(ADDRESS_HOME_CITY, AutofillProfile::INVALID);
+  profile.SetValidityState(ADDRESS_HOME_STATE, AutofillProfile::EMPTY);
   EXPECT_EQ(AutofillProfile::VALID,
             profile.GetValidityState(ADDRESS_HOME_COUNTRY));
   EXPECT_EQ(AutofillProfile::INVALID,
             profile.GetValidityState(ADDRESS_HOME_CITY));
+  EXPECT_EQ(AutofillProfile::EMPTY,
+            profile.GetValidityState(ADDRESS_HOME_STATE));
 }
 
 TEST(AutofillProfileTest, ValidityStates_UnsupportedTypes) {
diff --git a/components/autofill_strings.grdp b/components/autofill_strings.grdp
index 2767c577..919fb970 100644
--- a/components/autofill_strings.grdp
+++ b/components/autofill_strings.grdp
@@ -207,7 +207,7 @@
       Do you want Chromium to save this card?
     </message>
   </if>
-  <if expr="is_linux and not is_chromeos">
+  <if expr="is_linux and not chromeos">
     <then>
       <message name="IDS_AUTOFILL_SAVE_CARD_PROMPT_TITLE_TO_CLOUD" desc="Title text for the Autofill save card prompt when the card is to be saved by uploading it to Google Payments and also saved locally. The prompt can be either a bubble or an infobar.">
         Do you want to save this card to your Google Account?
@@ -225,7 +225,7 @@
   <message name="IDS_AUTOFILL_SAVE_CARD_PROMPT_UPLOAD_EXPLANATION" desc="Explanation of the effect of the Autofill save card prompt when the card is to be saved by uploading it to Google Payments and also saved locally. The prompt can be either a bubble or an infobar.">
     Pay quickly on sites and apps across devices using cards you have saved with Google.
   </message>
-  <if expr="is_linux and not is_chromeos">
+  <if expr="is_linux and not chromeos">
     <then>
       <message name="IDS_AUTOFILL_SAVE_CARD_PROMPT_UPLOAD_EXPLANATION_V2" desc="Explanation of the effect of the Autofill save card prompt when the card is to be saved by uploading it to Google Payments, according to June 2017 UI guidelines. The prompt will be shown in a bubble below the omnibox.">
         To pay faster next time, save this card to your Google Account.
diff --git a/components/chrome_cleaner/public/typemaps/DEPS b/components/chrome_cleaner/public/typemaps/DEPS
index e9aaaf9..ecb7fab 100644
--- a/components/chrome_cleaner/public/typemaps/DEPS
+++ b/components/chrome_cleaner/public/typemaps/DEPS
@@ -1,4 +1,5 @@
 # Allow the typemaps to access their dependencies.
 include_rules = [
+  '+base/containers/span.h',
   '+base/files/file_path.h',
 ]
diff --git a/components/chrome_cleaner/public/typemaps/chrome_prompt_struct_traits.cc b/components/chrome_cleaner/public/typemaps/chrome_prompt_struct_traits.cc
index a24fe408..bf6c963 100644
--- a/components/chrome_cleaner/public/typemaps/chrome_prompt_struct_traits.cc
+++ b/components/chrome_cleaner/public/typemaps/chrome_prompt_struct_traits.cc
@@ -7,16 +7,16 @@
 namespace mojo {
 
 // static
-ConstCArray<uint16_t>
+base::span<const uint16_t>
 StructTraits<chrome_cleaner::mojom::FilePathDataView, base::FilePath>::value(
     const base::FilePath& file_path) {
 #if defined(OS_WIN)
-  return ConstCArray<uint16_t>(
+  return base::make_span(
       reinterpret_cast<const uint16_t*>(file_path.value().data()),
       file_path.value().size());
 #else
   NOTREACHED();
-  return ConstCArray<uint16_t>();
+  return base::span<const uint16_t>();
 #endif
 }
 
diff --git a/components/chrome_cleaner/public/typemaps/chrome_prompt_struct_traits.h b/components/chrome_cleaner/public/typemaps/chrome_prompt_struct_traits.h
index 76d123f..06c1f58 100644
--- a/components/chrome_cleaner/public/typemaps/chrome_prompt_struct_traits.h
+++ b/components/chrome_cleaner/public/typemaps/chrome_prompt_struct_traits.h
@@ -5,6 +5,7 @@
 #ifndef COMPONENTS_CHROME_CLEANER_PUBLIC_TYPEMAPS_CHROME_CLEANER_STRUCT_TRAITS_H_
 #define COMPONENTS_CHROME_CLEANER_PUBLIC_TYPEMAPS_CHROME_CLEANER_STRUCT_TRAITS_H_
 
+#include "base/containers/span.h"
 #include "base/files/file_path.h"
 #include "components/chrome_cleaner/public/interfaces/chrome_prompt.mojom.h"
 
@@ -12,7 +13,7 @@
 
 template <>
 struct StructTraits<chrome_cleaner::mojom::FilePathDataView, base::FilePath> {
-  static ConstCArray<uint16_t> value(const base::FilePath& file_path);
+  static base::span<const uint16_t> value(const base::FilePath& file_path);
   static bool Read(chrome_cleaner::mojom::FilePathDataView path_view,
                    base::FilePath* out);
 };
diff --git a/components/crash/content/app/BUILD.gn b/components/crash/content/app/BUILD.gn
index 0a9e9bd9..0c229701 100644
--- a/components/crash/content/app/BUILD.gn
+++ b/components/crash/content/app/BUILD.gn
@@ -78,7 +78,6 @@
 
     deps = [
       "//base",
-      "//chrome/install_static:install_static_util",
       "//components/browser_watcher:crash_stability",
       "//third_party/crashpad/crashpad/client",
       "//third_party/crashpad/crashpad/handler:handler_lib",
diff --git a/components/crash/content/app/fallback_crash_handling_win.cc b/components/crash/content/app/fallback_crash_handling_win.cc
index e83be627..b67db03 100644
--- a/components/crash/content/app/fallback_crash_handling_win.cc
+++ b/components/crash/content/app/fallback_crash_handling_win.cc
@@ -24,7 +24,8 @@
 
 namespace {
 
-std::unique_ptr<FallbackCrashHandlerLauncher> g_fallback_crash_handler_launcher;
+// Intentionally leaked on program exit.
+FallbackCrashHandlerLauncher* g_fallback_crash_handler_launcher = nullptr;
 
 LONG WINAPI FallbackUnhandledExceptionFilter(EXCEPTION_POINTERS* exc_ptrs) {
   if (!g_fallback_crash_handler_launcher)
@@ -69,9 +70,8 @@
 
   // This is necessary because chrome_elf stubs out the
   // SetUnhandledExceptionFilter in the IAT of chrome.exe.
-  typedef PTOP_LEVEL_EXCEPTION_FILTER(WINAPI *
-                                      SetUnhandledExceptionFilterFunction)(
-      PTOP_LEVEL_EXCEPTION_FILTER filter);
+  using SetUnhandledExceptionFilterFunction =
+      PTOP_LEVEL_EXCEPTION_FILTER(WINAPI*)(PTOP_LEVEL_EXCEPTION_FILTER filter);
   HMODULE kernel32 = GetModuleHandle(L"kernel32.dll");
   if (!kernel32)
     return false;
@@ -83,7 +83,7 @@
     return false;
 
   // Success, pass ownership to the global.
-  g_fallback_crash_handler_launcher = std::move(fallback_launcher);
+  g_fallback_crash_handler_launcher = fallback_launcher.release();
 
   set_unhandled_exception_filter(&FallbackUnhandledExceptionFilter);
 
diff --git a/components/cronet/android/BUILD.gn b/components/cronet/android/BUILD.gn
index 7e99bed..be22e2a9 100644
--- a/components/cronet/android/BUILD.gn
+++ b/components/cronet/android/BUILD.gn
@@ -197,6 +197,8 @@
     "//components/cronet/android/metrics_util.h",
     "//components/cronet/android/url_request_error.cc",
     "//components/cronet/android/url_request_error.h",
+    "//components/cronet/cronet_prefs_manager.cc",
+    "//components/cronet/cronet_prefs_manager.h",
     "//components/cronet/histogram_manager.cc",
     "//components/cronet/histogram_manager.h",
     "//components/cronet/host_cache_persistence_manager.cc",
diff --git a/components/cronet/android/cronet_url_request_context_adapter.cc b/components/cronet/android/cronet_url_request_context_adapter.cc
index d17cce2a..285e4fc 100644
--- a/components/cronet/android/cronet_url_request_context_adapter.cc
+++ b/components/cronet/android/cronet_url_request_context_adapter.cc
@@ -37,15 +37,10 @@
 #include "components/cronet/android/cert/cert_verifier_cache_serializer.h"
 #include "components/cronet/android/cert/proto/cert_verification.pb.h"
 #include "components/cronet/android/cronet_library_loader.h"
+#include "components/cronet/cronet_prefs_manager.h"
 #include "components/cronet/histogram_manager.h"
 #include "components/cronet/host_cache_persistence_manager.h"
 #include "components/cronet/url_request_context_config.h"
-#include "components/prefs/pref_change_registrar.h"
-#include "components/prefs/pref_filter.h"
-#include "components/prefs/pref_registry.h"
-#include "components/prefs/pref_registry_simple.h"
-#include "components/prefs/pref_service.h"
-#include "components/prefs/pref_service_factory.h"
 #include "jni/CronetUrlRequestContext_jni.h"
 #include "net/base/load_flags.h"
 #include "net/base/logging_network_change_observer.h"
@@ -56,11 +51,9 @@
 #include "net/cert/cert_verifier.h"
 #include "net/cookies/cookie_monster.h"
 #include "net/http/http_auth_handler_factory.h"
-#include "net/http/http_server_properties_manager.h"
 #include "net/log/file_net_log_observer.h"
 #include "net/log/net_log_util.h"
 #include "net/nqe/external_estimate_provider.h"
-#include "net/nqe/network_qualities_prefs_manager.h"
 #include "net/nqe/network_quality_estimator_params.h"
 #include "net/proxy/proxy_config_service_android.h"
 #include "net/proxy/proxy_service.h"
@@ -113,224 +106,6 @@
 static base::LazyInstance<NetLogWithNetworkChangeEvents>::Leaky g_net_log =
     LAZY_INSTANCE_INITIALIZER;
 
-// Name of the pref used for host cache persistence.
-const char kHostCachePref[] = "net.host_cache";
-// Name of the pref used for HTTP server properties persistence.
-const char kHttpServerPropertiesPref[] = "net.http_server_properties";
-// Name of the pref used for NQE persistence.
-const char kNetworkQualitiesPref[] = "net.network_qualities";
-// Current version of disk storage.
-const int32_t kStorageVersion = 1;
-// Version number used when the version of disk storage is unknown.
-const uint32_t kStorageVersionUnknown = 0;
-// Name of preference directory.
-const char kPrefsDirectoryName[] = "prefs";
-// Name of preference file.
-const char kPrefsFileName[] = "local_prefs.json";
-
-// Connects the HttpServerPropertiesManager's storage to the prefs.
-class PrefServiceAdapter
-    : public net::HttpServerPropertiesManager::PrefDelegate {
- public:
-  explicit PrefServiceAdapter(PrefService* pref_service)
-      : pref_service_(pref_service), path_(kHttpServerPropertiesPref) {
-    pref_change_registrar_.Init(pref_service_);
-  }
-
-  ~PrefServiceAdapter() override {}
-
-  // PrefDelegate implementation.
-  bool HasServerProperties() override {
-    return pref_service_->HasPrefPath(path_);
-  }
-  const base::DictionaryValue& GetServerProperties() const override {
-    // Guaranteed not to return null when the pref is registered
-    // (RegisterProfilePrefs was called).
-    return *pref_service_->GetDictionary(path_);
-  }
-  void SetServerProperties(const base::DictionaryValue& value) override {
-    return pref_service_->Set(path_, value);
-  }
-  void StartListeningForUpdates(const base::Closure& callback) override {
-    pref_change_registrar_.Add(path_, callback);
-  }
-  void StopListeningForUpdates() override {
-    pref_change_registrar_.RemoveAll();
-  }
-
- private:
-  PrefService* pref_service_;
-  const std::string path_;
-  PrefChangeRegistrar pref_change_registrar_;
-
-  DISALLOW_COPY_AND_ASSIGN(PrefServiceAdapter);
-};
-
-class NetworkQualitiesPrefDelegateImpl
-    : public net::NetworkQualitiesPrefsManager::PrefDelegate {
- public:
-  // Caller must guarantee that |pref_service| outlives |this|.
-  explicit NetworkQualitiesPrefDelegateImpl(PrefService* pref_service)
-      : pref_service_(pref_service),
-        lossy_prefs_writing_task_posted_(false),
-        weak_ptr_factory_(this) {
-    DCHECK(pref_service_);
-  }
-
-  ~NetworkQualitiesPrefDelegateImpl() override {}
-
-  // net::NetworkQualitiesPrefsManager::PrefDelegate implementation.
-  void SetDictionaryValue(const base::DictionaryValue& value) override {
-    DCHECK(thread_checker_.CalledOnValidThread());
-
-    pref_service_->Set(kNetworkQualitiesPref, value);
-    if (lossy_prefs_writing_task_posted_)
-      return;
-
-    // Post the task that schedules the writing of the lossy prefs.
-    lossy_prefs_writing_task_posted_ = true;
-
-    // Delay after which the task that schedules the writing of the lossy prefs.
-    // This is needed in case the writing of the lossy prefs is not scheduled
-    // automatically. The delay was chosen so that it is large enough that it
-    // does not affect the startup performance.
-    static const int32_t kUpdatePrefsDelaySeconds = 10;
-
-    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-        FROM_HERE,
-        base::Bind(
-            &NetworkQualitiesPrefDelegateImpl::SchedulePendingLossyWrites,
-            weak_ptr_factory_.GetWeakPtr()),
-        base::TimeDelta::FromSeconds(kUpdatePrefsDelaySeconds));
-  }
-  std::unique_ptr<base::DictionaryValue> GetDictionaryValue() override {
-    DCHECK(thread_checker_.CalledOnValidThread());
-    UMA_HISTOGRAM_EXACT_LINEAR("NQE.Prefs.ReadCount", 1, 2);
-    return pref_service_->GetDictionary(kNetworkQualitiesPref)
-        ->CreateDeepCopy();
-  }
-
- private:
-  // Schedules the writing of the lossy prefs.
-  void SchedulePendingLossyWrites() {
-    DCHECK(thread_checker_.CalledOnValidThread());
-    UMA_HISTOGRAM_EXACT_LINEAR("NQE.Prefs.WriteCount", 1, 2);
-    pref_service_->SchedulePendingLossyWrites();
-    lossy_prefs_writing_task_posted_ = false;
-  }
-
-  PrefService* pref_service_;
-
-  // True if the task that schedules the writing of the lossy prefs has been
-  // posted.
-  bool lossy_prefs_writing_task_posted_;
-
-  base::ThreadChecker thread_checker_;
-
-  base::WeakPtrFactory<NetworkQualitiesPrefDelegateImpl> weak_ptr_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(NetworkQualitiesPrefDelegateImpl);
-};
-
-// Connects the SdchOwner's storage to the prefs.
-class SdchOwnerPrefStorage : public net::SdchOwner::PrefStorage,
-                             public PrefStore::Observer {
- public:
-  explicit SdchOwnerPrefStorage(PersistentPrefStore* storage)
-      : storage_(storage), storage_key_("SDCH"), init_observer_(nullptr) {}
-  ~SdchOwnerPrefStorage() override {
-    if (init_observer_)
-      storage_->RemoveObserver(this);
-  }
-
-  ReadError GetReadError() const override {
-    PersistentPrefStore::PrefReadError error = storage_->GetReadError();
-
-    DCHECK_NE(
-        error,
-        PersistentPrefStore::PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE);
-    DCHECK_NE(error, PersistentPrefStore::PREF_READ_ERROR_MAX_ENUM);
-
-    switch (error) {
-      case PersistentPrefStore::PREF_READ_ERROR_NONE:
-        return PERSISTENCE_FAILURE_NONE;
-
-      case PersistentPrefStore::PREF_READ_ERROR_NO_FILE:
-        return PERSISTENCE_FAILURE_REASON_NO_FILE;
-
-      case PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE:
-      case PersistentPrefStore::PREF_READ_ERROR_JSON_TYPE:
-      case PersistentPrefStore::PREF_READ_ERROR_FILE_OTHER:
-      case PersistentPrefStore::PREF_READ_ERROR_FILE_LOCKED:
-      case PersistentPrefStore::PREF_READ_ERROR_JSON_REPEAT:
-        return PERSISTENCE_FAILURE_REASON_READ_FAILED;
-
-      case PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED:
-      case PersistentPrefStore::PREF_READ_ERROR_FILE_NOT_SPECIFIED:
-      case PersistentPrefStore::PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE:
-      case PersistentPrefStore::PREF_READ_ERROR_MAX_ENUM:
-      default:
-        // We don't expect these other failures given our usage of prefs.
-        NOTREACHED();
-        return PERSISTENCE_FAILURE_REASON_OTHER;
-    }
-  }
-
-  bool GetValue(const base::DictionaryValue** result) const override {
-    const base::Value* result_value = nullptr;
-    if (!storage_->GetValue(storage_key_, &result_value))
-      return false;
-    return result_value->GetAsDictionary(result);
-  }
-
-  bool GetMutableValue(base::DictionaryValue** result) override {
-    base::Value* result_value = nullptr;
-    if (!storage_->GetMutableValue(storage_key_, &result_value))
-      return false;
-    return result_value->GetAsDictionary(result);
-  }
-
-  void SetValue(std::unique_ptr<base::DictionaryValue> value) override {
-    storage_->SetValue(storage_key_, std::move(value),
-                       WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
-  }
-
-  void ReportValueChanged() override {
-    storage_->ReportValueChanged(storage_key_,
-                                 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
-  }
-
-  bool IsInitializationComplete() override {
-    return storage_->IsInitializationComplete();
-  }
-
-  void StartObservingInit(net::SdchOwner* observer) override {
-    DCHECK(!init_observer_);
-    init_observer_ = observer;
-    storage_->AddObserver(this);
-  }
-
-  void StopObservingInit() override {
-    DCHECK(init_observer_);
-    init_observer_ = nullptr;
-    storage_->RemoveObserver(this);
-  }
-
- private:
-  // PrefStore::Observer implementation.
-  void OnPrefValueChanged(const std::string& key) override {}
-  void OnInitializationCompleted(bool succeeded) override {
-    init_observer_->OnPrefStorageInitializationComplete(succeeded);
-  }
-
-  PersistentPrefStore* storage_;  // Non-owning.
-  const std::string storage_key_;
-
-  net::SdchOwner* init_observer_;  // Non-owning.
-
-  DISALLOW_COPY_AND_ASSIGN(SdchOwnerPrefStorage);
-};
-
 class BasicNetworkDelegate : public net::NetworkDelegateImpl {
  public:
   BasicNetworkDelegate() {}
@@ -416,59 +191,6 @@
   return str;
 }
 
-bool IsCurrentVersion(const base::FilePath& version_filepath) {
-  if (!base::PathExists(version_filepath))
-    return false;
-  base::File version_file(version_filepath,
-                          base::File::FLAG_OPEN | base::File::FLAG_READ);
-  uint32_t version = kStorageVersionUnknown;
-  int bytes_read =
-      version_file.Read(0, reinterpret_cast<char*>(&version), sizeof(version));
-  if (bytes_read != sizeof(version)) {
-    DLOG(WARNING) << "Cannot read from version file.";
-    return false;
-  }
-  return version == kStorageVersion;
-}
-
-// TODO(xunjieli): Handle failures.
-void InitializeStorageDirectory(const base::FilePath& dir) {
-  // Checks version file and clear old storage.
-  base::FilePath version_filepath = dir.Append("version");
-  if (IsCurrentVersion(version_filepath)) {
-    // The version is up to date, so there is nothing to do.
-    return;
-  }
-  // Delete old directory recursively and create a new directory.
-  // base::DeleteFile returns true if the directory does not exist, so it is
-  // fine if there is nothing on disk.
-  if (!(base::DeleteFile(dir, true) && base::CreateDirectory(dir))) {
-    DLOG(WARNING) << "Cannot purge directory.";
-    return;
-  }
-  base::File new_version_file(version_filepath, base::File::FLAG_CREATE_ALWAYS |
-                                                    base::File::FLAG_WRITE);
-
-  if (!new_version_file.IsValid()) {
-    DLOG(WARNING) << "Cannot create a version file.";
-    return;
-  }
-
-  DCHECK(new_version_file.created());
-  uint32_t new_version = kStorageVersion;
-  int bytes_written = new_version_file.Write(
-      0, reinterpret_cast<char*>(&new_version), sizeof(new_version));
-  if (bytes_written != sizeof(new_version)) {
-    DLOG(WARNING) << "Cannot write to version file.";
-    return;
-  }
-  base::FilePath prefs_dir = dir.Append(FILE_PATH_LITERAL(kPrefsDirectoryName));
-  if (!base::CreateDirectory(prefs_dir)) {
-    DLOG(WARNING) << "Cannot create prefs directory";
-    return;
-  }
-}
-
 }  // namespace
 
 namespace cronet {
@@ -476,7 +198,6 @@
 CronetURLRequestContextAdapter::CronetURLRequestContextAdapter(
     std::unique_ptr<URLRequestContextConfig> context_config)
     : network_thread_(new base::Thread("network")),
-      http_server_properties_manager_(nullptr),
       context_config_(std::move(context_config)),
       is_context_initialized_(false),
       default_load_flags_(net::LOAD_NORMAL) {
@@ -488,12 +209,9 @@
 CronetURLRequestContextAdapter::~CronetURLRequestContextAdapter() {
   DCHECK(GetNetworkTaskRunner()->BelongsToCurrentThread());
 
-  if (http_server_properties_manager_)
-    http_server_properties_manager_->ShutdownOnPrefSequence();
-  if (network_qualities_prefs_manager_)
-    network_qualities_prefs_manager_->ShutdownOnPrefSequence();
-  if (pref_service_)
-    pref_service_->CommitPendingWrite();
+  if (cronet_prefs_manager_)
+    cronet_prefs_manager_->PrepareForShutdown();
+
   if (network_quality_estimator_) {
     network_quality_estimator_->RemoveRTTObserver(this);
     network_quality_estimator_->RemoveThroughputObserver(this);
@@ -605,9 +323,8 @@
   // Initializing |network_qualities_prefs_manager_| may post a callback to
   // |this|. So, |network_qualities_prefs_manager_| should be initialized after
   // |jcronet_url_request_context_| has been constructed.
-  DCHECK(jcronet_url_request_context_.obj() != nullptr);
-  network_qualities_prefs_manager_->InitializeOnNetworkThread(
-      network_quality_estimator_.get());
+  DCHECK(jcronet_url_request_context_.obj());
+  cronet_prefs_manager_->SetupNqePersistence(network_quality_estimator_.get());
 }
 
 void CronetURLRequestContextAdapter::InitializeOnNetworkThread(
@@ -639,58 +356,6 @@
   effective_experimental_options_ =
       std::move(config->effective_experimental_options);
 
-  // Set up pref file if storage path is specified.
-  if (!config->storage_path.empty()) {
-    base::FilePath storage_path(config->storage_path);
-    // Make sure storage directory has correct version.
-    InitializeStorageDirectory(storage_path);
-    base::FilePath filepath =
-        storage_path.Append(FILE_PATH_LITERAL(kPrefsDirectoryName))
-            .Append(FILE_PATH_LITERAL(kPrefsFileName));
-    json_pref_store_ =
-        new JsonPrefStore(filepath, GetFileThread()->task_runner(),
-                          std::unique_ptr<PrefFilter>());
-
-    // Register prefs and set up the PrefService.
-    PrefServiceFactory factory;
-    factory.set_user_prefs(json_pref_store_);
-    scoped_refptr<PrefRegistrySimple> registry(new PrefRegistrySimple());
-    registry->RegisterDictionaryPref(kHttpServerPropertiesPref,
-                                     base::MakeUnique<base::DictionaryValue>());
-    if (config->enable_network_quality_estimator) {
-      // Use lossy prefs to limit the overhead of reading/writing the prefs.
-      registry->RegisterDictionaryPref(kNetworkQualitiesPref,
-                                       PrefRegistry::LOSSY_PREF);
-    }
-    if (config->enable_host_cache_persistence) {
-      registry->RegisterListPref(kHostCachePref);
-    }
-
-    {
-      SCOPED_UMA_HISTOGRAM_TIMER("Net.Cronet.PrefsInitTime");
-      pref_service_ = factory.Create(registry.get());
-    }
-
-    // Set up the HttpServerPropertiesManager.
-    std::unique_ptr<net::HttpServerPropertiesManager>
-        http_server_properties_manager(new net::HttpServerPropertiesManager(
-            new PrefServiceAdapter(pref_service_.get()),
-            base::ThreadTaskRunnerHandle::Get(), GetNetworkTaskRunner(),
-            g_net_log.Get().net_log()));
-    http_server_properties_manager->InitializeOnNetworkSequence();
-    http_server_properties_manager_ = http_server_properties_manager.get();
-    context_builder.SetHttpServerProperties(
-        std::move(http_server_properties_manager));
-  }
-
-  // Explicitly disable the persister for Cronet to avoid persistence of dynamic
-  // HPKP. This is a safety measure ensuring that nobody enables the persistence
-  // of HPKP by specifying transport_security_persister_path in the future.
-  context_builder.set_transport_security_persister_path(base::FilePath());
-
-  // Disable net::CookieStore and net::ChannelIDService.
-  context_builder.SetCookieAndChannelIdStores(nullptr, nullptr);
-
   if (config->enable_network_quality_estimator) {
     DCHECK(!network_quality_estimator_);
     std::unique_ptr<net::NetworkQualityEstimatorParams> nqe_params =
@@ -709,34 +374,41 @@
     network_quality_estimator_->AddEffectiveConnectionTypeObserver(this);
     network_quality_estimator_->AddRTTAndThroughputEstimatesObserver(this);
 
-    // Set up network quality prefs if the storage path is specified.
-    if (!config->storage_path.empty()) {
-      DCHECK(!network_qualities_prefs_manager_);
-      network_qualities_prefs_manager_ =
-          base::MakeUnique<net::NetworkQualitiesPrefsManager>(
-              base::MakeUnique<NetworkQualitiesPrefDelegateImpl>(
-                  pref_service_.get()));
-      PostTaskToNetworkThread(FROM_HERE,
-                              base::Bind(&CronetURLRequestContextAdapter::
-                                             InitializeNQEPrefsOnNetworkThread,
-                                         base::Unretained(this)));
-    }
     context_builder.set_network_quality_estimator(
         network_quality_estimator_.get());
   }
 
+  DCHECK(!cronet_prefs_manager_);
+
+  // Set up pref file if storage path is specified.
+  if (!config->storage_path.empty()) {
+    base::FilePath storage_path(config->storage_path);
+    // Set up the HttpServerPropertiesManager.
+    cronet_prefs_manager_ = std::make_unique<CronetPrefsManager>(
+        config->storage_path, GetNetworkTaskRunner(),
+        GetFileThread()->task_runner(),
+        config->enable_network_quality_estimator,
+        config->enable_host_cache_persistence, g_net_log.Get().net_log(),
+        &context_builder);
+  }
+
+  // Explicitly disable the persister for Cronet to avoid persistence of dynamic
+  // HPKP. This is a safety measure ensuring that nobody enables the persistence
+  // of HPKP by specifying transport_security_persister_path in the future.
+  context_builder.set_transport_security_persister_path(base::FilePath());
+
+  // Disable net::CookieStore and net::ChannelIDService.
+  context_builder.SetCookieAndChannelIdStores(nullptr, nullptr);
+
   context_ = context_builder.Build();
 
   // Set up host cache persistence if it's enabled. Happens after building the
   // URLRequestContext to get access to the HostCache.
-  if (pref_service_ && config->enable_host_cache_persistence) {
+  if (config->enable_host_cache_persistence && cronet_prefs_manager_) {
     net::HostCache* host_cache = context_->host_resolver()->GetHostCache();
-    host_cache_persistence_manager_ =
-        base::MakeUnique<HostCachePersistenceManager>(
-            host_cache, pref_service_.get(), kHostCachePref,
-            base::TimeDelta::FromMilliseconds(
-                config->host_cache_persistence_delay_ms),
-            g_net_log.Get().net_log());
+    cronet_prefs_manager_->SetupHostCachePersistence(
+        host_cache, config->host_cache_persistence_delay_ms,
+        g_net_log.Get().net_log());
   }
 
   context_->set_check_cleartext_permitted(true);
@@ -749,10 +421,8 @@
     DCHECK(context_->sdch_manager());
     sdch_owner_.reset(
         new net::SdchOwner(context_->sdch_manager(), context_.get()));
-    if (json_pref_store_) {
-      sdch_owner_->EnablePersistentStorage(
-          base::MakeUnique<SdchOwnerPrefStorage>(json_pref_store_.get()));
-    }
+    if (cronet_prefs_manager_)
+      cronet_prefs_manager_->SetupSdchPersistence(sdch_owner_.get());
   }
 
   if (config->enable_quic) {
@@ -826,6 +496,19 @@
                                                  jcronet_url_request_context);
 
   is_context_initialized_ = true;
+
+  // Set up network quality prefs.
+  if (config->enable_network_quality_estimator && cronet_prefs_manager_) {
+    // TODO(crbug.com/758401): execute the content of
+    // InitializeNQEPrefsOnNetworkThread method directly (i.e. without posting)
+    // after the bug has been fixed.
+    PostTaskToNetworkThread(
+        FROM_HERE,
+        base::Bind(
+            &CronetURLRequestContextAdapter::InitializeNQEPrefsOnNetworkThread,
+            base::Unretained(this)));
+  }
+
   while (!tasks_waiting_for_context_.empty()) {
     tasks_waiting_for_context_.front().Run();
     tasks_waiting_for_context_.pop();
diff --git a/components/cronet/android/cronet_url_request_context_adapter.h b/components/cronet/android/cronet_url_request_context_adapter.h
index fe0e156ea..23c95f9 100644
--- a/components/cronet/android/cronet_url_request_context_adapter.h
+++ b/components/cronet/android/cronet_url_request_context_adapter.h
@@ -32,9 +32,7 @@
 }  // namespace base
 
 namespace net {
-class HttpServerPropertiesManager;
 class NetLog;
-class NetworkQualitiesPrefsManager;
 class ProxyConfigService;
 class SdchOwner;
 class URLRequestContext;
@@ -42,7 +40,7 @@
 }  // namespace net
 
 namespace cronet {
-class HostCachePersistenceManager;
+class CronetPrefsManager;
 class TestUtil;
 
 struct URLRequestContextConfig;
@@ -208,12 +206,12 @@
   // signals that it is safe to access the NetLog files.
   void StopNetLogCompleted();
 
-  std::unique_ptr<base::DictionaryValue> GetNetLogInfo() const;
-
   // Initializes Network Quality Estimator (NQE) prefs manager on network
   // thread.
   void InitializeNQEPrefsOnNetworkThread() const;
 
+  std::unique_ptr<base::DictionaryValue> GetNetLogInfo() const;
+
   // Network thread is owned by |this|, but is destroyed from java thread.
   base::Thread* network_thread_;
 
@@ -222,19 +220,29 @@
 
   std::unique_ptr<net::FileNetLogObserver> net_log_file_observer_;
 
-  // |pref_service_| should outlive the HttpServerPropertiesManager owned by
-  // |context_| and the HostCachePersistenceManager.
-  std::unique_ptr<PrefService> pref_service_;
-  std::unique_ptr<net::URLRequestContext> context_;
-  std::unique_ptr<net::ProxyConfigService> proxy_config_service_;
-  scoped_refptr<JsonPrefStore> json_pref_store_;
-  net::HttpServerPropertiesManager* http_server_properties_manager_;
+  // A network quality estimator. This member variable has to be destroyed after
+  // destroying |cronet_prefs_manager_|, which owns NetworkQualityPrefsManager
+  // that weakly references |network_quality_estimator_|.
+  std::unique_ptr<net::NetworkQualityEstimator> network_quality_estimator_;
 
-  // |sdch_owner_| should be destroyed before |json_pref_store_|, because
+  // Manages the PrefService and all associated persistence managers
+  // such as NetworkQualityPrefsManager, HostCachePersistenceManager, etc.
+  // It should be destroyed before |network_quality_estimator_| but after
+  // |sdch_owner_|. It also owns a PrefService object should outlive |context_|.
+  std::unique_ptr<CronetPrefsManager> cronet_prefs_manager_;
+
+  std::unique_ptr<net::URLRequestContext> context_;
+
+  // |sdch_owner_| should be destroyed before |cronet_prefs_manager_|, because
   // tearing down |sdch_owner_| forces |json_pref_store_| to flush pending
-  // writes to the disk.
+  // writes to the disk. |json_pref_store_| is owned by |cronet_prefs_manager_|.
+  // |sdch_owner_| should also be destroyed before |context_|. This will
+  // unregister SdchManager observers before the context is destroyed.
+  // SdchManager should not be destroy until all observers are unregistered.
   std::unique_ptr<net::SdchOwner> sdch_owner_;
 
+  std::unique_ptr<net::ProxyConfigService> proxy_config_service_;
+
   // Context config is only valid until context is initialized.
   std::unique_ptr<URLRequestContextConfig> context_config_;
 
@@ -246,18 +254,6 @@
   bool is_context_initialized_;
   int default_load_flags_;
 
-  // A network quality estimator.
-  std::unique_ptr<net::NetworkQualityEstimator> network_quality_estimator_;
-
-  // Manages the writing and reading of the network quality prefs.
-  std::unique_ptr<net::NetworkQualitiesPrefsManager>
-      network_qualities_prefs_manager_;
-
-  // Manages reading and writing the HostCache pref when persistence is enabled.
-  // Must be destroyed before |context_| (because it owns the HostResolverImpl,
-  // which owns the HostCache) and |pref_service_|.
-  std::unique_ptr<HostCachePersistenceManager> host_cache_persistence_manager_;
-
   // Java object that owns this CronetURLRequestContextAdapter.
   base::android::ScopedJavaGlobalRef<jobject> jcronet_url_request_context_;
 
diff --git a/components/cronet/cronet_prefs_manager.cc b/components/cronet/cronet_prefs_manager.cc
new file mode 100644
index 0000000..66c78087
--- /dev/null
+++ b/components/cronet/cronet_prefs_manager.cc
@@ -0,0 +1,408 @@
+// 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 "components/cronet/cronet_prefs_manager.h"
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/time/time.h"
+#include "components/cronet/host_cache_persistence_manager.h"
+#include "components/prefs/json_pref_store.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
+#include "components/prefs/pref_service_factory.h"
+#include "net/http/http_server_properties_manager.h"
+#include "net/nqe/network_qualities_prefs_manager.h"
+#include "net/sdch/sdch_owner.h"
+#include "net/url_request/url_request_context_builder.h"
+
+namespace cronet {
+namespace {
+
+// Name of the pref used for HTTP server properties persistence.
+const char kHttpServerPropertiesPref[] = "net.http_server_properties";
+// Name of preference directory.
+const char kPrefsDirectoryName[] = "prefs";
+// Name of preference file.
+const char kPrefsFileName[] = "local_prefs.json";
+// Current version of disk storage.
+const int32_t kStorageVersion = 1;
+// Version number used when the version of disk storage is unknown.
+const uint32_t kStorageVersionUnknown = 0;
+// Name of the pref used for host cache persistence.
+const char kHostCachePref[] = "net.host_cache";
+// Name of the pref used for NQE persistence.
+const char kNetworkQualitiesPref[] = "net.network_qualities";
+
+bool IsCurrentVersion(const base::FilePath& version_filepath) {
+  if (!base::PathExists(version_filepath))
+    return false;
+  base::File version_file(version_filepath,
+                          base::File::FLAG_OPEN | base::File::FLAG_READ);
+  uint32_t version = kStorageVersionUnknown;
+  int bytes_read =
+      version_file.Read(0, reinterpret_cast<char*>(&version), sizeof(version));
+  if (bytes_read != sizeof(version)) {
+    DLOG(WARNING) << "Cannot read from version file.";
+    return false;
+  }
+  return version == kStorageVersion;
+}
+
+// TODO(xunjieli): Handle failures.
+void InitializeStorageDirectory(const base::FilePath& dir) {
+  // Checks version file and clear old storage.
+  base::FilePath version_filepath = dir.Append("version");
+  if (IsCurrentVersion(version_filepath)) {
+    // The version is up to date, so there is nothing to do.
+    return;
+  }
+  // Delete old directory recursively and create a new directory.
+  // base::DeleteFile returns true if the directory does not exist, so it is
+  // fine if there is nothing on disk.
+  if (!(base::DeleteFile(dir, true) && base::CreateDirectory(dir))) {
+    DLOG(WARNING) << "Cannot purge directory.";
+    return;
+  }
+  base::File new_version_file(version_filepath, base::File::FLAG_CREATE_ALWAYS |
+                                                    base::File::FLAG_WRITE);
+
+  if (!new_version_file.IsValid()) {
+    DLOG(WARNING) << "Cannot create a version file.";
+    return;
+  }
+
+  DCHECK(new_version_file.created());
+  uint32_t new_version = kStorageVersion;
+  int bytes_written = new_version_file.Write(
+      0, reinterpret_cast<char*>(&new_version), sizeof(new_version));
+  if (bytes_written != sizeof(new_version)) {
+    DLOG(WARNING) << "Cannot write to version file.";
+    return;
+  }
+  base::FilePath prefs_dir = dir.Append(FILE_PATH_LITERAL(kPrefsDirectoryName));
+  if (!base::CreateDirectory(prefs_dir)) {
+    DLOG(WARNING) << "Cannot create prefs directory";
+    return;
+  }
+}
+
+// Connects the HttpServerPropertiesManager's storage to the prefs.
+class PrefServiceAdapter
+    : public net::HttpServerPropertiesManager::PrefDelegate {
+ public:
+  explicit PrefServiceAdapter(PrefService* pref_service)
+      : pref_service_(pref_service), path_(kHttpServerPropertiesPref) {
+    pref_change_registrar_.Init(pref_service_);
+  }
+
+  ~PrefServiceAdapter() override {}
+
+  // PrefDelegate implementation.
+  bool HasServerProperties() override {
+    return pref_service_->HasPrefPath(path_);
+  }
+
+  const base::DictionaryValue& GetServerProperties() const override {
+    // Guaranteed not to return null when the pref is registered
+    // (RegisterProfilePrefs was called).
+    return *pref_service_->GetDictionary(path_);
+  }
+
+  void SetServerProperties(const base::DictionaryValue& value) override {
+    return pref_service_->Set(path_, value);
+  }
+
+  void StartListeningForUpdates(const base::Closure& callback) override {
+    pref_change_registrar_.Add(path_, callback);
+  }
+
+  void StopListeningForUpdates() override {
+    pref_change_registrar_.RemoveAll();
+  }
+
+ private:
+  PrefService* pref_service_;
+  const std::string path_;
+  PrefChangeRegistrar pref_change_registrar_;
+
+  DISALLOW_COPY_AND_ASSIGN(PrefServiceAdapter);
+};  // class PrefServiceAdapter
+
+class NetworkQualitiesPrefDelegateImpl
+    : public net::NetworkQualitiesPrefsManager::PrefDelegate {
+ public:
+  // Caller must guarantee that |pref_service| outlives |this|.
+  explicit NetworkQualitiesPrefDelegateImpl(PrefService* pref_service)
+      : pref_service_(pref_service),
+        lossy_prefs_writing_task_posted_(false),
+        weak_ptr_factory_(this) {
+    DCHECK(pref_service_);
+  }
+
+  ~NetworkQualitiesPrefDelegateImpl() override {}
+
+  // net::NetworkQualitiesPrefsManager::PrefDelegate implementation.
+  void SetDictionaryValue(const base::DictionaryValue& value) override {
+    DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+    pref_service_->Set(kNetworkQualitiesPref, value);
+    if (lossy_prefs_writing_task_posted_)
+      return;
+
+    // Post the task that schedules the writing of the lossy prefs.
+    lossy_prefs_writing_task_posted_ = true;
+
+    // Delay after which the task that schedules the writing of the lossy prefs.
+    // This is needed in case the writing of the lossy prefs is not scheduled
+    // automatically. The delay was chosen so that it is large enough that it
+    // does not affect the startup performance.
+    static const int32_t kUpdatePrefsDelaySeconds = 10;
+
+    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+        FROM_HERE,
+        base::Bind(
+            &NetworkQualitiesPrefDelegateImpl::SchedulePendingLossyWrites,
+            weak_ptr_factory_.GetWeakPtr()),
+        base::TimeDelta::FromSeconds(kUpdatePrefsDelaySeconds));
+  }
+  std::unique_ptr<base::DictionaryValue> GetDictionaryValue() override {
+    DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+    UMA_HISTOGRAM_EXACT_LINEAR("NQE.Prefs.ReadCount", 1, 2);
+    return pref_service_->GetDictionary(kNetworkQualitiesPref)
+        ->CreateDeepCopy();
+  }
+
+ private:
+  // Schedules the writing of the lossy prefs.
+  void SchedulePendingLossyWrites() {
+    DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+    UMA_HISTOGRAM_EXACT_LINEAR("NQE.Prefs.WriteCount", 1, 2);
+    pref_service_->SchedulePendingLossyWrites();
+    lossy_prefs_writing_task_posted_ = false;
+  }
+
+  PrefService* pref_service_;
+
+  // True if the task that schedules the writing of the lossy prefs has been
+  // posted.
+  bool lossy_prefs_writing_task_posted_;
+
+  THREAD_CHECKER(thread_checker_);
+
+  base::WeakPtrFactory<NetworkQualitiesPrefDelegateImpl> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(NetworkQualitiesPrefDelegateImpl);
+};
+
+// Connects the SdchOwner's storage to the prefs.
+class SdchOwnerPrefStorage : public net::SdchOwner::PrefStorage,
+                             public PrefStore::Observer {
+ public:
+  explicit SdchOwnerPrefStorage(PersistentPrefStore* storage)
+      : storage_(storage), storage_key_("SDCH"), init_observer_(nullptr) {}
+  ~SdchOwnerPrefStorage() override {
+    if (init_observer_)
+      storage_->RemoveObserver(this);
+  }
+
+  ReadError GetReadError() const override {
+    PersistentPrefStore::PrefReadError error = storage_->GetReadError();
+
+    DCHECK_NE(
+        error,
+        PersistentPrefStore::PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE);
+    DCHECK_NE(error, PersistentPrefStore::PREF_READ_ERROR_MAX_ENUM);
+
+    switch (error) {
+      case PersistentPrefStore::PREF_READ_ERROR_NONE:
+        return PERSISTENCE_FAILURE_NONE;
+
+      case PersistentPrefStore::PREF_READ_ERROR_NO_FILE:
+        return PERSISTENCE_FAILURE_REASON_NO_FILE;
+
+      case PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE:
+      case PersistentPrefStore::PREF_READ_ERROR_JSON_TYPE:
+      case PersistentPrefStore::PREF_READ_ERROR_FILE_OTHER:
+      case PersistentPrefStore::PREF_READ_ERROR_FILE_LOCKED:
+      case PersistentPrefStore::PREF_READ_ERROR_JSON_REPEAT:
+        return PERSISTENCE_FAILURE_REASON_READ_FAILED;
+
+      case PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED:
+      case PersistentPrefStore::PREF_READ_ERROR_FILE_NOT_SPECIFIED:
+      case PersistentPrefStore::PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE:
+      case PersistentPrefStore::PREF_READ_ERROR_MAX_ENUM:
+      default:
+        // We don't expect these other failures given our usage of prefs.
+        NOTREACHED();
+        return PERSISTENCE_FAILURE_REASON_OTHER;
+    }
+  }
+
+  bool GetValue(const base::DictionaryValue** result) const override {
+    const base::Value* result_value = nullptr;
+    if (!storage_->GetValue(storage_key_, &result_value))
+      return false;
+    return result_value->GetAsDictionary(result);
+  }
+
+  bool GetMutableValue(base::DictionaryValue** result) override {
+    base::Value* result_value = nullptr;
+    if (!storage_->GetMutableValue(storage_key_, &result_value))
+      return false;
+    return result_value->GetAsDictionary(result);
+  }
+
+  void SetValue(std::unique_ptr<base::DictionaryValue> value) override {
+    storage_->SetValue(storage_key_, std::move(value),
+                       WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+  }
+
+  void ReportValueChanged() override {
+    storage_->ReportValueChanged(storage_key_,
+                                 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+  }
+
+  bool IsInitializationComplete() override {
+    return storage_->IsInitializationComplete();
+  }
+
+  void StartObservingInit(net::SdchOwner* observer) override {
+    DCHECK(!init_observer_);
+    init_observer_ = observer;
+    storage_->AddObserver(this);
+  }
+
+  void StopObservingInit() override {
+    DCHECK(init_observer_);
+    init_observer_ = nullptr;
+    storage_->RemoveObserver(this);
+  }
+
+ private:
+  // PrefStore::Observer implementation.
+  void OnPrefValueChanged(const std::string& key) override {}
+  void OnInitializationCompleted(bool succeeded) override {
+    init_observer_->OnPrefStorageInitializationComplete(succeeded);
+  }
+
+  PersistentPrefStore* storage_;  // Non-owning.
+  const std::string storage_key_;
+
+  net::SdchOwner* init_observer_;  // Non-owning.
+
+  DISALLOW_COPY_AND_ASSIGN(SdchOwnerPrefStorage);
+};
+
+}  // namespace
+
+CronetPrefsManager::CronetPrefsManager(
+    const std::string& storage_path,
+    scoped_refptr<base::SingleThreadTaskRunner> network_task_runner,
+    scoped_refptr<base::SequencedTaskRunner> file_task_runner,
+    bool enable_network_quality_estimator,
+    bool enable_host_cache_persistence,
+    net::NetLog* net_log,
+    net::URLRequestContextBuilder* context_builder)
+    : http_server_properties_manager_(nullptr) {
+  DCHECK(network_task_runner->BelongsToCurrentThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  base::FilePath storage_file_path(storage_path);
+
+  // Make sure storage directory has correct version.
+  InitializeStorageDirectory(storage_file_path);
+  base::FilePath filepath =
+      storage_file_path.Append(FILE_PATH_LITERAL(kPrefsDirectoryName))
+          .Append(FILE_PATH_LITERAL(kPrefsFileName));
+
+  json_pref_store_ = new JsonPrefStore(filepath, file_task_runner,
+                                       std::unique_ptr<PrefFilter>());
+
+  // Register prefs and set up the PrefService.
+  PrefServiceFactory factory;
+  factory.set_user_prefs(json_pref_store_);
+  scoped_refptr<PrefRegistrySimple> registry(new PrefRegistrySimple());
+  registry->RegisterDictionaryPref(kHttpServerPropertiesPref,
+                                   base::MakeUnique<base::DictionaryValue>());
+
+  if (enable_network_quality_estimator) {
+    // Use lossy prefs to limit the overhead of reading/writing the prefs.
+    registry->RegisterDictionaryPref(kNetworkQualitiesPref,
+                                     PrefRegistry::LOSSY_PREF);
+  }
+
+  if (enable_host_cache_persistence) {
+    registry->RegisterListPref(kHostCachePref);
+  }
+
+  {
+    SCOPED_UMA_HISTOGRAM_TIMER("Net.Cronet.PrefsInitTime");
+    pref_service_ = factory.Create(registry.get());
+  }
+
+  http_server_properties_manager_ = new net::HttpServerPropertiesManager(
+      new PrefServiceAdapter(pref_service_.get()), network_task_runner,
+      network_task_runner, net_log);
+  http_server_properties_manager_->InitializeOnNetworkSequence();
+
+  // Passes |http_server_properties_manager_| ownership to |context_builder|.
+  // The ownership will be subsequently passed to UrlRequestContext.
+  context_builder->SetHttpServerProperties(
+      std::unique_ptr<net::HttpServerPropertiesManager>(
+          http_server_properties_manager_));
+}
+
+CronetPrefsManager::~CronetPrefsManager() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+}
+
+void CronetPrefsManager::SetupNqePersistence(
+    net::NetworkQualityEstimator* nqe) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  network_qualities_prefs_manager_ =
+      base::MakeUnique<net::NetworkQualitiesPrefsManager>(
+          base::MakeUnique<NetworkQualitiesPrefDelegateImpl>(
+              pref_service_.get()));
+
+  network_qualities_prefs_manager_->InitializeOnNetworkThread(nqe);
+}
+
+void CronetPrefsManager::SetupHostCachePersistence(
+    net::HostCache* host_cache,
+    int host_cache_persistence_delay_ms,
+    net::NetLog* net_log) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  host_cache_persistence_manager_ =
+      base::MakeUnique<HostCachePersistenceManager>(
+          host_cache, pref_service_.get(), kHostCachePref,
+          base::TimeDelta::FromMilliseconds(host_cache_persistence_delay_ms),
+          net_log);
+}
+
+void CronetPrefsManager::SetupSdchPersistence(net::SdchOwner* sdch_owner) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  sdch_owner->EnablePersistentStorage(
+      base::MakeUnique<SdchOwnerPrefStorage>(json_pref_store_.get()));
+}
+
+void CronetPrefsManager::PrepareForShutdown() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  if (pref_service_)
+    pref_service_->CommitPendingWrite();
+
+  // Shutdown managers on the Pref sequence.
+  http_server_properties_manager_->ShutdownOnPrefSequence();
+  if (network_qualities_prefs_manager_)
+    network_qualities_prefs_manager_->ShutdownOnPrefSequence();
+
+  // TODO(crbug.com/758711): revisit to see whether the logic can be simplified
+  // after SDCH is removed. Destroy |host_cache_persistence_manager_| before the
+  // caller destroys UrlRequestContext.
+  host_cache_persistence_manager_.reset(nullptr);
+}
+
+}  // namespace cronet
diff --git a/components/cronet/cronet_prefs_manager.h b/components/cronet/cronet_prefs_manager.h
new file mode 100644
index 0000000..d87063e91
--- /dev/null
+++ b/components/cronet/cronet_prefs_manager.h
@@ -0,0 +1,94 @@
+// 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_CRONET_CRONET_PREFS_MANAGER_H_
+#define COMPONENTS_CRONET_CRONET_PREFS_MANAGER_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/threading/thread_checker.h"
+
+class JsonPrefStore;
+class NetLog;
+class PrefService;
+
+namespace base {
+class SingleThreadTaskRunner;
+class SequencedTaskRunner;
+}  // namespace base
+
+namespace net {
+class HttpServerPropertiesManager;
+class HostCache;
+class NetLog;
+class NetworkQualitiesPrefsManager;
+class NetworkQualityEstimator;
+class SdchOwner;
+class URLRequestContextBuilder;
+}  // namespace net
+
+namespace cronet {
+class HostCachePersistenceManager;
+
+// Manages the PrefService, JsonPrefStore and all associated persistence
+// managers used by Cronet such as NetworkQualityPrefsManager,
+// HostCachePersistenceManager, etc. The constructor, destructor and all
+// other methods of this class should be called on the network thread.
+class CronetPrefsManager {
+ public:
+  CronetPrefsManager(
+      const std::string& storage_path,
+      scoped_refptr<base::SingleThreadTaskRunner> network_task_runner,
+      scoped_refptr<base::SequencedTaskRunner> file_task_runner,
+      bool enable_network_quality_estimator,
+      bool enable_host_cache_persistence,
+      net::NetLog* net_log,
+      net::URLRequestContextBuilder* context_builder);
+
+  virtual ~CronetPrefsManager();
+
+  void SetupNqePersistence(net::NetworkQualityEstimator* nqe);
+
+  void SetupHostCachePersistence(net::HostCache* host_cache,
+                                 int host_cache_persistence_delay_ms,
+                                 net::NetLog* net_log);
+
+  void SetupSdchPersistence(net::SdchOwner* sdch_owner);
+
+  // Prepares |this| for shutdown.
+  void PrepareForShutdown();
+
+ private:
+  // |pref_service_| should outlive the HttpServerPropertiesManager owned by
+  // |host_cache_persistence_manager_|.
+  std::unique_ptr<PrefService> pref_service_;
+  scoped_refptr<JsonPrefStore> json_pref_store_;
+
+  // The ownership of this object is assumed by |net::URLRequestContextBuilder|
+  // in this class constructor. The ownership is later passed to
+  // |net::URLRequestContext|, which should outlive this class.
+  net::HttpServerPropertiesManager* http_server_properties_manager_;
+
+  // Manages the writing and reading of the network quality prefs.
+  std::unique_ptr<net::NetworkQualitiesPrefsManager>
+      network_qualities_prefs_manager_;
+
+  // Manages reading and writing the HostCache pref when persistence is enabled.
+  // Must be destroyed before |context_| owned by CronetUrlContextAdapter
+  // (because it owns the HostResolverImpl,
+  // which owns the HostCache) and |pref_service_|.
+  std::unique_ptr<HostCachePersistenceManager> host_cache_persistence_manager_;
+
+  // Checks that all methods are called on the network thread.
+  THREAD_CHECKER(thread_checker_);
+
+  DISALLOW_COPY_AND_ASSIGN(CronetPrefsManager);
+
+};  // class CronetPrefsManager
+
+}  // namespace cronet
+
+#endif  // COMPONENTS_CRONET_CRONET_PREFS_MANAGER_H_
diff --git a/components/data_reduction_proxy/core/browser/BUILD.gn b/components/data_reduction_proxy/core/browser/BUILD.gn
index b5d3c92..8050850 100644
--- a/components/data_reduction_proxy/core/browser/BUILD.gn
+++ b/components/data_reduction_proxy/core/browser/BUILD.gn
@@ -64,6 +64,7 @@
       "//components/prefs",
       "//components/previews/core",
       "//components/variations",
+      "//components/variations/net",
       "//crypto",
       "//google_apis",
       "//net:net",
@@ -91,6 +92,7 @@
     "//components/prefs",
     "//components/previews/core",
     "//components/variations",
+    "//components/variations/net",
     "//crypto",
     "//google_apis",
     "//net",
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc
index e90e662..2453c726 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc
@@ -28,6 +28,7 @@
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_util.h"
 #include "components/data_reduction_proxy/proto/client_config.pb.h"
 #include "components/data_use_measurement/core/data_use_user_data.h"
+#include "components/variations/net/variations_http_headers.h"
 #include "net/base/host_port_pair.h"
 #include "net/base/load_flags.h"
 #include "net/base/load_timing_info.h"
@@ -390,6 +391,15 @@
 
   fetcher_ = std::move(fetcher);
   fetch_in_progress_ = true;
+
+  // Attach variations headers.
+  net::HttpRequestHeaders headers;
+  variations::AppendVariationHeaders(config_service_url_, false /* incognito */,
+                                     false /* uma_enabled */,
+                                     false /* is_signed_in */, &headers);
+  if (!headers.IsEmpty())
+    fetcher_->SetExtraRequestHeaders(headers.ToString());
+
   fetcher_->Start();
 }
 
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate.cc
index 4985301..82b1530e 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate.cc
@@ -143,12 +143,8 @@
 bool DataReductionProxyDelegate::IsTrustedSpdyProxy(
     const net::ProxyServer& proxy_server) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  if (!proxy_server.is_https() ||
-      !params::IsIncludedInTrustedSpdyProxyFieldTrial() ||
-      !proxy_server.is_valid()) {
-    return false;
-  }
-  return config_ && config_->IsDataReductionProxy(proxy_server, nullptr);
+  return proxy_server.is_valid() && proxy_server.is_https() && config_ &&
+         config_->IsDataReductionProxy(proxy_server, nullptr);
 }
 
 void DataReductionProxyDelegate::OnTunnelHeadersReceived(
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate_unittest.cc
index 425d5c9..ab7f157 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate_unittest.cc
@@ -152,43 +152,29 @@
           .Build();
 
   const struct {
-    bool is_in_trusted_spdy_proxy_field_trial;
     net::ProxyServer::Scheme first_proxy_scheme;
     net::ProxyServer::Scheme second_proxy_scheme;
     bool expect_proxy_is_trusted;
   } test_cases[] = {
-      {false, net::ProxyServer::SCHEME_HTTP, net::ProxyServer::SCHEME_INVALID,
-       false},
-      {true, net::ProxyServer::SCHEME_HTTP, net::ProxyServer::SCHEME_INVALID,
-       false},
-      {true, net::ProxyServer::SCHEME_QUIC, net::ProxyServer::SCHEME_INVALID,
-       false},
-      {true, net::ProxyServer::SCHEME_HTTP, net::ProxyServer::SCHEME_HTTP,
-       false},
-      {true, net::ProxyServer::SCHEME_INVALID, net::ProxyServer::SCHEME_INVALID,
+      {net::ProxyServer::SCHEME_HTTP, net::ProxyServer::SCHEME_INVALID, false},
+      {net::ProxyServer::SCHEME_QUIC, net::ProxyServer::SCHEME_INVALID, false},
+      {net::ProxyServer::SCHEME_HTTP, net::ProxyServer::SCHEME_HTTP, false},
+      {net::ProxyServer::SCHEME_INVALID, net::ProxyServer::SCHEME_INVALID,
        false},
       // First proxy is HTTPS, and second is invalid.
-      {true, net::ProxyServer::SCHEME_HTTPS, net::ProxyServer::SCHEME_INVALID,
-       true},
+      {net::ProxyServer::SCHEME_HTTPS, net::ProxyServer::SCHEME_INVALID, true},
       // First proxy is invalid, and second proxy is HTTPS.
-      {true, net::ProxyServer::SCHEME_INVALID, net::ProxyServer::SCHEME_HTTPS,
-       true},
+      {net::ProxyServer::SCHEME_INVALID, net::ProxyServer::SCHEME_HTTPS, true},
       // First proxy is HTTPS, and second is HTTP.
-      {true, net::ProxyServer::SCHEME_HTTPS, net::ProxyServer::SCHEME_HTTPS,
-       true},
+      {net::ProxyServer::SCHEME_HTTPS, net::ProxyServer::SCHEME_HTTPS, true},
       // Second proxy is HTTPS, and first is HTTP.
-      {true, net::ProxyServer::SCHEME_HTTP, net::ProxyServer::SCHEME_HTTPS,
-       true},
-      {true, net::ProxyServer::SCHEME_QUIC, net::ProxyServer::SCHEME_INVALID,
-       false},
-      {true, net::ProxyServer::SCHEME_QUIC, net::ProxyServer::SCHEME_HTTP,
-       false},
-      {true, net::ProxyServer::SCHEME_QUIC, net::ProxyServer::SCHEME_HTTPS,
-       true},
+      {net::ProxyServer::SCHEME_HTTP, net::ProxyServer::SCHEME_HTTPS, true},
+      {net::ProxyServer::SCHEME_QUIC, net::ProxyServer::SCHEME_INVALID, false},
+      {net::ProxyServer::SCHEME_QUIC, net::ProxyServer::SCHEME_HTTP, false},
+      {net::ProxyServer::SCHEME_QUIC, net::ProxyServer::SCHEME_HTTPS, true},
   };
   for (const auto& test : test_cases) {
     ASSERT_EQ(test.expect_proxy_is_trusted,
-              test.is_in_trusted_spdy_proxy_field_trial &&
                   (test.first_proxy_scheme == net::ProxyServer::SCHEME_HTTPS ||
                    test.second_proxy_scheme == net::ProxyServer::SCHEME_HTTPS))
         << (&test - test_cases);
@@ -224,15 +210,6 @@
         test_context->io_data()->net_log());
 
     base::FieldTrialList field_trial_list(nullptr);
-    EXPECT_TRUE(params::IsIncludedInTrustedSpdyProxyFieldTrial());
-    if (!test.is_in_trusted_spdy_proxy_field_trial) {
-      // Trusted Spdy proxy field trial experiment is enabled by default.
-      base::FieldTrialList::CreateFieldTrial(
-          params::GetTrustedSpdyProxyFieldTrialName(), "Control");
-    }
-    EXPECT_EQ(test.is_in_trusted_spdy_proxy_field_trial,
-              params::IsIncludedInTrustedSpdyProxyFieldTrial());
-
     EXPECT_EQ(test.expect_proxy_is_trusted,
               delegate.IsTrustedSpdyProxy(first_proxy) ||
                   delegate.IsTrustedSpdyProxy(second_proxy))
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_params.cc b/components/data_reduction_proxy/core/common/data_reduction_proxy_params.cc
index a6ed6590..653e4cd 100644
--- a/components/data_reduction_proxy/core/common/data_reduction_proxy_params.cc
+++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_params.cc
@@ -42,8 +42,6 @@
 const char kBlackListTransitionFieldTrial[] =
     "DataReductionProxyPreviewsBlackListTransition";
 
-const char kTrustedSpdyProxyFieldTrialName[] = "DataReductionTrustedSpdyProxy";
-
 // Default URL for retrieving the Data Reduction Proxy configuration.
 const char kClientConfigURL[] =
     "https://datasaver.googleapis.com/v1/clientConfigs";
@@ -136,25 +134,6 @@
   return base::FieldTrialList::FindFullName("DataCompressionProxyHoldback");
 }
 
-const char* GetTrustedSpdyProxyFieldTrialName() {
-  return kTrustedSpdyProxyFieldTrialName;
-}
-
-bool IsIncludedInTrustedSpdyProxyFieldTrial() {
-  if (base::StartsWith(base::FieldTrialList::FindFullName(
-                           GetTrustedSpdyProxyFieldTrialName()),
-                       kControl, base::CompareCase::SENSITIVE)) {
-    return false;
-  }
-  if (base::StartsWith(base::FieldTrialList::FindFullName(
-                           GetTrustedSpdyProxyFieldTrialName()),
-                       kDisabled, base::CompareCase::SENSITIVE)) {
-    return false;
-  }
-  // Trusted SPDY proxy experiment is enabled by default.
-  return true;
-}
-
 const char* GetLoFiFieldTrialName() {
   return kLoFiFieldTrial;
 }
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_params.h b/components/data_reduction_proxy/core/common/data_reduction_proxy_params.h
index a9f9b65..2e5f44d 100644
--- a/components/data_reduction_proxy/core/common/data_reduction_proxy_params.h
+++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_params.h
@@ -45,13 +45,6 @@
 // not included in a group.
 std::string HoldbackFieldTrialGroup();
 
-// Returns the name of the trusted SPDY/HTTP2 proxy field trial.
-const char* GetTrustedSpdyProxyFieldTrialName();
-
-// Returns true if this client is part of the enabled group of the trusted
-// SPDY/HTTP2 proxy field trial.
-bool IsIncludedInTrustedSpdyProxyFieldTrial();
-
 // Returns true if this client is part of the field trial that should display
 // a promotion for the data reduction proxy on Android One devices. This is for
 // testing purposes and should not be called outside of tests.
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_params_unittest.cc b/components/data_reduction_proxy/core/common/data_reduction_proxy_params_unittest.cc
index 63e8cda5..3bb4c86 100644
--- a/components/data_reduction_proxy/core/common/data_reduction_proxy_params_unittest.cc
+++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_params_unittest.cc
@@ -340,27 +340,6 @@
   }
 }
 
-TEST_F(DataReductionProxyParamsTest, TrustedSpdyProxyFieldTrial) {
-  const struct {
-    std::string trial_group_name;
-    bool expected_enabled;
-  } tests[] = {
-      {"Enabled", true},     {"Enabled_Control", true}, {"Control", false},
-      {"Disabled", false},   {"enabled", true},         {"Enabled", true},
-      {std::string(), true},
-  };
-
-  for (const auto& test : tests) {
-    variations::testing::ClearAllVariationParams();
-    base::FieldTrialList field_trial_list(nullptr);
-    base::FieldTrialList::CreateFieldTrial(
-        params::GetTrustedSpdyProxyFieldTrialName(), test.trial_group_name);
-
-    EXPECT_EQ(test.expected_enabled,
-              params::IsIncludedInTrustedSpdyProxyFieldTrial());
-  }
-}
-
 // Tests if the QUIC field trial is set correctly.
 TEST_F(DataReductionProxyParamsTest, QuicFieldTrial) {
   const struct {
diff --git a/components/flags_ui/flags_storage.h b/components/flags_ui/flags_storage.h
index 67d3dfe1..67cb833 100644
--- a/components/flags_ui/flags_storage.h
+++ b/components/flags_ui/flags_storage.h
@@ -21,6 +21,8 @@
   virtual std::set<std::string> GetFlags() = 0;
   // Stores the |flags| and returns true on success.
   virtual bool SetFlags(const std::set<std::string>& flags) = 0;
+  // Lands pending changes to disk immediately.
+  virtual void CommitPendingWrites() = 0;
 };
 
 }  // namespace flags_ui
diff --git a/components/flags_ui/pref_service_flags_storage.cc b/components/flags_ui/pref_service_flags_storage.cc
index 2153c1bf6..a8b1d8c 100644
--- a/components/flags_ui/pref_service_flags_storage.cc
+++ b/components/flags_ui/pref_service_flags_storage.cc
@@ -48,6 +48,10 @@
   return true;
 }
 
+void PrefServiceFlagsStorage::CommitPendingWrites() {
+  prefs_->CommitPendingWrite();
+}
+
 // static
 void PrefServiceFlagsStorage::RegisterPrefs(PrefRegistrySimple* registry) {
   registry->RegisterListPref(prefs::kEnabledLabsExperiments);
diff --git a/components/flags_ui/pref_service_flags_storage.h b/components/flags_ui/pref_service_flags_storage.h
index b230ac11..3a98c8b 100644
--- a/components/flags_ui/pref_service_flags_storage.h
+++ b/components/flags_ui/pref_service_flags_storage.h
@@ -27,6 +27,7 @@
 
   std::set<std::string> GetFlags() override;
   bool SetFlags(const std::set<std::string>& flags) override;
+  void CommitPendingWrites() override;
 
   static void RegisterPrefs(PrefRegistrySimple* registry);
 
diff --git a/components/offline_pages/core/prefetch/download_completed_task_unittest.cc b/components/offline_pages/core/prefetch/download_completed_task_unittest.cc
index a70258bd..06a94319 100644
--- a/components/offline_pages/core/prefetch/download_completed_task_unittest.cc
+++ b/components/offline_pages/core/prefetch/download_completed_task_unittest.cc
@@ -115,7 +115,7 @@
   EXPECT_EQ(PrefetchItemErrorCode::DOWNLOAD_ERROR, item->error_code);
   EXPECT_EQ(kTestGUID, item->guid);
   EXPECT_TRUE(item->file_path.empty());
-  EXPECT_EQ(0, item->file_size);
+  EXPECT_EQ(-1, item->file_size);
 }
 
 TEST_F(DownloadCompletedTaskTest, NoUpdateOnMismatchedDownloadSuccess) {
diff --git a/components/offline_pages/core/prefetch/metrics_finalization_task.cc b/components/offline_pages/core/prefetch/metrics_finalization_task.cc
index d0fd2f1..449d291 100644
--- a/components/offline_pages/core/prefetch/metrics_finalization_task.cc
+++ b/components/offline_pages/core/prefetch/metrics_finalization_task.cc
@@ -4,12 +4,14 @@
 
 #include "components/offline_pages/core/prefetch/metrics_finalization_task.h"
 
+#include <algorithm>
 #include <memory>
 #include <vector>
 
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_macros.h"
 #include "components/offline_pages/core/offline_time_utils.h"
 #include "components/offline_pages/core/prefetch/prefetch_types.h"
 #include "components/offline_pages/core/prefetch/store/prefetch_store.h"
@@ -49,8 +51,7 @@
   int64_t file_size;
 };
 
-std::unique_ptr<std::vector<PrefetchItemStats>> FetchUrlsSync(
-    sql::Connection* db) {
+std::vector<PrefetchItemStats> FetchUrlsSync(sql::Connection* db) {
   static const char kSql[] = R"(
   SELECT offline_id, generate_bundle_attempts, get_operation_attempts,
     download_initiation_attempts, archive_body_length, creation_time,
@@ -61,9 +62,9 @@
   sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
   statement.BindInt(0, static_cast<int>(PrefetchItemState::FINISHED));
 
-  auto urls = base::MakeUnique<std::vector<PrefetchItemStats>>();
+  std::vector<PrefetchItemStats> urls;
   while (statement.Step()) {
-    PrefetchItemStats stats(
+    urls.emplace_back(
         statement.ColumnInt64(0),  // offline_id
         statement.ColumnInt(1),    // generate_bundle_attempts
         statement.ColumnInt(2),    // get_operation_attempts
@@ -74,8 +75,6 @@
             statement.ColumnInt(6)),  // error_code
         statement.ColumnInt64(7)      // file_size
         );
-
-    urls->push_back(stats);
   }
 
   return urls;
@@ -94,7 +93,92 @@
   return statement.Run();
 }
 
-bool SelectUrlsToPrefetchSync(sql::Connection* db) {
+// These constants represent important indexes in the
+// OfflinePrefetchArchiveActualSizeVsExpected histogram enum.
+const int kEnumZeroArchiveBodyLength = 0;
+const int kEnumRange0To100Min = 1;
+const int kEnumRange0To100Max = 10;
+const int kEnumSizeMatches100 = 11;
+const int kEnumRange100To200OrMoreMin = 12;
+const int kEnumRange100To200OrMoreMax = 22;
+
+// Value meaning the file size should not be reported (not a valid index in the
+// OfflinePrefetchArchiveActualSizeVsExpected enum).
+const int kShouldNotReportFileSize = -1;
+
+// Returned values conform to the definition of the
+// OfflinePrefetchArchiveActualSizeVsExpected histogram enum. Except for -1
+// which means "do not report".
+int GetFileSizeEnumValueFor(int64_t archive_body_length, int64_t file_size) {
+  // Archiving service reported body length as zero.
+  if (archive_body_length == 0)
+    return kEnumZeroArchiveBodyLength;
+
+  // For other cases, reporting should only happen if both size values were set
+  // meaning the item reached at least the successfully downloaded state.
+  if (archive_body_length < 0 || file_size < 0)
+    return kShouldNotReportFileSize;
+
+  if (archive_body_length == file_size)
+    return kEnumSizeMatches100;
+
+  // For smaller sizes than expected, return an index in the 0% <= ratio < 100%
+  // range.
+  double ratio = static_cast<double>(file_size) / archive_body_length;
+  if (archive_body_length > file_size) {
+    return std::max(
+        kEnumRange0To100Min,
+        std::min(kEnumRange0To100Max, static_cast<int>(ratio * 10) + 1));
+  }
+
+  // Otherwise return an index in the 100% < ratio range.
+  return std::max(
+      kEnumRange100To200OrMoreMin,
+      std::min(kEnumRange100To200OrMoreMax, static_cast<int>(ratio * 10) + 2));
+}
+
+void ReportMetricsFor(const PrefetchItemStats& url, const base::Time now) {
+  // Lifetime reporting.
+  static const int kFourWeeksInSeconds =
+      base::TimeDelta::FromDays(28).InSeconds();
+  const bool successful = url.error_code == PrefetchItemErrorCode::SUCCESS;
+  int64_t lifetime_seconds = (now - url.creation_time).InSeconds();
+  if (successful) {
+    UMA_HISTOGRAM_CUSTOM_COUNTS(
+        "OfflinePages.Prefetching.ItemLifetime.Successful", lifetime_seconds, 1,
+        kFourWeeksInSeconds, 50);
+  } else {
+    UMA_HISTOGRAM_CUSTOM_COUNTS("OfflinePages.Prefetching.ItemLifetime.Failed",
+                                lifetime_seconds, 1, kFourWeeksInSeconds, 50);
+  }
+
+  // Error code reporting.
+  UMA_HISTOGRAM_SPARSE_SLOWLY("OfflinePages.Prefetching.FinishedItemErrorCode",
+                              static_cast<int>(url.error_code));
+
+  // Unexpected file size reporting.
+  int file_size_enum_value =
+      GetFileSizeEnumValueFor(url.archive_body_length, url.file_size);
+  if (file_size_enum_value != kShouldNotReportFileSize) {
+    UMA_HISTOGRAM_ENUMERATION(
+        "OfflinePages.Prefetching.DownloadedArchiveSizeVsExpected",
+        file_size_enum_value, kEnumRange100To200OrMoreMax);
+  }
+
+  // Attempt counts reporting.
+  static const int kMaxPossibleRetries = 20;
+  UMA_HISTOGRAM_EXACT_LINEAR(
+      "OfflinePages.Prefetching.ActionRetryAttempts.GeneratePageBundle",
+      url.generate_bundle_attempts, kMaxPossibleRetries);
+  UMA_HISTOGRAM_EXACT_LINEAR(
+      "OfflinePages.Prefetching.ActionRetryAttempts.GetOperation",
+      url.get_operation_attempts, kMaxPossibleRetries);
+  UMA_HISTOGRAM_EXACT_LINEAR(
+      "OfflinePages.Prefetching.ActionRetryAttempts.DownloadInitiation",
+      url.download_initiation_attempts, kMaxPossibleRetries);
+}
+
+bool ReportMetricsAndFinalizeSync(sql::Connection* db) {
   if (!db)
     return false;
 
@@ -102,16 +186,15 @@
   if (!transaction.Begin())
     return false;
 
-  auto urls = FetchUrlsSync(db);
+  const std::vector<PrefetchItemStats> urls = FetchUrlsSync(db);
 
   base::Time now = base::Time::Now();
-  for (const auto& url : *urls) {
+  for (const auto& url : urls) {
     MarkUrlAsZombie(db, now, url.offline_id);
   }
 
   if (transaction.Commit()) {
-    // TODO(dewittj): Report interesting UMA metrics for each prefetch item.
-    for (const auto& url : *urls) {
+    for (const auto& url : urls) {
       DVLOG(1) << "Finalized prefetch item: (" << url.offline_id << ", "
                << url.generate_bundle_attempts << ", "
                << url.get_operation_attempts << ", "
@@ -119,8 +202,8 @@
                << url.archive_body_length << ", " << url.creation_time << ", "
                << static_cast<int>(url.error_code) << ", " << url.file_size
                << ")";
+      ReportMetricsFor(url, now);
     }
-
     return true;
   }
 
@@ -136,7 +219,7 @@
 
 void MetricsFinalizationTask::Run() {
   prefetch_store_->Execute(
-      base::BindOnce(&SelectUrlsToPrefetchSync),
+      base::BindOnce(&ReportMetricsAndFinalizeSync),
       base::BindOnce(&MetricsFinalizationTask::MetricsFinalized,
                      weak_factory_.GetWeakPtr()));
 }
diff --git a/components/offline_pages/core/prefetch/metrics_finalization_task_unittest.cc b/components/offline_pages/core/prefetch/metrics_finalization_task_unittest.cc
index 92d70a6..ecdf30f 100644
--- a/components/offline_pages/core/prefetch/metrics_finalization_task_unittest.cc
+++ b/components/offline_pages/core/prefetch/metrics_finalization_task_unittest.cc
@@ -7,6 +7,7 @@
 #include <set>
 
 #include "base/memory/ptr_util.h"
+#include "base/test/histogram_tester.h"
 #include "components/offline_pages/core/prefetch/mock_prefetch_item_generator.h"
 #include "components/offline_pages/core/prefetch/prefetch_item.h"
 #include "components/offline_pages/core/prefetch/prefetch_types.h"
@@ -117,4 +118,136 @@
   EXPECT_EQ(1U, items_in_new_request_state.count(unfinished_item));
 }
 
+TEST_F(MetricsFinalizationTaskTest, MetricsAreReported) {
+  PrefetchItem successful_item =
+      item_generator()->CreateItem(PrefetchItemState::FINISHED);
+  successful_item.generate_bundle_attempts = 1;
+  successful_item.get_operation_attempts = 1;
+  successful_item.download_initiation_attempts = 1;
+  ASSERT_TRUE(store_util()->InsertPrefetchItem(successful_item));
+
+  PrefetchItem failed_item =
+      item_generator()->CreateItem(PrefetchItemState::RECEIVED_GCM);
+  failed_item.state = PrefetchItemState::FINISHED;
+  failed_item.error_code = PrefetchItemErrorCode::ARCHIVING_FAILED;
+  ASSERT_TRUE(store_util()->InsertPrefetchItem(failed_item));
+
+  PrefetchItem unfinished_item =
+      item_generator()->CreateItem(PrefetchItemState::NEW_REQUEST);
+  ASSERT_TRUE(store_util()->InsertPrefetchItem(unfinished_item));
+
+  // Execute the metrics task.
+  base::HistogramTester histogram_tester;
+  ExpectTaskCompletes(metrics_finalization_task_.get());
+  metrics_finalization_task_->Run();
+  RunUntilIdle();
+
+  std::set<PrefetchItem> all_items;
+  EXPECT_EQ(3U, store_util()->GetAllItems(&all_items));
+  EXPECT_EQ(2U, FilterByState(all_items, PrefetchItemState::ZOMBIE).size());
+  EXPECT_EQ(1U,
+            FilterByState(all_items, PrefetchItemState::NEW_REQUEST).size());
+
+  // One successful and one failed samples.
+  histogram_tester.ExpectUniqueSample(
+      "OfflinePages.Prefetching.ItemLifetime.Successful", 0, 1);
+  histogram_tester.ExpectUniqueSample(
+      "OfflinePages.Prefetching.ItemLifetime.Failed", 0, 1);
+
+  // One sample for each_error code value.
+  histogram_tester.ExpectTotalCount(
+      "OfflinePages.Prefetching.FinishedItemErrorCode", 2);
+  histogram_tester.ExpectBucketCount(
+      "OfflinePages.Prefetching.FinishedItemErrorCode",
+      static_cast<int>(PrefetchItemErrorCode::SUCCESS), 1);
+  histogram_tester.ExpectBucketCount(
+      "OfflinePages.Prefetching.FinishedItemErrorCode",
+      static_cast<int>(PrefetchItemErrorCode::ARCHIVING_FAILED), 1);
+
+  // One sample at the "size matches (100%)" bucket.
+  histogram_tester.ExpectUniqueSample(
+      "OfflinePages.Prefetching.DownloadedArchiveSizeVsExpected", 11, 1);
+
+  // Attempt values match what was set above (non set values default to 0).
+  histogram_tester.ExpectTotalCount(
+      "OfflinePages.Prefetching.ActionRetryAttempts.GeneratePageBundle", 2);
+  histogram_tester.ExpectBucketCount(
+      "OfflinePages.Prefetching.ActionRetryAttempts.GeneratePageBundle", 0, 1);
+  histogram_tester.ExpectBucketCount(
+      "OfflinePages.Prefetching.ActionRetryAttempts.GeneratePageBundle", 1, 1);
+  histogram_tester.ExpectTotalCount(
+      "OfflinePages.Prefetching.ActionRetryAttempts.GetOperation", 2);
+  histogram_tester.ExpectBucketCount(
+      "OfflinePages.Prefetching.ActionRetryAttempts.GetOperation", 0, 1);
+  histogram_tester.ExpectBucketCount(
+      "OfflinePages.Prefetching.ActionRetryAttempts.GetOperation", 1, 1);
+  histogram_tester.ExpectTotalCount(
+      "OfflinePages.Prefetching.ActionRetryAttempts.DownloadInitiation", 2);
+  histogram_tester.ExpectBucketCount(
+      "OfflinePages.Prefetching.ActionRetryAttempts.DownloadInitiation", 0, 1);
+  histogram_tester.ExpectBucketCount(
+      "OfflinePages.Prefetching.ActionRetryAttempts.DownloadInitiation", 1, 1);
+}
+
+TEST_F(MetricsFinalizationTaskTest, FileSizeMetricsAreReportedCorrectly) {
+  PrefetchItem zero_body_length =
+      item_generator()->CreateItem(PrefetchItemState::RECEIVED_BUNDLE);
+  zero_body_length.state = PrefetchItemState::FINISHED;
+  zero_body_length.archive_body_length = 0;
+  zero_body_length.file_size = -1;
+  ASSERT_TRUE(store_util()->InsertPrefetchItem(zero_body_length));
+
+  PrefetchItem smaller_than_expected =
+      item_generator()->CreateItem(PrefetchItemState::FINISHED);
+  smaller_than_expected.archive_body_length = 1000;
+  smaller_than_expected.file_size = 999;
+  ASSERT_TRUE(store_util()->InsertPrefetchItem(smaller_than_expected));
+
+  PrefetchItem sizes_match =
+      item_generator()->CreateItem(PrefetchItemState::FINISHED);
+  sizes_match.archive_body_length = 1000;
+  sizes_match.file_size = 1000;
+  ASSERT_TRUE(store_util()->InsertPrefetchItem(sizes_match));
+
+  PrefetchItem larger_than_expected =
+      item_generator()->CreateItem(PrefetchItemState::FINISHED);
+  larger_than_expected.archive_body_length = 1000;
+  larger_than_expected.file_size = 1001;
+  ASSERT_TRUE(store_util()->InsertPrefetchItem(larger_than_expected));
+
+  PrefetchItem much_larger_than_expected =
+      item_generator()->CreateItem(PrefetchItemState::FINISHED);
+  much_larger_than_expected.archive_body_length = 1000;
+  much_larger_than_expected.file_size = 10000;
+  ASSERT_TRUE(store_util()->InsertPrefetchItem(much_larger_than_expected));
+
+  // Execute the metrics task.
+  base::HistogramTester histogram_tester;
+  ExpectTaskCompletes(metrics_finalization_task_.get());
+  metrics_finalization_task_->Run();
+  RunUntilIdle();
+
+  std::set<PrefetchItem> all_items;
+  EXPECT_EQ(5U, store_util()->GetAllItems(&all_items));
+  EXPECT_EQ(5U, FilterByState(all_items, PrefetchItemState::ZOMBIE).size());
+
+  histogram_tester.ExpectTotalCount(
+      "OfflinePages.Prefetching.DownloadedArchiveSizeVsExpected", 5);
+  // One sample at the "archive_body_length = 0" bucket.
+  histogram_tester.ExpectBucketCount(
+      "OfflinePages.Prefetching.DownloadedArchiveSizeVsExpected", 0, 1);
+  // One sample at the "90% to 100%" bucket.
+  histogram_tester.ExpectBucketCount(
+      "OfflinePages.Prefetching.DownloadedArchiveSizeVsExpected", 10, 1);
+  // One sample at the "size matches (100%)" bucket.
+  histogram_tester.ExpectBucketCount(
+      "OfflinePages.Prefetching.DownloadedArchiveSizeVsExpected", 11, 1);
+  // One sample at the "100% to 110%" bucket.
+  histogram_tester.ExpectBucketCount(
+      "OfflinePages.Prefetching.DownloadedArchiveSizeVsExpected", 12, 1);
+  // One sample at the "above 200%" bucket.
+  histogram_tester.ExpectBucketCount(
+      "OfflinePages.Prefetching.DownloadedArchiveSizeVsExpected", 22, 1);
+}
+
 }  // namespace offline_pages
diff --git a/components/offline_pages/core/prefetch/prefetch_item.h b/components/offline_pages/core/prefetch/prefetch_item.h
index d40e90a..0baaf7c6 100644
--- a/components/offline_pages/core/prefetch/prefetch_item.h
+++ b/components/offline_pages/core/prefetch/prefetch_item.h
@@ -113,7 +113,7 @@
   base::FilePath file_path;
 
   // The size of the archive file.
-  int64_t file_size = 0;
+  int64_t file_size = -1;
 };
 
 std::ostream& operator<<(std::ostream& out, const PrefetchItem& pi);
diff --git a/components/offline_pages/core/prefetch/prefetch_types.h b/components/offline_pages/core/prefetch/prefetch_types.h
index 05fafa1..4ae7ff69 100644
--- a/components/offline_pages/core/prefetch/prefetch_types.h
+++ b/components/offline_pages/core/prefetch/prefetch_types.h
@@ -106,6 +106,15 @@
 // Error codes used to identify the reason why a prefetch entry has finished
 // processing in the pipeline. This values are only meaningful for entries in
 // the "finished" state.
+//
+// New entries can be added anywhere as long as they are assigned unique values
+// and kept in ascending order. Deprecated entries should be labeled as such but
+// never removed. Assigned values should never be reused. Remember to update the
+// MAX value if adding a new trailing item.
+//
+// Changes to this enum must be reflected in the respective metrics enum named
+// PrefetchItemErrorCode in enums.xml. Use the exact same integer value for each
+// mirrored entry.
 enum class PrefetchItemErrorCode {
   // The entry had gone through the pipeline and successfully completed
   // prefetching. Explicitly setting to 0 as that is the default value for the
@@ -141,6 +150,8 @@
   GENERATE_PAGE_BUNDLE_REQUEST_MAX_ATTEMPTS_REACHED = 1300,
   // Exceeded maximum retries for download.
   DOWNLOAD_MAX_ATTEMPTS_REACHED = 1400,
+  // Note: Must always have the same value as the last actual entry.
+  MAX = DOWNLOAD_MAX_ATTEMPTS_REACHED
 };
 
 // Callback invoked upon completion of a prefetch request.
diff --git a/components/offline_pages/core/prefetch/store/prefetch_store.cc b/components/offline_pages/core/prefetch/store/prefetch_store.cc
index 3476c08..8624bff 100644
--- a/components/offline_pages/core/prefetch/store/prefetch_store.cc
+++ b/components/offline_pages/core/prefetch/store/prefetch_store.cc
@@ -57,7 +57,7 @@
     " creation_time INTEGER NOT NULL,"
     " freshness_time INTEGER NOT NULL,"
     " error_code INTEGER NOT NULL DEFAULT 0,"
-    " file_size INTEGER NOT NULL DEFAULT 0,"
+    " file_size INTEGER NOT NULL DEFAULT -1,"
     // Variable length columns come later.
     " guid VARCHAR NOT NULL DEFAULT '',"
     " client_namespace VARCHAR NOT NULL DEFAULT '',"
diff --git a/components/omnibox/browser/physical_web_provider.cc b/components/omnibox/browser/physical_web_provider.cc
index 4da2a94..a605b3c 100644
--- a/components/omnibox/browser/physical_web_provider.cc
+++ b/components/omnibox/browser/physical_web_provider.cc
@@ -241,7 +241,7 @@
 
     match.fill_into_edit =
         AutocompleteInput::FormattedStringWithEquivalentMeaning(
-            url, match.contents, client_->GetSchemeClassifier());
+            url, url_formatter::FormatUrl(url), client_->GetSchemeClassifier());
 
     match.description = title;
     match.description_class.push_back(
diff --git a/components/omnibox/browser/titled_url_match_utils.cc b/components/omnibox/browser/titled_url_match_utils.cc
index c21e560..d09444a3 100644
--- a/components/omnibox/browser/titled_url_match_utils.cc
+++ b/components/omnibox/browser/titled_url_match_utils.cc
@@ -68,8 +68,6 @@
       titled_url_match.title_match_positions;
   CorrectTitleAndMatchPositions(&title, &new_title_match_positions);
   const base::string16& url_utf16 = base::UTF8ToUTF16(url.spec());
-  size_t inline_autocomplete_offset = URLPrefix::GetInlineAutocompleteOffset(
-      input.text(), fixed_up_input_text, false, url_utf16);
   match.destination_url = url;
   const size_t match_start =
       titled_url_match.url_match_positions.empty()
@@ -80,25 +78,29 @@
       ((match_start == base::string16::npos) || (match_start != 0));
   std::vector<size_t> offsets = TitledUrlMatch::OffsetsFromMatchPositions(
       titled_url_match.url_match_positions);
-  // In addition to knowing how |offsets| is transformed, we need to know how
-  // |inline_autocomplete_offset| is transformed.  We add it to the end of
-  // |offsets|, compute how everything is transformed, then remove it from the
-  // end.
-  offsets.push_back(inline_autocomplete_offset);
-  auto format_types =
-      AutocompleteMatch::GetFormatTypes(!trim_http, false, false);
   match.contents = url_formatter::FormatUrlWithOffsets(
-      url, format_types, net::UnescapeRule::SPACES, nullptr, nullptr, &offsets);
-  inline_autocomplete_offset = offsets.back();
-  offsets.pop_back();
+      url, AutocompleteMatch::GetFormatTypes(!trim_http, false, false),
+      net::UnescapeRule::SPACES, nullptr, nullptr, &offsets);
   TitledUrlMatch::MatchPositions new_url_match_positions =
       TitledUrlMatch::ReplaceOffsetsInMatchPositions(
           titled_url_match.url_match_positions, offsets);
   match.contents_class = ClassificationsFromMatchPositions(
       new_url_match_positions, match.contents.size(), true);
+
+  // The inline_autocomplete_offset should be adjusted based on the formatting
+  // applied to |fill_into_edit|.
+  size_t inline_autocomplete_offset = URLPrefix::GetInlineAutocompleteOffset(
+      input.text(), fixed_up_input_text, false, url_utf16);
+  auto fill_into_edit_format_types = url_formatter::kFormatUrlOmitDefaults;
+  if (!trim_http)
+    fill_into_edit_format_types &= ~url_formatter::kFormatUrlOmitHTTP;
   match.fill_into_edit =
       AutocompleteInput::FormattedStringWithEquivalentMeaning(
-          url, match.contents, scheme_classifier);
+          url,
+          url_formatter::FormatUrl(url, fill_into_edit_format_types,
+                                   net::UnescapeRule::SPACES, nullptr, nullptr,
+                                   &inline_autocomplete_offset),
+          scheme_classifier);
   if (inline_autocomplete_offset != base::string16::npos) {
     // |inline_autocomplete_offset| may be beyond the end of the
     // |fill_into_edit| if the user has typed an URL with a scheme and the
diff --git a/components/omnibox/browser/zero_suggest_provider.cc b/components/omnibox/browser/zero_suggest_provider.cc
index 5cfc550..45d7d144 100644
--- a/components/omnibox/browser/zero_suggest_provider.cc
+++ b/components/omnibox/browser/zero_suggest_provider.cc
@@ -369,7 +369,8 @@
                                             nullptr, nullptr);
   match.fill_into_edit +=
       AutocompleteInput::FormattedStringWithEquivalentMeaning(
-          navigation.url(), match.contents, client()->GetSchemeClassifier());
+          navigation.url(), url_formatter::FormatUrl(navigation.url()),
+          client()->GetSchemeClassifier());
 
   AutocompleteMatch::ClassifyLocationInString(base::string16::npos, 0,
       match.contents.length(), ACMatchClassification::URL,
diff --git a/components/password_manager/core/browser/DEPS b/components/password_manager/core/browser/DEPS
index cf549c2..0a0aeea 100644
--- a/components/password_manager/core/browser/DEPS
+++ b/components/password_manager/core/browser/DEPS
@@ -15,6 +15,9 @@
 ]
 
 specific_include_rules = {
+  "password_autofill_manager_unittest\.cc": [
+    "+components/ukm",
+  ],
   "password_form_manager_unittest\.cc": [
     "+components/ukm",
   ],
diff --git a/components/password_manager/core/browser/password_autofill_manager.cc b/components/password_manager/core/browser/password_autofill_manager.cc
index b254179..941cfa28 100644
--- a/components/password_manager/core/browser/password_autofill_manager.cc
+++ b/components/password_manager/core/browser/password_autofill_manager.cc
@@ -26,7 +26,10 @@
 #include "components/autofill/core/common/autofill_data_validation.h"
 #include "components/autofill/core/common/autofill_util.h"
 #include "components/password_manager/core/browser/android_affiliation/affiliation_utils.h"
+#include "components/password_manager/core/browser/password_manager.h"
+#include "components/password_manager/core/browser/password_manager_client.h"
 #include "components/password_manager/core/browser/password_manager_driver.h"
+#include "components/password_manager/core/browser/password_manager_metrics_recorder.h"
 #include "components/password_manager/core/common/password_manager_features.h"
 #include "components/security_state/core/security_state.h"
 #include "components/strings/grit/components_strings.h"
@@ -383,6 +386,28 @@
 
     metrics_util::LogContextOfShowAllSavedPasswordsAccepted(
         show_all_saved_passwords_shown_context_);
+
+    PasswordManager* password_manager =
+        password_manager_driver_->GetPasswordManager();
+    PasswordManagerClient* client =
+        password_manager ? password_manager->client() : nullptr;
+    if (client) {
+      using UserAction =
+          password_manager::PasswordManagerMetricsRecorder::PageLevelUserAction;
+      switch (show_all_saved_passwords_shown_context_) {
+        case metrics_util::SHOW_ALL_SAVED_PASSWORDS_CONTEXT_PASSWORD:
+          client->GetMetricsRecorder().RecordPageLevelUserAction(
+              UserAction::kShowAllPasswordsWhileSomeAreSuggested);
+          break;
+        case metrics_util::SHOW_ALL_SAVED_PASSWORDS_CONTEXT_MANUAL_FALLBACK:
+          client->GetMetricsRecorder().RecordPageLevelUserAction(
+              UserAction::kShowAllPasswordsWhileNoneAreSuggested);
+          break;
+        case metrics_util::SHOW_ALL_SAVED_PASSWORDS_CONTEXT_NONE:
+        case metrics_util::SHOW_ALL_SAVED_PASSWORDS_CONTEXT_COUNT:
+          NOTREACHED();
+      }
+    }
   }
 
   autofill_client_->HideAutofillPopup();
diff --git a/components/password_manager/core/browser/password_autofill_manager_unittest.cc b/components/password_manager/core/browser/password_autofill_manager_unittest.cc
index 918443da7..000553f 100644
--- a/components/password_manager/core/browser/password_autofill_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_autofill_manager_unittest.cc
@@ -21,12 +21,14 @@
 #include "components/autofill/core/common/autofill_switches.h"
 #include "components/autofill/core/common/form_field_data.h"
 #include "components/autofill/core/common/password_form_fill_data.h"
+#include "components/password_manager/core/browser/password_manager.h"
 #include "components/password_manager/core/browser/password_manager_metrics_util.h"
 #include "components/password_manager/core/browser/stub_password_manager_client.h"
 #include "components/password_manager/core/browser/stub_password_manager_driver.h"
 #include "components/password_manager/core/common/password_manager_features.h"
 #include "components/security_state/core/security_state.h"
 #include "components/strings/grit/components_strings.h"
+#include "components/ukm/test_ukm_recorder.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -54,20 +56,28 @@
 
 namespace {
 
+constexpr char kMainFrameUrl[] = "https://example.com/";
+
 class MockPasswordManagerDriver : public StubPasswordManagerDriver {
  public:
   MOCK_METHOD2(FillSuggestion,
                void(const base::string16&, const base::string16&));
   MOCK_METHOD2(PreviewSuggestion,
                void(const base::string16&, const base::string16&));
+  MOCK_METHOD0(GetPasswordManager, PasswordManager*());
 };
 
 class TestPasswordManagerClient : public StubPasswordManagerClient {
  public:
+  TestPasswordManagerClient() : main_frame_url_(kMainFrameUrl) {}
+  ~TestPasswordManagerClient() override = default;
+
   MockPasswordManagerDriver* mock_driver() { return &driver_; }
+  const GURL& GetMainFrameURL() const override { return main_frame_url_; }
 
  private:
   MockPasswordManagerDriver driver_;
+  GURL main_frame_url_;
 };
 
 class MockAutofillClient : public autofill::TestAutofillClient {
@@ -963,11 +973,17 @@
   const char kAcceptedContextHistogram[] =
       "PasswordManager.ShowAllSavedPasswordsAcceptedContext";
   base::HistogramTester histograms;
+  ukm::TestAutoSetUkmRecorder test_ukm_recorder;
 
   auto client = base::MakeUnique<TestPasswordManagerClient>();
   auto autofill_client = base::MakeUnique<MockAutofillClient>();
+  auto manager =
+      base::MakeUnique<password_manager::PasswordManager>(client.get());
   InitializePasswordAutofillManager(client.get(), autofill_client.get());
 
+  ON_CALL(*(client->mock_driver()), GetPasswordManager())
+      .WillByDefault(testing::Return(manager.get()));
+
   gfx::RectF element_bounds;
   autofill::PasswordFormFillData data;
   data.username_field.value = test_username_;
@@ -1023,6 +1039,20 @@
   histograms.ExpectUniqueSample(
       kAcceptedContextHistogram,
       metrics_util::SHOW_ALL_SAVED_PASSWORDS_CONTEXT_PASSWORD, 1);
+
+  // Trigger UKM reporting, which happens at destruction time.
+  manager.reset();
+  autofill_client.reset();
+  client.reset();
+
+  const ukm::UkmSource* source =
+      test_ukm_recorder.GetSourceForUrl(kMainFrameUrl);
+  ASSERT_TRUE(source);
+  test_ukm_recorder.ExpectMetric(
+      *source, "PageWithPassword", password_manager::kUkmPageLevelUserAction,
+      static_cast<int64_t>(
+          password_manager::PasswordManagerMetricsRecorder::
+              PageLevelUserAction::kShowAllPasswordsWhileSomeAreSuggested));
 }
 
 TEST_F(PasswordAutofillManagerTest, ShowStandaloneShowAllPasswords) {
@@ -1031,11 +1061,17 @@
   const char kAcceptedContextHistogram[] =
       "PasswordManager.ShowAllSavedPasswordsAcceptedContext";
   base::HistogramTester histograms;
+  ukm::TestAutoSetUkmRecorder test_ukm_recorder;
 
   auto client = base::MakeUnique<TestPasswordManagerClient>();
   auto autofill_client = base::MakeUnique<MockAutofillClient>();
+  auto manager =
+      base::MakeUnique<password_manager::PasswordManager>(client.get());
   InitializePasswordAutofillManager(client.get(), autofill_client.get());
 
+  ON_CALL(*(client->mock_driver()), GetPasswordManager())
+      .WillByDefault(testing::Return(manager.get()));
+
   gfx::RectF element_bounds;
   autofill::PasswordFormFillData data;
   data.username_field.value = test_username_;
@@ -1077,6 +1113,20 @@
   histograms.ExpectUniqueSample(
       kAcceptedContextHistogram,
       metrics_util::SHOW_ALL_SAVED_PASSWORDS_CONTEXT_MANUAL_FALLBACK, 1);
+
+  // Trigger UKM reporting, which happens at destruction time.
+  manager.reset();
+  autofill_client.reset();
+  client.reset();
+
+  const ukm::UkmSource* source =
+      test_ukm_recorder.GetSourceForUrl(kMainFrameUrl);
+  ASSERT_TRUE(source);
+  test_ukm_recorder.ExpectMetric(
+      *source, "PageWithPassword", password_manager::kUkmPageLevelUserAction,
+      static_cast<int64_t>(
+          password_manager::PasswordManagerMetricsRecorder::
+              PageLevelUserAction::kShowAllPasswordsWhileNoneAreSuggested));
 }
 
 // Tests that the "Show all passwords" fallback doesn't shows up in non-password
diff --git a/components/password_manager/core/browser/password_manager_metrics_recorder.cc b/components/password_manager/core/browser/password_manager_metrics_recorder.cc
index 14d2026..b92e79a 100644
--- a/components/password_manager/core/browser/password_manager_metrics_recorder.cc
+++ b/components/password_manager/core/browser/password_manager_metrics_recorder.cc
@@ -18,6 +18,7 @@
 // URL Keyed Metrics.
 const char kUkmUserModifiedPasswordField[] = "UserModifiedPasswordField";
 const char kUkmProvisionalSaveFailure[] = "ProvisionalSaveFailure";
+const char kUkmPageLevelUserAction[] = "PageLevelUserAction";
 
 PasswordManagerMetricsRecorder::PasswordManagerMetricsRecorder(
     ukm::UkmRecorder* ukm_recorder,
@@ -96,6 +97,11 @@
   }
 }
 
+void PasswordManagerMetricsRecorder::RecordPageLevelUserAction(
+    PasswordManagerMetricsRecorder::PageLevelUserAction action) {
+  RecordUkmMetric(kUkmPageLevelUserAction, static_cast<int64_t>(action));
+}
+
 void PasswordManagerMetricsRecorder::RecordUkmMetric(const char* metric_name,
                                                      int64_t value) {
   if (ukm_entry_builder_)
diff --git a/components/password_manager/core/browser/password_manager_metrics_recorder.h b/components/password_manager/core/browser/password_manager_metrics_recorder.h
index 8b95daa8..551f4ebb 100644
--- a/components/password_manager/core/browser/password_manager_metrics_recorder.h
+++ b/components/password_manager/core/browser/password_manager_metrics_recorder.h
@@ -28,6 +28,9 @@
 // offer to save a credential.
 extern const char kUkmProvisionalSaveFailure[];
 
+// UKM that records a PasswordManagerMetricsRecorder::PageLevelUserAction.
+extern const char kUkmPageLevelUserAction[];
+
 class BrowserSavePasswordProgressLogger;
 
 // The pupose of this class is to record various types of metrics about the
@@ -62,6 +65,16 @@
     MAX_FAILURE_VALUE
   };
 
+  // This enum represents user actions on a page with a password form that
+  // cannot (reliably) be attributed to a specific form manager.
+  enum class PageLevelUserAction {
+    kUnknown = 0,
+
+    // User chose to open the password viewer as part of a manual fallback.
+    kShowAllPasswordsWhileSomeAreSuggested,
+    kShowAllPasswordsWhileNoneAreSuggested,
+  };
+
   // Records UKM metrics and reports them on destruction. The |source_id| is
   // (re-)bound to |main_frame_url| shortly before reporting. As such it is
   // crucial that the |source_id| is never bound to a different URL by another
@@ -92,6 +105,9 @@
                                     const GURL& form_origin,
                                     BrowserSavePasswordProgressLogger* logger);
 
+  // Records a user action.
+  void RecordPageLevelUserAction(PageLevelUserAction action);
+
  private:
   // Records a metric into |ukm_entry_builder_| if it is not nullptr.
   void RecordUkmMetric(const char* metric_name, int64_t value);
diff --git a/components/password_manager/core/browser/password_manager_metrics_util.h b/components/password_manager/core/browser/password_manager_metrics_util.h
index 03d1d9ee..aa247738 100644
--- a/components/password_manager/core/browser/password_manager_metrics_util.h
+++ b/components/password_manager/core/browser/password_manager_metrics_util.h
@@ -249,7 +249,12 @@
 // - PasswordManager.ShowAllSavedPasswordsShownContext
 enum ShowAllSavedPasswordsContext {
   SHOW_ALL_SAVED_PASSWORDS_CONTEXT_NONE,
+  // The "Show all saved passwords..." fallback is shown below a list of
+  // available passwords.
   SHOW_ALL_SAVED_PASSWORDS_CONTEXT_PASSWORD,
+  // The "Show all saved passwords..." fallback is shown when no available
+  // passwords can be suggested to the user, e.g. because none are saved or
+  // because of technical issues.
   SHOW_ALL_SAVED_PASSWORDS_CONTEXT_MANUAL_FALLBACK,
   SHOW_ALL_SAVED_PASSWORDS_CONTEXT_COUNT
 };
diff --git a/components/payments/content/BUILD.gn b/components/payments/content/BUILD.gn
index 433dd62..0d788c1 100644
--- a/components/payments/content/BUILD.gn
+++ b/components/payments/content/BUILD.gn
@@ -42,12 +42,9 @@
     "payment_details_validation.h",
     "payment_manifest_parser_host.cc",
     "payment_manifest_parser_host.h",
-    "payments_validators.cc",
-    "payments_validators.h",
   ]
 
   deps = [
-    "//base",
     "//components/autofill/core/browser",
     "//components/payments/core",
     "//components/payments/mojom:mojom_parser",
@@ -55,9 +52,7 @@
     "//content/public/browser",
     "//net",
     "//third_party/WebKit/public:blink_headers",
-    "//third_party/re2",
     "//ui/base",
-    "//url",
   ]
 
   public_deps = [
@@ -72,7 +67,6 @@
     "payment_request_spec_unittest.cc",
     "payment_request_state_unittest.cc",
     "payment_response_helper_unittest.cc",
-    "payments_validators_unittest.cc",
   ]
 
   deps = [
diff --git a/components/payments/content/DEPS b/components/payments/content/DEPS
index 62650f08..a2a378b6 100644
--- a/components/payments/content/DEPS
+++ b/components/payments/content/DEPS
@@ -10,6 +10,5 @@
   "+net",
   "+third_party/WebKit/public/platform/modules/payments",
   "+third_party/libphonenumber",
-  "+third_party/re2",
   "+ui/base",
 ]
diff --git a/components/payments/content/payment_details_validation.cc b/components/payments/content/payment_details_validation.cc
index 59b5a02e..9e92682a9 100644
--- a/components/payments/content/payment_details_validation.cc
+++ b/components/payments/content/payment_details_validation.cc
@@ -7,7 +7,7 @@
 #include <set>
 #include <vector>
 
-#include "components/payments/content/payments_validators.h"
+#include "components/payments/core/payments_validators.h"
 #include "third_party/WebKit/public/platform/modules/payments/payment_request.mojom.h"
 
 namespace payments {
diff --git a/components/payments/content/payment_response_helper.cc b/components/payments/content/payment_response_helper.cc
index 7e06cdd3..06a6687 100644
--- a/components/payments/content/payment_response_helper.cc
+++ b/components/payments/content/payment_response_helper.cc
@@ -13,9 +13,9 @@
 #include "components/autofill/core/browser/autofill_data_util.h"
 #include "components/autofill/core/browser/autofill_type.h"
 #include "components/payments/content/payment_request_spec.h"
-#include "components/payments/content/payments_validators.h"
 #include "components/payments/core/payment_request_data_util.h"
 #include "components/payments/core/payment_request_delegate.h"
+#include "components/payments/core/payments_validators.h"
 
 namespace payments {
 
diff --git a/components/payments/core/BUILD.gn b/components/payments/core/BUILD.gn
index ec728ab..411ccc9 100644
--- a/components/payments/core/BUILD.gn
+++ b/components/payments/core/BUILD.gn
@@ -38,6 +38,8 @@
     "payment_request_delegate.h",
     "payments_profile_comparator.cc",
     "payments_profile_comparator.h",
+    "payments_validators.cc",
+    "payments_validators.h",
     "strings_util.cc",
     "strings_util.h",
     "subkey_requester.cc",
@@ -55,6 +57,7 @@
     "//components/ukm",
     "//net",
     "//third_party/libphonenumber",
+    "//third_party/re2",
     "//ui/base",
     "//url",
   ]
@@ -101,6 +104,7 @@
     "payment_method_data_unittest.cc",
     "payment_request_data_util_unittest.cc",
     "payments_profile_comparator_unittest.cc",
+    "payments_validators_unittest.cc",
     "strings_util_unittest.cc",
     "subkey_requester_unittest.cc",
   ]
diff --git a/components/payments/core/DEPS b/components/payments/core/DEPS
index 31749cb1..207e47c5 100644
--- a/components/payments/core/DEPS
+++ b/components/payments/core/DEPS
@@ -13,6 +13,7 @@
   "+services/metrics/public",
   "+third_party/libaddressinput",
   "+third_party/libphonenumber",
+  "+third_party/re2",
   "+ui/base",
 ]
 
diff --git a/components/payments/content/payments_validators.cc b/components/payments/core/payments_validators.cc
similarity index 81%
rename from components/payments/content/payments_validators.cc
rename to components/payments/core/payments_validators.cc
index 908582d..67318af 100644
--- a/components/payments/content/payments_validators.cc
+++ b/components/payments/core/payments_validators.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 "components/payments/content/payments_validators.h"
+#include "components/payments/core/payments_validators.h"
 
 #include "third_party/re2/src/re2/re2.h"
 #include "url/gurl.h"
@@ -117,31 +117,6 @@
 }
 
 // static
-bool PaymentsValidators::IsValidShippingAddress(
-    const mojom::PaymentAddressPtr& address,
-    std::string* optional_error_message) {
-  if (!IsValidCountryCodeFormat(address->country, optional_error_message))
-    return false;
-
-  if (!IsValidLanguageCodeFormat(address->language_code,
-                                 optional_error_message))
-    return false;
-
-  if (!IsValidScriptCodeFormat(address->script_code, optional_error_message))
-    return false;
-
-  if (address->language_code.empty() && !address->script_code.empty()) {
-    if (optional_error_message)
-      *optional_error_message =
-          "If language code is empty, then script code should also be empty";
-
-    return false;
-  }
-
-  return true;
-}
-
-// static
 bool PaymentsValidators::IsValidErrorMsgFormat(
     const std::string& error,
     std::string* optional_error_message) {
diff --git a/components/payments/content/payments_validators.h b/components/payments/core/payments_validators.h
similarity index 78%
rename from components/payments/content/payments_validators.h
rename to components/payments/core/payments_validators.h
index 3079d23..4fed720 100644
--- a/components/payments/content/payments_validators.h
+++ b/components/payments/core/payments_validators.h
@@ -2,13 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_PAYMENTS_CONTENT_PAYMENTS_VALIDATORS_H_
-#define COMPONENTS_PAYMENTS_CONTENT_PAYMENTS_VALIDATORS_H_
+#ifndef COMPONENTS_PAYMENTS_CORE_PAYMENTS_VALIDATORS_H_
+#define COMPONENTS_PAYMENTS_CORE_PAYMENTS_VALIDATORS_H_
 
 #include <string>
 
 #include "base/macros.h"
-#include "third_party/WebKit/public/platform/modules/payments/payment_request.mojom.h"
 
 namespace payments {
 
@@ -47,14 +46,6 @@
                                std::string* language_code,
                                std::string* script_code);
 
-  // Returns true if the payment address is valid:
-  //  - Has a valid region code
-  //  - Has a valid language code, if any.
-  //  - Has a valid script code, if any.
-  // A script code should be present only if language code is present.
-  static bool IsValidShippingAddress(const mojom::PaymentAddressPtr& address,
-                                     std::string* optional_error_message);
-
   // Returns false if |error| is too long (greater than 2048).
   static bool IsValidErrorMsgFormat(const std::string& code,
                                     std::string* optional_error_message);
@@ -65,4 +56,4 @@
 
 }  // namespace payments
 
-#endif  // COMPONENTS_PAYMENTS_CONTENT_PAYMENTS_VALIDATORS_H_
+#endif  // COMPONENTS_PAYMENTS_CORE_PAYMENTS_VALIDATORS_H_
diff --git a/components/payments/content/payments_validators_unittest.cc b/components/payments/core/payments_validators_unittest.cc
similarity index 84%
rename from components/payments/content/payments_validators_unittest.cc
rename to components/payments/core/payments_validators_unittest.cc
index 4b23440..4e459b9 100644
--- a/components/payments/content/payments_validators_unittest.cc
+++ b/components/payments/core/payments_validators_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/payments/content/payments_validators.h"
+#include "components/payments/core/payments_validators.h"
 
 #include <ostream>  // NOLINT
 #include "testing/gtest/include/gtest/gtest.h"
@@ -272,58 +272,5 @@
                     LanguageTagTestCase("en-Latn-US", "en", "Latn"),
                     LanguageTagTestCase("en-US", "en", "")));
 
-struct ShippingAddressTestCase {
-  ShippingAddressTestCase(const char* country_code,
-                          const char* language_code,
-                          const char* script_code,
-                          bool expected_valid)
-      : country_code(country_code),
-        language_code(language_code),
-        script_code(script_code),
-        expected_valid(expected_valid) {}
-  ~ShippingAddressTestCase() {}
-
-  const char* country_code;
-  const char* language_code;
-  const char* script_code;
-  bool expected_valid;
-};
-
-class PaymentsShippingAddressValidatorTest
-    : public testing::TestWithParam<ShippingAddressTestCase> {};
-
-TEST_P(PaymentsShippingAddressValidatorTest, IsValidShippingAddress) {
-  payments::mojom::PaymentAddressPtr address =
-      payments::mojom::PaymentAddress::New();
-  address->country = GetParam().country_code;
-  address->language_code = GetParam().language_code;
-  address->script_code = GetParam().script_code;
-
-  std::string error_message;
-  EXPECT_EQ(GetParam().expected_valid,
-            payments::PaymentsValidators::IsValidShippingAddress(
-                address, &error_message))
-      << error_message;
-  EXPECT_EQ(GetParam().expected_valid, error_message.empty()) << error_message;
-
-  EXPECT_EQ(
-      GetParam().expected_valid,
-      payments::PaymentsValidators::IsValidShippingAddress(address, nullptr));
-}
-
-INSTANTIATE_TEST_CASE_P(
-    ShippingAddresses,
-    PaymentsShippingAddressValidatorTest,
-    testing::Values(
-        ShippingAddressTestCase("US", "en", "Latn", true),
-        ShippingAddressTestCase("US", "en", "", true),
-        ShippingAddressTestCase("US", "", "", true),
-        // Invalid shipping addresses
-        ShippingAddressTestCase("", "", "", false),
-        ShippingAddressTestCase("InvalidCountryCode", "", "", false),
-        ShippingAddressTestCase("US", "InvalidLanguageCode", "", false),
-        ShippingAddressTestCase("US", "en", "InvalidScriptCode", false),
-        ShippingAddressTestCase("US", "", "Latn", false)));
-
 }  // namespace
 }  // namespace payments
diff --git a/components/printing/browser/print_manager_utils.cc b/components/printing/browser/print_manager_utils.cc
index 7be67b19..3d71a98 100644
--- a/components/printing/browser/print_manager_utils.cc
+++ b/components/printing/browser/print_manager_utils.cc
@@ -32,6 +32,7 @@
   params->display_header_footer = settings.display_header_footer();
   params->title = settings.title();
   params->url = settings.url();
+  params->printed_doc_type = SkiaDocumentType::PDF;
 }
 
 }  // namespace printing
diff --git a/components/printing/common/BUILD.gn b/components/printing/common/BUILD.gn
index e3f8e73..11fca1b 100644
--- a/components/printing/common/BUILD.gn
+++ b/components/printing/common/BUILD.gn
@@ -13,6 +13,7 @@
     "//base",
     "//ipc",
     "//printing",
+    "//printing/common:common",
     "//third_party/WebKit/public:blink_headers",
     "//ui/gfx",
     "//ui/gfx/geometry",
diff --git a/components/printing/common/print_messages.cc b/components/printing/common/print_messages.cc
index de036345..4a7c94d 100644
--- a/components/printing/common/print_messages.cc
+++ b/components/printing/common/print_messages.cc
@@ -61,7 +61,8 @@
       display_header_footer(false),
       title(),
       url(),
-      should_print_backgrounds(false) {}
+      should_print_backgrounds(false),
+      printed_doc_type(printing::SkiaDocumentType::PDF) {}
 
 PrintMsg_Print_Params::PrintMsg_Print_Params(
     const PrintMsg_Print_Params& other) = default;
@@ -89,6 +90,7 @@
   title = base::string16();
   url = base::string16();
   should_print_backgrounds = false;
+  printed_doc_type = printing::SkiaDocumentType::PDF;
 }
 
 PrintMsg_PrintPages_Params::PrintMsg_PrintPages_Params()
diff --git a/components/printing/common/print_messages.h b/components/printing/common/print_messages.h
index fd39591..f9b1daf9 100644
--- a/components/printing/common/print_messages.h
+++ b/components/printing/common/print_messages.h
@@ -15,6 +15,7 @@
 #include "build/build_config.h"
 #include "components/printing/common/printing_param_traits_macros.h"
 #include "ipc/ipc_message_macros.h"
+#include "printing/common/pdf_metafile_utils.h"
 #include "printing/features/features.h"
 #include "printing/page_range.h"
 #include "printing/page_size_margins.h"
@@ -60,6 +61,7 @@
   base::string16 title;
   base::string16 url;
   bool should_print_backgrounds;
+  printing::SkiaDocumentType printed_doc_type;
 };
 
 struct PrintMsg_PrintPages_Params {
@@ -101,6 +103,8 @@
 
 IPC_ENUM_TRAITS_MAX_VALUE(blink::WebPrintScalingOption,
                           blink::kWebPrintScalingOptionLast)
+IPC_ENUM_TRAITS_MAX_VALUE(printing::SkiaDocumentType,
+                          printing::SkiaDocumentType::MAX)
 
 // Parameters for a render request.
 IPC_STRUCT_TRAITS_BEGIN(PrintMsg_Print_Params)
@@ -166,6 +170,9 @@
 
   // True if print backgrounds is requested by the user.
   IPC_STRUCT_TRAITS_MEMBER(should_print_backgrounds)
+
+  // The document type of printed page(s) from render.
+  IPC_STRUCT_TRAITS_MEMBER(printed_doc_type)
 IPC_STRUCT_TRAITS_END()
 
 IPC_STRUCT_TRAITS_BEGIN(printing::PageRange)
diff --git a/components/printing/renderer/print_render_frame_helper.cc b/components/printing/renderer/print_render_frame_helper.cc
index 005bc33..bded6831 100644
--- a/components/printing/renderer/print_render_frame_helper.cc
+++ b/components/printing/renderer/print_render_frame_helper.cc
@@ -804,7 +804,6 @@
       prev_scroll_offset_ = web_frame->ToWebLocalFrame()->GetScrollOffset();
   }
   prev_view_size_ = web_view->Size();
-
   web_view->Resize(print_layout_size);
 }
 
@@ -1300,8 +1299,8 @@
   const PrintMsg_Print_Params& print_params = print_pages_params_->params;
   const std::vector<int>& pages = print_pages_params_->pages;
 
-  if (!print_preview_context_.CreatePreviewDocument(prep_frame_view_.release(),
-                                                    pages)) {
+  if (!print_preview_context_.CreatePreviewDocument(
+          prep_frame_view_.release(), pages, print_params.printed_doc_type)) {
     return false;
   }
 
@@ -1407,7 +1406,8 @@
   std::unique_ptr<PdfMetafileSkia> draft_metafile;
   PdfMetafileSkia* initial_render_metafile = print_preview_context_.metafile();
   if (print_preview_context_.IsModifiable() && is_print_ready_metafile_sent_) {
-    draft_metafile = base::MakeUnique<PdfMetafileSkia>(SkiaDocumentType::PDF);
+    draft_metafile =
+        base::MakeUnique<PdfMetafileSkia>(print_params.printed_doc_type);
     initial_render_metafile = draft_metafile.get();
   }
 
@@ -1425,7 +1425,7 @@
     DCHECK(!draft_metafile.get());
     draft_metafile =
         print_preview_context_.metafile()->GetMetafileForCurrentPage(
-            SkiaDocumentType::PDF);
+            print_params.printed_doc_type);
   }
   return PreviewPageRendered(page_number, draft_metafile.get());
 }
@@ -1438,9 +1438,8 @@
   PdfMetafileSkia* metafile = print_preview_context_.metafile();
   PrintHostMsg_DidPreviewDocument_Params preview_params;
 
-  // Ask the browser to create the shared memory for us.
   if (!CopyMetafileDataToSharedMem(*metafile,
-                                   &(preview_params.metafile_data_handle))) {
+                                   &preview_params.metafile_data_handle)) {
     LOG(ERROR) << "CopyMetafileDataToSharedMem failed";
     print_preview_context_.set_error(PREVIEW_ERROR_METAFILE_COPY_FAILED);
     return false;
@@ -2140,13 +2139,13 @@
   }
 
   PrintHostMsg_DidPreviewPage_Params preview_page_params;
-  // Get the size of the resulting metafile.
-  if (!CopyMetafileDataToSharedMem(
-          *metafile, &(preview_page_params.metafile_data_handle))) {
+  if (!CopyMetafileDataToSharedMem(*metafile,
+                                   &preview_page_params.metafile_data_handle)) {
     LOG(ERROR) << "CopyMetafileDataToSharedMem failed";
     print_preview_context_.set_error(PREVIEW_ERROR_METAFILE_COPY_FAILED);
     return false;
   }
+
   preview_page_params.data_size = metafile->GetDataSize();
   preview_page_params.page_number = page_number;
   preview_page_params.preview_request_id =
@@ -2196,7 +2195,8 @@
 
 bool PrintRenderFrameHelper::PrintPreviewContext::CreatePreviewDocument(
     PrepareFrameAndViewForPrint* prepared_frame,
-    const std::vector<int>& pages) {
+    const std::vector<int>& pages,
+    SkiaDocumentType doc_type) {
   DCHECK_EQ(INITIALIZED, state_);
   state_ = RENDERING;
 
@@ -2211,7 +2211,7 @@
     return false;
   }
 
-  metafile_ = base::MakeUnique<PdfMetafileSkia>(SkiaDocumentType::PDF);
+  metafile_ = base::MakeUnique<PdfMetafileSkia>(doc_type);
   CHECK(metafile_->Init());
 
   current_page_index_ = 0;
diff --git a/components/printing/renderer/print_render_frame_helper.h b/components/printing/renderer/print_render_frame_helper.h
index 05bf469..1922098b 100644
--- a/components/printing/renderer/print_render_frame_helper.h
+++ b/components/printing/renderer/print_render_frame_helper.h
@@ -353,8 +353,11 @@
       const PrintMsg_PrintPages_Params& params,
       int page_count);
 
-  // Given the |canvas| to draw on, prints the appropriate headers and footers
-  // to |canvas| using information from the remaining parameters.
+  // Helper function to find document type.
+  static SkiaDocumentType GetDocType(const PrintMsg_Print_Params& params);
+
+  // Given the |device| and |canvas| to draw on, prints the appropriate headers
+  // and footers using strings from |header_footer_info| on to the canvas.
   static void PrintHeaderAndFooter(blink::WebCanvas* canvas,
                                    int page_number,
                                    int total_pages,
@@ -433,7 +436,8 @@
     // Create the print preview document. |pages| is empty to print all pages.
     // Takes ownership of |prepared_frame|.
     bool CreatePreviewDocument(PrepareFrameAndViewForPrint* prepared_frame,
-                               const std::vector<int>& pages);
+                               const std::vector<int>& pages,
+                               SkiaDocumentType doc_type);
 
     // Called after a page gets rendered. |page_time| is how long the
     // rendering took.
diff --git a/components/printing/renderer/print_render_frame_helper_linux.cc b/components/printing/renderer/print_render_frame_helper_linux.cc
index c49b4ea..f5ae4cdc 100644
--- a/components/printing/renderer/print_render_frame_helper_linux.cc
+++ b/components/printing/renderer/print_render_frame_helper_linux.cc
@@ -46,10 +46,11 @@
 #if BUILDFLAG(ENABLE_BASIC_PRINTING)
 bool PrintRenderFrameHelper::PrintPagesNative(blink::WebLocalFrame* frame,
                                               int page_count) {
-  PdfMetafileSkia metafile(SkiaDocumentType::PDF);
+  const PrintMsg_PrintPages_Params& params = *print_pages_params_;
+  const PrintMsg_Print_Params& print_params = params.params;
+  PdfMetafileSkia metafile(print_params.printed_doc_type);
   CHECK(metafile.Init());
 
-  const PrintMsg_PrintPages_Params& params = *print_pages_params_;
   std::vector<int> printed_pages = GetPrintedPages(params, page_count);
   if (printed_pages.empty())
     return false;
diff --git a/components/printing/renderer/print_render_frame_helper_mac.mm b/components/printing/renderer/print_render_frame_helper_mac.mm
index 589a1a4..4214995 100644
--- a/components/printing/renderer/print_render_frame_helper_mac.mm
+++ b/components/printing/renderer/print_render_frame_helper_mac.mm
@@ -45,7 +45,7 @@
     const std::vector<int>& printed_pages,
     int page_count,
     blink::WebLocalFrame* frame) {
-  PdfMetafileSkia metafile(SkiaDocumentType::PDF);
+  PdfMetafileSkia metafile(params.printed_doc_type);
   CHECK(metafile.Init());
 
   gfx::Size page_size_in_dpi;
@@ -64,7 +64,7 @@
 
   // Ask the browser to create the shared memory for us.
   if (!CopyMetafileDataToSharedMem(metafile,
-                                   &(page_params.metafile_data_handle))) {
+                                   &page_params.metafile_data_handle)) {
     // TODO(thestig): Fail and return false instead.
     page_params.data_size = 0;
   }
@@ -80,7 +80,6 @@
 bool PrintRenderFrameHelper::RenderPreviewPage(
     int page_number,
     const PrintMsg_Print_Params& print_params) {
-  PrintMsg_Print_Params printParams = print_params;
   std::unique_ptr<PdfMetafileSkia> draft_metafile;
   PdfMetafileSkia* initial_render_metafile = print_preview_context_.metafile();
 
@@ -88,14 +87,15 @@
       print_preview_context_.IsModifiable() && is_print_ready_metafile_sent_;
 
   if (render_to_draft) {
-    draft_metafile.reset(new PdfMetafileSkia(SkiaDocumentType::PDF));
+    draft_metafile =
+        base::MakeUnique<PdfMetafileSkia>(print_params.printed_doc_type);
     CHECK(draft_metafile->Init());
     initial_render_metafile = draft_metafile.get();
   }
 
   base::TimeTicks begin_time = base::TimeTicks::Now();
   gfx::Size page_size;
-  RenderPage(printParams, page_number,
+  RenderPage(print_params, page_number,
              print_preview_context_.total_page_count(),
              print_preview_context_.prepared_frame(), true,
              initial_render_metafile, &page_size, NULL);
@@ -110,7 +110,7 @@
       DCHECK(!draft_metafile.get());
       draft_metafile =
           print_preview_context_.metafile()->GetMetafileForCurrentPage(
-              SkiaDocumentType::PDF);
+              print_params.printed_doc_type);
     }
   }
   return PreviewPageRendered(page_number, draft_metafile.get());
diff --git a/components/printing/renderer/print_render_frame_helper_pdf_win.cc b/components/printing/renderer/print_render_frame_helper_pdf_win.cc
index d1e31f4..a746fd9 100644
--- a/components/printing/renderer/print_render_frame_helper_pdf_win.cc
+++ b/components/printing/renderer/print_render_frame_helper_pdf_win.cc
@@ -26,7 +26,7 @@
   std::vector<gfx::Rect> content_area_in_dpi(printed_pages.size());
   std::vector<gfx::Rect> printable_area_in_dpi(printed_pages.size());
 
-  PdfMetafileSkia metafile(SkiaDocumentType::PDF);
+  PdfMetafileSkia metafile(params.params.printed_doc_type);
   CHECK(metafile.Init());
 
   for (size_t i = 0; i < printed_pages.size(); ++i) {
diff --git a/components/safe_browsing/features.cc b/components/safe_browsing/features.cc
index f4085095..f2e708ea 100644
--- a/components/safe_browsing/features.cc
+++ b/components/safe_browsing/features.cc
@@ -22,6 +22,9 @@
 const base::Feature kAdSamplerTriggerFeature{"SafeBrowsingAdSamplerTrigger",
                                              base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kGaiaPasswordReuseReporting{
+    "SyncPasswordReuseEvent", base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kGoogleBrandedPhishingWarning{
     "PasswordProtectionGoogleBrandedPhishingWarning",
     base::FEATURE_DISABLED_BY_DEFAULT};
@@ -48,9 +51,6 @@
 const base::Feature kProtectedPasswordEntryPinging{
     "ProtectedPasswordEntryPinging", base::FEATURE_ENABLED_BY_DEFAULT};
 
-const base::Feature kSyncPasswordReuseEvent{"SyncPasswordReuseEvent",
-                                            base::FEATURE_DISABLED_BY_DEFAULT};
-
 const base::Feature kThreatDomDetailsTagAndAttributeFeature{
     "ThreatDomDetailsTagAttributes", base::FEATURE_DISABLED_BY_DEFAULT};
 
@@ -67,13 +67,13 @@
   bool probabilistically_enabled;
 } kExperimentalFeatures[]{
     {&kAdSamplerTriggerFeature, false},
+    {&kGaiaPasswordReuseReporting, true},
     {&kGoogleBrandedPhishingWarning, false},
     {&kLocalDatabaseManagerEnabled, true},
     {&kParallelUrlCheck, true},
     {&kPasswordFieldOnFocusPinging, true},
     {&kPasswordProtectionInterstitial, false},
     {&kProtectedPasswordEntryPinging, true},
-    {&kSyncPasswordReuseEvent, true},
     {&kThreatDomDetailsTagAndAttributeFeature, false},
     {&kV4OnlyEnabled, true},
 };
diff --git a/components/safe_browsing/features.h b/components/safe_browsing/features.h
index 7b222fc8..7d3cea8c 100644
--- a/components/safe_browsing/features.h
+++ b/components/safe_browsing/features.h
@@ -20,14 +20,14 @@
 namespace safe_browsing {
 // Features list
 extern const base::Feature kAdSamplerTriggerFeature;
+// Gates logging of GaiaPasswordReuse user events.
+extern const base::Feature kGaiaPasswordReuseReporting;
 extern const base::Feature kGoogleBrandedPhishingWarning;
 extern const base::Feature kLocalDatabaseManagerEnabled;
 extern const base::Feature kParallelUrlCheck;
 extern const base::Feature kPasswordFieldOnFocusPinging;
 extern const base::Feature kPasswordProtectionInterstitial;
 extern const base::Feature kProtectedPasswordEntryPinging;
-// Gates registration for SyncPasswordReuseEvent events.
-extern const base::Feature kSyncPasswordReuseEvent;
 // Specifies which non-resource HTML Elements to collect based on their tag and
 // attributes. It's a single param containing a comma-separated list of pairs.
 // For example: "tag1,id,tag1,height,tag2,foo" - this will collect elements with
diff --git a/components/security_state/content/content_utils.cc b/components/security_state/content/content_utils.cc
index c532fc1..d7608b4a 100644
--- a/components/security_state/content/content_utils.cc
+++ b/components/security_state/content/content_utils.cc
@@ -40,9 +40,7 @@
     case security_state::NONE:
     case security_state::HTTP_SHOW_WARNING:
       return blink::kWebSecurityStyleNeutral;
-    case security_state::SECURITY_WARNING:
     case security_state::SECURE_WITH_POLICY_INSTALLED_CERT:
-      return blink::kWebSecurityStyleWarning;
     case security_state::EV_SECURE:
     case security_state::SECURE:
       return blink::kWebSecurityStyleSecure;
diff --git a/components/security_state/core/security_state.h b/components/security_state/core/security_state.h
index 2b3c366..dab5dd88 100644
--- a/components/security_state/core/security_state.h
+++ b/components/security_state/core/security_state.h
@@ -56,11 +56,10 @@
   // HTTPS (non-EV) with valid cert.
   SECURE,
 
-  // Obsolete, do not use. TODO(lgarron): Remove via https://crbug.com/645698.
-  SECURITY_WARNING,
-
   // HTTPS, but the certificate verification chain is anchored on a
   // certificate that was installed by the system administrator.
+  //
+  // Currently used only on ChromeOS.
   SECURE_WITH_POLICY_INSTALLED_CERT,
 
   // Attempted HTTPS and failed, page not authenticated, HTTPS with
diff --git a/components/security_state/core/security_state_ui.cc b/components/security_state/core/security_state_ui.cc
index 31aff66..91b53ea8 100644
--- a/components/security_state/core/security_state_ui.cc
+++ b/components/security_state/core/security_state_ui.cc
@@ -15,7 +15,6 @@
     case HTTP_SHOW_WARNING:
     case EV_SECURE:
     case SECURE:
-    case SECURITY_WARNING:
     case SECURE_WITH_POLICY_INSTALLED_CERT:
     case DANGEROUS:
       return true;
diff --git a/components/storage_monitor/storage_monitor_chromeos.cc b/components/storage_monitor/storage_monitor_chromeos.cc
index fea7ccd..366d97e 100644
--- a/components/storage_monitor/storage_monitor_chromeos.cc
+++ b/components/storage_monitor/storage_monitor_chromeos.cc
@@ -187,6 +187,10 @@
                                        chromeos::FormatError error_code,
                                        const std::string& device_path) {}
 
+void StorageMonitorCros::OnRenameEvent(DiskMountManager::RenameEvent event,
+                                       chromeos::RenameError error_code,
+                                       const std::string& device_path) {}
+
 void StorageMonitorCros::SetMediaTransferProtocolManagerForTest(
     device::MediaTransferProtocolManager* test_manager) {
   DCHECK(!media_transfer_protocol_manager_);
diff --git a/components/storage_monitor/storage_monitor_chromeos.h b/components/storage_monitor/storage_monitor_chromeos.h
index b427c220..30034971 100644
--- a/components/storage_monitor/storage_monitor_chromeos.h
+++ b/components/storage_monitor/storage_monitor_chromeos.h
@@ -57,6 +57,9 @@
   void OnFormatEvent(chromeos::disks::DiskMountManager::FormatEvent event,
                      chromeos::FormatError error_code,
                      const std::string& device_path) override;
+  void OnRenameEvent(chromeos::disks::DiskMountManager::RenameEvent event,
+                     chromeos::RenameError error_code,
+                     const std::string& device_path) override;
 
   // StorageMonitor implementation.
   bool GetStorageInfoForPath(const base::FilePath& path,
diff --git a/components/storage_monitor/storage_monitor_chromeos_unittest.cc b/components/storage_monitor/storage_monitor_chromeos_unittest.cc
index 00f5ff6..c4127fb2 100644
--- a/components/storage_monitor/storage_monitor_chromeos_unittest.cc
+++ b/components/storage_monitor/storage_monitor_chromeos_unittest.cc
@@ -48,6 +48,7 @@
 const char kUniqueId1[] = "FFFF-FFFF";
 const char kUniqueId2[] = "FFFF-FF0F";
 const char kVendorName[] = "CompanyA";
+const char kFileSystemType[] = "exfat";
 
 uint64_t kDevice1SizeInBytes = 113048;
 uint64_t kDevice2SizeInBytes = 212312;
@@ -199,17 +200,10 @@
     uint64_t device_size_in_bytes) {
   if (error_code == chromeos::MOUNT_ERROR_NONE) {
     disk_mount_manager_mock_->CreateDiskEntryForMountDevice(
-        mount_info,
-        unique_id,
-        device_label,
-        vendor_name,
-        product_name,
-        device_type,
-        device_size_in_bytes,
-        false /* is_parent */,
-        true /* has_media */,
-        false /* on_boot_device */,
-        true /* on_removable_device */);
+        mount_info, unique_id, device_label, vendor_name, product_name,
+        device_type, device_size_in_bytes, false /* is_parent */,
+        true /* has_media */, false /* on_boot_device */,
+        true /* on_removable_device */, kFileSystemType);
   }
   monitor_->OnMountEvent(DiskMountManager::MOUNTING, error_code, mount_info);
   scoped_task_environment_.RunUntilIdle();
diff --git a/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.cc b/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.cc
index 9781615..59e0d671 100644
--- a/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.cc
+++ b/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.cc
@@ -24,23 +24,6 @@
 
 namespace subresource_filter {
 
-namespace {
-
-// Records histograms about the pattern of redirect chains, and about the
-// pattern of whether the last URL in the chain matched the activation list.
-#define REPORT_REDIRECT_PATTERN_FOR_SUFFIX(suffix, is_matched, chain_size)    \
-  do {                                                                        \
-    UMA_HISTOGRAM_BOOLEAN("SubresourceFilter.PageLoad.FinalURLMatch." suffix, \
-                          is_matched);                                        \
-    if (is_matched) {                                                         \
-      UMA_HISTOGRAM_COUNTS(                                                   \
-          "SubresourceFilter.PageLoad.RedirectChainLength." suffix,           \
-          chain_size);                                                        \
-    };                                                                        \
-  } while (0)
-
-}  // namespace
-
 SubresourceFilterSafeBrowsingActivationThrottle::
     SubresourceFilterSafeBrowsingActivationThrottle(
         content::NavigationHandle* handle,
@@ -73,11 +56,37 @@
   // The last check could be ongoing when the navigation is cancelled.
   if (check_results_.empty() || !check_results_.back().finished)
     return;
+  ActivationList matched_list = GetListForThreatTypeAndMetadata(
+      check_results_.back().threat_type, check_results_.back().threat_metadata);
+
   // TODO(csharrison): Log more metrics based on check_results_.
-  RecordRedirectChainMatchPatternForList(
-      ActivationList::SOCIAL_ENG_ADS_INTERSTITIAL);
-  RecordRedirectChainMatchPatternForList(ActivationList::PHISHING_INTERSTITIAL);
-  RecordRedirectChainMatchPatternForList(ActivationList::SUBRESOURCE_FILTER);
+  UMA_HISTOGRAM_ENUMERATION("SubresourceFilter.PageLoad.ActivationList",
+                            matched_list,
+                            static_cast<int>(ActivationList::LAST) + 1);
+
+  size_t chain_size = check_results_.size();
+  switch (matched_list) {
+    case ActivationList::SOCIAL_ENG_ADS_INTERSTITIAL:
+      UMA_HISTOGRAM_COUNTS(
+          "SubresourceFilter.PageLoad.RedirectChainLength."
+          "SocialEngineeringAdsInterstitial",
+          chain_size);
+      break;
+    case ActivationList::PHISHING_INTERSTITIAL:
+      UMA_HISTOGRAM_COUNTS(
+          "SubresourceFilter.PageLoad.RedirectChainLength."
+          "PhishingInterstitial",
+          chain_size);
+      break;
+    case ActivationList::SUBRESOURCE_FILTER:
+      UMA_HISTOGRAM_COUNTS(
+          "SubresourceFilter.PageLoad.RedirectChainLength."
+          "SubresourceFilterOnly",
+          chain_size);
+      break;
+    default:
+      break;
+  }
 }
 
 bool SubresourceFilterSafeBrowsingActivationThrottle::NavigationIsPageReload(
@@ -303,30 +312,4 @@
   return false;
 }
 
-void SubresourceFilterSafeBrowsingActivationThrottle::
-    RecordRedirectChainMatchPatternForList(ActivationList activation_list) {
-  DCHECK(check_results_.back().finished);
-  ActivationList matched_list = GetListForThreatTypeAndMetadata(
-      check_results_.back().threat_type, check_results_.back().threat_metadata);
-  bool is_matched = matched_list == activation_list;
-  size_t chain_size = check_results_.size();
-  switch (activation_list) {
-    case ActivationList::SOCIAL_ENG_ADS_INTERSTITIAL:
-      REPORT_REDIRECT_PATTERN_FOR_SUFFIX("SocialEngineeringAdsInterstitial",
-                                         is_matched, chain_size);
-      break;
-    case ActivationList::PHISHING_INTERSTITIAL:
-      REPORT_REDIRECT_PATTERN_FOR_SUFFIX("PhishingInterstitial", is_matched,
-                                         chain_size);
-      break;
-    case ActivationList::SUBRESOURCE_FILTER:
-      REPORT_REDIRECT_PATTERN_FOR_SUFFIX("SubresourceFilterOnly", is_matched,
-                                         chain_size);
-      break;
-    default:
-      NOTREACHED();
-      break;
-  }
-}
-
 }  //  namespace subresource_filter
diff --git a/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.h b/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.h
index e729cd2..e9feaef 100644
--- a/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.h
+++ b/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.h
@@ -75,8 +75,6 @@
       const Configuration::ActivationConditions& conditions,
       ActivationList matched_list) const;
 
-  void RecordRedirectChainMatchPatternForList(ActivationList activation_list);
-
   std::vector<SubresourceFilterSafeBrowsingClient::CheckResult> check_results_;
 
   scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
diff --git a/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle_unittest.cc b/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle_unittest.cc
index 4f889625..2670aefb 100644
--- a/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle_unittest.cc
+++ b/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle_unittest.cc
@@ -69,7 +69,7 @@
 const char kSafeBrowsingCheckTime[] =
     "SubresourceFilter.SafeBrowsing.CheckTime";
 const char kMatchesPatternHistogramName[] =
-    "SubresourceFilter.PageLoad.FinalURLMatch.";
+    "SubresourceFilter.PageLoad.ActivationList";
 const char kNavigationChainSize[] =
     "SubresourceFilter.PageLoad.RedirectChainLength.";
 
@@ -148,13 +148,6 @@
      safe_browsing::ThreatPatternType::NONE},
 };
 
-void ExpectSampleForSuffix(const std::string& suffix,
-                           const std::string& match_suffix,
-                           const base::HistogramTester& tester) {
-  tester.ExpectUniqueSample(kMatchesPatternHistogramName + suffix,
-                            (suffix == match_suffix), 1);
-}
-
 }  //  namespace
 
 class SubresourceFilterSafeBrowsingActivationThrottleTest
@@ -860,19 +853,18 @@
        ListNotMatched_NoActivation) {
   const ActivationListTestData& test_data = GetParam();
   const GURL url(kURL);
-  const std::string suffix(GetSuffixForList(test_data.activation_list_type));
   SimulateStartAndExpectProceed(url);
   SimulateCommitAndExpectProceed();
   EXPECT_EQ(ActivationDecision::ACTIVATION_CONDITIONS_NOT_MET,
             *observer()->GetPageActivationForLastCommittedLoad());
-  ExpectSampleForSuffix("SocialEngineeringAdsInterstitial", std::string(),
-                        tester());
-  ExpectSampleForSuffix("PhishingInterstitial", std::string(), tester());
-  ExpectSampleForSuffix("SubresourceFilterOnly", std::string(), tester());
+  tester().ExpectUniqueSample(kMatchesPatternHistogramName,
+                              static_cast<int>(ActivationList::NONE), 1);
 
   tester().ExpectTotalCount(kSafeBrowsingNavigationDelay, 1);
   tester().ExpectTotalCount(kSafeBrowsingNavigationDelayNoSpeculation, 1);
   tester().ExpectTotalCount(kSafeBrowsingCheckTime, 1);
+
+  const std::string suffix(GetSuffixForList(test_data.activation_list_type));
   tester().ExpectTotalCount(kNavigationChainSize + suffix, 0);
 }
 
@@ -885,10 +877,10 @@
   SimulateCommitAndExpectProceed();
   EXPECT_EQ(ActivationDecision::ACTIVATED,
             *observer()->GetPageActivationForLastCommittedLoad());
+  tester().ExpectUniqueSample(kMatchesPatternHistogramName,
+                              static_cast<int>(test_data.activation_list_type),
+                              1);
   const std::string suffix(GetSuffixForList(test_data.activation_list_type));
-  ExpectSampleForSuffix("SocialEngineeringAdsInterstitial", suffix, tester());
-  ExpectSampleForSuffix("PhishingInterstitial", suffix, tester());
-  ExpectSampleForSuffix("SubresourceFilterOnly", suffix, tester());
   tester().ExpectUniqueSample(kNavigationChainSize + suffix, 1, 1);
 }
 
@@ -896,16 +888,15 @@
        ListNotMatchedAfterRedirect_NoActivation) {
   const ActivationListTestData& test_data = GetParam();
   const GURL url(kURL);
-  const std::string suffix(GetSuffixForList(test_data.activation_list_type));
   SimulateStartAndExpectProceed(url);
   SimulateRedirectAndExpectProceed(GURL(kRedirectURL));
   SimulateCommitAndExpectProceed();
   EXPECT_EQ(ActivationDecision::ACTIVATION_CONDITIONS_NOT_MET,
             *observer()->GetPageActivationForLastCommittedLoad());
-  ExpectSampleForSuffix("SocialEngineeringAdsInterstitial", std::string(),
-                        tester());
-  ExpectSampleForSuffix("PhishingInterstitial", std::string(), tester());
-  ExpectSampleForSuffix("SubresourceFilterOnly", std::string(), tester());
+  tester().ExpectUniqueSample(kMatchesPatternHistogramName,
+                              static_cast<int>(ActivationList::NONE), 1);
+
+  const std::string suffix(GetSuffixForList(test_data.activation_list_type));
   tester().ExpectTotalCount(kNavigationChainSize + suffix, 0);
 }
 
@@ -913,17 +904,18 @@
        ListMatchedAfterRedirect_Activation) {
   const ActivationListTestData& test_data = GetParam();
   const GURL url(kURL);
-  const std::string suffix(GetSuffixForList(test_data.activation_list_type));
   ConfigureForMatchParam(GURL(kRedirectURL));
   SimulateStartAndExpectProceed(url);
   SimulateRedirectAndExpectProceed(GURL(kRedirectURL));
   SimulateCommitAndExpectProceed();
   EXPECT_EQ(ActivationDecision::ACTIVATED,
             *observer()->GetPageActivationForLastCommittedLoad());
+  tester().ExpectUniqueSample(kMatchesPatternHistogramName,
+                              static_cast<int>(test_data.activation_list_type),
+                              1);
+
+  const std::string suffix(GetSuffixForList(test_data.activation_list_type));
   tester().ExpectUniqueSample(kNavigationChainSize + suffix, 2, 1);
-  ExpectSampleForSuffix("SocialEngineeringAdsInterstitial", suffix, tester());
-  ExpectSampleForSuffix("PhishingInterstitial", suffix, tester());
-  ExpectSampleForSuffix("SubresourceFilterOnly", suffix, tester());
 }
 
 TEST_P(SubresourceFilterSafeBrowsingActivationThrottleParamTest,
@@ -968,10 +960,11 @@
   SimulateCommitAndExpectProceed();
   EXPECT_EQ(ActivationDecision::ACTIVATED,
             *observer()->GetPageActivationForLastCommittedLoad());
+  tester().ExpectUniqueSample(kMatchesPatternHistogramName,
+                              static_cast<int>(test_data.activation_list_type),
+                              1);
+
   const std::string suffix(GetSuffixForList(test_data.activation_list_type));
-  ExpectSampleForSuffix("SocialEngineeringAdsInterstitial", suffix, tester());
-  ExpectSampleForSuffix("PhishingInterstitial", suffix, tester());
-  ExpectSampleForSuffix("SubresourceFilterOnly", suffix, tester());
   tester().ExpectUniqueSample(kNavigationChainSize + suffix, 1, 1);
 
   tester().ExpectTimeBucketCount(kSafeBrowsingNavigationDelay,
@@ -996,10 +989,11 @@
   SimulateCommitAndExpectProceed();
   EXPECT_EQ(ActivationDecision::ACTIVATED,
             *observer()->GetPageActivationForLastCommittedLoad());
+  tester().ExpectUniqueSample(kMatchesPatternHistogramName,
+                              static_cast<int>(test_data.activation_list_type),
+                              1);
+
   const std::string suffix(GetSuffixForList(test_data.activation_list_type));
-  ExpectSampleForSuffix("SocialEngineeringAdsInterstitial", suffix, tester());
-  ExpectSampleForSuffix("PhishingInterstitial", suffix, tester());
-  ExpectSampleForSuffix("SubresourceFilterOnly", suffix, tester());
   tester().ExpectUniqueSample(kNavigationChainSize + suffix, 2, 1);
 
   tester().ExpectTimeBucketCount(kSafeBrowsingNavigationDelay,
@@ -1077,10 +1071,10 @@
     auto check_histogram = [&](std::string suffix) {
       bool matches =
           suffix == suffix_param && test_data.blacklisted_urls.back();
-      histogram_tester.ExpectBucketCount(kMatchesPatternHistogramName + suffix,
-                                         matches, 1);
-
       if (matches) {
+        histogram_tester.ExpectUniqueSample(
+            kMatchesPatternHistogramName,
+            static_cast<int>(GetParam().activation_list_type), 1);
         histogram_tester.ExpectBucketCount(kNavigationChainSize + suffix,
                                            test_data.navigation_chain.size(),
                                            1);
diff --git a/components/subresource_filter/core/common/activation_list.h b/components/subresource_filter/core/common/activation_list.h
index e17cce07..9caa27b2 100644
--- a/components/subresource_filter/core/common/activation_list.h
+++ b/components/subresource_filter/core/common/activation_list.h
@@ -9,12 +9,16 @@
 
 namespace subresource_filter {
 
-enum class ActivationList {
+// This enum backs a histogram. Make sure all updates are reflected in
+// enums.xml.
+enum class ActivationList : int {
   NONE,
   SOCIAL_ENG_ADS_INTERSTITIAL,
   PHISHING_INTERSTITIAL,
   SUBRESOURCE_FILTER,
-  LAST = SUBRESOURCE_FILTER,
+
+  // Make sure new elements added update the LAST value.
+  LAST = SUBRESOURCE_FILTER
 };
 
 // For logging use only.
diff --git a/components/sync/protocol/proto_enum_conversions.cc b/components/sync/protocol/proto_enum_conversions.cc
index 88e040a7..937ebadb 100644
--- a/components/sync/protocol/proto_enum_conversions.cc
+++ b/components/sync/protocol/proto_enum_conversions.cc
@@ -317,24 +317,24 @@
 }
 
 const char* ProtoEnumToString(
-    sync_pb::UserEventSpecifics::SyncPasswordReuseEvent::PasswordReuseDetected::
+    sync_pb::UserEventSpecifics::GaiaPasswordReuse::PasswordReuseDetected::
         SafeBrowsingStatus::ReportingPopulation
             safe_browsing_reporting_population) {
-  ASSERT_ENUM_BOUNDS(sync_pb::UserEventSpecifics::SyncPasswordReuseEvent::
+  ASSERT_ENUM_BOUNDS(sync_pb::UserEventSpecifics::GaiaPasswordReuse::
                          PasswordReuseDetected::SafeBrowsingStatus,
                      ReportingPopulation, REPORTING_POPULATION_UNSPECIFIED,
                      SCOUT);
   switch (safe_browsing_reporting_population) {
-    ENUM_CASE(sync_pb::UserEventSpecifics::SyncPasswordReuseEvent::
+    ENUM_CASE(sync_pb::UserEventSpecifics::GaiaPasswordReuse::
                   PasswordReuseDetected::SafeBrowsingStatus,
               REPORTING_POPULATION_UNSPECIFIED);
-    ENUM_CASE(sync_pb::UserEventSpecifics::SyncPasswordReuseEvent::
+    ENUM_CASE(sync_pb::UserEventSpecifics::GaiaPasswordReuse::
                   PasswordReuseDetected::SafeBrowsingStatus,
               NONE);
-    ENUM_CASE(sync_pb::UserEventSpecifics::SyncPasswordReuseEvent::
+    ENUM_CASE(sync_pb::UserEventSpecifics::GaiaPasswordReuse::
                   PasswordReuseDetected::SafeBrowsingStatus,
               EXTENDED_REPORTING);
-    ENUM_CASE(sync_pb::UserEventSpecifics::SyncPasswordReuseEvent::
+    ENUM_CASE(sync_pb::UserEventSpecifics::GaiaPasswordReuse::
                   PasswordReuseDetected::SafeBrowsingStatus,
               SCOUT);
   }
@@ -343,22 +343,22 @@
 }
 
 const char* ProtoEnumToString(
-    sync_pb::UserEventSpecifics::SyncPasswordReuseEvent::
+    sync_pb::UserEventSpecifics::GaiaPasswordReuse::
         PasswordReuseDialogInteraction::InteractionResult interaction_result) {
-  ASSERT_ENUM_BOUNDS(sync_pb::UserEventSpecifics::SyncPasswordReuseEvent::
+  ASSERT_ENUM_BOUNDS(sync_pb::UserEventSpecifics::GaiaPasswordReuse::
                          PasswordReuseDialogInteraction,
                      InteractionResult, UNSPECIFIED, WARNING_UI_IGNORED);
   switch (interaction_result) {
-    ENUM_CASE(sync_pb::UserEventSpecifics::SyncPasswordReuseEvent::
+    ENUM_CASE(sync_pb::UserEventSpecifics::GaiaPasswordReuse::
                   PasswordReuseDialogInteraction,
               UNSPECIFIED);
-    ENUM_CASE(sync_pb::UserEventSpecifics::SyncPasswordReuseEvent::
+    ENUM_CASE(sync_pb::UserEventSpecifics::GaiaPasswordReuse::
                   PasswordReuseDialogInteraction,
               WARNING_ACTION_TAKEN);
-    ENUM_CASE(sync_pb::UserEventSpecifics::SyncPasswordReuseEvent::
+    ENUM_CASE(sync_pb::UserEventSpecifics::GaiaPasswordReuse::
                   PasswordReuseDialogInteraction,
               WARNING_ACTION_IGNORED);
-    ENUM_CASE(sync_pb::UserEventSpecifics::SyncPasswordReuseEvent::
+    ENUM_CASE(sync_pb::UserEventSpecifics::GaiaPasswordReuse::
                   PasswordReuseDialogInteraction,
               WARNING_UI_IGNORED);
   }
@@ -367,54 +367,54 @@
 }
 
 const char* ProtoEnumToString(
-    sync_pb::UserEventSpecifics::SyncPasswordReuseEvent::PasswordReuseLookup::
+    sync_pb::UserEventSpecifics::GaiaPasswordReuse::PasswordReuseLookup::
         LookupResult lookup_result) {
   ASSERT_ENUM_BOUNDS(
-      sync_pb::UserEventSpecifics::SyncPasswordReuseEvent::PasswordReuseLookup,
+      sync_pb::UserEventSpecifics::GaiaPasswordReuse::PasswordReuseLookup,
       LookupResult, UNSPECIFIED, URL_UNSUPPORTED);
   switch (lookup_result) {
-    ENUM_CASE(sync_pb::UserEventSpecifics::SyncPasswordReuseEvent::
-                  PasswordReuseLookup,
-              UNSPECIFIED);
-    ENUM_CASE(sync_pb::UserEventSpecifics::SyncPasswordReuseEvent::
-                  PasswordReuseLookup,
-              WHITELIST_HIT);
-    ENUM_CASE(sync_pb::UserEventSpecifics::SyncPasswordReuseEvent::
-                  PasswordReuseLookup,
-              CACHE_HIT);
-    ENUM_CASE(sync_pb::UserEventSpecifics::SyncPasswordReuseEvent::
-                  PasswordReuseLookup,
-              REQUEST_SUCCESS);
-    ENUM_CASE(sync_pb::UserEventSpecifics::SyncPasswordReuseEvent::
-                  PasswordReuseLookup,
-              REQUEST_FAILURE);
-    ENUM_CASE(sync_pb::UserEventSpecifics::SyncPasswordReuseEvent::
-                  PasswordReuseLookup,
-              URL_UNSUPPORTED);
+    ENUM_CASE(
+        sync_pb::UserEventSpecifics::GaiaPasswordReuse::PasswordReuseLookup,
+        UNSPECIFIED);
+    ENUM_CASE(
+        sync_pb::UserEventSpecifics::GaiaPasswordReuse::PasswordReuseLookup,
+        WHITELIST_HIT);
+    ENUM_CASE(
+        sync_pb::UserEventSpecifics::GaiaPasswordReuse::PasswordReuseLookup,
+        CACHE_HIT);
+    ENUM_CASE(
+        sync_pb::UserEventSpecifics::GaiaPasswordReuse::PasswordReuseLookup,
+        REQUEST_SUCCESS);
+    ENUM_CASE(
+        sync_pb::UserEventSpecifics::GaiaPasswordReuse::PasswordReuseLookup,
+        REQUEST_FAILURE);
+    ENUM_CASE(
+        sync_pb::UserEventSpecifics::GaiaPasswordReuse::PasswordReuseLookup,
+        URL_UNSUPPORTED);
   }
   NOTREACHED();
   return "";
 }
 
 const char* ProtoEnumToString(
-    sync_pb::UserEventSpecifics::SyncPasswordReuseEvent::PasswordReuseLookup::
+    sync_pb::UserEventSpecifics::GaiaPasswordReuse::PasswordReuseLookup::
         ReputationVerdict verdict) {
   ASSERT_ENUM_BOUNDS(
-      sync_pb::UserEventSpecifics::SyncPasswordReuseEvent::PasswordReuseLookup,
+      sync_pb::UserEventSpecifics::GaiaPasswordReuse::PasswordReuseLookup,
       ReputationVerdict, VERDICT_UNSPECIFIED, PHISHING);
   switch (verdict) {
-    ENUM_CASE(sync_pb::UserEventSpecifics::SyncPasswordReuseEvent::
-                  PasswordReuseLookup,
-              VERDICT_UNSPECIFIED);
-    ENUM_CASE(sync_pb::UserEventSpecifics::SyncPasswordReuseEvent::
-                  PasswordReuseLookup,
-              SAFE);
-    ENUM_CASE(sync_pb::UserEventSpecifics::SyncPasswordReuseEvent::
-                  PasswordReuseLookup,
-              LOW_REPUTATION);
-    ENUM_CASE(sync_pb::UserEventSpecifics::SyncPasswordReuseEvent::
-                  PasswordReuseLookup,
-              PHISHING);
+    ENUM_CASE(
+        sync_pb::UserEventSpecifics::GaiaPasswordReuse::PasswordReuseLookup,
+        VERDICT_UNSPECIFIED);
+    ENUM_CASE(
+        sync_pb::UserEventSpecifics::GaiaPasswordReuse::PasswordReuseLookup,
+        SAFE);
+    ENUM_CASE(
+        sync_pb::UserEventSpecifics::GaiaPasswordReuse::PasswordReuseLookup,
+        LOW_REPUTATION);
+    ENUM_CASE(
+        sync_pb::UserEventSpecifics::GaiaPasswordReuse::PasswordReuseLookup,
+        PHISHING);
   }
   NOTREACHED();
   return "";
diff --git a/components/sync/protocol/proto_enum_conversions.h b/components/sync/protocol/proto_enum_conversions.h
index 14e0aa6..25c2509 100644
--- a/components/sync/protocol/proto_enum_conversions.h
+++ b/components/sync/protocol/proto_enum_conversions.h
@@ -69,20 +69,20 @@
     sync_pb::UserEventSpecifics::Translation::Interaction interaction);
 
 const char* ProtoEnumToString(
-    sync_pb::UserEventSpecifics::SyncPasswordReuseEvent::PasswordReuseDetected::
+    sync_pb::UserEventSpecifics::GaiaPasswordReuse::PasswordReuseDetected::
         SafeBrowsingStatus::ReportingPopulation
             safe_browsing_reporting_population);
 
 const char* ProtoEnumToString(
-    sync_pb::UserEventSpecifics::SyncPasswordReuseEvent::
+    sync_pb::UserEventSpecifics::GaiaPasswordReuse::
         PasswordReuseDialogInteraction::InteractionResult interaction_result);
 
 const char* ProtoEnumToString(
-    sync_pb::UserEventSpecifics::SyncPasswordReuseEvent::PasswordReuseLookup::
+    sync_pb::UserEventSpecifics::GaiaPasswordReuse::PasswordReuseLookup::
         LookupResult lookup_result);
 
 const char* ProtoEnumToString(
-    sync_pb::UserEventSpecifics::SyncPasswordReuseEvent::PasswordReuseLookup::
+    sync_pb::UserEventSpecifics::GaiaPasswordReuse::PasswordReuseLookup::
         ReputationVerdict verdict);
 
 const char* ProtoEnumToString(
diff --git a/components/sync/protocol/proto_visitors.h b/components/sync/protocol/proto_visitors.h
index 155030c..5e94aa67 100644
--- a/components/sync/protocol/proto_visitors.h
+++ b/components/sync/protocol/proto_visitors.h
@@ -811,30 +811,32 @@
 VISIT_PROTO_FIELDS(const sync_pb::SyncedNotificationSpecifics& proto) {}
 
 VISIT_PROTO_FIELDS(
-    const sync_pb::UserEventSpecifics::SyncPasswordReuseEvent& proto) {
+    const sync_pb::UserEventSpecifics::GaiaPasswordReuse& proto) {
   VISIT(reuse_detected);
   VISIT(reuse_lookup);
   VISIT(dialog_interaction);
 }
 
-VISIT_PROTO_FIELDS(const sync_pb::UserEventSpecifics::SyncPasswordReuseEvent::
-                       PasswordReuseDetected& proto) {
+VISIT_PROTO_FIELDS(
+    const sync_pb::UserEventSpecifics::GaiaPasswordReuse::PasswordReuseDetected&
+        proto) {
   VISIT(status);
 }
 
-VISIT_PROTO_FIELDS(const sync_pb::UserEventSpecifics::SyncPasswordReuseEvent::
+VISIT_PROTO_FIELDS(const sync_pb::UserEventSpecifics::GaiaPasswordReuse::
                        PasswordReuseDetected::SafeBrowsingStatus& proto) {
   VISIT(enabled);
   VISIT_ENUM(safe_browsing_reporting_population);
 }
 
-VISIT_PROTO_FIELDS(const sync_pb::UserEventSpecifics::SyncPasswordReuseEvent::
+VISIT_PROTO_FIELDS(const sync_pb::UserEventSpecifics::GaiaPasswordReuse::
                        PasswordReuseDialogInteraction& proto) {
   VISIT_ENUM(interaction_result);
 }
 
-VISIT_PROTO_FIELDS(const sync_pb::UserEventSpecifics::SyncPasswordReuseEvent::
-                       PasswordReuseLookup& proto) {
+VISIT_PROTO_FIELDS(
+    const sync_pb::UserEventSpecifics::GaiaPasswordReuse::PasswordReuseLookup&
+        proto) {
   VISIT_ENUM(lookup_result);
   VISIT_ENUM(verdict);
   VISIT(verdict_token);
@@ -916,7 +918,7 @@
   VISIT(field_trial_event);
   VISIT(language_detection_event);
   VISIT(translation_event);
-  VISIT(sync_password_reuse_event);
+  VISIT(gaia_password_reuse_event);
 }
 
 VISIT_PROTO_FIELDS(const sync_pb::UserEventSpecifics::Test& proto) {}
diff --git a/components/sync/protocol/user_event_specifics.proto b/components/sync/protocol/user_event_specifics.proto
index df0b2b5..4eaac85 100644
--- a/components/sync/protocol/user_event_specifics.proto
+++ b/components/sync/protocol/user_event_specifics.proto
@@ -94,7 +94,7 @@
   }
 
   // User reused their GAIA password on another website.
-  message SyncPasswordReuseEvent {
+  message GaiaPasswordReuse {
     // Logged when we detect a password re-use event on a non-GAIA site.
     // If the user hasn’t enabled SafeBrowsing, this will be the last event.
     message PasswordReuseDetected {
@@ -177,7 +177,7 @@
     FieldTrial field_trial_event = 9;
     LanguageDetection language_detection_event = 10;
     Translation translation_event = 11;
-    // Password reuse event.
-    SyncPasswordReuseEvent sync_password_reuse_event = 104;
+    // Happens when a user types their Google account password on another site.
+    GaiaPasswordReuse gaia_password_reuse_event = 104;
   }
 }
diff --git a/components/toolbar/toolbar_model_impl.cc b/components/toolbar/toolbar_model_impl.cc
index 13b7676c..8122ef3 100644
--- a/components/toolbar/toolbar_model_impl.cc
+++ b/components/toolbar/toolbar_model_impl.cc
@@ -89,9 +89,6 @@
     case security_state::EV_SECURE:
     case security_state::SECURE:
       return toolbar::kHttpsValidIcon;
-    case security_state::SECURITY_WARNING:
-      // Surface Dubious as Neutral.
-      return toolbar::kHttpIcon;
     case security_state::SECURE_WITH_POLICY_INSTALLED_CERT:
       return vector_icons::kBusinessIcon;
     case security_state::DANGEROUS:
diff --git a/content/browser/background_fetch/background_fetch_context.cc b/content/browser/background_fetch/background_fetch_context.cc
index 675bd895..646da31 100644
--- a/content/browser/background_fetch/background_fetch_context.cc
+++ b/content/browser/background_fetch/background_fetch_context.cc
@@ -37,12 +37,13 @@
 
 BackgroundFetchContext::BackgroundFetchContext(
     BrowserContext* browser_context,
-    scoped_refptr<ServiceWorkerContextWrapper> service_worker_context)
+    const scoped_refptr<ServiceWorkerContextWrapper>& service_worker_context)
     : browser_context_(browser_context),
       data_manager_(
-          base::MakeUnique<BackgroundFetchDataManager>(browser_context)),
+          base::MakeUnique<BackgroundFetchDataManager>(browser_context,
+                                                       service_worker_context)),
       event_dispatcher_(base::MakeUnique<BackgroundFetchEventDispatcher>(
-          std::move(service_worker_context))),
+          service_worker_context)),
       weak_factory_(this) {
   // Although this lives only on the IO thread, it is constructed on UI thread.
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
diff --git a/content/browser/background_fetch/background_fetch_context.h b/content/browser/background_fetch/background_fetch_context.h
index be613c4..8a2059d 100644
--- a/content/browser/background_fetch/background_fetch_context.h
+++ b/content/browser/background_fetch/background_fetch_context.h
@@ -48,8 +48,9 @@
  public:
   // The BackgroundFetchContext will watch the ServiceWorkerContextWrapper so
   // that it can respond to service worker events such as unregister.
-  BackgroundFetchContext(BrowserContext* browser_context,
-                         scoped_refptr<ServiceWorkerContextWrapper> context);
+  BackgroundFetchContext(
+      BrowserContext* browser_context,
+      const scoped_refptr<ServiceWorkerContextWrapper>& service_worker_context);
 
   // Finishes initializing the Background Fetch context on the IO thread by
   // setting the |request_context_getter|.
diff --git a/content/browser/background_fetch/background_fetch_data_manager.cc b/content/browser/background_fetch/background_fetch_data_manager.cc
index 7e2805a5..28d89eb 100644
--- a/content/browser/background_fetch/background_fetch_data_manager.cc
+++ b/content/browser/background_fetch/background_fetch_data_manager.cc
@@ -7,20 +7,63 @@
 #include <algorithm>
 #include <queue>
 
+#include "base/command_line.h"
 #include "base/memory/ptr_util.h"
 #include "content/browser/background_fetch/background_fetch_constants.h"
 #include "content/browser/background_fetch/background_fetch_context.h"
 #include "content/browser/background_fetch/background_fetch_cross_origin_filter.h"
 #include "content/browser/background_fetch/background_fetch_request_info.h"
 #include "content/browser/blob_storage/chrome_blob_storage_context.h"
+#include "content/browser/service_worker/service_worker_context_wrapper.h"
 #include "content/public/browser/blob_handle.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/download_interrupt_reasons.h"
 #include "content/public/browser/download_item.h"
+#include "content/public/common/content_switches.h"
 #include "services/network/public/interfaces/fetch_api.mojom.h"
 
 namespace content {
 
+namespace {
+
+enum class DatabaseStatus { kOk, kFailed, kNotFound };
+
+DatabaseStatus ToDatabaseStatus(ServiceWorkerStatusCode status) {
+  switch (status) {
+    case SERVICE_WORKER_OK:
+      return DatabaseStatus::kOk;
+    case SERVICE_WORKER_ERROR_FAILED:
+    case SERVICE_WORKER_ERROR_ABORT:
+      // FAILED is for invalid arguments (e.g. empty key) or database errors.
+      // ABORT is for unexpected failures, e.g. because shutdown is in progress.
+      // BackgroundFetchDataManager handles both of these the same way.
+      return DatabaseStatus::kFailed;
+    case SERVICE_WORKER_ERROR_NOT_FOUND:
+      // This can also happen for writes, if the ServiceWorkerRegistration has
+      // been deleted.
+      return DatabaseStatus::kNotFound;
+    case SERVICE_WORKER_ERROR_START_WORKER_FAILED:
+    case SERVICE_WORKER_ERROR_PROCESS_NOT_FOUND:
+    case SERVICE_WORKER_ERROR_EXISTS:
+    case SERVICE_WORKER_ERROR_INSTALL_WORKER_FAILED:
+    case SERVICE_WORKER_ERROR_ACTIVATE_WORKER_FAILED:
+    case SERVICE_WORKER_ERROR_IPC_FAILED:
+    case SERVICE_WORKER_ERROR_NETWORK:
+    case SERVICE_WORKER_ERROR_SECURITY:
+    case SERVICE_WORKER_ERROR_EVENT_WAITUNTIL_REJECTED:
+    case SERVICE_WORKER_ERROR_STATE:
+    case SERVICE_WORKER_ERROR_TIMEOUT:
+    case SERVICE_WORKER_ERROR_SCRIPT_EVALUATE_FAILED:
+    case SERVICE_WORKER_ERROR_DISK_CACHE:
+    case SERVICE_WORKER_ERROR_REDUNDANT:
+    case SERVICE_WORKER_ERROR_DISALLOWED:
+    case SERVICE_WORKER_ERROR_MAX_VALUE:
+      break;
+  }
+  NOTREACHED();
+  return DatabaseStatus::kFailed;
+}
+
 // Returns whether the response contained in the Background Fetch |request| is
 // considered OK. See https://fetch.spec.whatwg.org/#ok-status aka a successful
 // 2xx status per https://tools.ietf.org/html/rfc7231#section-6.3.
@@ -29,6 +72,194 @@
   return status >= 200 && status < 300;
 }
 
+const char kRegistrationKeyPrefix[] = "bgf_registration_";
+
+std::string RegistrationKey(
+    const BackgroundFetchRegistrationId& registration_id) {
+  return kRegistrationKeyPrefix + registration_id.id();
+}
+
+}  // namespace
+
+// A DatabaseTask is an asynchronous "transaction" that needs to read/write the
+// Service Worker Database.
+//
+// Only one DatabaseTask can run at once per StoragePartition, and no other code
+// reads/writes Background Fetch keys, so each task effectively has an exclusive
+// lock, except that core Service Worker code may delete all keys for a
+// ServiceWorkerRegistration or the entire database at any time.
+class BackgroundFetchDataManager::DatabaseTask {
+ public:
+  virtual ~DatabaseTask() = default;
+
+  void Run() {
+    DCHECK_CURRENTLY_ON(BrowserThread::IO);
+    DCHECK(!data_manager_->database_tasks_.empty());
+    DCHECK_EQ(data_manager_->database_tasks_.front().get(), this);
+    Start();
+  }
+
+ protected:
+  explicit DatabaseTask(BackgroundFetchDataManager* data_manager)
+      : data_manager_(data_manager) {}
+
+  // The task should begin reading/writing when this is called.
+  virtual void Start() = 0;
+
+  // Each task MUST call this once finished, even if exceptions occur, to
+  // release their lock and allow the next task to execute.
+  void Finished() {
+    DCHECK_CURRENTLY_ON(BrowserThread::IO);
+    DCHECK(!data_manager_->database_tasks_.empty());
+    DCHECK_EQ(data_manager_->database_tasks_.front().get(), this);
+    // Keep a reference to |this| on the stack, so |this| lives until |self|
+    // goes out of scope instead of being destroyed when |pop| is called.
+    std::unique_ptr<DatabaseTask> self(
+        std::move(data_manager_->database_tasks_.front()));
+    data_manager_->database_tasks_.pop();
+    if (!data_manager_->database_tasks_.empty())
+      data_manager_->database_tasks_.front()->Run();
+  }
+
+  ServiceWorkerContextWrapper* service_worker_context() {
+    DCHECK(data_manager_->service_worker_context_);
+    return data_manager_->service_worker_context_.get();
+  }
+
+ private:
+  BackgroundFetchDataManager* data_manager_;  // Owns this.
+
+  DISALLOW_COPY_AND_ASSIGN(DatabaseTask);
+};
+
+namespace {
+
+class CreateRegistrationTask : public BackgroundFetchDataManager::DatabaseTask {
+ public:
+  CreateRegistrationTask(
+      BackgroundFetchDataManager* data_manager,
+      const BackgroundFetchRegistrationId& registration_id,
+      const std::vector<ServiceWorkerFetchRequest>& requests,
+      const BackgroundFetchOptions& options,
+      BackgroundFetchDataManager::CreateRegistrationCallback callback)
+      : DatabaseTask(data_manager),
+        registration_id_(registration_id),
+        requests_(requests),
+        options_(options),
+        callback_(std::move(callback)),
+        weak_factory_(this) {}
+
+  ~CreateRegistrationTask() override = default;
+
+  void Start() override {
+    service_worker_context()->GetRegistrationUserData(
+        registration_id_.service_worker_registration_id(),
+        {RegistrationKey(registration_id_)},
+        base::Bind(&CreateRegistrationTask::DidGetRegistration,
+                   weak_factory_.GetWeakPtr()));
+  }
+
+ private:
+  void DidGetRegistration(const std::vector<std::string>& data,
+                          ServiceWorkerStatusCode status) {
+    switch (ToDatabaseStatus(status)) {
+      case DatabaseStatus::kNotFound:
+        service_worker_context()->StoreRegistrationUserData(
+            registration_id_.service_worker_registration_id(),
+            registration_id_.origin().GetURL(),
+            {
+                {RegistrationKey(registration_id_), "TODO VALUE"}
+                // TODO(crbug.com/757760): Store requests as well.
+            },
+            base::Bind(&CreateRegistrationTask::DidStoreRegistration,
+                       weak_factory_.GetWeakPtr()));
+        return;
+      case DatabaseStatus::kOk:
+        std::move(callback_).Run(
+            blink::mojom::BackgroundFetchError::DUPLICATED_ID);
+        Finished();  // Destroys |this|.
+        return;
+      case DatabaseStatus::kFailed:
+        std::move(callback_).Run(
+            blink::mojom::BackgroundFetchError::STORAGE_ERROR);
+        Finished();  // Destroys |this|.
+        return;
+    }
+  }
+
+  void DidStoreRegistration(ServiceWorkerStatusCode status) {
+    switch (ToDatabaseStatus(status)) {
+      case DatabaseStatus::kOk:
+        std::move(callback_).Run(blink::mojom::BackgroundFetchError::NONE);
+        Finished();  // Destroys |this|.
+        return;
+      case DatabaseStatus::kFailed:
+      case DatabaseStatus::kNotFound:
+        std::move(callback_).Run(
+            blink::mojom::BackgroundFetchError::STORAGE_ERROR);
+        Finished();  // Destroys |this|.
+        return;
+    }
+  }
+
+  BackgroundFetchRegistrationId registration_id_;
+  std::vector<ServiceWorkerFetchRequest> requests_;
+  BackgroundFetchOptions options_;
+  BackgroundFetchDataManager::CreateRegistrationCallback callback_;
+
+  base::WeakPtrFactory<CreateRegistrationTask> weak_factory_;  // Keep as last.
+
+  DISALLOW_COPY_AND_ASSIGN(CreateRegistrationTask);
+};
+
+class DeleteRegistrationTask : public BackgroundFetchDataManager::DatabaseTask {
+ public:
+  DeleteRegistrationTask(
+      BackgroundFetchDataManager* data_manager,
+      const BackgroundFetchRegistrationId& registration_id,
+      BackgroundFetchDataManager::DeleteRegistrationCallback callback)
+      : DatabaseTask(data_manager),
+        registration_id_(registration_id),
+        callback_(std::move(callback)),
+        weak_factory_(this) {}
+
+  void Start() override {
+    service_worker_context()->ClearRegistrationUserData(
+        registration_id_.service_worker_registration_id(),
+        {
+            RegistrationKey(registration_id_)
+            // TODO(crbug.com/757760): Delete requests as well.
+        },
+        base::Bind(&DeleteRegistrationTask::DidDeleteRegistration,
+                   weak_factory_.GetWeakPtr()));
+  }
+
+ private:
+  void DidDeleteRegistration(ServiceWorkerStatusCode status) {
+    switch (ToDatabaseStatus(status)) {
+      case DatabaseStatus::kOk:
+      case DatabaseStatus::kNotFound:
+        std::move(callback_).Run(blink::mojom::BackgroundFetchError::NONE);
+        Finished();  // Destroys |this|.
+        return;
+      case DatabaseStatus::kFailed:
+        std::move(callback_).Run(
+            blink::mojom::BackgroundFetchError::STORAGE_ERROR);
+        Finished();  // Destroys |this|.
+        return;
+    }
+  }
+
+  BackgroundFetchRegistrationId registration_id_;
+  BackgroundFetchDataManager::DeleteRegistrationCallback callback_;
+
+  base::WeakPtrFactory<DeleteRegistrationTask> weak_factory_;  // Keep as last.
+
+  DISALLOW_COPY_AND_ASSIGN(DeleteRegistrationTask);
+};
+
+}  // namespace
+
 // The Registration Data class encapsulates the data stored for a particular
 // Background Fetch registration. This roughly matches the on-disk format that
 // will be adhered to in the future.
@@ -124,12 +355,12 @@
 };
 
 BackgroundFetchDataManager::BackgroundFetchDataManager(
-    BrowserContext* browser_context)
-    : weak_ptr_factory_(this) {
-  // Constructed on the UI thread, then used on a different thread.
+    BrowserContext* browser_context,
+    scoped_refptr<ServiceWorkerContextWrapper> service_worker_context)
+    : service_worker_context_(std::move(service_worker_context)),
+      weak_ptr_factory_(this) {
+  // Constructed on the UI thread, then used on the IO thread.
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DETACH_FROM_SEQUENCE(sequence_checker_);
-
   DCHECK(browser_context);
 
   // Store the blob storage context for the given |browser_context|.
@@ -139,7 +370,7 @@
 }
 
 BackgroundFetchDataManager::~BackgroundFetchDataManager() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 }
 
 void BackgroundFetchDataManager::CreateRegistration(
@@ -147,7 +378,14 @@
     const std::vector<ServiceWorkerFetchRequest>& requests,
     const BackgroundFetchOptions& options,
     CreateRegistrationCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kEnableBackgroundFetchPersistence)) {
+    AddDatabaseTask(std::make_unique<CreateRegistrationTask>(
+        this, registration_id, requests, options, std::move(callback)));
+    return;
+  }
 
   if (registrations_.find(registration_id) != registrations_.end()) {
     std::move(callback).Run(blink::mojom::BackgroundFetchError::DUPLICATED_ID);
@@ -165,7 +403,7 @@
 void BackgroundFetchDataManager::PopNextRequest(
     const BackgroundFetchRegistrationId& registration_id,
     NextRequestCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   auto iter = registrations_.find(registration_id);
   DCHECK(iter != registrations_.end());
@@ -183,7 +421,7 @@
     const BackgroundFetchRegistrationId& registration_id,
     BackgroundFetchRequestInfo* request,
     const std::string& download_guid) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   auto iter = registrations_.find(registration_id);
   DCHECK(iter != registrations_.end());
@@ -196,7 +434,7 @@
     const BackgroundFetchRegistrationId& registration_id,
     BackgroundFetchRequestInfo* request,
     MarkedCompleteCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   auto iter = registrations_.find(registration_id);
   DCHECK(iter != registrations_.end());
@@ -211,7 +449,7 @@
 void BackgroundFetchDataManager::GetSettledFetchesForRegistration(
     const BackgroundFetchRegistrationId& registration_id,
     SettledFetchesCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   auto iter = registrations_.find(registration_id);
   DCHECK(iter != registrations_.end());
@@ -251,8 +489,6 @@
 
       if (request->GetFileSize() > 0) {
         DCHECK(!request->GetFilePath().empty());
-        // CreateFileBackedBlob DCHECKs that it is called on the IO thread. This
-        // imposes a more specific requirement than our sequence_checker_.
         std::unique_ptr<BlobHandle> blob_handle =
             blob_storage_context_->CreateFileBackedBlob(
                 request->GetFilePath(), 0 /* offset */, request->GetFileSize(),
@@ -289,7 +525,14 @@
 void BackgroundFetchDataManager::DeleteRegistration(
     const BackgroundFetchRegistrationId& registration_id,
     DeleteRegistrationCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kEnableBackgroundFetchPersistence)) {
+    AddDatabaseTask(std::make_unique<DeleteRegistrationTask>(
+        this, registration_id, std::move(callback)));
+    return;
+  }
 
   auto iter = registrations_.find(registration_id);
   if (iter == registrations_.end()) {
@@ -302,4 +545,11 @@
   std::move(callback).Run(blink::mojom::BackgroundFetchError::NONE);
 }
 
+void BackgroundFetchDataManager::AddDatabaseTask(
+    std::unique_ptr<DatabaseTask> task) {
+  database_tasks_.push(std::move(task));
+  if (database_tasks_.size() == 1)
+    database_tasks_.front()->Run();
+}
+
 }  // namespace content
diff --git a/content/browser/background_fetch/background_fetch_data_manager.h b/content/browser/background_fetch/background_fetch_data_manager.h
index 424d5be..8e9892c7 100644
--- a/content/browser/background_fetch/background_fetch_data_manager.h
+++ b/content/browser/background_fetch/background_fetch_data_manager.h
@@ -11,9 +11,9 @@
 #include <unordered_map>
 
 #include "base/callback_forward.h"
+#include "base/containers/queue.h"
 #include "base/macros.h"
 #include "base/optional.h"
-#include "base/sequence_checker.h"
 #include "content/browser/background_fetch/background_fetch_registration_id.h"
 #include "content/common/content_export.h"
 #include "third_party/WebKit/public/platform/modules/background_fetch/background_fetch.mojom.h"
@@ -26,11 +26,20 @@
 class BlobHandle;
 class BrowserContext;
 class ChromeBlobStorageContext;
+class ServiceWorkerContextWrapper;
 
-// The BackgroundFetchDataManager keeps track of all of the outstanding requests
-// which are in process in the DownloadManager. When Chromium restarts, it is
-// responsibile for reconnecting all the in progress downloads with an observer
-// which will keep the metadata up to date.
+// The BackgroundFetchDataManager is a wrapper around persistent storage (the
+// Service Worker database), exposing APIs for the read and write queries needed
+// for Background Fetch.
+//
+// There must only be a single instance of this class per StoragePartition, and
+// it must only be used on the IO thread, since it relies on there being no
+// other code concurrently reading/writing the Background Fetch keys of the same
+// Service Worker database (except for deletions, e.g. it's safe for the Service
+// Worker code to remove a ServiceWorkerRegistration and all its keys).
+//
+// Schema design doc:
+// https://docs.google.com/document/d/1-WPPTP909Gb5PnaBOKP58tPVLw2Fq0Ln-u1EBviIBns/edit
 class CONTENT_EXPORT BackgroundFetchDataManager {
  public:
   using CreateRegistrationCallback =
@@ -47,7 +56,11 @@
                               std::vector<BackgroundFetchSettledFetch>,
                               std::vector<std::unique_ptr<BlobHandle>>)>;
 
-  explicit BackgroundFetchDataManager(BrowserContext* browser_context);
+  class DatabaseTask;
+
+  BackgroundFetchDataManager(
+      BrowserContext* browser_context,
+      scoped_refptr<ServiceWorkerContextWrapper> service_worker_context);
   ~BackgroundFetchDataManager();
 
   // Creates and stores a new registration with the given properties. Will
@@ -95,6 +108,10 @@
 
   class RegistrationData;
 
+  void AddDatabaseTask(std::unique_ptr<DatabaseTask> task);
+
+  scoped_refptr<ServiceWorkerContextWrapper> service_worker_context_;
+
   // The blob storage request with which response information will be stored.
   scoped_refptr<ChromeBlobStorageContext> blob_storage_context_;
 
@@ -102,7 +119,9 @@
   std::map<BackgroundFetchRegistrationId, std::unique_ptr<RegistrationData>>
       registrations_;
 
-  SEQUENCE_CHECKER(sequence_checker_);
+  // Pending database operations, serialized to ensure consistency.
+  // Invariant: the frontmost task, if any, has already been started.
+  base::queue<std::unique_ptr<DatabaseTask>> database_tasks_;
 
   base::WeakPtrFactory<BackgroundFetchDataManager> weak_ptr_factory_;
 
diff --git a/content/browser/background_fetch/background_fetch_data_manager_unittest.cc b/content/browser/background_fetch/background_fetch_data_manager_unittest.cc
index f531f09..d0c465f3 100644
--- a/content/browser/background_fetch/background_fetch_data_manager_unittest.cc
+++ b/content/browser/background_fetch/background_fetch_data_manager_unittest.cc
@@ -7,13 +7,17 @@
 #include <memory>
 #include <string>
 
+#include "base/barrier_closure.h"
 #include "base/bind_helpers.h"
 #include "base/callback_helpers.h"
+#include "base/command_line.h"
 #include "base/memory/ptr_util.h"
 #include "base/run_loop.h"
 #include "content/browser/background_fetch/background_fetch_request_info.h"
 #include "content/browser/background_fetch/background_fetch_test_base.h"
+#include "content/browser/service_worker/service_worker_context_wrapper.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/common/content_switches.h"
 
 namespace content {
 namespace {
@@ -24,12 +28,19 @@
 
 class BackgroundFetchDataManagerTest : public BackgroundFetchTestBase {
  public:
-  BackgroundFetchDataManagerTest()
-      : background_fetch_data_manager_(
-            base::MakeUnique<BackgroundFetchDataManager>(browser_context())) {}
+  BackgroundFetchDataManagerTest() {
+    RestartDataManagerFromPersistentStorage();
+  }
   ~BackgroundFetchDataManagerTest() override = default;
 
- protected:
+  // Re-creates the data manager. Useful for testing that data was persisted.
+  void RestartDataManagerFromPersistentStorage() {
+    background_fetch_data_manager_ =
+        base::MakeUnique<BackgroundFetchDataManager>(
+            browser_context(),
+            embedded_worker_test_helper()->context_wrapper());
+  }
+
   // Synchronous version of BackgroundFetchDataManager::CreateRegistration().
   void CreateRegistration(
       const BackgroundFetchRegistrationId& registration_id,
@@ -61,7 +72,6 @@
     run_loop.Run();
   }
 
- private:
   void DidCreateRegistration(base::Closure quit_closure,
                              blink::mojom::BackgroundFetchError* out_error,
                              blink::mojom::BackgroundFetchError error) {
@@ -78,7 +88,6 @@
     quit_closure.Run();
   }
 
-  std::string job_guid_;
   std::unique_ptr<BackgroundFetchDataManager> background_fetch_data_manager_;
 };
 
@@ -95,27 +104,111 @@
   blink::mojom::BackgroundFetchError error;
 
   // Deleting the not-yet-created registration should fail.
-  ASSERT_NO_FATAL_FAILURE(DeleteRegistration(registration_id, &error));
+  DeleteRegistration(registration_id, &error);
   EXPECT_EQ(error, blink::mojom::BackgroundFetchError::INVALID_ID);
 
   // Creating the initial registration should succeed.
-  ASSERT_NO_FATAL_FAILURE(
-      CreateRegistration(registration_id, requests, options, &error));
+  CreateRegistration(registration_id, requests, options, &error);
   EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
 
   // Attempting to create it again should yield an error.
-  ASSERT_NO_FATAL_FAILURE(
-      CreateRegistration(registration_id, requests, options, &error));
+  CreateRegistration(registration_id, requests, options, &error);
   EXPECT_EQ(error, blink::mojom::BackgroundFetchError::DUPLICATED_ID);
 
   // Deleting the registration should succeed.
-  ASSERT_NO_FATAL_FAILURE(DeleteRegistration(registration_id, &error));
+  DeleteRegistration(registration_id, &error);
   EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
 
   // And then recreating the registration again should work fine.
-  ASSERT_NO_FATAL_FAILURE(
-      CreateRegistration(registration_id, requests, options, &error));
+  CreateRegistration(registration_id, requests, options, &error);
   EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
 }
 
+TEST_F(BackgroundFetchDataManagerTest, CreateAndDeleteRegistrationPersisted) {
+  // Tests that the BackgroundFetchDataManager persists created registrations to
+  // the Service Worker DB.
+
+  base::CommandLine::ForCurrentProcess()->AppendSwitch(
+      switches::kEnableBackgroundFetchPersistence);
+
+  BackgroundFetchRegistrationId registration_id;
+  ASSERT_TRUE(CreateRegistrationId(kExampleId, &registration_id));
+
+  std::vector<ServiceWorkerFetchRequest> requests;
+  BackgroundFetchOptions options;
+
+  blink::mojom::BackgroundFetchError error;
+
+  // Creating the initial registration should succeed.
+  CreateRegistration(registration_id, requests, options, &error);
+  EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
+
+  RestartDataManagerFromPersistentStorage();
+
+  // Attempting to create it again should yield an error, even after restarting.
+  CreateRegistration(registration_id, requests, options, &error);
+  EXPECT_EQ(error, blink::mojom::BackgroundFetchError::DUPLICATED_ID);
+
+  // Deleting the registration should succeed.
+  DeleteRegistration(registration_id, &error);
+  EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
+
+  RestartDataManagerFromPersistentStorage();
+
+  // And then recreating the registration again should work fine, even after
+  // restarting.
+  CreateRegistration(registration_id, requests, options, &error);
+  EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
+}
+
+TEST_F(BackgroundFetchDataManagerTest, CreateInParallel) {
+  // Tests that multiple parallel calls to the BackgroundFetchDataManager are
+  // linearized and handled one at a time, rather than producing inconsistent
+  // results due to interleaving.
+
+  base::CommandLine::ForCurrentProcess()->AppendSwitch(
+      switches::kEnableBackgroundFetchPersistence);
+
+  BackgroundFetchRegistrationId registration_id;
+  ASSERT_TRUE(CreateRegistrationId(kExampleId, &registration_id));
+
+  std::vector<ServiceWorkerFetchRequest> requests;
+  BackgroundFetchOptions options;
+
+  std::vector<blink::mojom::BackgroundFetchError> errors(5);
+
+  const int num_parallel_creates = 5;
+
+  base::RunLoop run_loop;
+  base::RepeatingClosure quit_once_all_finished_closure =
+      base::BarrierClosure(num_parallel_creates, run_loop.QuitClosure());
+  for (int i = 0; i < num_parallel_creates; i++) {
+    background_fetch_data_manager_->CreateRegistration(
+        registration_id, requests, options,
+        base::BindOnce(&BackgroundFetchDataManagerTest::DidCreateRegistration,
+                       base::Unretained(this), quit_once_all_finished_closure,
+                       &errors[i]));
+  }
+  run_loop.Run();
+
+  int success_count = 0;
+  int duplicated_id_count = 0;
+  for (auto error : errors) {
+    switch (error) {
+      case blink::mojom::BackgroundFetchError::NONE:
+        success_count++;
+        break;
+      case blink::mojom::BackgroundFetchError::DUPLICATED_ID:
+        duplicated_id_count++;
+        break;
+      default:
+        break;
+    }
+  }
+  // Exactly one of the calls should have succeeded in creating a registration,
+  // and all the others should have failed with DUPLICATED_ID.
+  EXPECT_EQ(1, success_count);
+  EXPECT_EQ(num_parallel_creates - 1, duplicated_id_count);
+}
+
 }  // namespace content
diff --git a/content/browser/background_fetch/background_fetch_event_dispatcher.cc b/content/browser/background_fetch/background_fetch_event_dispatcher.cc
index 820a62c..d694845 100644
--- a/content/browser/background_fetch/background_fetch_event_dispatcher.cc
+++ b/content/browser/background_fetch/background_fetch_event_dispatcher.cc
@@ -64,8 +64,8 @@
 }  // namespace
 
 BackgroundFetchEventDispatcher::BackgroundFetchEventDispatcher(
-    const scoped_refptr<ServiceWorkerContextWrapper>& service_worker_context)
-    : service_worker_context_(service_worker_context) {
+    scoped_refptr<ServiceWorkerContextWrapper> service_worker_context)
+    : service_worker_context_(std::move(service_worker_context)) {
   // Constructed on the UI thread, then lives on the IO thread.
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
diff --git a/content/browser/background_fetch/background_fetch_event_dispatcher.h b/content/browser/background_fetch/background_fetch_event_dispatcher.h
index 9d781ef..ab3d5c4 100644
--- a/content/browser/background_fetch/background_fetch_event_dispatcher.h
+++ b/content/browser/background_fetch/background_fetch_event_dispatcher.h
@@ -38,7 +38,7 @@
   };
 
   explicit BackgroundFetchEventDispatcher(
-      const scoped_refptr<ServiceWorkerContextWrapper>& service_worker_context);
+      scoped_refptr<ServiceWorkerContextWrapper> service_worker_context);
   ~BackgroundFetchEventDispatcher();
 
   // Dispatches the `backgroundfetchabort` event, which indicates that an active
diff --git a/content/browser/background_fetch/background_fetch_job_controller_unittest.cc b/content/browser/background_fetch/background_fetch_job_controller_unittest.cc
index b9650bf..9ac670c 100644
--- a/content/browser/background_fetch/background_fetch_job_controller_unittest.cc
+++ b/content/browser/background_fetch/background_fetch_job_controller_unittest.cc
@@ -18,6 +18,7 @@
 #include "content/browser/background_fetch/background_fetch_data_manager.h"
 #include "content/browser/background_fetch/background_fetch_registration_id.h"
 #include "content/browser/background_fetch/background_fetch_test_base.h"
+#include "content/browser/service_worker/service_worker_context_wrapper.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/download_item.h"
 #include "content/public/browser/storage_partition.h"
@@ -35,7 +36,9 @@
 
 class BackgroundFetchJobControllerTest : public BackgroundFetchTestBase {
  public:
-  BackgroundFetchJobControllerTest() : data_manager_(browser_context()) {}
+  BackgroundFetchJobControllerTest()
+      : data_manager_(browser_context(),
+                      embedded_worker_test_helper()->context_wrapper()) {}
   ~BackgroundFetchJobControllerTest() override = default;
 
   // Creates a new Background Fetch registration, whose id will be stored in
diff --git a/content/browser/child_process_launcher_helper_fuchsia.cc b/content/browser/child_process_launcher_helper_fuchsia.cc
index 5fb81ee..571b95b 100644
--- a/content/browser/child_process_launcher_helper_fuchsia.cc
+++ b/content/browser/child_process_launcher_helper_fuchsia.cc
@@ -4,6 +4,10 @@
 
 #include "content/browser/child_process_launcher_helper.h"
 
+#include "base/command_line.h"
+#include "base/process/launch.h"
+#include "mojo/edk/embedder/platform_channel_pair.h"
+
 namespace content {
 namespace internal {
 
@@ -12,6 +16,7 @@
     bool background,
     bool boost_for_pending_views,
     ChildProcessImportance importance) {
+  DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER);
   // TODO(fuchsia): Implement this. (crbug.com/707031)
   NOTIMPLEMENTED();
 }
@@ -20,17 +25,13 @@
     const ChildProcessLauncherHelper::Process& process,
     bool known_dead,
     int* exit_code) {
-  // TODO(fuchsia): Implement this. (crbug.com/707031)
-  NOTIMPLEMENTED();
-  return base::GetTerminationStatus(0, 0);
+  return base::GetTerminationStatus(process.process.Handle(), exit_code);
 }
 
 // static
 bool ChildProcessLauncherHelper::TerminateProcess(const base::Process& process,
                                                   int exit_code,
                                                   bool wait) {
-  // TODO(fuchsia): Implement this. (crbug.com/707031)
-  NOTIMPLEMENTED();
   return process.Terminate(exit_code, wait);
 }
 
@@ -49,29 +50,31 @@
 }
 
 void ChildProcessLauncherHelper::BeforeLaunchOnClientThread() {
-  // TODO(fuchsia): Implement this. (crbug.com/707031)
-  NOTIMPLEMENTED();
+  DCHECK_CURRENTLY_ON(client_thread_id_);
 }
 
 mojo::edk::ScopedPlatformHandle
 ChildProcessLauncherHelper::PrepareMojoPipeHandlesOnClientThread() {
-  // TODO(fuchsia): Implement this. (crbug.com/707031)
-  NOTIMPLEMENTED();
+  DCHECK_CURRENTLY_ON(client_thread_id_);
+
+  // By doing nothing here, StartLaunchOnClientThread() will construct a channel
+  // pair instead.
   return mojo::edk::ScopedPlatformHandle();
 }
 
 std::unique_ptr<FileMappedForLaunch>
 ChildProcessLauncherHelper::GetFilesToMap() {
-  // TODO(fuchsia): Implement this. (crbug.com/707031)
-  NOTIMPLEMENTED();
-  return nullptr;
+  DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER);
+  return std::unique_ptr<FileMappedForLaunch>();
 }
 
 void ChildProcessLauncherHelper::BeforeLaunchOnLauncherThread(
     const PosixFileDescriptorInfo& files_to_register,
     base::LaunchOptions* options) {
-  // TODO(fuchsia): Implement this. (crbug.com/707031)
-  NOTIMPLEMENTED();
+  DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER);
+
+  mojo::edk::PlatformChannelPair::PrepareToPassHandleToChildProcess(
+      mojo_client_handle(), command_line(), &options->handles_to_transfer);
 }
 
 ChildProcessLauncherHelper::Process
@@ -80,23 +83,33 @@
     std::unique_ptr<FileMappedForLaunch> files_to_register,
     bool* is_synchronous_launch,
     int* launch_result) {
-  // TODO(fuchsia): Implement this. (crbug.com/707031)
-  NOTIMPLEMENTED();
-  return Process();
+  DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER);
+  DCHECK(mojo_client_handle().is_valid());
+
+  // TODO(750938): Implement sandboxed/isolated subprocess launching.
+  Process child_process;
+  child_process.process = base::LaunchProcess(*command_line(), options);
+  return child_process;
 }
 
 void ChildProcessLauncherHelper::AfterLaunchOnLauncherThread(
     const ChildProcessLauncherHelper::Process& process,
     const base::LaunchOptions& options) {
-  // TODO(fuchsia): Implement this. (crbug.com/707031)
-  NOTIMPLEMENTED();
+  DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER);
+
+  if (process.process.IsValid()) {
+    // |mojo_client_handle_| has already been transferred to the child process
+    // by this point. Remove it from the scoped container so that we don't
+    // erroneously delete it.
+    ignore_result(mojo_client_handle_.release());
+  }
 }
 
 // static
 void ChildProcessLauncherHelper::ForceNormalProcessTerminationSync(
     ChildProcessLauncherHelper::Process process) {
-  // TODO(fuchsia): Implement this. (crbug.com/707031)
-  NOTIMPLEMENTED();
+  DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER);
+  process.process.Terminate(RESULT_CODE_NORMAL_EXIT, true);
 }
 
 }  // namespace internal
diff --git a/content/browser/devtools/protocol/security_handler.cc b/content/browser/devtools/protocol/security_handler.cc
index 803f58f..4632562 100644
--- a/content/browser/devtools/protocol/security_handler.cc
+++ b/content/browser/devtools/protocol/security_handler.cc
@@ -4,7 +4,10 @@
 
 #include "content/browser/devtools/protocol/security_handler.h"
 
+#include <memory>
 #include <string>
+#include <utility>
+#include <vector>
 
 #include "base/base64.h"
 #include "content/browser/devtools/devtools_session.h"
@@ -34,8 +37,6 @@
       return Security::SecurityStateEnum::Neutral;
     case blink::kWebSecurityStyleInsecure:
       return Security::SecurityStateEnum::Insecure;
-    case blink::kWebSecurityStyleWarning:
-      return Security::SecurityStateEnum::Warning;
     case blink::kWebSecurityStyleSecure:
       return Security::SecurityStateEnum::Secure;
     default:
diff --git a/content/browser/renderer_host/input/web_input_event_builders_mac.mm b/content/browser/renderer_host/input/web_input_event_builders_mac.mm
index 2e028da5..f85a8db7 100644
--- a/content/browser/renderer_host/input/web_input_event_builders_mac.mm
+++ b/content/browser/renderer_host/input/web_input_event_builders_mac.mm
@@ -313,8 +313,11 @@
       NOTIMPLEMENTED();
   }
 
+  // Set id = 0 for all mouse events, disable multi-pen on mac for now.
+  // NSMouseExited and NSMouseEntered events don't have deviceID.
+  // Therefore pen exit and enter events can't get correct id.
   blink::WebMouseEvent result(event_type, ModifiersFromEvent(event),
-                              [event timestamp]);
+                              [event timestamp], 0);
   result.click_count = click_count;
   result.button = button;
   SetWebEventLocationFromEventInView(&result, event, view);
@@ -329,7 +332,6 @@
   // Set stylus properties for events with a subtype of
   // NSTabletPointEventSubtype.
   NSEventSubtype subtype = [event subtype];
-  result.id = [event deviceID];
   if (subtype == NSTabletPointEventSubtype) {
     result.force = [event pressure];
     NSPoint tilt = [event tilt];
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index f2d1dd0a..c8bfba7e 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -147,7 +147,6 @@
 #include "content/common/service_manager/service_manager_connection_impl.h"
 #include "content/common/site_isolation_policy.h"
 #include "content/common/view_messages.h"
-#include "content/common/worker_url_loader_factory_provider.mojom.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/content_browser_client.h"
@@ -700,52 +699,6 @@
                            std::move(request));
 }
 
-class WorkerURLLoaderFactoryProviderImpl
-    : public mojom::WorkerURLLoaderFactoryProvider {
- public:
-  static void Create(
-      int render_process_id,
-      scoped_refptr<ResourceMessageFilter> resource_message_filter,
-      scoped_refptr<ServiceWorkerContextWrapper> service_worker_context,
-      mojom::WorkerURLLoaderFactoryProviderRequest request) {
-    DCHECK(base::FeatureList::IsEnabled(features::kOffMainThreadFetch));
-    mojo::MakeStrongBinding(
-        base::MakeUnique<WorkerURLLoaderFactoryProviderImpl>(
-            render_process_id, resource_message_filter->GetWeakPtr(),
-            std::move(service_worker_context)),
-        std::move(request));
-  }
-  WorkerURLLoaderFactoryProviderImpl(
-      int render_process_id,
-      base::WeakPtr<ResourceMessageFilter> resource_message_filter,
-      scoped_refptr<ServiceWorkerContextWrapper> service_worker_context)
-      : render_process_id_(render_process_id),
-        url_loader_factory_binding_(resource_message_filter.get()),
-        service_worker_context_(std::move(service_worker_context)) {}
-  ~WorkerURLLoaderFactoryProviderImpl() override {}
-
-  void GetURLLoaderFactoryAndRegisterClient(
-      mojom::URLLoaderFactoryAssociatedRequest loader_request,
-      mojom::ServiceWorkerWorkerClientAssociatedPtrInfo client_ptr_info,
-      int service_worker_provider_id) override {
-    url_loader_factory_binding_.Bind(std::move(loader_request));
-    service_worker_context_->BindWorkerFetchContext(render_process_id_,
-                                                    service_worker_provider_id,
-                                                    std::move(client_ptr_info));
-  }
-
-  void GetURLLoaderFactory(
-      mojom::URLLoaderFactoryAssociatedRequest loader_request) override {
-    url_loader_factory_binding_.Bind(std::move(loader_request));
-  }
-
- private:
-  const int render_process_id_;
-  mojo::AssociatedBinding<mojom::URLLoaderFactory> url_loader_factory_binding_;
-
-  scoped_refptr<ServiceWorkerContextWrapper> service_worker_context_;
-};
-
 class RenderProcessHostIsReadyObserver : public RenderProcessHostObserver {
  public:
   RenderProcessHostIsReadyObserver(RenderProcessHost* render_process_host,
@@ -1941,15 +1894,6 @@
   registry->AddInterface(
       base::Bind(&metrics::CreateSingleSampleMetricsProvider));
 
-  if (base::FeatureList::IsEnabled(features::kOffMainThreadFetch)) {
-    scoped_refptr<ServiceWorkerContextWrapper> service_worker_context(
-        static_cast<ServiceWorkerContextWrapper*>(
-            storage_partition_impl_->GetServiceWorkerContext()));
-    registry->AddInterface(
-        base::Bind(&WorkerURLLoaderFactoryProviderImpl::Create, GetID(),
-                   resource_message_filter_, service_worker_context));
-  }
-
   registry->AddInterface(
       base::Bind(&CreateReportingServiceProxy, storage_partition_impl_));
 
diff --git a/content/browser/service_worker/service_worker_context_core.cc b/content/browser/service_worker/service_worker_context_core.cc
index 0e35919..64ae63e2 100644
--- a/content/browser/service_worker/service_worker_context_core.cc
+++ b/content/browser/service_worker/service_worker_context_core.cc
@@ -935,15 +935,4 @@
   CheckFetchHandlerOfInstalledServiceWorker(callback, registration);
 }
 
-void ServiceWorkerContextCore::BindWorkerFetchContext(
-    int render_process_id,
-    int service_worker_provider_id,
-    mojom::ServiceWorkerWorkerClientAssociatedPtrInfo client_ptr_info) {
-  ServiceWorkerProviderHost* provider_host =
-      GetProviderHost(render_process_id, service_worker_provider_id);
-  if (!provider_host)
-    return;
-  provider_host->BindWorkerFetchContext(std::move(client_ptr_info));
-}
-
 }  // namespace content
diff --git a/content/browser/service_worker/service_worker_context_core.h b/content/browser/service_worker/service_worker_context_core.h
index c9f18bf..13b74143 100644
--- a/content/browser/service_worker/service_worker_context_core.h
+++ b/content/browser/service_worker/service_worker_context_core.h
@@ -24,7 +24,6 @@
 #include "content/browser/service_worker/service_worker_registration_status.h"
 #include "content/browser/service_worker/service_worker_storage.h"
 #include "content/common/content_export.h"
-#include "content/common/worker_url_loader_factory_provider.mojom.h"
 #include "content/public/browser/service_worker_context.h"
 
 class GURL;
@@ -299,14 +298,6 @@
   // version. The count resets to zero when the worker successfully starts.
   int GetVersionFailureCount(int64_t version_id);
 
-  // Binds the ServiceWorkerWorkerClient of a dedicated (or shared) worker to
-  // the parent frame's ServiceWorkerProviderHost. (This is used only when
-  // off-main-thread-fetch is enabled.)
-  void BindWorkerFetchContext(
-      int render_process_id,
-      int service_worker_provider_id,
-      mojom::ServiceWorkerWorkerClientAssociatedPtrInfo client_ptr_info);
-
   base::WeakPtr<storage::BlobStorageContext> blob_storage_context() {
     return blob_storage_context_;
   }
diff --git a/content/browser/service_worker/service_worker_context_wrapper.cc b/content/browser/service_worker/service_worker_context_wrapper.cc
index 4d540d9d..d477baa 100644
--- a/content/browser/service_worker/service_worker_context_wrapper.cc
+++ b/content/browser/service_worker/service_worker_context_wrapper.cc
@@ -1019,16 +1019,6 @@
                           base::BindOnce(callback, result));
 }
 
-void ServiceWorkerContextWrapper::BindWorkerFetchContext(
-    int render_process_id,
-    int service_worker_provider_id,
-    mojom::ServiceWorkerWorkerClientAssociatedPtrInfo client_ptr_info) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  context()->BindWorkerFetchContext(render_process_id,
-                                    service_worker_provider_id,
-                                    std::move(client_ptr_info));
-}
-
 ServiceWorkerContextCore* ServiceWorkerContextWrapper::context() {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   return context_core_.get();
diff --git a/content/browser/service_worker/service_worker_context_wrapper.h b/content/browser/service_worker/service_worker_context_wrapper.h
index d6c47ebf..29d3839 100644
--- a/content/browser/service_worker/service_worker_context_wrapper.h
+++ b/content/browser/service_worker/service_worker_context_wrapper.h
@@ -18,7 +18,6 @@
 #include "content/browser/service_worker/service_worker_context_core.h"
 #include "content/browser/service_worker/service_worker_context_core_observer.h"
 #include "content/common/content_export.h"
-#include "content/common/worker_url_loader_factory_provider.mojom.h"
 #include "content/public/browser/service_worker_context.h"
 
 namespace base {
@@ -249,14 +248,6 @@
   // Must be called from the IO thread.
   bool OriginHasForeignFetchRegistrations(const GURL& origin);
 
-  // Binds the ServiceWorkerWorkerClient of a dedicated (or shared) worker to
-  // the parent frame's ServiceWorkerProviderHost. (This is used only when
-  // off-main-thread-fetch is enabled.)
-  void BindWorkerFetchContext(
-      int render_process_id,
-      int service_worker_provider_id,
-      mojom::ServiceWorkerWorkerClientAssociatedPtrInfo client_ptr_info);
-
  private:
   friend class BackgroundSyncManagerTest;
   friend class base::RefCountedThreadSafe<ServiceWorkerContextWrapper>;
diff --git a/content/browser/service_worker/service_worker_provider_host.cc b/content/browser/service_worker/service_worker_provider_host.cc
index 2fc5134..81ace230 100644
--- a/content/browser/service_worker/service_worker_provider_host.cc
+++ b/content/browser/service_worker/service_worker_provider_host.cc
@@ -6,7 +6,6 @@
 
 #include <utility>
 
-#include "base/feature_list.h"
 #include "base/guid.h"
 #include "base/memory/ptr_util.h"
 #include "base/stl_util.h"
@@ -29,7 +28,6 @@
 #include "content/public/common/browser_side_navigation_policy.h"
 #include "content/public/common/child_process_host.h"
 #include "content/public/common/content_client.h"
-#include "content/public/common/content_features.h"
 #include "content/public/common/origin_util.h"
 #include "content/public/common/resource_request_body.h"
 #include "mojo/public/cpp/bindings/strong_associated_binding.h"
@@ -151,31 +149,6 @@
       process_id, std::move(info), context, dispatcher_host));
 }
 
-void ServiceWorkerProviderHost::BindWorkerFetchContext(
-    mojom::ServiceWorkerWorkerClientAssociatedPtrInfo client_ptr_info) {
-  DCHECK(base::FeatureList::IsEnabled(features::kOffMainThreadFetch));
-  mojom::ServiceWorkerWorkerClientAssociatedPtr client;
-  client.Bind(std::move(client_ptr_info));
-  client.set_connection_error_handler(
-      base::BindOnce(&ServiceWorkerProviderHost::UnregisterWorkerFetchContext,
-                     base::Unretained(this), client.get()));
-
-  if (controller_)
-    client->SetControllerServiceWorker(controller_->version_id());
-
-  auto result = worker_clients_.insert(
-      std::make_pair<mojom::ServiceWorkerWorkerClient*,
-                     mojom::ServiceWorkerWorkerClientAssociatedPtr>(
-          client.get(), std::move(client)));
-  DCHECK(result.second);
-}
-
-void ServiceWorkerProviderHost::UnregisterWorkerFetchContext(
-    mojom::ServiceWorkerWorkerClient* client) {
-  DCHECK(worker_clients_.count(client));
-  worker_clients_.erase(client);
-}
-
 ServiceWorkerProviderHost::ServiceWorkerProviderHost(
     int render_process_id,
     ServiceWorkerProviderHostInfo info,
@@ -335,9 +308,6 @@
     version->AddControllee(this);
     controller_event_dispatcher_ =
         base::MakeUnique<BrowserSideServiceWorkerEventDispatcher>(version);
-    for (const auto& pair : worker_clients_) {
-      pair.second->SetControllerServiceWorker(version->version_id());
-    }
   }
   if (previous_version.get())
     previous_version->RemoveControllee(this);
diff --git a/content/browser/service_worker/service_worker_provider_host.h b/content/browser/service_worker/service_worker_provider_host.h
index f8bc22c..6c2a123 100644
--- a/content/browser/service_worker/service_worker_provider_host.h
+++ b/content/browser/service_worker/service_worker_provider_host.h
@@ -12,7 +12,6 @@
 #include <memory>
 #include <set>
 #include <string>
-#include <unordered_map>
 #include <vector>
 
 #include "base/gtest_prod_util.h"
@@ -26,7 +25,6 @@
 #include "content/common/service_worker/service_worker_provider_host_info.h"
 #include "content/common/service_worker/service_worker_provider_interfaces.mojom.h"
 #include "content/common/service_worker/service_worker_types.h"
-#include "content/common/worker_url_loader_factory_provider.mojom.h"
 #include "content/public/common/request_context_frame_type.h"
 #include "content/public/common/request_context_type.h"
 #include "content/public/common/resource_type.h"
@@ -325,12 +323,6 @@
   // cache.
   void NotifyControllerLost();
 
-  // Binds the ServiceWorkerWorkerClient of a dedicated (or shared) worker to
-  // the parent frame's ServiceWorkerProviderHost. (This is used only when
-  // off-main-thread-fetch is enabled.)
-  void BindWorkerFetchContext(
-      mojom::ServiceWorkerWorkerClientAssociatedPtrInfo client_ptr_info);
-
  private:
   friend class ForeignFetchRequestHandlerTest;
   friend class LinkHeaderServiceWorkerTest;
@@ -409,10 +401,6 @@
   void SendSetControllerServiceWorker(ServiceWorkerVersion* version,
                                       bool notify_controllerchange);
 
-  // Clears the information of the ServiceWorkerWorkerClient of dedicated (or
-  // shared) worker, when the connection to the worker is disconnected.
-  void UnregisterWorkerFetchContext(mojom::ServiceWorkerWorkerClient*);
-
   const std::string client_uuid_;
   const base::TimeTicks create_time_;
   int render_process_id_;
@@ -467,12 +455,6 @@
 
   std::vector<base::Closure> queued_events_;
 
-  // Keeps ServiceWorkerWorkerClient pointers of dedicated or shared workers
-  // which are associated with the ServiceWorkerProviderHost.
-  std::unordered_map<mojom::ServiceWorkerWorkerClient*,
-                     mojom::ServiceWorkerWorkerClientAssociatedPtr>
-      worker_clients_;
-
   DISALLOW_COPY_AND_ASSIGN(ServiceWorkerProviderHost);
 };
 
diff --git a/content/child/child_url_loader_factory_getter.cc b/content/child/child_url_loader_factory_getter.cc
index 0a8d0e0..8ff679b 100644
--- a/content/child/child_url_loader_factory_getter.cc
+++ b/content/child/child_url_loader_factory_getter.cc
@@ -6,6 +6,30 @@
 
 namespace content {
 
+ChildURLLoaderFactoryGetter::Info::Info(
+    mojom::URLLoaderFactoryPtrInfo network_loader_factory_info,
+    mojom::URLLoaderFactoryPtrInfo blob_loader_factory_info)
+    : network_loader_factory_info_(std::move(network_loader_factory_info)),
+      blob_loader_factory_info_(std::move(blob_loader_factory_info)) {}
+
+ChildURLLoaderFactoryGetter::Info::Info(Info&& other)
+    : network_loader_factory_info_(
+          std::move(other.network_loader_factory_info_)),
+      blob_loader_factory_info_(std::move(other.blob_loader_factory_info_)) {}
+
+ChildURLLoaderFactoryGetter::Info::~Info() = default;
+
+scoped_refptr<ChildURLLoaderFactoryGetter>
+ChildURLLoaderFactoryGetter::Info::Bind() {
+  DCHECK(network_loader_factory_info_.is_valid());
+  mojom::URLLoaderFactoryPtr network_loader_factory;
+  mojom::URLLoaderFactoryPtr blob_loader_factory;
+  network_loader_factory.Bind(std::move(network_loader_factory_info_));
+  blob_loader_factory.Bind(std::move(blob_loader_factory_info_));
+  return base::MakeRefCounted<ChildURLLoaderFactoryGetter>(
+      std::move(network_loader_factory), std::move(blob_loader_factory));
+}
+
 ChildURLLoaderFactoryGetter::ChildURLLoaderFactoryGetter() = default;
 
 ChildURLLoaderFactoryGetter::ChildURLLoaderFactoryGetter(
@@ -14,6 +38,24 @@
     : network_loader_factory_(std::move(network_loader_factory)),
       blob_loader_factory_getter_(std::move(blob_loader_factory_getter)) {}
 
+ChildURLLoaderFactoryGetter::ChildURLLoaderFactoryGetter(
+    PossiblyAssociatedURLLoaderFactory network_loader_factory,
+    PossiblyAssociatedURLLoaderFactory blob_loader_factory)
+    : network_loader_factory_(std::move(network_loader_factory)),
+      blob_loader_factory_(std::move(blob_loader_factory)) {}
+
+ChildURLLoaderFactoryGetter::Info ChildURLLoaderFactoryGetter::GetClonedInfo() {
+  mojom::URLLoaderFactoryPtrInfo network_loader_factory_info;
+  GetNetworkLoaderFactory()->Clone(
+      mojo::MakeRequest(&network_loader_factory_info));
+
+  mojom::URLLoaderFactoryPtrInfo blob_loader_factory_info;
+  GetBlobLoaderFactory()->Clone(mojo::MakeRequest(&blob_loader_factory_info));
+
+  return Info(std::move(network_loader_factory_info),
+              std::move(blob_loader_factory_info));
+}
+
 mojom::URLLoaderFactory*
 ChildURLLoaderFactoryGetter::GetNetworkLoaderFactory() {
   return network_loader_factory_.get();
@@ -21,7 +63,9 @@
 
 mojom::URLLoaderFactory* ChildURLLoaderFactoryGetter::GetBlobLoaderFactory() {
   if (!blob_loader_factory_) {
-    DCHECK(!blob_loader_factory_getter_.is_null());
+    if (blob_loader_factory_getter_.is_null()) {
+      return GetNetworkLoaderFactory();
+    }
     blob_loader_factory_ = std::move(blob_loader_factory_getter_).Run();
   }
   return blob_loader_factory_.get();
diff --git a/content/child/child_url_loader_factory_getter.h b/content/child/child_url_loader_factory_getter.h
index db0952e..65e51bdf 100644
--- a/content/child/child_url_loader_factory_getter.h
+++ b/content/child/child_url_loader_factory_getter.h
@@ -24,11 +24,34 @@
   using URLLoaderFactoryGetterCallback =
       base::OnceCallback<mojom::URLLoaderFactoryPtr()>;
 
+  // Info class stores necessary information to create a clone of
+  // ChildURLLoaderFactoryGetter in worker thread.
+  class Info {
+   public:
+    Info(mojom::URLLoaderFactoryPtrInfo network_loader_factory_info,
+         mojom::URLLoaderFactoryPtrInfo blob_loader_factory_info);
+    Info(Info&& other);
+    ~Info();
+
+    scoped_refptr<ChildURLLoaderFactoryGetter> Bind();
+
+   private:
+    mojom::URLLoaderFactoryPtrInfo network_loader_factory_info_;
+    mojom::URLLoaderFactoryPtrInfo blob_loader_factory_info_;
+  };
+
   ChildURLLoaderFactoryGetter();
+
   ChildURLLoaderFactoryGetter(
       PossiblyAssociatedURLLoaderFactory network_loader_factory,
       URLLoaderFactoryGetterCallback blob_loader_factory_getter);
 
+  ChildURLLoaderFactoryGetter(
+      PossiblyAssociatedURLLoaderFactory network_loader_factory,
+      PossiblyAssociatedURLLoaderFactory blob_loader_factory_getter);
+
+  Info GetClonedInfo();
+
   mojom::URLLoaderFactory* GetNetworkLoaderFactory();
   mojom::URLLoaderFactory* GetBlobLoaderFactory();
 
diff --git a/content/child/loader/cors_url_loader_factory.cc b/content/child/loader/cors_url_loader_factory.cc
index 8d5583cc..7ece1ce 100644
--- a/content/child/loader/cors_url_loader_factory.cc
+++ b/content/child/loader/cors_url_loader_factory.cc
@@ -46,10 +46,11 @@
 }
 
 void CORSURLLoaderFactory::Clone(mojom::URLLoaderFactoryRequest request) {
-  // Cloning a CORSURLLoaderFactory so that it can be used from a different
-  // thread is not useful because that thread will still be dependent on the
-  // current one to handle requests.
-  NOTREACHED();
+  mojom::URLLoaderFactoryPtr network_loader_factory_copy;
+  network_loader_factory_->Clone(
+      mojo::MakeRequest(&network_loader_factory_copy));
+  CORSURLLoaderFactory::CreateAndBind(std::move(network_loader_factory_copy),
+                                      std::move(request));
 }
 
 }  // namespace content
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index 03571df..904b755f 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -320,11 +320,8 @@
   WebRuntimeFeatures::EnableServiceWorkerScriptStreaming(
       base::FeatureList::IsEnabled(features::kServiceWorkerScriptStreaming));
 
-  if (!base::FeatureList::IsEnabled(features::kNetworkService)) {
-    // http://crbug.com/756571: fix this to work with network service.
-    WebRuntimeFeatures::EnableOffMainThreadFetch(
-        base::FeatureList::IsEnabled(features::kOffMainThreadFetch));
-  }
+  WebRuntimeFeatures::EnableOffMainThreadFetch(
+      base::FeatureList::IsEnabled(features::kOffMainThreadFetch));
 
   WebRuntimeFeatures::EnableMojoBlobs(
       base::FeatureList::IsEnabled(features::kMojoBlobs));
diff --git a/content/child/service_worker/service_worker_provider_context.cc b/content/child/service_worker/service_worker_provider_context.cc
index 198a8202..e5994504 100644
--- a/content/child/service_worker/service_worker_provider_context.cc
+++ b/content/child/service_worker/service_worker_provider_context.cc
@@ -5,8 +5,10 @@
 #include "content/child/service_worker/service_worker_provider_context.h"
 
 #include <utility>
+#include <vector>
 
 #include "base/macros.h"
+#include "base/stl_util.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "content/child/child_thread_impl.h"
 #include "content/child/child_url_loader_factory_getter.h"
@@ -45,6 +47,15 @@
 
   // Tracks feature usage for UseCounter.
   std::set<uint32_t> used_features;
+
+  // Keeps ServiceWorkerWorkerClient pointers of dedicated or shared workers
+  // which are associated with the ServiceWorkerProviderContext.
+  // - If this ServiceWorkerProviderContext is for a Document, then
+  //   |worker_clients| contains all its dedicated workers.
+  // - If this ServiceWorkerProviderContext is for a SharedWorker (technically
+  //   speaking, for its shadow page), then |worker_clients| has one element:
+  //   the shared worker.
+  std::vector<mojom::ServiceWorkerWorkerClientPtr> worker_clients;
 };
 
 // Holds state for service worker execution contexts.
@@ -130,6 +141,14 @@
   DCHECK(state);
   DCHECK(!state->controller ||
          state->controller->handle_id() != kInvalidServiceWorkerHandleId);
+
+  if (controller) {
+    for (const auto& worker : state->worker_clients) {
+      // This is a Mojo interface call to the (dedicated or shared) worker
+      // thread.
+      worker->SetControllerServiceWorker(controller->version_id());
+    }
+  }
   state->controller = std::move(controller);
   state->used_features = used_features;
   if (event_dispatcher_ptr_info.is_valid()) {
@@ -169,6 +188,30 @@
   return controllee_state_->used_features;
 }
 
+mojom::ServiceWorkerWorkerClientRequest
+ServiceWorkerProviderContext::CreateWorkerClientRequest() {
+  DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence());
+  DCHECK(controllee_state_);
+  mojom::ServiceWorkerWorkerClientPtr client;
+  mojom::ServiceWorkerWorkerClientRequest request = mojo::MakeRequest(&client);
+  client.set_connection_error_handler(base::BindOnce(
+      &ServiceWorkerProviderContext::UnregisterWorkerFetchContext,
+      base::Unretained(this), client.get()));
+  controllee_state_->worker_clients.push_back(std::move(client));
+  return request;
+}
+
+void ServiceWorkerProviderContext::UnregisterWorkerFetchContext(
+    mojom::ServiceWorkerWorkerClient* client) {
+  DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence());
+  DCHECK(controllee_state_);
+  base::EraseIf(
+      controllee_state_->worker_clients,
+      [client](const mojom::ServiceWorkerWorkerClientPtr& client_ptr) {
+        return client_ptr.get() == client;
+      });
+}
+
 void ServiceWorkerProviderContext::OnNetworkProviderDestroyed() {
   provider_host_.reset();
 }
diff --git a/content/child/service_worker/service_worker_provider_context.h b/content/child/service_worker/service_worker_provider_context.h
index f2a47e9..2a2083c5b0 100644
--- a/content/child/service_worker/service_worker_provider_context.h
+++ b/content/child/service_worker/service_worker_provider_context.h
@@ -6,8 +6,6 @@
 #define CONTENT_CHILD_SERVICE_WORKER_SERVICE_WORKER_PROVIDER_CONTEXT_H_
 
 #include <memory>
-#include <set>
-#include <vector>
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
@@ -113,6 +111,16 @@
   void CountFeature(uint32_t feature);
   const std::set<uint32_t>& used_features() const;
 
+  // For service worker clients. Creates a ServiceWorkerWorkerClientRequest
+  // which can be used to bind with a WorkerFetchContextImpl in a (dedicated or
+  // shared) worker thread and receive SetControllerServiceWorker() method call
+  // from the main thread.
+  // A dedicated worker's WorkerFetchContext calls CreateWorkerClientRequest()
+  // on its parent Document's ServiceWorkerProviderContext. A shared worker's
+  // fetch context calls CreateWorkerClientRequest() on its own
+  // ServiceWorkerProviderContext.
+  mojom::ServiceWorkerWorkerClientRequest CreateWorkerClientRequest();
+
   // Called when ServiceWorkerNetworkProvider is destructed. This function
   // severs the Mojo binding to the browser-side ServiceWorkerProviderHost. The
   // reason ServiceWorkerNetworkProvider is special compared to the other
@@ -134,6 +142,10 @@
   ~ServiceWorkerProviderContext() override;
   void DestructOnMainThread() const;
 
+  // Clears the information of the ServiceWorkerWorkerClient of dedicated (or
+  // shared) worker, when the connection to the worker is disconnected.
+  void UnregisterWorkerFetchContext(mojom::ServiceWorkerWorkerClient*);
+
   const int provider_id_;
   scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
 
diff --git a/content/child/v8_value_converter_impl_unittest.cc b/content/child/v8_value_converter_impl_unittest.cc
index 62d0663..c3bac08 100644
--- a/content/child/v8_value_converter_impl_unittest.cc
+++ b/content/child/v8_value_converter_impl_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/stl_util.h"
+#include "base/test/scoped_task_environment.h"
 #include "base/test/values_test_util.h"
 #include "base/values.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -212,6 +213,8 @@
     }
   }
 
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+
   v8::Isolate* isolate_;
 
   // Context for the JavaScript in the test.
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index 9dd41b16..1ce689f 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -637,7 +637,6 @@
     "storage_partition_service.mojom",
     "video_capture.mojom",
     "widget.mojom",
-    "worker_url_loader_factory_provider.mojom",
   ]
 
   import_dirs = [ "//mojo/services" ]
diff --git a/content/common/message_port.cc b/content/common/message_port.cc
index cc0d27b..0cb337b 100644
--- a/content/common/message_port.cc
+++ b/content/common/message_port.cc
@@ -5,6 +5,7 @@
 #include "content/common/message_port.h"
 
 #include "base/bind.h"
+#include "base/containers/span.h"
 #include "base/logging.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "content/common/message_port.mojom.h"
@@ -54,8 +55,7 @@
   // HTML MessagePorts have no way of reporting when the peer is gone.
 
   MessagePortMessage msg;
-  msg.encoded_message =
-      mojo::ConstCArray<uint8_t>(encoded_message, encoded_message_size);
+  msg.encoded_message = base::make_span(encoded_message, encoded_message_size);
   msg.ports.resize(ports.size());
   for (size_t i = 0; i < ports.size(); ++i)
     msg.ports[i] = ports[i].ReleaseHandle();
diff --git a/content/common/message_port_message.h b/content/common/message_port_message.h
index dc9a554..086fe75 100644
--- a/content/common/message_port_message.h
+++ b/content/common/message_port_message.h
@@ -6,7 +6,8 @@
 #define CONTENT_COMMON_MESSAGE_PORT_MESSAGE_H_
 
 #include <vector>
-#include "mojo/public/cpp/bindings/array_traits_span.h"
+
+#include "base/containers/span.h"
 #include "mojo/public/cpp/system/message_pipe.h"
 
 namespace content {
@@ -22,7 +23,7 @@
   // |owned_encoded_message| and just serializes whatever |encoded_message|
   // points to. When deserializing |owned_encoded_message| is set to the data
   // and |encoded_message| is set to point to |owned_encoded_message|.
-  mojo::ConstCArray<uint8_t> encoded_message;
+  base::span<const uint8_t> encoded_message;
   std::vector<uint8_t> owned_encoded_message;
 
   // Any ports being transfered as part of this message.
diff --git a/content/common/message_port_message_struct_traits.cc b/content/common/message_port_message_struct_traits.cc
index 31cb5e0..355bbdab 100644
--- a/content/common/message_port_message_struct_traits.cc
+++ b/content/common/message_port_message_struct_traits.cc
@@ -4,6 +4,8 @@
 
 #include "content/common/message_port_message_struct_traits.h"
 
+#include "base/containers/span.h"
+
 namespace mojo {
 
 bool StructTraits<content::mojom::MessagePortMessage::DataView,
@@ -14,8 +16,7 @@
       !data.ReadPorts(&out->ports))
     return false;
 
-  out->encoded_message = mojo::ConstCArray<uint8_t>(
-      out->owned_encoded_message.data(), out->owned_encoded_message.size());
+  out->encoded_message = out->owned_encoded_message;
   return true;
 }
 
diff --git a/content/common/message_port_message_struct_traits.h b/content/common/message_port_message_struct_traits.h
index 3a6bb97..44d8bcf 100644
--- a/content/common/message_port_message_struct_traits.h
+++ b/content/common/message_port_message_struct_traits.h
@@ -5,6 +5,7 @@
 #ifndef CONTENT_COMMON_MESSAGE_PORT_MESSAGE_STRUCT_TRAITS_H_
 #define CONTENT_COMMON_MESSAGE_PORT_MESSAGE_STRUCT_TRAITS_H_
 
+#include "base/containers/span.h"
 #include "content/common/message_port.mojom.h"
 #include "content/common/message_port_message.h"
 
@@ -13,7 +14,7 @@
 template <>
 struct StructTraits<content::mojom::MessagePortMessage::DataView,
                     content::MessagePortMessage> {
-  static ConstCArray<uint8_t> encoded_message(
+  static base::span<const uint8_t> encoded_message(
       content::MessagePortMessage& input) {
     return input.encoded_message;
   }
diff --git a/content/common/service_worker/service_worker_provider_interfaces.mojom b/content/common/service_worker/service_worker_provider_interfaces.mojom
index 9f62bba..fbec377 100644
--- a/content/common/service_worker/service_worker_provider_interfaces.mojom
+++ b/content/common/service_worker/service_worker_provider_interfaces.mojom
@@ -4,6 +4,24 @@
 
 module content.mojom;
 
+// ServiceWorkerWorkerClient represents a service worker client that is a worker
+// (i.e., a shared worker or dedicated worker). We use this interface to let the
+// WorkerFetchContextImpl in the worker thread know about the change of
+// controlling service worker by calling SetControllerServiceWorker() from the
+// ServiceWorkerProviderContext in the main thread of the renderer process.
+//
+// TODO(horo): We should implement ServiceWorkerProvider in the worker thread
+// instead of using this interface to support WorkerNavigator.serviceWorker
+// when we can make the worker thread totally independent from the main thread.
+// Currently we handle synchronous resource loading such as importScripts() and
+// synchronous XHR in the main thread. So We need to know whether the worker is
+// controlled by a service worker or not both in the main thread and in the
+// worker thread.
+interface ServiceWorkerWorkerClient {
+  // Called when the worker is controlled by a new service worker.
+  SetControllerServiceWorker(int64 controller_version_id);
+};
+
 // mojom::ServiceWorkerProviderHost is a browser-side interface. The renderer
 // uses this interface to request the browser to do operations involving service
 // worker registrations.
@@ -24,4 +42,4 @@
   // disassociateRegistration();
   // setControllerServiceWorker();
   // messageToDocument();
-};
\ No newline at end of file
+};
diff --git a/content/common/worker_url_loader_factory_provider.mojom b/content/common/worker_url_loader_factory_provider.mojom
deleted file mode 100644
index 71a4956..0000000
--- a/content/common/worker_url_loader_factory_provider.mojom
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-module content.mojom;
-
-import "content/public/common/url_loader_factory.mojom";
-
-// A renderer-side interface that is returned by CreateWorkerFetchContext for
-// the browser to notify the renderer process when there's a controller change.
-interface ServiceWorkerWorkerClient {
-  // Called when the ServiceWorkerWorkerClient (which is a dedicated worker or
-  // a shared worker) is controlled by a new service worker.
-  SetControllerServiceWorker(int64 controller_version_id);
-};
-
-// A browser-side interface which provides URLLoaderFactory for worker contexts
-// in the renderer process.
-interface WorkerURLLoaderFactoryProvider {
-  // Called from a dedicated (or shared) worker thread to get the
-  // URLLoaderFactory and register ServiceWorkerWorkerClient of the worker.
-  // SetControllerServiceWorker method of the passed ServiceWorkerWorkerClient
-  // interface will be called when the parent frame and the worker context is
-  // controlled by a service worker. |service_worker_provider_id| is the service
-  // worker provider id of the parent frame.
-  GetURLLoaderFactoryAndRegisterClient(
-      associated URLLoaderFactory& loader,
-      associated ServiceWorkerWorkerClient client,
-      int32 service_worker_provider_id);
-
-  // Called from the service worker thread to get the URLLoaderFactory.
-  GetURLLoaderFactory(associated URLLoaderFactory& loader);
-};
diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc
index b4ca9f5f..480a90fc 100644
--- a/content/public/common/content_switches.cc
+++ b/content/public/common/content_switches.cc
@@ -346,6 +346,10 @@
 const char kEnablePreferCompositingToLCDText[] =
     "enable-prefer-compositing-to-lcd-text";
 
+// Enable work-in-progress persistent storage for the Background Fetch API.
+const char kEnableBackgroundFetchPersistence[] =
+    "enable-background-fetch-persistence";
+
 // Enable one or more Blink runtime-enabled features.
 // Use names from RuntimeEnabledFeatures.json5, separated by commas.
 // Applied before kDisableBlinkFeatures, and after other flags that change these
diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h
index 7dab9f9..cf17a89 100644
--- a/content/public/common/content_switches.h
+++ b/content/public/common/content_switches.h
@@ -111,6 +111,7 @@
 CONTENT_EXPORT extern const char kEnableAggressiveDOMStorageFlushing[];
 CONTENT_EXPORT extern const char kEnablePreferCompositingToLCDText[];
 CONTENT_EXPORT extern const char kEnableBlinkFeatures[];
+CONTENT_EXPORT extern const char kEnableBackgroundFetchPersistence[];
 CONTENT_EXPORT extern const char kEnableBrowserSideNavigation[];
 CONTENT_EXPORT extern const char kEnableDisplayList2dCanvas[];
 CONTENT_EXPORT extern const char kEnableDistanceFieldText[];
diff --git a/content/renderer/media/video_capture_impl_manager_unittest.cc b/content/renderer/media/video_capture_impl_manager_unittest.cc
index 5189c7f..d500ec2 100644
--- a/content/renderer/media/video_capture_impl_manager_unittest.cc
+++ b/content/renderer/media/video_capture_impl_manager_unittest.cc
@@ -9,8 +9,8 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/ref_counted.h"
-#include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
 #include "content/child/child_process.h"
 #include "content/common/media/media_stream_options.h"
 #include "content/common/video_capture.mojom.h"
@@ -204,7 +204,7 @@
                    base::Unretained(this)));
   }
 
-  const base::MessageLoop message_loop_;
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
   ChildProcess child_process_;
   base::RunLoop cleanup_run_loop_;
   std::unique_ptr<MockVideoCaptureImplManager> manager_;
diff --git a/content/renderer/media/video_capture_impl_unittest.cc b/content/renderer/media/video_capture_impl_unittest.cc
index 58b630a1..45f1532 100644
--- a/content/renderer/media/video_capture_impl_unittest.cc
+++ b/content/renderer/media/video_capture_impl_unittest.cc
@@ -6,7 +6,7 @@
 
 #include "base/macros.h"
 #include "base/memory/shared_memory.h"
-#include "base/message_loop/message_loop.h"
+#include "base/test/scoped_task_environment.h"
 #include "content/child/child_process.h"
 #include "content/common/video_capture.mojom.h"
 #include "content/renderer/media/video_capture_impl.h"
@@ -183,7 +183,7 @@
     video_capture_impl_->OnStateChanged(state);
   }
 
-  const base::MessageLoop message_loop_;
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
   const ChildProcess child_process_;
   const std::unique_ptr<VideoCaptureImpl> video_capture_impl_;
   MockMojoVideoCaptureHost mock_video_capture_host_;
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 83a4099..cfe5d88 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -75,7 +75,6 @@
 #include "content/common/site_isolation_policy.h"
 #include "content/common/swapped_out_messages.h"
 #include "content/common/view_messages.h"
-#include "content/common/worker_url_loader_factory_provider.mojom.h"
 #include "content/public/common/appcache_info.h"
 #include "content/public/common/associated_interface_provider.h"
 #include "content/public/common/bindings_policy.h"
@@ -720,15 +719,6 @@
   return result;
 }
 
-mojom::URLLoaderFactoryPtr GetBlobURLLoaderFactoryGetter() {
-  mojom::URLLoaderFactoryPtr blob_loader_factory;
-  if (base::FeatureList::IsEnabled(features::kNetworkService)) {
-    RenderThreadImpl::current()->GetRendererHost()->GetBlobURLLoaderFactory(
-        mojo::MakeRequest(&blob_loader_factory));
-  }
-  return blob_loader_factory;
-}
-
 }  // namespace
 
 // The following methods are outside of the anonymous namespace to ensure that
@@ -3010,30 +3000,31 @@
 std::unique_ptr<blink::WebWorkerFetchContext>
 RenderFrameImpl::CreateWorkerFetchContext() {
   DCHECK(base::FeatureList::IsEnabled(features::kOffMainThreadFetch));
-  mojom::WorkerURLLoaderFactoryProviderPtr worker_url_loader_factory_provider;
-  RenderThreadImpl::current()
-      ->blink_platform_impl()
-      ->GetInterfaceProvider()
-      ->GetInterface(mojo::MakeRequest(&worker_url_loader_factory_provider));
+  blink::WebServiceWorkerNetworkProvider* web_provider =
+      frame_->GetDocumentLoader()->GetServiceWorkerNetworkProvider();
+  DCHECK(web_provider);
+  ServiceWorkerNetworkProvider* provider =
+      ServiceWorkerNetworkProvider::FromWebServiceWorkerNetworkProvider(
+          web_provider);
+  mojom::ServiceWorkerWorkerClientRequest request =
+      provider->context()->CreateWorkerClientRequest();
+
+  ChildURLLoaderFactoryGetter* url_loader_factory_getter =
+      GetDefaultURLLoaderFactoryGetter();
+  DCHECK(url_loader_factory_getter);
   std::unique_ptr<WorkerFetchContextImpl> worker_fetch_context =
       base::MakeUnique<WorkerFetchContextImpl>(
-          worker_url_loader_factory_provider.PassInterface());
+          std::move(request), url_loader_factory_getter->GetClonedInfo());
+
   worker_fetch_context->set_parent_frame_id(routing_id_);
   worker_fetch_context->set_site_for_cookies(
       frame_->GetDocument().SiteForCookies());
   worker_fetch_context->set_is_secure_context(
       frame_->GetDocument().IsSecureContext());
-  blink::WebServiceWorkerNetworkProvider* web_provider =
-      frame_->GetDocumentLoader()->GetServiceWorkerNetworkProvider();
-  if (web_provider) {
-    ServiceWorkerNetworkProvider* provider =
-        ServiceWorkerNetworkProvider::FromWebServiceWorkerNetworkProvider(
-            web_provider);
     worker_fetch_context->set_service_worker_provider_id(
         provider->provider_id());
     worker_fetch_context->set_is_controlled_by_service_worker(
         provider->IsControlledByServiceWorker());
-  }
   for (auto& observer : observers_)
     observer.WillCreateWorkerFetchContext(worker_fetch_context.get());
   return std::move(worker_fetch_context);
@@ -6839,11 +6830,8 @@
   RenderThreadImpl* render_thread = RenderThreadImpl::current();
   DCHECK(render_thread);
   if (!url_loader_factory_getter_) {
-    url_loader_factory_getter_ =
-        base::MakeRefCounted<ChildURLLoaderFactoryGetter>(
-            render_thread->blink_platform_impl()
-                ->CreateNetworkURLLoaderFactory(),
-            base::BindOnce(&GetBlobURLLoaderFactoryGetter));
+    url_loader_factory_getter_ = render_thread->blink_platform_impl()
+                                     ->CreateDefaultURLLoaderFactoryGetter();
   }
   return url_loader_factory_getter_.get();
 }
diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc
index ea5625d1..68d0ec27 100644
--- a/content/renderer/renderer_blink_platform_impl.cc
+++ b/content/renderer/renderer_blink_platform_impl.cc
@@ -8,6 +8,7 @@
 #include <utility>
 
 #include "base/command_line.h"
+#include "base/feature_list.h"
 #include "base/files/file_path.h"
 #include "base/guid.h"
 #include "base/lazy_instance.h"
@@ -26,6 +27,7 @@
 #include "build/build_config.h"
 #include "components/url_formatter/url_formatter.h"
 #include "content/child/blob_storage/webblobregistry_impl.h"
+#include "content/child/child_url_loader_factory_getter.h"
 #include "content/child/database_util.h"
 #include "content/child/file_info_util.h"
 #include "content/child/fileapi/webfilesystem_impl.h"
@@ -197,6 +199,13 @@
       .output_params();
 }
 
+mojom::URLLoaderFactoryPtr GetBlobURLLoaderFactoryGetter() {
+  mojom::URLLoaderFactoryPtr blob_loader_factory;
+  RenderThreadImpl::current()->GetRendererHost()->GetBlobURLLoaderFactory(
+      mojo::MakeRequest(&blob_loader_factory));
+  return blob_loader_factory;
+}
+
 }  // namespace
 
 //------------------------------------------------------------------------------
@@ -326,6 +335,15 @@
       url_loader_factory_.get());
 }
 
+scoped_refptr<ChildURLLoaderFactoryGetter>
+RendererBlinkPlatformImpl::CreateDefaultURLLoaderFactoryGetter() {
+  return base::MakeRefCounted<ChildURLLoaderFactoryGetter>(
+      CreateNetworkURLLoaderFactory(),
+      base::FeatureList::IsEnabled(features::kNetworkService)
+          ? base::BindOnce(&GetBlobURLLoaderFactoryGetter)
+          : ChildURLLoaderFactoryGetter::URLLoaderFactoryGetterCallback());
+}
+
 PossiblyAssociatedInterfacePtr<mojom::URLLoaderFactory>
 RendererBlinkPlatformImpl::CreateNetworkURLLoaderFactory() {
   ChildThreadImpl* child_thread = ChildThreadImpl::current();
diff --git a/content/renderer/renderer_blink_platform_impl.h b/content/renderer/renderer_blink_platform_impl.h
index 450612d..2a9c4ac 100644
--- a/content/renderer/renderer_blink_platform_impl.h
+++ b/content/renderer/renderer_blink_platform_impl.h
@@ -56,6 +56,7 @@
 
 namespace content {
 class BlinkInterfaceProviderImpl;
+class ChildURLLoaderFactoryGetter;
 class LocalStorageCachedAreas;
 class PlatformEventObserverBase;
 class QuotaMessageFilter;
@@ -251,6 +252,12 @@
   PossiblyAssociatedInterfacePtr<mojom::URLLoaderFactory>
   CreateNetworkURLLoaderFactory();
 
+  // Returns non-null.
+  // It is invalid to call this in an incomplete env where
+  // RenderThreadImpl::current() returns nullptr (e.g. in some tests).
+  scoped_refptr<ChildURLLoaderFactoryGetter>
+  CreateDefaultURLLoaderFactoryGetter();
+
  private:
   bool CheckPreparsedJsCachingEnabled() const;
 
diff --git a/content/renderer/service_worker/service_worker_context_client.cc b/content/renderer/service_worker/service_worker_context_client.cc
index b568e07..77874ff 100644
--- a/content/renderer/service_worker/service_worker_context_client.cc
+++ b/content/renderer/service_worker/service_worker_context_client.cc
@@ -19,6 +19,7 @@
 #include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
 #include "content/child/background_sync/background_sync_type_converters.h"
+#include "content/child/child_url_loader_factory_getter.h"
 #include "content/child/notifications/notification_data_conversions.h"
 #include "content/child/request_extra_data.h"
 #include "content/child/service_worker/service_worker_dispatcher.h"
@@ -39,7 +40,6 @@
 #include "content/common/service_worker/service_worker_messages.h"
 #include "content/common/service_worker/service_worker_status_code.h"
 #include "content/common/service_worker/service_worker_utils.h"
-#include "content/common/worker_url_loader_factory_provider.mojom.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/push_event_payload.h"
 #include "content/public/common/referrer.h"
@@ -1243,15 +1243,15 @@
 ServiceWorkerContextClient::CreateServiceWorkerFetchContext() {
   DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence());
   DCHECK(base::FeatureList::IsEnabled(features::kOffMainThreadFetch));
-  mojom::WorkerURLLoaderFactoryProviderPtr worker_url_loader_factory_provider;
-  RenderThreadImpl::current()
-      ->blink_platform_impl()
-      ->GetInterfaceProvider()
-      ->GetInterface(mojo::MakeRequest(&worker_url_loader_factory_provider));
 
+  scoped_refptr<ChildURLLoaderFactoryGetter> url_loader_factory_getter =
+      RenderThreadImpl::current()
+          ->blink_platform_impl()
+          ->CreateDefaultURLLoaderFactoryGetter();
+  DCHECK(url_loader_factory_getter);
   // Blink is responsible for deleting the returned object.
   return base::MakeUnique<ServiceWorkerFetchContextImpl>(
-      script_url_, worker_url_loader_factory_provider.PassInterface(),
+      script_url_, url_loader_factory_getter->GetClonedInfo(),
       provider_context_->provider_id());
 }
 
diff --git a/content/renderer/service_worker/service_worker_fetch_context_impl.cc b/content/renderer/service_worker/service_worker_fetch_context_impl.cc
index b12ba76..754ae2f 100644
--- a/content/renderer/service_worker/service_worker_fetch_context_impl.cc
+++ b/content/renderer/service_worker/service_worker_fetch_context_impl.cc
@@ -4,37 +4,45 @@
 
 #include "content/renderer/service_worker/service_worker_fetch_context_impl.h"
 
+#include "base/feature_list.h"
 #include "content/child/request_extra_data.h"
 #include "content/child/resource_dispatcher.h"
 #include "content/child/web_url_loader_impl.h"
+#include "content/public/common/content_features.h"
 
 namespace content {
 
 ServiceWorkerFetchContextImpl::ServiceWorkerFetchContextImpl(
     const GURL& worker_script_url,
-    mojom::WorkerURLLoaderFactoryProviderPtrInfo provider_info,
+    ChildURLLoaderFactoryGetter::Info url_loader_factory_getter_info,
     int service_worker_provider_id)
     : worker_script_url_(worker_script_url),
-      provider_info_(std::move(provider_info)),
+      url_loader_factory_getter_info_(
+          std::move(url_loader_factory_getter_info)),
       service_worker_provider_id_(service_worker_provider_id) {}
 
 ServiceWorkerFetchContextImpl::~ServiceWorkerFetchContextImpl() {}
 
 void ServiceWorkerFetchContextImpl::InitializeOnWorkerThread(
     base::SingleThreadTaskRunner* loading_task_runner) {
-  DCHECK(provider_info_.is_valid());
   resource_dispatcher_ =
       base::MakeUnique<ResourceDispatcher>(nullptr, loading_task_runner);
-  provider_.Bind(std::move(provider_info_));
-  provider_->GetURLLoaderFactory(mojo::MakeRequest(&url_loader_factory_));
+
+  url_loader_factory_getter_ = url_loader_factory_getter_info_.Bind();
 }
 
 std::unique_ptr<blink::WebURLLoader>
 ServiceWorkerFetchContextImpl::CreateURLLoader(
     const blink::WebURLRequest& request,
     base::SingleThreadTaskRunner* task_runner) {
+  if (request.Url().ProtocolIs(url::kBlobScheme)) {
+    return base::MakeUnique<content::WebURLLoaderImpl>(
+        resource_dispatcher_.get(), task_runner,
+        url_loader_factory_getter_->GetBlobLoaderFactory());
+  }
   return base::MakeUnique<content::WebURLLoaderImpl>(
-      resource_dispatcher_.get(), task_runner, url_loader_factory_.get());
+      resource_dispatcher_.get(), task_runner,
+      url_loader_factory_getter_->GetNetworkLoaderFactory());
 }
 
 void ServiceWorkerFetchContextImpl::WillSendRequest(
diff --git a/content/renderer/service_worker/service_worker_fetch_context_impl.h b/content/renderer/service_worker/service_worker_fetch_context_impl.h
index 230dbad..b6d0ff8 100644
--- a/content/renderer/service_worker/service_worker_fetch_context_impl.h
+++ b/content/renderer/service_worker/service_worker_fetch_context_impl.h
@@ -5,7 +5,8 @@
 #ifndef CONTENT_RENDERER_SERVICE_WORKER_SERVICE_WORKER_FETCH_CONTEXT_IMPL_H_
 #define CONTENT_RENDERER_SERVICE_WORKER_SERVICE_WORKER_FETCH_CONTEXT_IMPL_H_
 
-#include "content/common/worker_url_loader_factory_provider.mojom.h"
+#include "content/child/child_url_loader_factory_getter.h"
+#include "content/public/common/url_loader_factory.mojom.h"
 #include "third_party/WebKit/public/platform/WebWorkerFetchContext.h"
 #include "url/gurl.h"
 
@@ -20,7 +21,7 @@
  public:
   ServiceWorkerFetchContextImpl(
       const GURL& worker_script_url,
-      mojom::WorkerURLLoaderFactoryProviderPtrInfo provider_info,
+      ChildURLLoaderFactoryGetter::Info url_loader_factory_getter_info,
       int service_worker_provider_id);
   ~ServiceWorkerFetchContextImpl() override;
 
@@ -37,13 +38,13 @@
 
  private:
   const GURL worker_script_url_;
-  mojom::WorkerURLLoaderFactoryProviderPtrInfo provider_info_;
+  // Consumed on the worker thread to create |url_loader_factory_getter_|.
+  ChildURLLoaderFactoryGetter::Info url_loader_factory_getter_info_;
   const int service_worker_provider_id_;
 
   // Initialized on the worker thread when InitializeOnWorkerThread() is called.
   std::unique_ptr<ResourceDispatcher> resource_dispatcher_;
-  mojom::WorkerURLLoaderFactoryProviderPtr provider_;
-  mojom::URLLoaderFactoryAssociatedPtr url_loader_factory_;
+  scoped_refptr<ChildURLLoaderFactoryGetter> url_loader_factory_getter_;
 
   bool is_data_saver_enabled_ = false;
 };
diff --git a/content/renderer/service_worker/worker_fetch_context_impl.cc b/content/renderer/service_worker/worker_fetch_context_impl.cc
index bb9274d..7fa2f6b 100644
--- a/content/renderer/service_worker/worker_fetch_context_impl.cc
+++ b/content/renderer/service_worker/worker_fetch_context_impl.cc
@@ -4,46 +4,54 @@
 
 #include "content/renderer/service_worker/worker_fetch_context_impl.h"
 
+#include "base/feature_list.h"
 #include "content/child/child_thread_impl.h"
 #include "content/child/request_extra_data.h"
 #include "content/child/resource_dispatcher.h"
 #include "content/child/thread_safe_sender.h"
 #include "content/child/web_url_loader_impl.h"
 #include "content/common/frame_messages.h"
-#include "mojo/public/cpp/bindings/associated_binding.h"
+#include "content/public/common/content_features.h"
+#include "mojo/public/cpp/bindings/binding.h"
 
 namespace content {
 
 WorkerFetchContextImpl::WorkerFetchContextImpl(
-    mojom::WorkerURLLoaderFactoryProviderPtrInfo provider_info)
-    : provider_info_(std::move(provider_info)),
+    mojom::ServiceWorkerWorkerClientRequest request,
+    ChildURLLoaderFactoryGetter::Info url_loader_factory_getter_info)
+    : binding_(this),
+      request_(std::move(request)),
+      url_loader_factory_getter_info_(
+          std::move(url_loader_factory_getter_info)),
       thread_safe_sender_(ChildThreadImpl::current()->thread_safe_sender()) {}
 
 WorkerFetchContextImpl::~WorkerFetchContextImpl() {}
 
 void WorkerFetchContextImpl::InitializeOnWorkerThread(
     base::SingleThreadTaskRunner* loading_task_runner) {
+  DCHECK(request_.is_pending());
   DCHECK(loading_task_runner->RunsTasksInCurrentSequence());
   DCHECK(!resource_dispatcher_);
-  DCHECK(!binding_);
+  DCHECK(!binding_.is_bound());
   resource_dispatcher_ =
       base::MakeUnique<ResourceDispatcher>(nullptr, loading_task_runner);
-  binding_ = base::MakeUnique<
-      mojo::AssociatedBinding<mojom::ServiceWorkerWorkerClient>>(this);
-  DCHECK(provider_info_.is_valid());
-  provider_.Bind(std::move(provider_info_));
-  mojom::ServiceWorkerWorkerClientAssociatedPtrInfo ptr_info;
-  binding_->Bind(mojo::MakeRequest(&ptr_info));
-  provider_->GetURLLoaderFactoryAndRegisterClient(
-      mojo::MakeRequest(&url_loader_factory_), std::move(ptr_info),
-      service_worker_provider_id_);
+
+  url_loader_factory_getter_ = url_loader_factory_getter_info_.Bind();
+
+  binding_.Bind(std::move(request_));
 }
 
 std::unique_ptr<blink::WebURLLoader> WorkerFetchContextImpl::CreateURLLoader(
     const blink::WebURLRequest& request,
     base::SingleThreadTaskRunner* task_runner) {
+  if (request.Url().ProtocolIs(url::kBlobScheme)) {
+    return base::MakeUnique<content::WebURLLoaderImpl>(
+        resource_dispatcher_.get(), task_runner,
+        url_loader_factory_getter_->GetBlobLoaderFactory());
+  }
   return base::MakeUnique<content::WebURLLoaderImpl>(
-      resource_dispatcher_.get(), task_runner, url_loader_factory_.get());
+      resource_dispatcher_.get(), task_runner,
+      url_loader_factory_getter_->GetNetworkLoaderFactory());
 }
 
 void WorkerFetchContextImpl::WillSendRequest(blink::WebURLRequest& request) {
diff --git a/content/renderer/service_worker/worker_fetch_context_impl.h b/content/renderer/service_worker/worker_fetch_context_impl.h
index 1bb00d5a..2d2ac525 100644
--- a/content/renderer/service_worker/worker_fetch_context_impl.h
+++ b/content/renderer/service_worker/worker_fetch_context_impl.h
@@ -5,11 +5,13 @@
 #ifndef CONTENT_RENDERER_SERVICE_WORKER_WORKER_FETCH_CONTEXT_IMPL_H_
 #define CONTENT_RENDERER_SERVICE_WORKER_WORKER_FETCH_CONTEXT_IMPL_H_
 
+#include "content/child/child_url_loader_factory_getter.h"
+#include "content/common/service_worker/service_worker_provider_interfaces.mojom.h"
 #include "content/common/service_worker/service_worker_types.h"
-#include "content/common/worker_url_loader_factory_provider.mojom.h"
 #include "content/public/common/service_worker_modes.h"
+#include "content/public/common/url_loader_factory.mojom.h"
 #include "ipc/ipc_message.h"
-#include "mojo/public/cpp/bindings/associated_binding.h"
+#include "mojo/public/cpp/bindings/binding.h"
 #include "third_party/WebKit/public/platform/WebApplicationCacheHost.h"
 #include "third_party/WebKit/public/platform/WebWorkerFetchContext.h"
 #include "url/gurl.h"
@@ -35,8 +37,9 @@
 class WorkerFetchContextImpl : public blink::WebWorkerFetchContext,
                                public mojom::ServiceWorkerWorkerClient {
  public:
-  explicit WorkerFetchContextImpl(
-      mojom::WorkerURLLoaderFactoryProviderPtrInfo provider_info);
+  WorkerFetchContextImpl(
+      mojom::ServiceWorkerWorkerClientRequest request,
+      ChildURLLoaderFactoryGetter::Info url_loader_factory_getter_info);
   ~WorkerFetchContextImpl() override;
 
   // blink::WebWorkerFetchContext implementation:
@@ -79,16 +82,19 @@
  private:
   bool Send(IPC::Message* message);
 
-  mojom::WorkerURLLoaderFactoryProviderPtrInfo provider_info_;
+  mojo::Binding<mojom::ServiceWorkerWorkerClient> binding_;
+
+  mojom::ServiceWorkerWorkerClientRequest request_;
+  // Consumed on the worker thread to create |url_loader_factory_getter_|.
+  ChildURLLoaderFactoryGetter::Info url_loader_factory_getter_info_;
+
   int service_worker_provider_id_ = kInvalidServiceWorkerProviderId;
   bool is_controlled_by_service_worker_ = false;
 
   // Initialized on the worker thread when InitializeOnWorkerThread() is called.
   std::unique_ptr<ResourceDispatcher> resource_dispatcher_;
-  std::unique_ptr<mojo::AssociatedBinding<mojom::ServiceWorkerWorkerClient>>
-      binding_;
-  mojom::WorkerURLLoaderFactoryProviderPtr provider_;
-  mojom::URLLoaderFactoryAssociatedPtr url_loader_factory_;
+
+  scoped_refptr<ChildURLLoaderFactoryGetter> url_loader_factory_getter_;
 
   // Updated when mojom::ServiceWorkerWorkerClient::SetControllerServiceWorker()
   // is called from the browser process via mojo IPC.
diff --git a/content/renderer/shared_worker/embedded_shared_worker_stub.cc b/content/renderer/shared_worker/embedded_shared_worker_stub.cc
index df4e6cc..e36a57f 100644
--- a/content/renderer/shared_worker/embedded_shared_worker_stub.cc
+++ b/content/renderer/shared_worker/embedded_shared_worker_stub.cc
@@ -11,6 +11,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "content/child/appcache/appcache_dispatcher.h"
 #include "content/child/appcache/web_application_cache_host_impl.h"
+#include "content/child/child_url_loader_factory_getter.h"
 #include "content/child/request_extra_data.h"
 #include "content/child/scoped_child_process_reference.h"
 #include "content/child/service_worker/service_worker_handle_reference.h"
@@ -19,7 +20,6 @@
 #include "content/child/shared_worker_devtools_agent.h"
 #include "content/child/webmessageportchannel_impl.h"
 #include "content/common/worker_messages.h"
-#include "content/common/worker_url_loader_factory_provider.mojom.h"
 #include "content/public/common/appcache_info.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/origin_util.h"
@@ -71,10 +71,10 @@
 };
 
 // Called on the main thread only and blink owns it.
-class WebServiceWorkerNetworkProviderImpl
+class WebServiceWorkerNetworkProviderForSharedWorker
     : public blink::WebServiceWorkerNetworkProvider {
  public:
-  WebServiceWorkerNetworkProviderImpl(
+  WebServiceWorkerNetworkProviderForSharedWorker(
       std::unique_ptr<ServiceWorkerNetworkProvider> provider,
       bool is_secure_context)
       : provider_(std::move(provider)), is_secure_context_(is_secure_context) {}
@@ -114,6 +114,8 @@
     return kInvalidServiceWorkerVersionId;
   }
 
+  ServiceWorkerNetworkProvider* provider() { return provider_.get(); }
+
   // TODO(kinuko): Implement CreateURLLoader with provider_->context()->
   // subresource_loader if Servicification is enabled.
 
@@ -239,7 +241,7 @@
       ServiceWorkerNetworkProvider::CreateForSharedWorker(route_id_));
 
   // Blink is responsible for deleting the returned object.
-  return base::MakeUnique<WebServiceWorkerNetworkProviderImpl>(
+  return base::MakeUnique<WebServiceWorkerNetworkProviderForSharedWorker>(
       std::move(provider), IsOriginSecure(url_));
 }
 
@@ -261,14 +263,22 @@
 EmbeddedSharedWorkerStub::CreateWorkerFetchContext(
     blink::WebServiceWorkerNetworkProvider* web_network_provider) {
   DCHECK(base::FeatureList::IsEnabled(features::kOffMainThreadFetch));
-  mojom::WorkerURLLoaderFactoryProviderPtr worker_url_loader_factory_provider;
-  RenderThreadImpl::current()
-      ->blink_platform_impl()
-      ->GetInterfaceProvider()
-      ->GetInterface(mojo::MakeRequest(&worker_url_loader_factory_provider));
-  std::unique_ptr<WorkerFetchContextImpl> worker_fetch_context =
-      base::MakeUnique<WorkerFetchContextImpl>(
-          worker_url_loader_factory_provider.PassInterface());
+  DCHECK(web_network_provider);
+  mojom::ServiceWorkerWorkerClientRequest request =
+      static_cast<WebServiceWorkerNetworkProviderForSharedWorker*>(
+          web_network_provider)
+          ->provider()
+          ->context()
+          ->CreateWorkerClientRequest();
+
+  scoped_refptr<ChildURLLoaderFactoryGetter> url_loader_factory_getter =
+      RenderThreadImpl::current()
+          ->blink_platform_impl()
+          ->CreateDefaultURLLoaderFactoryGetter();
+  DCHECK(url_loader_factory_getter);
+  auto worker_fetch_context = base::MakeUnique<WorkerFetchContextImpl>(
+      std::move(request), url_loader_factory_getter->GetClonedInfo());
+
   // TODO(horo): To get the correct first_party_to_cookies for the shared
   // worker, we need to check the all documents bounded by the shared worker.
   // (crbug.com/723553)
diff --git a/content/test/data/accessibility/event/menulist-collapse-expected-win.txt b/content/test/data/accessibility/event/menulist-collapse-expected-win.txt
index 2b58648..5ed2eb42 100644
--- a/content/test/data/accessibility/event/menulist-collapse-expected-win.txt
+++ b/content/test/data/accessibility/event/menulist-collapse-expected-win.txt
@@ -1,2 +1,2 @@
-EVENT_OBJECT_STATECHANGE on role=ROLE_SYSTEM_MENUITEM name="Apple" FOCUSED,INVISIBLE,FOCUSABLE,SELECTABLE
+EVENT_OBJECT_STATECHANGE on role=ROLE_SYSTEM_LISTITEM name="Apple" FOCUSED,INVISIBLE,FOCUSABLE,SELECTABLE
 EVENT_OBJECT_VALUECHANGE on role=ROLE_SYSTEM_COMBOBOX COLLAPSED,FOCUSABLE,HASPOPUP
diff --git a/content/test/data/accessibility/event/menulist-focus-expected-win.txt b/content/test/data/accessibility/event/menulist-focus-expected-win.txt
index 5e7cc2d..ac94789 100644
--- a/content/test/data/accessibility/event/menulist-focus-expected-win.txt
+++ b/content/test/data/accessibility/event/menulist-focus-expected-win.txt
@@ -1 +1 @@
-EVENT_OBJECT_FOCUS on role=ROLE_SYSTEM_MENUITEM name="Apple" SELECTED,FOCUSED,FOCUSABLE,SELECTABLE
+EVENT_OBJECT_FOCUS on role=ROLE_SYSTEM_LISTITEM name="Apple" SELECTED,FOCUSED,FOCUSABLE,SELECTABLE
diff --git a/content/test/data/accessibility/html/modal-dialog-closed-expected-win.txt b/content/test/data/accessibility/html/modal-dialog-closed-expected-win.txt
index f1fc9ec..be1c7779 100644
--- a/content/test/data/accessibility/html/modal-dialog-closed-expected-win.txt
+++ b/content/test/data/accessibility/html/modal-dialog-closed-expected-win.txt
@@ -2,6 +2,6 @@
 ++ROLE_SYSTEM_STATICTEXT name='Test that elements respawn in the accessibility tree after a modal dialog closes.'
 ++IA2_ROLE_SECTION
 ++++ROLE_SYSTEM_COMBOBOX COLLAPSED FOCUSABLE HASPOPUP
-++++++ROLE_SYSTEM_MENUPOPUP INVISIBLE
-++++++++ROLE_SYSTEM_MENUITEM name='This should be in the tree.' SELECTED FOCUSABLE
+++++++ROLE_SYSTEM_LIST INVISIBLE
+++++++++ROLE_SYSTEM_LISTITEM name='This should be in the tree.' SELECTED FOCUSABLE
 ++IA2_ROLE_COLOR_CHOOSER FOCUSABLE
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/select-expected-win.txt b/content/test/data/accessibility/html/select-expected-win.txt
index 37c8ee04..14d5a78 100644
--- a/content/test/data/accessibility/html/select-expected-win.txt
+++ b/content/test/data/accessibility/html/select-expected-win.txt
@@ -1,20 +1,20 @@
 ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE ia2_hypertext='<obj0>'
 ++IA2_ROLE_SECTION ia2_hypertext='<obj0><obj1><obj2><obj3><obj4>'
 ++++ROLE_SYSTEM_COMBOBOX COLLAPSED FOCUSABLE HASPOPUP ia2_hypertext='<obj0>'
-++++++ROLE_SYSTEM_MENUPOPUP INVISIBLE ia2_hypertext='<obj0><obj1><obj2>'
-++++++++ROLE_SYSTEM_MENUITEM name='Placeholder option' SELECTED FOCUSABLE SELECTABLE ia2_hypertext='Placeholder option'
-++++++++ROLE_SYSTEM_MENUITEM name='Option 1' INVISIBLE FOCUSABLE SELECTABLE ia2_hypertext='Option 1'
-++++++++ROLE_SYSTEM_MENUITEM name='Option 2' INVISIBLE FOCUSABLE SELECTABLE ia2_hypertext='Option 2'
+++++++ROLE_SYSTEM_LIST INVISIBLE ia2_hypertext='<obj0><obj1><obj2>'
+++++++++ROLE_SYSTEM_LISTITEM name='Placeholder option' SELECTED FOCUSABLE SELECTABLE ia2_hypertext='Placeholder option'
+++++++++ROLE_SYSTEM_LISTITEM name='Option 1' INVISIBLE FOCUSABLE SELECTABLE ia2_hypertext='Option 1'
+++++++++ROLE_SYSTEM_LISTITEM name='Option 2' INVISIBLE FOCUSABLE SELECTABLE ia2_hypertext='Option 2'
 ++++ROLE_SYSTEM_COMBOBOX COLLAPSED FOCUSABLE HASPOPUP ia2_hypertext='<obj0>'
-++++++ROLE_SYSTEM_MENUPOPUP INVISIBLE ia2_hypertext='<obj0><obj1><obj2>'
-++++++++ROLE_SYSTEM_MENUITEM name='Option 1' INVISIBLE FOCUSABLE SELECTABLE ia2_hypertext='Option 1'
-++++++++ROLE_SYSTEM_MENUITEM name='Option 2' SELECTED FOCUSABLE SELECTABLE ia2_hypertext='Option 2'
-++++++++ROLE_SYSTEM_MENUITEM name='Option 3' INVISIBLE FOCUSABLE SELECTABLE ia2_hypertext='Option 3'
+++++++ROLE_SYSTEM_LIST INVISIBLE ia2_hypertext='<obj0><obj1><obj2>'
+++++++++ROLE_SYSTEM_LISTITEM name='Option 1' INVISIBLE FOCUSABLE SELECTABLE ia2_hypertext='Option 1'
+++++++++ROLE_SYSTEM_LISTITEM name='Option 2' SELECTED FOCUSABLE SELECTABLE ia2_hypertext='Option 2'
+++++++++ROLE_SYSTEM_LISTITEM name='Option 3' INVISIBLE FOCUSABLE SELECTABLE ia2_hypertext='Option 3'
 ++++ROLE_SYSTEM_COMBOBOX COLLAPSED FOCUSABLE HASPOPUP IA2_STATE_REQUIRED ia2_hypertext='<obj0>'
-++++++ROLE_SYSTEM_MENUPOPUP INVISIBLE ia2_hypertext='<obj0><obj1><obj2>'
-++++++++ROLE_SYSTEM_MENUITEM name='Option 1' SELECTED FOCUSABLE SELECTABLE ia2_hypertext='Option 1'
-++++++++ROLE_SYSTEM_MENUITEM name='Option 2' INVISIBLE FOCUSABLE SELECTABLE ia2_hypertext='Option 2'
-++++++++ROLE_SYSTEM_MENUITEM name='Option 3' INVISIBLE FOCUSABLE SELECTABLE ia2_hypertext='Option 3'
+++++++ROLE_SYSTEM_LIST INVISIBLE ia2_hypertext='<obj0><obj1><obj2>'
+++++++++ROLE_SYSTEM_LISTITEM name='Option 1' SELECTED FOCUSABLE SELECTABLE ia2_hypertext='Option 1'
+++++++++ROLE_SYSTEM_LISTITEM name='Option 2' INVISIBLE FOCUSABLE SELECTABLE ia2_hypertext='Option 2'
+++++++++ROLE_SYSTEM_LISTITEM name='Option 3' INVISIBLE FOCUSABLE SELECTABLE ia2_hypertext='Option 3'
 ++++ROLE_SYSTEM_LIST FOCUSABLE MULTISELECTABLE EXTSELECTABLE ia2_hypertext='<obj0><obj1><obj2>'
 ++++++ROLE_SYSTEM_LISTITEM name='Option 1' FOCUSABLE SELECTABLE ia2_hypertext='Option 1'
 ++++++ROLE_SYSTEM_LISTITEM name='Option 2' FOCUSABLE SELECTABLE ia2_hypertext='Option 2'
@@ -22,4 +22,4 @@
 ++++ROLE_SYSTEM_LIST FOCUSABLE ia2_hypertext='<obj0><obj1><obj2>'
 ++++++ROLE_SYSTEM_LISTITEM name='Option 1' FOCUSABLE SELECTABLE ia2_hypertext='Option 1'
 ++++++ROLE_SYSTEM_LISTITEM name='Option 2' FOCUSABLE SELECTABLE ia2_hypertext='Option 2'
-++++++ROLE_SYSTEM_LISTITEM name='Option 3' FOCUSABLE SELECTABLE ia2_hypertext='Option 3'
\ No newline at end of file
+++++++ROLE_SYSTEM_LISTITEM name='Option 3' FOCUSABLE SELECTABLE ia2_hypertext='Option 3'
diff --git a/device/gamepad/public/interfaces/gamepad_struct_traits.cc b/device/gamepad/public/interfaces/gamepad_struct_traits.cc
index 2b95544b..56e77b7 100644
--- a/device/gamepad/public/interfaces/gamepad_struct_traits.cc
+++ b/device/gamepad/public/interfaces/gamepad_struct_traits.cc
@@ -4,6 +4,8 @@
 
 #include "device/gamepad/public/interfaces/gamepad_struct_traits.h"
 
+#include "base/containers/span.h"
+
 namespace mojo {
 
 // static
@@ -130,19 +132,18 @@
 }
 
 // static
-ConstCArray<uint16_t>
+base::span<const uint16_t>
 StructTraits<device::mojom::GamepadDataView, device::Gamepad>::id(
     const device::Gamepad& r) {
   size_t id_length = 0;
   while (id_length < device::Gamepad::kIdLengthCap && r.id[id_length] != 0) {
     id_length++;
   }
-  return ConstCArray<uint16_t>(reinterpret_cast<const uint16_t*>(r.id),
-                               id_length);
+  return base::make_span(reinterpret_cast<const uint16_t*>(r.id), id_length);
 }
 
 // static
-ConstCArray<uint16_t>
+base::span<const uint16_t>
 StructTraits<device::mojom::GamepadDataView, device::Gamepad>::mapping(
     const device::Gamepad& r) {
   size_t mapping_length = 0;
@@ -150,8 +151,8 @@
          r.mapping[mapping_length] != 0) {
     mapping_length++;
   }
-  return ConstCArray<uint16_t>(reinterpret_cast<const uint16_t*>(r.mapping),
-                               mapping_length);
+  return base::make_span(reinterpret_cast<const uint16_t*>(r.mapping),
+                         mapping_length);
 }
 
 // static
@@ -161,22 +162,22 @@
   out->connected = data.connected();
 
   memset(out->id, 0, sizeof(out->id));
-  CArray<uint16_t> id(reinterpret_cast<uint16_t*>(out->id),
-                      device::Gamepad::kIdLengthCap);
+  base::span<uint16_t> id(reinterpret_cast<uint16_t*>(out->id),
+                          device::Gamepad::kIdLengthCap);
   if (!data.ReadId(&id)) {
     return false;
   }
 
   out->timestamp = data.timestamp();
 
-  CArray<double> axes(out->axes);
+  base::span<double> axes(out->axes);
   if (!data.ReadAxes(&axes)) {
     return false;
   }
   // static_cast is safe when "data.ReadAxes(&axes)" above returns true.
   out->axes_length = static_cast<unsigned>(axes.size());
 
-  CArray<device::GamepadButton> buttons(out->buttons);
+  base::span<device::GamepadButton> buttons(out->buttons);
   if (!data.ReadButtons(&buttons)) {
     return false;
   }
@@ -184,8 +185,8 @@
   out->buttons_length = static_cast<unsigned>(buttons.size());
 
   memset(out->mapping, 0, sizeof(out->mapping));
-  CArray<uint16_t> mapping(reinterpret_cast<uint16_t*>(out->mapping),
-                           device::Gamepad::kMappingLengthCap);
+  base::span<uint16_t> mapping(reinterpret_cast<uint16_t*>(out->mapping),
+                               device::Gamepad::kMappingLengthCap);
   if (!data.ReadMapping(&mapping)) {
     return false;
   }
diff --git a/device/gamepad/public/interfaces/gamepad_struct_traits.h b/device/gamepad/public/interfaces/gamepad_struct_traits.h
index 531f6bb..e175270 100644
--- a/device/gamepad/public/interfaces/gamepad_struct_traits.h
+++ b/device/gamepad/public/interfaces/gamepad_struct_traits.h
@@ -7,6 +7,7 @@
 
 #include <stddef.h>
 
+#include "base/containers/span.h"
 #include "device/gamepad/public/cpp/gamepad.h"
 #include "device/gamepad/public/interfaces/gamepad.mojom.h"
 #include "mojo/public/cpp/bindings/array_traits_span.h"
@@ -91,11 +92,12 @@
 struct StructTraits<device::mojom::GamepadDataView, device::Gamepad> {
   static bool connected(const device::Gamepad& r) { return r.connected; }
   static uint64_t timestamp(const device::Gamepad& r) { return r.timestamp; }
-  static ConstCArray<double> axes(const device::Gamepad& r) {
-    return ConstCArray<double>(r.axes, r.axes_length);
+  static base::span<const double> axes(const device::Gamepad& r) {
+    return base::make_span(r.axes, r.axes_length);
   }
-  static ConstCArray<device::GamepadButton> buttons(const device::Gamepad& r) {
-    return ConstCArray<device::GamepadButton>(r.buttons, r.buttons_length);
+  static base::span<const device::GamepadButton> buttons(
+      const device::Gamepad& r) {
+    return base::make_span(r.buttons, r.buttons_length);
   }
   static const device::GamepadPose& pose(const device::Gamepad& r) {
     return r.pose;
@@ -105,8 +107,8 @@
   }
   static uint32_t display_id(const device::Gamepad& r) { return r.display_id; }
 
-  static ConstCArray<uint16_t> id(const device::Gamepad& r);
-  static ConstCArray<uint16_t> mapping(const device::Gamepad& r);
+  static base::span<const uint16_t> id(const device::Gamepad& r);
+  static base::span<const uint16_t> mapping(const device::Gamepad& r);
   static bool Read(device::mojom::GamepadDataView data, device::Gamepad* out);
 };
 
diff --git a/docs/speed/perf_bot_sheriffing.md b/docs/speed/perf_bot_sheriffing.md
index 61a4ca4..1660697 100644
--- a/docs/speed/perf_bot_sheriffing.md
+++ b/docs/speed/perf_bot_sheriffing.md
@@ -342,7 +342,7 @@
 For example:
 *  If a single story is failing on a single platform, disable only that story on that platform.
 *  If multiple stories are failing across all platforms, disable those stories on all platforms.
-*  If all stories are failing on a single platform, disable all stories on that platform. 
+*  If all stories are failing on a single platform, disable all stories on that platform.
 
 You can do this with [StoryExpectations](https://cs.chromium.org/chromium/src/third_party/catapult/telemetry/telemetry/story/expectations.py).
 
@@ -368,7 +368,7 @@
 * foo
 ```
 
-Buildbot output for failing run on platfromQ
+Buildbot output for failing run on platform Q
 ```
 bar.benchmark_baz
 Bot id: 'buildxxx-xx'
@@ -403,6 +403,19 @@
 
 To find the currently supported disabling conditions view the [expectations file](https://cs.chromium.org/chromium/src/third_party/catapult/telemetry/telemetry/story/expectations.py).
 
+In the case that a benchmark is failing in its entirety on a platfrom that it
+should noramally run on, you can temporarily disable it by using
+DisableBenchmark():
+
+```
+class BarBenchmark(perf_benchmark.PerfBenchmark):
+  ...
+  def GetExpectations(self):
+    class StoryExpectations(story.expectations.StoryExpectations):
+      def SetExpectations(self):
+        self.DisableBenchmark([story.expectation.PLATFORM_Q], 'crbug.com/9876')
+```
+
 If for some reason you are unable to disable at the granularity you would like,
 disable the test at the lowest granularity possible and contact rnephew@ to
 suggest new disabling criteria.
diff --git a/extensions/BUILD.gn b/extensions/BUILD.gn
index 4c06d532..d1e1fc2 100644
--- a/extensions/BUILD.gn
+++ b/extensions/BUILD.gn
@@ -102,8 +102,6 @@
     "common/permissions/mock_manifest_permission.h",
     "common/permissions/permission_message_test_util.cc",
     "common/permissions/permission_message_test_util.h",
-    "common/test_util.cc",
-    "common/test_util.h",
     "renderer/test_extensions_renderer_client.cc",
     "renderer/test_extensions_renderer_client.h",
     "test/background_page_watcher.cc",
diff --git a/extensions/browser/api/storage/storage_api_unittest.cc b/extensions/browser/api/storage/storage_api_unittest.cc
index 0587221..bc4f095d 100644
--- a/extensions/browser/api/storage/storage_api_unittest.cc
+++ b/extensions/browser/api/storage/storage_api_unittest.cc
@@ -24,7 +24,6 @@
 #include "extensions/browser/value_store/value_store.h"
 #include "extensions/browser/value_store/value_store_factory_impl.h"
 #include "extensions/common/manifest.h"
-#include "extensions/common/test_util.h"
 #include "third_party/leveldatabase/src/include/leveldb/db.h"
 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
 
diff --git a/extensions/browser/guest_view/web_view/web_view_permission_helper.cc b/extensions/browser/guest_view/web_view/web_view_permission_helper.cc
index b6cd132..1b6f240b 100644
--- a/extensions/browser/guest_view/web_view/web_view_permission_helper.cc
+++ b/extensions/browser/guest_view/web_view/web_view_permission_helper.cc
@@ -301,15 +301,6 @@
       render_process_id, render_frame_id, request_id, url, blocked_by_policy);
 }
 
-void WebViewPermissionHelper::FileSystemAccessedSync(int render_process_id,
-                                                     int render_frame_id,
-                                                     const GURL& url,
-                                                     bool blocked_by_policy,
-                                                     IPC::Message* reply_msg) {
-  web_view_permission_helper_delegate_->FileSystemAccessedSync(
-      render_process_id, render_frame_id, url, blocked_by_policy, reply_msg);
-}
-
 int WebViewPermissionHelper::RequestPermission(
     WebViewPermissionType permission_type,
     const base::DictionaryValue& request_info,
diff --git a/extensions/browser/guest_view/web_view/web_view_permission_helper.h b/extensions/browser/guest_view/web_view/web_view_permission_helper.h
index f7dab53..963afbb 100644
--- a/extensions/browser/guest_view/web_view/web_view_permission_helper.h
+++ b/extensions/browser/guest_view/web_view/web_view_permission_helper.h
@@ -97,23 +97,6 @@
                                const GURL& url,
                                bool blocked_by_policy);
 
-  // Called when file system access is requested by the guest content using the
-  // synchronous HTML5 file system API in a worker thread or shared worker. The
-  // request is plumbed through the <webview> permission request API. The
-  // request will be:
-  // - Allowed if the embedder explicitly allowed it.
-  // - Denied if the embedder explicitly denied.
-  // - Determined by the guest's content settings if the embedder does not
-  // perform an explicit action.
-  // If access was blocked due to the page's content settings,
-  // |blocked_by_policy| should be true, and this function should invoke
-  // OnContentBlocked.
-  void FileSystemAccessedSync(int render_process_id,
-                              int render_frame_id,
-                              const GURL& url,
-                              bool blocked_by_policy,
-                              IPC::Message* reply_msg);
-
   enum PermissionResponseAction { DENY, ALLOW, DEFAULT };
 
   enum SetPermissionResult {
diff --git a/extensions/browser/guest_view/web_view/web_view_permission_helper_delegate.h b/extensions/browser/guest_view/web_view/web_view_permission_helper_delegate.h
index 8bdec71..547caa7 100644
--- a/extensions/browser/guest_view/web_view/web_view_permission_helper_delegate.h
+++ b/extensions/browser/guest_view/web_view/web_view_permission_helper_delegate.h
@@ -62,24 +62,6 @@
       const GURL& url,
       bool blocked_by_policy) {}
 
-  // Called when file system access is requested by the guest content using the
-  // synchronous HTML5 file system API in a worker thread or shared worker. The
-  // request is plumbed through the <webview> permission request API. The
-  // request will be:
-  // - Allowed if the embedder explicitly allowed it.
-  // - Denied if the embedder explicitly denied.
-  // - Determined by the guest's content settings if the embedder does not
-  // perform an explicit action.
-  // If access was blocked due to the page's content settings,
-  // |blocked_by_policy| should be true, and this function should invoke
-  // OnContentBlocked.
-  virtual void FileSystemAccessedSync(
-      int render_process_id,
-      int render_frame_id,
-      const GURL& url,
-      bool blocked_by_policy,
-      IPC::Message* reply_msg) {}
-
   WebViewPermissionHelper* web_view_permission_helper() const {
     return web_view_permission_helper_;
   }
diff --git a/extensions/common/manifest_test.cc b/extensions/common/manifest_test.cc
index 6a966e6..645a13b 100644
--- a/extensions/common/manifest_test.cc
+++ b/extensions/common/manifest_test.cc
@@ -15,7 +15,6 @@
 #include "base/values.h"
 #include "extensions/common/extension_l10n_util.h"
 #include "extensions/common/extension_paths.h"
-#include "extensions/common/test_util.h"
 #include "ui/base/l10n/l10n_util.h"
 
 namespace extensions {
diff --git a/extensions/common/test_util.cc b/extensions/common/test_util.cc
deleted file mode 100644
index ee895fda..0000000
--- a/extensions/common/test_util.cc
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "extensions/common/test_util.h"
-
-#include <utility>
-
-#include "extensions/common/extension.h"
-#include "extensions/common/extension_builder.h"
-#include "extensions/common/value_builder.h"
-
-namespace extensions {
-namespace test_util {
-
-}  // namespace test_util
-}  // namespace extensions
diff --git a/extensions/common/test_util.h b/extensions/common/test_util.h
deleted file mode 100644
index cb450fe..0000000
--- a/extensions/common/test_util.h
+++ /dev/null
@@ -1,18 +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 EXTENSIONS_COMMON_TEST_UTIL_H_
-#define EXTENSIONS_COMMON_TEST_UTIL_H_
-
-#include <string>
-
-#include "base/memory/ref_counted.h"
-
-namespace extensions {
-namespace test_util {
-
-}  // namespace test_util
-}  // namespace extensions
-
-#endif  // EXTENSIONS_COMMON_TEST_UTIL_H_
diff --git a/gpu/BUILD.gn b/gpu/BUILD.gn
index f5ec5cc..4f0042e 100644
--- a/gpu/BUILD.gn
+++ b/gpu/BUILD.gn
@@ -226,6 +226,8 @@
     "command_buffer/client/buffer_tracker_unittest.cc",
     "command_buffer/client/client_discardable_manager_unittest.cc",
     "command_buffer/client/cmd_buffer_helper_test.cc",
+    "command_buffer/client/command_buffer_direct_locked.cc",
+    "command_buffer/client/command_buffer_direct_locked.h",
     "command_buffer/client/fenced_allocator_test.cc",
     "command_buffer/client/gles2_implementation_unittest.cc",
     "command_buffer/client/mapped_memory_unittest.cc",
diff --git a/gpu/command_buffer/client/cmd_buffer_helper_test.cc b/gpu/command_buffer/client/cmd_buffer_helper_test.cc
index 1e6b82b..bdc77645 100644
--- a/gpu/command_buffer/client/cmd_buffer_helper_test.cc
+++ b/gpu/command_buffer/client/cmd_buffer_helper_test.cc
@@ -17,7 +17,7 @@
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
 #include "gpu/command_buffer/client/cmd_buffer_helper.h"
-#include "gpu/command_buffer/service/command_buffer_direct.h"
+#include "gpu/command_buffer/client/command_buffer_direct_locked.h"
 #include "gpu/command_buffer/service/mocks.h"
 #include "gpu/command_buffer/service/transfer_buffer_manager.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -37,71 +37,6 @@
     kTotalNumCommandEntries * sizeof(CommandBufferEntry);
 const int32_t kUnusedCommandId = 5;  // we use 0 and 2 currently.
 
-// Override CommandBufferDirect::Flush() to lock flushing and simulate
-// the buffer becoming full in asynchronous mode.
-class CommandBufferDirectLocked : public CommandBufferDirect {
- public:
-  explicit CommandBufferDirectLocked(
-      TransferBufferManager* transfer_buffer_manager)
-      : CommandBufferDirect(transfer_buffer_manager) {}
-  ~CommandBufferDirectLocked() override {}
-
-  // Overridden from CommandBufferDirect
-  void Flush(int32_t put_offset) override {
-    flush_count_++;
-    if (!flush_locked_) {
-      last_flush_ = -1;
-      previous_put_offset_ = put_offset;
-      CommandBufferDirect::Flush(put_offset);
-    } else {
-      last_flush_ = put_offset;
-    }
-  }
-
-  void LockFlush() { flush_locked_ = true; }
-
-  void UnlockFlush() { flush_locked_ = false; }
-
-  int FlushCount() { return flush_count_; }
-
-  State WaitForGetOffsetInRange(uint32_t set_get_buffer_count,
-                                int32_t start,
-                                int32_t end) override {
-    // Flush only if it's required to unblock this Wait.
-    if (last_flush_ != -1 && !InRange(start, end, previous_put_offset_)) {
-      previous_put_offset_ = last_flush_;
-      CommandBufferDirect::Flush(last_flush_);
-      last_flush_ = -1;
-    }
-    return CommandBufferDirect::WaitForGetOffsetInRange(set_get_buffer_count,
-                                                        start, end);
-  }
-
-  scoped_refptr<Buffer> CreateTransferBuffer(size_t size,
-                                             int32_t* id) override {
-    if (fail_create_transfer_buffer_) {
-      *id = -1;
-      return nullptr;
-    } else {
-      return CommandBufferDirect::CreateTransferBuffer(size, id);
-    }
-  }
-
-  int GetServicePutOffset() { return previous_put_offset_; }
-
-  void set_fail_create_transfer_buffer(bool fail) {
-    fail_create_transfer_buffer_ = fail;
-  }
-
- private:
-  bool fail_create_transfer_buffer_ = false;
-  bool flush_locked_ = false;
-  int last_flush_ = -1;
-  int previous_put_offset_ = 0;
-  int flush_count_ = 0;
-  DISALLOW_COPY_AND_ASSIGN(CommandBufferDirectLocked);
-};
-
 // Test fixture for CommandBufferHelper test - Creates a CommandBufferHelper,
 // using a CommandBufferServiceLocked with a mock AsyncAPIInterface for its
 // interface (calling it directly, not through the RPC mechanism).
diff --git a/gpu/command_buffer/client/command_buffer_direct_locked.cc b/gpu/command_buffer/client/command_buffer_direct_locked.cc
new file mode 100644
index 0000000..f51210e
--- /dev/null
+++ b/gpu/command_buffer/client/command_buffer_direct_locked.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 "gpu/command_buffer/client/command_buffer_direct_locked.h"
+
+namespace gpu {
+
+void CommandBufferDirectLocked::Flush(int32_t put_offset) {
+  flush_count_++;
+  client_put_offset_ = put_offset;
+  if (!flush_locked_)
+    DoFlush();
+}
+
+CommandBuffer::State CommandBufferDirectLocked::WaitForTokenInRange(
+    int32_t start,
+    int32_t end) {
+  State state = GetLastState();
+  if (state.error != error::kNoError || InRange(start, end, state.token)) {
+    return state;
+  } else {
+    DoFlush();
+    return CommandBufferDirect::WaitForTokenInRange(start, end);
+  }
+}
+
+CommandBuffer::State CommandBufferDirectLocked::WaitForGetOffsetInRange(
+    uint32_t set_get_buffer_count,
+    int32_t start,
+    int32_t end) {
+  State state = GetLastState();
+  if (state.error != error::kNoError ||
+      (InRange(start, end, state.get_offset) &&
+       (set_get_buffer_count == state.set_get_buffer_count))) {
+    return state;
+  } else {
+    DoFlush();
+    return CommandBufferDirect::WaitForGetOffsetInRange(set_get_buffer_count,
+                                                        start, end);
+  }
+}
+
+scoped_refptr<Buffer> CommandBufferDirectLocked::CreateTransferBuffer(
+    size_t size,
+    int32_t* id) {
+  if (fail_create_transfer_buffer_) {
+    *id = -1;
+    return nullptr;
+  } else {
+    return CommandBufferDirect::CreateTransferBuffer(size, id);
+  }
+}
+
+}  // namespace gpu
diff --git a/gpu/command_buffer/client/command_buffer_direct_locked.h b/gpu/command_buffer/client/command_buffer_direct_locked.h
new file mode 100644
index 0000000..a69ba1138
--- /dev/null
+++ b/gpu/command_buffer/client/command_buffer_direct_locked.h
@@ -0,0 +1,52 @@
+// 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 "gpu/command_buffer/service/command_buffer_direct.h"
+
+namespace gpu {
+
+// A CommandBuffer that allows "locking" flushes, that is delaying progress
+// until either it gets unlocked or the client waits for progress.
+class CommandBufferDirectLocked : public CommandBufferDirect {
+ public:
+  explicit CommandBufferDirectLocked(
+      TransferBufferManager* transfer_buffer_manager)
+      : CommandBufferDirect(transfer_buffer_manager) {}
+  ~CommandBufferDirectLocked() override {}
+
+  // Overridden from CommandBufferDirect
+  void Flush(int32_t put_offset) override;
+  CommandBuffer::State WaitForTokenInRange(int32_t start, int32_t end) override;
+  CommandBuffer::State WaitForGetOffsetInRange(uint32_t set_get_buffer_count,
+                                               int32_t start,
+                                               int32_t end) override;
+  scoped_refptr<Buffer> CreateTransferBuffer(size_t size, int32_t* id) override;
+
+  void LockFlush() { flush_locked_ = true; }
+
+  void UnlockFlush() { flush_locked_ = false; }
+
+  int FlushCount() { return flush_count_; }
+
+  int GetServicePutOffset() { return service_put_offset_; }
+
+  void set_fail_create_transfer_buffer(bool fail) {
+    fail_create_transfer_buffer_ = fail;
+  }
+
+ private:
+  void DoFlush() {
+    CommandBufferDirect::Flush(client_put_offset_);
+    service_put_offset_ = client_put_offset_;
+  }
+
+  bool fail_create_transfer_buffer_ = false;
+  bool flush_locked_ = false;
+  int client_put_offset_ = 0;
+  int service_put_offset_ = 0;
+  int flush_count_ = 0;
+  DISALLOW_COPY_AND_ASSIGN(CommandBufferDirectLocked);
+};
+
+}  // namespace gpu
diff --git a/gpu/command_buffer/client/fenced_allocator.cc b/gpu/command_buffer/client/fenced_allocator.cc
index 47450d31..4ca5ff9f 100644
--- a/gpu/command_buffer/client/fenced_allocator.cc
+++ b/gpu/command_buffer/client/fenced_allocator.cc
@@ -36,15 +36,10 @@
 }
 
 FencedAllocator::~FencedAllocator() {
-  // Free blocks pending tokens.
-  for (unsigned int i = 0; i < blocks_.size(); ++i) {
-    if (blocks_[i].state == FREE_PENDING_TOKEN) {
-      i = WaitForTokenAndFreeBlock(i);
-    }
-  }
-
-  DCHECK_EQ(blocks_.size(), 1u);
-  DCHECK_EQ(blocks_[0].state, FREE);
+  // All IN_USE blocks should be released at this point. There may still be
+  // FREE_PENDING_TOKEN blocks, the assumption is that the underlying memory
+  // will not be re-used without higher level synchronization.
+  DCHECK_EQ(bytes_in_use_, 0u);
 }
 
 // Looks for a non-allocated block that is big enough. Search in the FREE
@@ -86,8 +81,9 @@
 // necessary.
 void FencedAllocator::Free(FencedAllocator::Offset offset) {
   BlockIndex index = GetBlockByOffset(offset);
-  DCHECK_NE(blocks_[index].state, FREE);
   Block &block = blocks_[index];
+  DCHECK_NE(block.state, FREE);
+  DCHECK_EQ(block.offset, offset);
 
   if (block.state == IN_USE)
     bytes_in_use_ -= block.size;
@@ -101,6 +97,7 @@
                                        int32_t token) {
   BlockIndex index = GetBlockByOffset(offset);
   Block &block = blocks_[index];
+  DCHECK_EQ(block.offset, offset);
   if (block.state == IN_USE)
     bytes_in_use_ -= block.size;
   block.state = FREE_PENDING_TOKEN;
@@ -171,10 +168,20 @@
 
 // Returns false if all blocks are actually FREE, in which
 // case they would be coalesced into one block, true otherwise.
-bool FencedAllocator::InUse() {
+bool FencedAllocator::InUseOrFreePending() {
   return blocks_.size() != 1 || blocks_[0].state != FREE;
 }
 
+FencedAllocator::State FencedAllocator::GetBlockStatusForTest(
+    Offset offset,
+    int32_t* token_if_pending) {
+  BlockIndex index = GetBlockByOffset(offset);
+  Block& block = blocks_[index];
+  if ((block.state == FREE_PENDING_TOKEN) && token_if_pending)
+    *token_if_pending = block.token;
+  return block.state;
+}
+
 // Collapse the block to the next one, then to the previous one. Provided the
 // structure is consistent, those are the only blocks eligible for collapse.
 FencedAllocator::BlockIndex FencedAllocator::CollapseFreeBlock(
@@ -247,7 +254,7 @@
   Block templ = { IN_USE, offset, 0, kUnusedToken };
   Container::iterator it = std::lower_bound(blocks_.begin(), blocks_.end(),
                                             templ, OffsetCmp());
-  DCHECK(it != blocks_.end() && it->offset == offset);
+  DCHECK(it != blocks_.end());
   return it-blocks_.begin();
 }
 
diff --git a/gpu/command_buffer/client/fenced_allocator.h b/gpu/command_buffer/client/fenced_allocator.h
index 0e8c64c..9c42d34 100644
--- a/gpu/command_buffer/client/fenced_allocator.h
+++ b/gpu/command_buffer/client/fenced_allocator.h
@@ -39,6 +39,9 @@
   // Allocation alignment, must be a power of two.
   enum : unsigned int { kAllocAlignment = 16 };
 
+  // Status of a block of memory, for book-keeping.
+  enum State { IN_USE, FREE, FREE_PENDING_TOKEN };
+
   // Creates a FencedAllocator. Note that the size of the buffer is passed, but
   // not its base address: everything is handled as offsets into the buffer.
   FencedAllocator(unsigned int size, CommandBufferHelper* helper);
@@ -90,19 +93,16 @@
   bool CheckConsistency();
 
   // True if any memory is allocated.
-  bool InUse();
+  bool InUseOrFreePending();
 
   // Return bytes of memory that is IN_USE
   size_t bytes_in_use() const { return bytes_in_use_; }
 
- private:
-  // Status of a block of memory, for book-keeping.
-  enum State {
-    IN_USE,
-    FREE,
-    FREE_PENDING_TOKEN
-  };
+  // Gets the status of a block, as well as the corresponding token if
+  // FREE_PENDING_TOKEN.
+  State GetBlockStatusForTest(Offset offset, int32_t* token_if_pending);
 
+ private:
   // Book-keeping sturcture that describes a block of memory.
   struct Block {
     State state;
@@ -252,14 +252,18 @@
   }
 
   // True if any memory is allocated.
-  bool InUse() {
-    return allocator_.InUse();
-  }
+  bool InUseOrFreePending() { return allocator_.InUseOrFreePending(); }
 
   FencedAllocator &allocator() { return allocator_; }
 
   size_t bytes_in_use() const { return allocator_.bytes_in_use(); }
 
+  FencedAllocator::State GetPointerStatusForTest(void* pointer,
+                                                 int32_t* token_if_pending) {
+    return allocator_.GetBlockStatusForTest(GetOffset(pointer),
+                                            token_if_pending);
+  }
+
  private:
   FencedAllocator allocator_;
   void* base_;
diff --git a/gpu/command_buffer/client/fenced_allocator_test.cc b/gpu/command_buffer/client/fenced_allocator_test.cc
index 000738d7a..6f96810 100644
--- a/gpu/command_buffer/client/fenced_allocator_test.cc
+++ b/gpu/command_buffer/client/fenced_allocator_test.cc
@@ -96,17 +96,17 @@
 // Checks basic alloc and free.
 TEST_F(FencedAllocatorTest, TestBasic) {
   allocator_->CheckConsistency();
-  EXPECT_FALSE(allocator_->InUse());
+  EXPECT_FALSE(allocator_->InUseOrFreePending());
 
   const unsigned int kSize = 16;
   FencedAllocator::Offset offset = allocator_->Alloc(kSize);
-  EXPECT_TRUE(allocator_->InUse());
+  EXPECT_TRUE(allocator_->InUseOrFreePending());
   EXPECT_NE(FencedAllocator::kInvalidOffset, offset);
   EXPECT_GE(kBufferSize, offset+kSize);
   EXPECT_TRUE(allocator_->CheckConsistency());
 
   allocator_->Free(offset);
-  EXPECT_FALSE(allocator_->InUse());
+  EXPECT_FALSE(allocator_->InUseOrFreePending());
   EXPECT_TRUE(allocator_->CheckConsistency());
 }
 
@@ -114,7 +114,7 @@
 TEST_F(FencedAllocatorTest, TestAllocZero) {
   FencedAllocator::Offset offset = allocator_->Alloc(0);
   EXPECT_EQ(FencedAllocator::kInvalidOffset, offset);
-  EXPECT_FALSE(allocator_->InUse());
+  EXPECT_FALSE(allocator_->InUseOrFreePending());
   EXPECT_TRUE(allocator_->CheckConsistency());
 }
 
@@ -224,7 +224,7 @@
     EXPECT_GE(kBufferSize, offsets[i]+kSize);
     EXPECT_TRUE(allocator_->CheckConsistency());
   }
-  EXPECT_TRUE(allocator_->InUse());
+  EXPECT_TRUE(allocator_->InUseOrFreePending());
 
   // No memory should be available.
   EXPECT_EQ(0u, allocator_->GetLargestFreeSize());
@@ -261,14 +261,14 @@
 
   // Check that the new largest free size takes into account the unused blocks.
   EXPECT_EQ(kSize * 3, allocator_->GetLargestFreeSize());
-  EXPECT_TRUE(allocator_->InUse());
+  EXPECT_TRUE(allocator_->InUseOrFreePending());
 
   // Free up everything.
   for (unsigned int i = 3; i < kAllocCount; ++i) {
     allocator_->Free(offsets[i]);
     EXPECT_TRUE(allocator_->CheckConsistency());
   }
-  EXPECT_FALSE(allocator_->InUse());
+  EXPECT_FALSE(allocator_->InUseOrFreePending());
 }
 
 // Tests GetLargestFreeSize
diff --git a/gpu/command_buffer/client/gles2_implementation.cc b/gpu/command_buffer/client/gles2_implementation.cc
index 9af758e..dcab25e1 100644
--- a/gpu/command_buffer/client/gles2_implementation.cc
+++ b/gpu/command_buffer/client/gles2_implementation.cc
@@ -342,8 +342,7 @@
 }
 
 void GLES2Implementation::FreeEverything() {
-  WaitForCmd();
-  query_tracker_->Shrink();
+  query_tracker_->Shrink(helper_);
   FreeUnusedSharedMemory();
   transfer_buffer_->Free();
   helper_->FreeRingBuffer();
diff --git a/gpu/command_buffer/client/gles2_implementation_unittest.cc b/gpu/command_buffer/client/gles2_implementation_unittest.cc
index e7b0d1a..b02322f 100644
--- a/gpu/command_buffer/client/gles2_implementation_unittest.cc
+++ b/gpu/command_buffer/client/gles2_implementation_unittest.cc
@@ -3389,11 +3389,11 @@
   struct EndCmds {
     cmds::EndQueryEXT end_query;
   };
+  commands = GetPut();
+  gl_->EndQueryEXT(GL_ANY_SAMPLES_PASSED_EXT);
   EndCmds expected_end_cmds;
   expected_end_cmds.end_query.Init(
       GL_ANY_SAMPLES_PASSED_EXT, query->submit_count());
-  commands = GetPut();
-  gl_->EndQueryEXT(GL_ANY_SAMPLES_PASSED_EXT);
   EXPECT_EQ(0, memcmp(
       &expected_end_cmds, commands, sizeof(expected_end_cmds)));
 
@@ -3406,11 +3406,12 @@
   // Test 2nd Begin/End increments count.
   base::subtle::Atomic32 old_submit_count = query->submit_count();
   gl_->BeginQueryEXT(GL_ANY_SAMPLES_PASSED_EXT, id1);
+  EXPECT_EQ(old_submit_count, query->submit_count());
+  commands = GetPut();
+  gl_->EndQueryEXT(GL_ANY_SAMPLES_PASSED_EXT);
   EXPECT_NE(old_submit_count, query->submit_count());
   expected_end_cmds.end_query.Init(
       GL_ANY_SAMPLES_PASSED_EXT, query->submit_count());
-  commands = GetPut();
-  gl_->EndQueryEXT(GL_ANY_SAMPLES_PASSED_EXT);
   EXPECT_EQ(0, memcmp(
       &expected_end_cmds, commands, sizeof(expected_end_cmds)));
 
@@ -3619,13 +3620,13 @@
     cmds::BeginQueryEXT begin_query;
     cmds::EndQueryEXT end_query;
   };
+  const void* commands = GetPut();
+  gl_->EndQueryEXT(GL_GET_ERROR_QUERY_CHROMIUM);
   EndCmds expected_end_cmds;
   expected_end_cmds.begin_query.Init(
       GL_GET_ERROR_QUERY_CHROMIUM, id, query->shm_id(), query->shm_offset());
   expected_end_cmds.end_query.Init(
       GL_GET_ERROR_QUERY_CHROMIUM, query->submit_count());
-  const void* commands = GetPut();
-  gl_->EndQueryEXT(GL_GET_ERROR_QUERY_CHROMIUM);
   EXPECT_EQ(0, memcmp(
       &expected_end_cmds, commands, sizeof(expected_end_cmds)));
   ClearCommands();
diff --git a/gpu/command_buffer/client/mapped_memory.cc b/gpu/command_buffer/client/mapped_memory.cc
index e65cdf7..be85f0fe9 100644
--- a/gpu/command_buffer/client/mapped_memory.cc
+++ b/gpu/command_buffer/client/mapped_memory.cc
@@ -50,6 +50,7 @@
 }
 
 MappedMemoryManager::~MappedMemoryManager() {
+  helper_->FlushLazy();
   CommandBuffer* cmd_buf = helper_->command_buffer();
   for (auto& chunk : chunks_) {
     cmd_buf->DestroyTransferBuffer(chunk->shm_id());
@@ -146,7 +147,9 @@
   while (iter != chunks_.end()) {
     MemoryChunk* chunk = (*iter).get();
     chunk->FreeUnused();
-    if (!chunk->InUse()) {
+    if (chunk->bytes_in_use() == 0u) {
+      if (chunk->InUseOrFreePending())
+        helper_->FlushLazy();
       cmd_buf->DestroyTransferBuffer(chunk->shm_id());
       allocated_memory_ -= chunk->GetSize();
       iter = chunks_.erase(iter);
@@ -202,6 +205,17 @@
   return true;
 }
 
+FencedAllocator::State MappedMemoryManager::GetPointerStatusForTest(
+    void* pointer,
+    int32_t* token_if_pending) {
+  for (auto& chunk : chunks_) {
+    if (chunk->IsInChunk(pointer)) {
+      return chunk->GetPointerStatusForTest(pointer, token_if_pending);
+    }
+  }
+  return FencedAllocator::FREE;
+}
+
 void ScopedMappedMemoryPtr::Release() {
   if (buffer_) {
     mapped_memory_manager_->FreePendingToken(buffer_, helper_->InsertToken());
diff --git a/gpu/command_buffer/client/mapped_memory.h b/gpu/command_buffer/client/mapped_memory.h
index ac4c33f..c23e602 100644
--- a/gpu/command_buffer/client/mapped_memory.h
+++ b/gpu/command_buffer/client/mapped_memory.h
@@ -105,15 +105,18 @@
                reinterpret_cast<const int8_t*>(shm_->memory()) + shm_->size();
   }
 
-  // Returns true of any memory in this chunk is in use.
-  bool InUse() {
-    return allocator_.InUse();
-  }
+  // Returns true of any memory in this chunk is in use or free pending token.
+  bool InUseOrFreePending() { return allocator_.InUseOrFreePending(); }
 
   size_t bytes_in_use() const {
     return allocator_.bytes_in_use();
   }
 
+  FencedAllocator::State GetPointerStatusForTest(void* pointer,
+                                                 int32_t* token_if_pending) {
+    return allocator_.GetPointerStatusForTest(pointer, token_if_pending);
+  }
+
  private:
   int32_t shm_id_;
   scoped_refptr<gpu::Buffer> shm_;
@@ -202,6 +205,11 @@
     return allocated_memory_;
   }
 
+  // Gets the status of a previous allocation, as well as the corresponding
+  // token if FREE_PENDING_TOKEN (and token_if_pending is not null).
+  FencedAllocator::State GetPointerStatusForTest(void* pointer,
+                                                 int32_t* token_if_pending);
+
  private:
   typedef std::vector<std::unique_ptr<MemoryChunk>> MemoryChunkVector;
 
diff --git a/gpu/command_buffer/client/mapped_memory_unittest.cc b/gpu/command_buffer/client/mapped_memory_unittest.cc
index c36f663..f178621 100644
--- a/gpu/command_buffer/client/mapped_memory_unittest.cc
+++ b/gpu/command_buffer/client/mapped_memory_unittest.cc
@@ -15,7 +15,7 @@
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
 #include "gpu/command_buffer/client/cmd_buffer_helper.h"
-#include "gpu/command_buffer/service/command_buffer_direct.h"
+#include "gpu/command_buffer/client/command_buffer_direct_locked.h"
 #include "gpu/command_buffer/service/mocks.h"
 #include "gpu/command_buffer/service/transfer_buffer_manager.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -37,7 +37,7 @@
   void SetUp() override {
     transfer_buffer_manager_ = base::MakeUnique<TransferBufferManager>(nullptr);
     command_buffer_.reset(
-        new CommandBufferDirect(transfer_buffer_manager_.get()));
+        new CommandBufferDirectLocked(transfer_buffer_manager_.get()));
     api_mock_.reset(new AsyncAPIMock(true, command_buffer_->service()));
     command_buffer_->set_handler(api_mock_.get());
 
@@ -57,7 +57,7 @@
   int32_t GetToken() { return command_buffer_->GetLastState().token; }
 
   std::unique_ptr<TransferBufferManager> transfer_buffer_manager_;
-  std::unique_ptr<CommandBufferDirect> command_buffer_;
+  std::unique_ptr<CommandBufferDirectLocked> command_buffer_;
   std::unique_ptr<AsyncAPIMock> api_mock_;
   std::unique_ptr<CommandBufferHelper> helper_;
   base::MessageLoop message_loop_;
@@ -242,23 +242,60 @@
 }
 
 TEST_F(MappedMemoryManagerTest, FreeUnused) {
+  command_buffer_->LockFlush();
   int32_t id = -1;
   unsigned int offset = 0xFFFFFFFFU;
-  void* m1 = manager_->Alloc(kBufferSize, &id, &offset);
-  void* m2 = manager_->Alloc(kBufferSize, &id, &offset);
+  const unsigned int kAllocSize = 2048;
+  manager_->set_chunk_size_multiple(kAllocSize * 2);
+
+  void* m1 = manager_->Alloc(kAllocSize, &id, &offset);
+  void* m2 = manager_->Alloc(kAllocSize, &id, &offset);
   ASSERT_TRUE(m1 != NULL);
   ASSERT_TRUE(m2 != NULL);
+  // m1 and m2 fit in one chunk
+  EXPECT_EQ(1u, manager_->num_chunks());
+
+  void* m3 = manager_->Alloc(kAllocSize, &id, &offset);
+  ASSERT_TRUE(m3 != NULL);
+  // m3 needs another chunk
   EXPECT_EQ(2u, manager_->num_chunks());
+
+  // Nothing to free, both chunks are in-use.
   manager_->FreeUnused();
   EXPECT_EQ(2u, manager_->num_chunks());
+
+  manager_->Free(m3);
+  EXPECT_EQ(2u, manager_->num_chunks());
+  // The second chunk is no longer in use, we can remove.
+  manager_->FreeUnused();
+  EXPECT_EQ(1u, manager_->num_chunks());
+
+  int32_t token = helper_->InsertToken();
+  manager_->FreePendingToken(m1, token);
+  // The way we hooked up the helper and engine, it won't process commands
+  // until it has to wait for something. Which means the token shouldn't have
+  // passed yet at this point.
+  EXPECT_GT(token, GetToken());
+  EXPECT_EQ(1u, manager_->num_chunks());
+
+  int old_flush_count = command_buffer_->FlushCount();
+  // The remaining chunk is still busy, can't free it.
+  manager_->FreeUnused();
+  EXPECT_EQ(1u, manager_->num_chunks());
+  // This should not have caused a Flush or a Finish.
+  EXPECT_GT(token, GetToken());
+  EXPECT_EQ(old_flush_count, command_buffer_->FlushCount());
+
   manager_->Free(m2);
-  EXPECT_EQ(2u, manager_->num_chunks());
-  manager_->FreeUnused();
   EXPECT_EQ(1u, manager_->num_chunks());
-  manager_->Free(m1);
-  EXPECT_EQ(1u, manager_->num_chunks());
+  // The remaining chunk is free pending token, we can release the shared
+  // memory.
   manager_->FreeUnused();
   EXPECT_EQ(0u, manager_->num_chunks());
+  // This should have triggered a Flush, but not forced a Finish, i.e. the token
+  // shouldn't have passed yet.
+  EXPECT_EQ(old_flush_count + 1, command_buffer_->FlushCount());
+  EXPECT_GT(token, GetToken());
 }
 
 TEST_F(MappedMemoryManagerTest, ChunkSizeMultiple) {
diff --git a/gpu/command_buffer/client/query_tracker.cc b/gpu/command_buffer/client/query_tracker.cc
index 4e241d6..a4b76f5 100644
--- a/gpu/command_buffer/client/query_tracker.cc
+++ b/gpu/command_buffer/client/query_tracker.cc
@@ -13,6 +13,7 @@
 #include <stdint.h>
 
 #include "base/atomicops.h"
+#include "base/memory/ptr_util.h"
 #include "base/numerics/safe_conversions.h"
 #include "gpu/command_buffer/client/gles2_cmd_helper.h"
 #include "gpu/command_buffer/client/gles2_implementation.h"
@@ -29,6 +30,22 @@
 
 QuerySyncManager::Bucket::~Bucket() = default;
 
+void QuerySyncManager::Bucket::FreePendingSyncs() {
+  auto it =
+      std::remove_if(pending_syncs.begin(), pending_syncs.end(),
+                     [this](const PendingSync& pending) {
+                       QuerySync* sync = this->syncs + pending.index;
+                       if (base::subtle::Acquire_Load(&sync->process_count) ==
+                           pending.submit_count) {
+                         this->in_use_query_syncs[pending.index] = false;
+                         return true;
+                       } else {
+                         return false;
+                       }
+                     });
+  pending_syncs.erase(it, pending_syncs.end());
+}
+
 QuerySyncManager::QuerySyncManager(MappedMemoryManager* manager)
     : mapped_memory_(manager) {
   DCHECK(manager);
@@ -37,7 +54,6 @@
 QuerySyncManager::~QuerySyncManager() {
   while (!buckets_.empty()) {
     mapped_memory_->Free(buckets_.front()->syncs);
-    delete buckets_.front();
     buckets_.pop_front();
   }
 }
@@ -45,11 +61,10 @@
 bool QuerySyncManager::Alloc(QuerySyncManager::QueryInfo* info) {
   DCHECK(info);
   Bucket* bucket = nullptr;
-  for (Bucket* bucket_candidate : buckets_) {
-    // In C++11 STL this could be replaced with
-    // if (!bucket_candidate->in_use_queries.all()) { ... }
-    if (bucket_candidate->in_use_queries.count() != kSyncsPerBucket) {
-      bucket = bucket_candidate;
+  for (auto& bucket_candidate : buckets_) {
+    bucket_candidate->FreePendingSyncs();
+    if (!bucket_candidate->in_use_query_syncs.all()) {
+      bucket = bucket_candidate.get();
       break;
     }
   }
@@ -62,62 +77,79 @@
       return false;
     }
     QuerySync* syncs = static_cast<QuerySync*>(mem);
-    bucket = new Bucket(syncs, shm_id, shm_offset);
-    buckets_.push_back(bucket);
+    buckets_.push_back(base::MakeUnique<Bucket>(syncs, shm_id, shm_offset));
+    bucket = buckets_.back().get();
   }
 
   size_t index_in_bucket = 0;
   for (size_t i = 0; i < kSyncsPerBucket; i++) {
-    if (!bucket->in_use_queries[i]) {
+    if (!bucket->in_use_query_syncs[i]) {
       index_in_bucket = i;
       break;
     }
   }
 
-  uint32_t shm_offset =
-      bucket->base_shm_offset + index_in_bucket * sizeof(QuerySync);
-  QuerySync* sync = bucket->syncs + index_in_bucket;
-  *info = QueryInfo(bucket, bucket->shm_id, shm_offset, sync);
+  *info = QueryInfo(bucket, index_in_bucket);
   info->sync->Reset();
-  bucket->in_use_queries[index_in_bucket] = true;
+  bucket->in_use_query_syncs[index_in_bucket] = true;
   return true;
 }
 
 void QuerySyncManager::Free(const QuerySyncManager::QueryInfo& info) {
-  DCHECK_NE(info.bucket->in_use_queries.count(), 0u);
-  unsigned short index_in_bucket = info.sync - info.bucket->syncs;
-  DCHECK(info.bucket->in_use_queries[index_in_bucket]);
-  info.bucket->in_use_queries[index_in_bucket] = false;
+  DCHECK_NE(info.bucket->in_use_query_syncs.count(), 0u);
+  unsigned short index_in_bucket = info.index();
+  DCHECK(info.bucket->in_use_query_syncs[index_in_bucket]);
+  if (base::subtle::Acquire_Load(&info.sync->process_count) !=
+      info.submit_count) {
+    // When you delete a query you can't mark its memory as unused until it's
+    // completed.
+    info.bucket->pending_syncs.push_back(
+        Bucket::PendingSync{index_in_bucket, info.submit_count});
+  } else {
+    info.bucket->in_use_query_syncs[index_in_bucket] = false;
+  }
 }
 
-void QuerySyncManager::Shrink() {
-  std::deque<Bucket*> new_buckets;
+void QuerySyncManager::Shrink(CommandBufferHelper* helper) {
+  std::deque<std::unique_ptr<Bucket>> new_buckets;
+  bool has_token = false;
+  uint32_t token = 0;
   while (!buckets_.empty()) {
-    Bucket* bucket = buckets_.front();
-    if (bucket->in_use_queries.any()) {
-      new_buckets.push_back(bucket);
+    std::unique_ptr<Bucket>& bucket = buckets_.front();
+    bucket->FreePendingSyncs();
+    if (bucket->in_use_query_syncs.any()) {
+      if (bucket->in_use_query_syncs.count() == bucket->pending_syncs.size()) {
+        // Every QuerySync that is in-use is just pending completion. We know
+        // the query has been deleted, so nothing on the service side will
+        // access the shared memory after current commands, so we can
+        // free-pending-token.
+        token = helper->InsertToken();
+        has_token = true;
+        mapped_memory_->FreePendingToken(bucket->syncs, token);
+      } else {
+        new_buckets.push_back(std::move(bucket));
+      }
     } else {
+      // Every QuerySync is free or completed, so we know the service side won't
+      // access it any more, so we can free immediately.
       mapped_memory_->Free(bucket->syncs);
-      delete bucket;
     }
     buckets_.pop_front();
   }
   buckets_.swap(new_buckets);
 }
 
-QueryTracker::Query::Query(GLuint id, GLenum target,
+QueryTracker::Query::Query(GLuint id,
+                           GLenum target,
                            const QuerySyncManager::QueryInfo& info)
     : id_(id),
       target_(target),
       info_(info),
       state_(kUninitialized),
-      submit_count_(0),
       token_(0),
       flush_count_(0),
       client_begin_time_us_(0),
-      result_(0) {
-    }
-
+      result_(0) {}
 
 void QueryTracker::Query::Begin(GLES2Implementation* gl) {
   // init memory, inc count
@@ -160,23 +192,25 @@
     }
   }
   flush_count_ = gl->helper()->flush_generation();
-  gl->helper()->EndQueryEXT(target(), submit_count());
-  MarkAsPending(gl->helper()->InsertToken());
+  int32_t submit_count = NextSubmitCount();
+  gl->helper()->EndQueryEXT(target(), submit_count);
+  MarkAsPending(gl->helper()->InsertToken(), submit_count);
 }
 
 void QueryTracker::Query::QueryCounter(GLES2Implementation* gl) {
   MarkAsActive();
   flush_count_ = gl->helper()->flush_generation();
+  int32_t submit_count = NextSubmitCount();
   gl->helper()->QueryCounterEXT(id(), target(), shm_id(), shm_offset(),
-                                submit_count());
-  MarkAsPending(gl->helper()->InsertToken());
+                                submit_count);
+  MarkAsPending(gl->helper()->InsertToken(), submit_count);
 }
 
 bool QueryTracker::Query::CheckResultsAvailable(
     CommandBufferHelper* helper) {
   if (Pending()) {
-    bool processed_all =
-        base::subtle::Acquire_Load(&info_.sync->process_count) == submit_count_;
+    bool processed_all = base::subtle::Acquire_Load(
+                             &info_.sync->process_count) == submit_count();
     // We check lost on the command buffer itself here instead of checking the
     // GLES2Implementation because the GLES2Implementation will not hear about
     // the loss until we exit out of this call stack (to avoid re-entrancy), and
@@ -228,14 +262,8 @@
 }
 
 QueryTracker::~QueryTracker() {
-  while (!queries_.empty()) {
-    delete queries_.begin()->second;
-    queries_.erase(queries_.begin());
-  }
-  while (!removed_queries_.empty()) {
-    delete removed_queries_.front();
-    removed_queries_.pop_front();
-  }
+  for (auto& kv : queries_)
+    query_sync_manager_.Free(kv.second->info_);
   if (disjoint_count_sync_) {
     mapped_memory_->Free(disjoint_count_sync_);
     disjoint_count_sync_ = nullptr;
@@ -244,21 +272,21 @@
 
 QueryTracker::Query* QueryTracker::CreateQuery(GLuint id, GLenum target) {
   DCHECK_NE(0u, id);
-  FreeCompletedQueries();
   QuerySyncManager::QueryInfo info;
   if (!query_sync_manager_.Alloc(&info)) {
     return nullptr;
   }
-  Query* query = new Query(id, target, info);
+  auto query = base::MakeUnique<Query>(id, target, info);
+  Query* query_ptr = query.get();
   std::pair<QueryIdMap::iterator, bool> result =
-      queries_.insert(std::make_pair(id, query));
+      queries_.emplace(id, std::move(query));
   DCHECK(result.second);
-  return query;
+  return query_ptr;
 }
 
 QueryTracker::Query* QueryTracker::GetQuery(GLuint client_id) {
   QueryIdMap::iterator it = queries_.find(client_id);
-  return it != queries_.end() ? it->second : nullptr;
+  return it != queries_.end() ? it->second.get() : nullptr;
 }
 
 QueryTracker::Query* QueryTracker::GetCurrentQuery(GLenum target) {
@@ -269,7 +297,7 @@
 void QueryTracker::RemoveQuery(GLuint client_id) {
   QueryIdMap::iterator it = queries_.find(client_id);
   if (it != queries_.end()) {
-    Query* query = it->second;
+    Query* query = it->second.get();
 
     // Erase from current targets map if it is the current target.
     const GLenum target = query->target();
@@ -278,38 +306,15 @@
       current_queries_.erase(target_it);
     }
 
-    // When you delete a query you can't mark its memory as unused until it's
-    // completed.
-    // Note: If you don't do this you won't mess up the service but you will
-    // mess up yourself.
-    removed_queries_.push_back(query);
-    queries_.erase(it);
-    FreeCompletedQueries();
-  }
-}
-
-void QueryTracker::Shrink() {
-  FreeCompletedQueries();
-  query_sync_manager_.Shrink();
-}
-
-void QueryTracker::FreeCompletedQueries() {
-  QueryList::iterator it = removed_queries_.begin();
-  while (it != removed_queries_.end()) {
-    Query* query = *it;
-    if (query->Pending() &&
-        base::subtle::Acquire_Load(&query->info_.sync->process_count) !=
-            query->submit_count()) {
-      ++it;
-      continue;
-    }
-
     query_sync_manager_.Free(query->info_);
-    it = removed_queries_.erase(it);
-    delete query;
+    queries_.erase(it);
   }
 }
 
+void QueryTracker::Shrink(CommandBufferHelper* helper) {
+  query_sync_manager_.Shrink(helper);
+}
+
 bool QueryTracker::BeginQuery(GLuint id, GLenum target,
                               GLES2Implementation* gl) {
   QueryTracker::Query* query = GetQuery(id);
diff --git a/gpu/command_buffer/client/query_tracker.h b/gpu/command_buffer/client/query_tracker.h
index c4505f1..a529100 100644
--- a/gpu/command_buffer/client/query_tracker.h
+++ b/gpu/command_buffer/client/query_tracker.h
@@ -13,9 +13,12 @@
 #include <bitset>
 #include <deque>
 #include <list>
+#include <memory>
 
 #include "base/atomicops.h"
+#include "base/containers/flat_map.h"
 #include "base/containers/hash_tables.h"
+#include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "gles2_impl_export.h"
 #include "gpu/command_buffer/common/gles2_cmd_format.h"
@@ -34,29 +37,34 @@
  public:
   static const size_t kSyncsPerBucket = 256;
 
-  struct Bucket {
+  struct GLES2_IMPL_EXPORT Bucket {
     Bucket(QuerySync* sync_mem, int32_t shm_id, uint32_t shm_offset);
     ~Bucket();
+
+    void FreePendingSyncs();
+
     QuerySync* syncs;
     int32_t shm_id;
     uint32_t base_shm_offset;
-    std::bitset<kSyncsPerBucket> in_use_queries;
+    std::bitset<kSyncsPerBucket> in_use_query_syncs;
+
+    struct PendingSync {
+      uint32_t index;
+      int32_t submit_count;
+    };
+    std::vector<PendingSync> pending_syncs;
   };
+
   struct QueryInfo {
-    QueryInfo(Bucket* bucket, int32_t id, uint32_t offset, QuerySync* sync_mem)
-        : bucket(bucket), shm_id(id), shm_offset(offset), sync(sync_mem) {}
+    QueryInfo(Bucket* bucket, uint32_t index)
+        : bucket(bucket), sync(bucket->syncs + index) {}
+    QueryInfo() {}
 
-    QueryInfo()
-        : bucket(NULL),
-          shm_id(0),
-          shm_offset(0),
-          sync(NULL) {
-    }
+    uint32_t index() const { return sync - bucket->syncs; }
 
-    Bucket* bucket;
-    int32_t shm_id;
-    uint32_t shm_offset;
-    QuerySync* sync;
+    Bucket* bucket = nullptr;
+    QuerySync* sync = nullptr;
+    int32_t submit_count = 0;
   };
 
   explicit QuerySyncManager(MappedMemoryManager* manager);
@@ -64,11 +72,13 @@
 
   bool Alloc(QueryInfo* info);
   void Free(const QueryInfo& sync);
-  void Shrink();
+  void Shrink(CommandBufferHelper* helper);
 
  private:
+  FRIEND_TEST_ALL_PREFIXES(QuerySyncManagerTest, Shrink);
+
   MappedMemoryManager* mapped_memory_;
-  std::deque<Bucket*> buckets_;
+  std::deque<std::unique_ptr<Bucket>> buckets_;
 
   DISALLOW_COPY_AND_ASSIGN(QuerySyncManager);
 };
@@ -95,23 +105,30 @@
       return id_;
     }
 
-    int32_t shm_id() const { return info_.shm_id; }
+    int32_t shm_id() const { return info_.bucket->shm_id; }
 
-    uint32_t shm_offset() const { return info_.shm_offset; }
+    uint32_t shm_offset() const {
+      return info_.bucket->base_shm_offset + sizeof(QuerySync) * info_.index();
+    }
 
     void MarkAsActive() {
       state_ = kActive;
-      ++submit_count_;
-      if (submit_count_ == INT_MAX)
-        submit_count_ = 1;
     }
 
-    void MarkAsPending(int32_t token) {
+    int32_t NextSubmitCount() const {
+      int32_t submit_count = info_.submit_count + 1;
+      if (submit_count == INT_MAX)
+        submit_count = 1;
+      return submit_count;
+    }
+
+    void MarkAsPending(int32_t token, int32_t submit_count) {
+      info_.submit_count = submit_count;
       token_ = token;
       state_ = kPending;
     }
 
-    base::subtle::Atomic32 submit_count() const { return submit_count_; }
+    base::subtle::Atomic32 submit_count() const { return info_.submit_count; }
 
     int32_t token() const { return token_; }
 
@@ -143,22 +160,20 @@
     GLenum target_;
     QuerySyncManager::QueryInfo info_;
     State state_;
-    base::subtle::Atomic32 submit_count_;
     int32_t token_;
     uint32_t flush_count_;
     uint64_t client_begin_time_us_;  // Only used for latency query target.
     uint64_t result_;
   };
 
-  QueryTracker(MappedMemoryManager* manager);
+  explicit QueryTracker(MappedMemoryManager* manager);
   ~QueryTracker();
 
   Query* CreateQuery(GLuint id, GLenum target);
   Query* GetQuery(GLuint id);
   Query* GetCurrentQuery(GLenum target);
   void RemoveQuery(GLuint id);
-  void Shrink();
-  void FreeCompletedQueries();
+  void Shrink(CommandBufferHelper* helper);
 
   bool BeginQuery(GLuint id, GLenum target, GLES2Implementation* gl);
   bool EndQuery(GLenum target, GLES2Implementation* gl);
@@ -175,13 +190,11 @@
   }
 
  private:
-  typedef base::hash_map<GLuint, Query*> QueryIdMap;
-  typedef base::hash_map<GLenum, Query*> QueryTargetMap;
-  typedef std::list<Query*> QueryList;
+  typedef base::hash_map<GLuint, std::unique_ptr<Query>> QueryIdMap;
+  typedef base::flat_map<GLenum, Query*> QueryTargetMap;
 
   QueryIdMap queries_;
   QueryTargetMap current_queries_;
-  QueryList removed_queries_;
   QuerySyncManager query_sync_manager_;
 
   // The shared memory used for synchronizing timer disjoint values.
diff --git a/gpu/command_buffer/client/query_tracker_unittest.cc b/gpu/command_buffer/client/query_tracker_unittest.cc
index 78ab936d1..8b5191e 100644
--- a/gpu/command_buffer/client/query_tracker_unittest.cc
+++ b/gpu/command_buffer/client/query_tracker_unittest.cc
@@ -20,6 +20,9 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using testing::_;
+using testing::AnyNumber;
+
 namespace gpu {
 namespace gles2 {
 
@@ -39,13 +42,14 @@
   }
 
   void TearDown() override {
+    EXPECT_CALL(*command_buffer_, DestroyTransferBuffer(_)).Times(AnyNumber());
     sync_manager_.reset();
     mapped_memory_.reset();
     helper_.reset();
     command_buffer_.reset();
   }
 
-  std::unique_ptr<CommandBuffer> command_buffer_;
+  std::unique_ptr<MockClientCommandBuffer> command_buffer_;
   std::unique_ptr<GLES2CmdHelper> helper_;
   std::unique_ptr<MappedMemoryManager> mapped_memory_;
   std::unique_ptr<QuerySyncManager> sync_manager_;
@@ -57,10 +61,10 @@
 
   for (size_t ii = 0; ii < arraysize(infos); ++ii) {
     EXPECT_TRUE(sync_manager_->Alloc(&infos[ii]));
-    EXPECT_NE(0, infos[ii].shm_id);
     ASSERT_TRUE(infos[ii].sync != NULL);
     EXPECT_EQ(0, infos[ii].sync->process_count);
     EXPECT_EQ(0u, infos[ii].sync->result);
+    EXPECT_EQ(0, infos[ii].submit_count);
   }
 
   for (size_t ii = 0; ii < arraysize(infos); ++ii) {
@@ -77,6 +81,126 @@
   }
 }
 
+TEST_F(QuerySyncManagerTest, FreePendingSyncs) {
+  QuerySyncManager::QueryInfo info;
+  EXPECT_TRUE(sync_manager_->Alloc(&info));
+  QuerySyncManager::Bucket* bucket = info.bucket;
+
+  // Mark the query as in-use.
+  ++info.submit_count;
+
+  // Freeing the QueryInfo should keep the QuerySync busy as it's still in-use,
+  // but should be tracked in pending_syncs.
+  sync_manager_->Free(info);
+  EXPECT_FALSE(bucket->pending_syncs.empty());
+  EXPECT_TRUE(bucket->in_use_query_syncs.any());
+
+  // FreePendingSyncs should not free in-use QuerySync.
+  bucket->FreePendingSyncs();
+  EXPECT_FALSE(bucket->pending_syncs.empty());
+  EXPECT_TRUE(bucket->in_use_query_syncs.any());
+
+  // Mark the query as completed.
+  info.sync->process_count = info.submit_count;
+
+  // FreePendingSyncs should free the QuerySync.
+  bucket->FreePendingSyncs();
+  EXPECT_TRUE(bucket->pending_syncs.empty());
+  EXPECT_FALSE(bucket->in_use_query_syncs.any());
+
+  // Allocate a new Query, mark it in-use
+  EXPECT_TRUE(sync_manager_->Alloc(&info));
+  bucket = info.bucket;
+  ++info.submit_count;
+
+  // Mark the query as completed
+  info.sync->process_count = info.submit_count;
+
+  // FreePendingSyncs should not free the QuerySync. Even though the query is
+  // completed, is has not been deleted yet.
+  bucket->FreePendingSyncs();
+  EXPECT_TRUE(bucket->in_use_query_syncs.any());
+
+  // Free the QueryInfo, it should be immediately freed.
+  sync_manager_->Free(info);
+  EXPECT_TRUE(bucket->pending_syncs.empty());
+  EXPECT_FALSE(bucket->in_use_query_syncs.any());
+}
+
+TEST_F(QuerySyncManagerTest, Shrink) {
+  QuerySyncManager::QueryInfo info;
+  EXPECT_TRUE(sync_manager_->Alloc(&info));
+  QuerySyncManager::Bucket* bucket = info.bucket;
+  QuerySync* syncs = bucket->syncs;
+
+  FencedAllocator::State state =
+      mapped_memory_->GetPointerStatusForTest(syncs, nullptr);
+  EXPECT_EQ(FencedAllocator::IN_USE, state);
+
+  // Shrink while a query is allocated - should not release anything.
+  sync_manager_->Shrink(helper_.get());
+  state = mapped_memory_->GetPointerStatusForTest(syncs, nullptr);
+  EXPECT_EQ(FencedAllocator::IN_USE, state);
+
+  // Free query that was never submitted.
+  sync_manager_->Free(info);
+  EXPECT_TRUE(bucket->pending_syncs.empty());
+  EXPECT_FALSE(bucket->in_use_query_syncs.any());
+
+  // Shrink should release the memory immediately.
+  sync_manager_->Shrink(helper_.get());
+  EXPECT_TRUE(sync_manager_->buckets_.empty());
+  state = mapped_memory_->GetPointerStatusForTest(syncs, nullptr);
+  EXPECT_EQ(FencedAllocator::FREE, state);
+
+  EXPECT_TRUE(sync_manager_->Alloc(&info));
+  bucket = info.bucket;
+  syncs = bucket->syncs;
+
+  state = mapped_memory_->GetPointerStatusForTest(syncs, nullptr);
+  EXPECT_EQ(FencedAllocator::IN_USE, state);
+
+  // Free a query that was submitted, but not completed.
+  ++info.submit_count;
+  sync_manager_->Free(info);
+  EXPECT_FALSE(bucket->pending_syncs.empty());
+  EXPECT_TRUE(bucket->in_use_query_syncs.any());
+
+  int32_t last_token = helper_->InsertToken();
+
+  // Shrink should release the memory, pending a new token.
+  sync_manager_->Shrink(helper_.get());
+  EXPECT_TRUE(sync_manager_->buckets_.empty());
+  int32_t token = 0;
+  state = mapped_memory_->GetPointerStatusForTest(syncs, &token);
+  EXPECT_EQ(FencedAllocator::FREE_PENDING_TOKEN, state);
+  EXPECT_EQ(last_token + 1, token);
+
+  EXPECT_TRUE(sync_manager_->Alloc(&info));
+  bucket = info.bucket;
+  syncs = bucket->syncs;
+
+  state = mapped_memory_->GetPointerStatusForTest(syncs, nullptr);
+  EXPECT_EQ(FencedAllocator::IN_USE, state);
+
+  // Free a query that was submitted, but not completed yet.
+  ++info.submit_count;
+  int32_t submit_count = info.submit_count;
+  QuerySync* sync = info.sync;
+  sync_manager_->Free(info);
+  EXPECT_FALSE(bucket->pending_syncs.empty());
+  EXPECT_TRUE(bucket->in_use_query_syncs.any());
+
+  // Complete the query after Free.
+  sync->process_count = submit_count;
+
+  // Shrink should free the memory immediately since the query is completed.
+  sync_manager_->Shrink(helper_.get());
+  EXPECT_TRUE(sync_manager_->buckets_.empty());
+  state = mapped_memory_->GetPointerStatusForTest(syncs, nullptr);
+  EXPECT_EQ(FencedAllocator::FREE, state);
+}
+
 class QueryTrackerTest : public testing::Test {
  protected:
   static const int32_t kNumCommandEntries = 400;
@@ -93,6 +217,8 @@
   }
 
   void TearDown() override {
+    helper_->CommandBufferHelper::Flush();
+    EXPECT_CALL(*command_buffer_, DestroyTransferBuffer(_)).Times(AnyNumber());
     query_tracker_.reset();
     mapped_memory_.reset();
     helper_.reset();
@@ -108,12 +234,12 @@
   }
 
   uint32_t GetBucketUsedCount(QuerySyncManager::Bucket* bucket) {
-    return bucket->in_use_queries.count();
+    return bucket->in_use_query_syncs.count();
   }
 
   uint32_t GetFlushGeneration() { return helper_->flush_generation(); }
 
-  std::unique_ptr<CommandBuffer> command_buffer_;
+  std::unique_ptr<MockClientCommandBuffer> command_buffer_;
   std::unique_ptr<GLES2CmdHelper> helper_;
   std::unique_ptr<MappedMemoryManager> mapped_memory_;
   std::unique_ptr<QueryTracker> query_tracker_;
@@ -156,10 +282,11 @@
   EXPECT_FALSE(query->NeverUsed());
   EXPECT_FALSE(query->Pending());
   EXPECT_EQ(0, query->token());
-  EXPECT_EQ(1, query->submit_count());
+  EXPECT_EQ(0, query->submit_count());
+  EXPECT_EQ(1, query->NextSubmitCount());
 
   // Check MarkAsPending.
-  query->MarkAsPending(kToken);
+  query->MarkAsPending(kToken, query->NextSubmitCount());
   EXPECT_FALSE(query->NeverUsed());
   EXPECT_TRUE(query->Pending());
   EXPECT_EQ(kToken, query->token());
@@ -216,7 +343,9 @@
   EXPECT_EQ(1u, GetBucketUsedCount(bucket));
 
   query->MarkAsActive();
-  query->MarkAsPending(kToken);
+  int32_t submit_count = query->NextSubmitCount();
+  query->MarkAsPending(kToken, submit_count);
+  QuerySync* sync = GetSync(query);
 
   query_tracker_->RemoveQuery(kId1);
   // Check we get nothing for a non-existent query.
@@ -224,17 +353,39 @@
 
   // Check that memory was not freed.
   EXPECT_EQ(1u, GetBucketUsedCount(bucket));
+  EXPECT_EQ(1u, bucket->pending_syncs.size());
 
   // Simulate GPU process marking it as available.
-  QuerySync* sync = GetSync(query);
-  sync->process_count = query->submit_count();
   sync->result = kResult;
+  sync->process_count = submit_count;
 
-  // Check FreeCompletedQueries.
-  query_tracker_->FreeCompletedQueries();
+  // Check FreePendingSyncs.
+  bucket->FreePendingSyncs();
   EXPECT_EQ(0u, GetBucketUsedCount(bucket));
 }
 
+TEST_F(QueryTrackerTest, RemoveActive) {
+  const GLuint kId1 = 123;
+
+  // Create a Query.
+  QueryTracker::Query* query =
+      query_tracker_->CreateQuery(kId1, GL_ANY_SAMPLES_PASSED_EXT);
+  ASSERT_TRUE(query != NULL);
+
+  QuerySyncManager::Bucket* bucket = GetBucket(query);
+  EXPECT_EQ(1u, GetBucketUsedCount(bucket));
+
+  query->MarkAsActive();
+
+  query_tracker_->RemoveQuery(kId1);
+  // Check we get nothing for a non-existent query.
+  EXPECT_TRUE(query_tracker_->GetQuery(kId1) == NULL);
+
+  // Check that memory was freed.
+  EXPECT_EQ(0u, GetBucketUsedCount(bucket));
+  EXPECT_EQ(0u, bucket->pending_syncs.size());
+}
+
 TEST_F(QueryTrackerTest, ManyQueries) {
   const GLuint kId1 = 123;
   const int32_t kToken = 46;
@@ -264,11 +415,13 @@
     GLuint query_id = kId1 + queries.size();
     EXPECT_EQ(query_id, query->id());
     query->MarkAsActive();
-    query->MarkAsPending(kToken);
+    int32_t submit_count = query->NextSubmitCount();
+    query->MarkAsPending(kToken, submit_count);
+    QuerySync* sync = GetSync(query);
 
     QuerySyncManager::Bucket* bucket = GetBucket(query);
     uint32_t use_count_before_remove = GetBucketUsedCount(bucket);
-    query_tracker_->FreeCompletedQueries();
+    bucket->FreePendingSyncs();
     EXPECT_EQ(use_count_before_remove, GetBucketUsedCount(bucket));
     query_tracker_->RemoveQuery(query_id);
     // Check we get nothing for a non-existent query.
@@ -278,12 +431,11 @@
     EXPECT_EQ(use_count_before_remove, GetBucketUsedCount(bucket));
 
     // Simulate GPU process marking it as available.
-    QuerySync* sync = GetSync(query);
-    sync->process_count = query->submit_count();
+    sync->process_count = submit_count;
     sync->result = kResult;
 
     // Check FreeCompletedQueries.
-    query_tracker_->FreeCompletedQueries();
+    bucket->FreePendingSyncs();
     EXPECT_EQ(use_count_before_remove - 1, GetBucketUsedCount(bucket));
   }
 }
diff --git a/gpu/command_buffer/service/gl_context_virtual.cc b/gpu/command_buffer/service/gl_context_virtual.cc
index 9615f23..da4a805a 100644
--- a/gpu/command_buffer/service/gl_context_virtual.cc
+++ b/gpu/command_buffer/service/gl_context_virtual.cc
@@ -96,8 +96,9 @@
   shared_context_->SetUnbindFboOnMakeCurrent();
 }
 
-gl::YUVToRGBConverter* GLContextVirtual::GetYUVToRGBConverter() {
-  return shared_context_->GetYUVToRGBConverter();
+gl::YUVToRGBConverter* GLContextVirtual::GetYUVToRGBConverter(
+    const gfx::ColorSpace& color_space) {
+  return shared_context_->GetYUVToRGBConverter(color_space);
 }
 
 void GLContextVirtual::ForceReleaseVirtuallyCurrent() {
diff --git a/gpu/command_buffer/service/gl_context_virtual.h b/gpu/command_buffer/service/gl_context_virtual.h
index 7af79b8..648b0c88 100644
--- a/gpu/command_buffer/service/gl_context_virtual.h
+++ b/gpu/command_buffer/service/gl_context_virtual.h
@@ -46,7 +46,8 @@
   void SetSafeToForceGpuSwitch() override;
   bool WasAllocatedUsingRobustnessExtension() override;
   void SetUnbindFboOnMakeCurrent() override;
-  gl::YUVToRGBConverter* GetYUVToRGBConverter() override;
+  gl::YUVToRGBConverter* GetYUVToRGBConverter(
+      const gfx::ColorSpace& color_space) override;
   void ForceReleaseVirtuallyCurrent() override;
 
  protected:
diff --git a/gpu/ipc/common/mailbox_struct_traits.cc b/gpu/ipc/common/mailbox_struct_traits.cc
index df56cc5d..53520fb 100644
--- a/gpu/ipc/common/mailbox_struct_traits.cc
+++ b/gpu/ipc/common/mailbox_struct_traits.cc
@@ -4,13 +4,15 @@
 
 #include "gpu/ipc/common/mailbox_struct_traits.h"
 
+#include "base/containers/span.h"
+
 namespace mojo {
 
 // static
 bool StructTraits<gpu::mojom::MailboxDataView, gpu::Mailbox>::Read(
     gpu::mojom::MailboxDataView data,
     gpu::Mailbox* out) {
-  CArray<int8_t> mailbox_name(out->name);
+  base::span<int8_t> mailbox_name(out->name);
   return data.ReadName(&mailbox_name);
 }
 
diff --git a/gpu/ipc/common/mailbox_struct_traits.h b/gpu/ipc/common/mailbox_struct_traits.h
index 9004723..c7dbc90 100644
--- a/gpu/ipc/common/mailbox_struct_traits.h
+++ b/gpu/ipc/common/mailbox_struct_traits.h
@@ -5,6 +5,7 @@
 #ifndef GPU_IPC_COMMON_MAILBOX_STRUCT_TRAITS_H_
 #define GPU_IPC_COMMON_MAILBOX_STRUCT_TRAITS_H_
 
+#include "base/containers/span.h"
 #include "gpu/command_buffer/common/mailbox.h"
 #include "gpu/ipc/common/mailbox.mojom-shared.h"
 #include "mojo/public/cpp/bindings/array_traits.h"
@@ -13,7 +14,7 @@
 
 template <>
 struct StructTraits<gpu::mojom::MailboxDataView, gpu::Mailbox> {
-  static ConstCArray<int8_t> name(const gpu::Mailbox& mailbox) {
+  static base::span<const int8_t> name(const gpu::Mailbox& mailbox) {
     return mailbox.name;
   }
   static bool Read(gpu::mojom::MailboxDataView data, gpu::Mailbox* out);
diff --git a/ios/chrome/browser/metrics/tab_usage_recorder.h b/ios/chrome/browser/metrics/tab_usage_recorder.h
index 45f6361b..1806bf0 100644
--- a/ios/chrome/browser/metrics/tab_usage_recorder.h
+++ b/ios/chrome/browser/metrics/tab_usage_recorder.h
@@ -242,6 +242,10 @@
   // be null during unit testing.
   PrerenderService* prerender_service_;
 
+  // Observers for NSNotificationCenter notifications.
+  __strong id<NSObject> application_backgrounding_observer_;
+  __strong id<NSObject> application_foregrounding_observer_;
+
   DISALLOW_COPY_AND_ASSIGN(TabUsageRecorder);
 };
 
diff --git a/ios/chrome/browser/metrics/tab_usage_recorder.mm b/ios/chrome/browser/metrics/tab_usage_recorder.mm
index 5039cae..621a5aa 100644
--- a/ios/chrome/browser/metrics/tab_usage_recorder.mm
+++ b/ios/chrome/browser/metrics/tab_usage_recorder.mm
@@ -70,10 +70,41 @@
       prerender_service_(prerender_service) {
   DCHECK(web_state_list_);
   web_state_list_->AddObserver(this);
+
+  // Register for backgrounding and foregrounding notifications. It is safe for
+  // the block to capture a pointer to |this| as they are unregistered in the
+  // destructor and thus the block are not called after the end of its lifetime.
+  application_backgrounding_observer_ = [[NSNotificationCenter defaultCenter]
+      addObserverForName:UIApplicationDidEnterBackgroundNotification
+                  object:nil
+                   queue:nil
+              usingBlock:^(NSNotification*) {
+                this->AppDidEnterBackground();
+              }];
+
+  application_foregrounding_observer_ = [[NSNotificationCenter defaultCenter]
+      addObserverForName:UIApplicationWillEnterForegroundNotification
+                  object:nil
+                   queue:nil
+              usingBlock:^(NSNotification*) {
+                this->AppWillEnterForeground();
+              }];
 }
 
 TabUsageRecorder::~TabUsageRecorder() {
   web_state_list_->RemoveObserver(this);
+
+  if (application_backgrounding_observer_) {
+    [[NSNotificationCenter defaultCenter]
+        removeObserver:application_backgrounding_observer_];
+    application_backgrounding_observer_ = nil;
+  }
+
+  if (application_foregrounding_observer_) {
+    [[NSNotificationCenter defaultCenter]
+        removeObserver:application_foregrounding_observer_];
+    application_foregrounding_observer_ = nil;
+  }
 }
 
 void TabUsageRecorder::InitialRestoredTabs(
diff --git a/ios/chrome/browser/tabs/tab_model.mm b/ios/chrome/browser/tabs/tab_model.mm
index 04b31eda..1702e3f5 100644
--- a/ios/chrome/browser/tabs/tab_model.mm
+++ b/ios/chrome/browser/tabs/tab_model.mm
@@ -339,12 +339,6 @@
            selector:@selector(applicationDidEnterBackground:)
                name:UIApplicationDidEnterBackgroundNotification
              object:nil];
-    // Register for foregrounding notification.
-    [[NSNotificationCenter defaultCenter]
-        addObserver:self
-           selector:@selector(applicationWillEnterForeground:)
-               name:UIApplicationWillEnterForegroundNotification
-             object:nil];
 
     // Associate with ios::ChromeBrowserState.
     RegisterTabModelWithChromeBrowserState(_browserState, self);
@@ -727,9 +721,6 @@
       web::BrowserState::GetCertificatePolicyCache(_browserState),
       _webStateList.get());
 
-  if (_tabUsageRecorder)
-    _tabUsageRecorder->AppDidEnterBackground();
-
   // Normally, the session is saved after some timer expires but since the app
   // is about to enter the background send YES to save the session immediately.
   [self saveSessionImmediately:YES];
@@ -741,11 +732,4 @@
   }
 }
 
-// Called when UIApplicationWillEnterForegroundNotification is received.
-- (void)applicationWillEnterForeground:(NSNotification*)notify {
-  if (_tabUsageRecorder) {
-    _tabUsageRecorder->AppWillEnterForeground();
-  }
-}
-
 @end
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_util.cc b/ios/chrome/browser/ui/omnibox/omnibox_util.cc
index 9c19783..b011e29 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_util.cc
+++ b/ios/chrome/browser/ui/omnibox/omnibox_util.cc
@@ -57,9 +57,6 @@
     case security_state::EV_SECURE:
     case security_state::SECURE:
       return IDR_IOS_OMNIBOX_HTTPS_VALID;
-    case security_state::SECURITY_WARNING:
-      // Surface Dubious as Neutral.
-      return IDR_IOS_OMNIBOX_HTTP;
     case security_state::SECURE_WITH_POLICY_INSTALLED_CERT:
       return IDR_IOS_OMNIBOX_HTTPS_POLICY_WARNING;
     case security_state::DANGEROUS:
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_view_ios.mm b/ios/chrome/browser/ui/omnibox/omnibox_view_ios.mm
index ef0ee5d..87fceb25 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_view_ios.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_view_ios.mm
@@ -640,7 +640,6 @@
     return;
   }
 
-  DCHECK_NE(security_state::SECURITY_WARNING, security_level);
   DCHECK_NE(security_state::SECURE_WITH_POLICY_INSTALLED_CERT, security_level);
 
   if (security_level == security_state::DANGEROUS) {
diff --git a/ios/chrome/browser/ui/payments/payment_request_cancel_pay_abort_egtest.mm b/ios/chrome/browser/ui/payments/payment_request_cancel_pay_abort_egtest.mm
index dade9d1..67d5c232 100644
--- a/ios/chrome/browser/ui/payments/payment_request_cancel_pay_abort_egtest.mm
+++ b/ios/chrome/browser/ui/payments/payment_request_cancel_pay_abort_egtest.mm
@@ -195,7 +195,8 @@
 
   // Tap the Confirm button.
   [[EarlGrey
-      selectElementWithMatcher:ButtonWithAccessibilityLabelId(IDS_ACCNAME_OK)]
+      selectElementWithMatcher:ButtonWithAccessibilityLabelId(
+                                   IDS_AUTOFILL_CARD_UNMASK_CONFIRM_BUTTON)]
       performAction:grey_tap()];
 
   // Confirm that the Payment Request UI is not showing.
diff --git a/ios/chrome/browser/ui/payments/payment_request_debit_egtest.mm b/ios/chrome/browser/ui/payments/payment_request_debit_egtest.mm
index 4c922808..84dce61 100644
--- a/ios/chrome/browser/ui/payments/payment_request_debit_egtest.mm
+++ b/ios/chrome/browser/ui/payments/payment_request_debit_egtest.mm
@@ -191,7 +191,8 @@
 
   // Tap the Confirm button.
   [[EarlGrey
-      selectElementWithMatcher:ButtonWithAccessibilityLabelId(IDS_ACCNAME_OK)]
+      selectElementWithMatcher:ButtonWithAccessibilityLabelId(
+                                   IDS_AUTOFILL_CARD_UNMASK_CONFIRM_BUTTON)]
       performAction:grey_tap()];
 
   // Verify that the CVC number is sent to the page.
diff --git a/ios/chrome/browser/ui/payments/payment_request_payment_response_egtest.mm b/ios/chrome/browser/ui/payments/payment_request_payment_response_egtest.mm
index b1191d0..caa6cdf 100644
--- a/ios/chrome/browser/ui/payments/payment_request_payment_response_egtest.mm
+++ b/ios/chrome/browser/ui/payments/payment_request_payment_response_egtest.mm
@@ -80,7 +80,8 @@
 
   // Tap the confirm button.
   [[EarlGrey
-      selectElementWithMatcher:ButtonWithAccessibilityLabelId(IDS_ACCNAME_OK)]
+      selectElementWithMatcher:ButtonWithAccessibilityLabelId(
+                                   IDS_AUTOFILL_CARD_UNMASK_CONFIRM_BUTTON)]
       performAction:grey_tap()];
 
   // Test that the card details were sent to the merchant.
@@ -147,7 +148,8 @@
 
   // Tap the confirm button.
   [[EarlGrey
-      selectElementWithMatcher:ButtonWithAccessibilityLabelId(IDS_ACCNAME_OK)]
+      selectElementWithMatcher:ButtonWithAccessibilityLabelId(
+                                   IDS_AUTOFILL_CARD_UNMASK_CONFIRM_BUTTON)]
       performAction:grey_tap()];
 
   // Test that the billing address was sent to the merchant.
@@ -200,7 +202,8 @@
 
   // Tap the confirm button.
   [[EarlGrey
-      selectElementWithMatcher:ButtonWithAccessibilityLabelId(IDS_ACCNAME_OK)]
+      selectElementWithMatcher:ButtonWithAccessibilityLabelId(
+                                   IDS_AUTOFILL_CARD_UNMASK_CONFIRM_BUTTON)]
       performAction:grey_tap()];
 
   // Test that the contact details were sent to the merchant.
@@ -234,7 +237,8 @@
 
   // Tap the confirm button.
   [[EarlGrey
-      selectElementWithMatcher:ButtonWithAccessibilityLabelId(IDS_ACCNAME_OK)]
+      selectElementWithMatcher:ButtonWithAccessibilityLabelId(
+                                   IDS_AUTOFILL_CARD_UNMASK_CONFIRM_BUTTON)]
       performAction:grey_tap()];
 
   // Test that the contact details were sent to the merchant.
diff --git a/ios/chrome/browser/ui/payments/payment_request_use_stats_egtest.mm b/ios/chrome/browser/ui/payments/payment_request_use_stats_egtest.mm
index b58a715..ff0e9105 100644
--- a/ios/chrome/browser/ui/payments/payment_request_use_stats_egtest.mm
+++ b/ios/chrome/browser/ui/payments/payment_request_use_stats_egtest.mm
@@ -77,7 +77,8 @@
 
   // Tap the confirm button.
   [[EarlGrey
-      selectElementWithMatcher:ButtonWithAccessibilityLabelId(IDS_ACCNAME_OK)]
+      selectElementWithMatcher:ButtonWithAccessibilityLabelId(
+                                   IDS_AUTOFILL_CARD_UNMASK_CONFIRM_BUTTON)]
       performAction:grey_tap()];
 }
 
diff --git a/ios/chrome/browser/ui/webui/flags_ui.cc b/ios/chrome/browser/ui/webui/flags_ui.cc
index d48673d..348518a 100644
--- a/ios/chrome/browser/ui/webui/flags_ui.cc
+++ b/ios/chrome/browser/ui/webui/flags_ui.cc
@@ -182,6 +182,7 @@
 
   SetFeatureEntryEnabled(flags_storage_.get(), entry_internal_name,
                          enable_str == "true");
+  flags_storage_->CommitPendingWrites();
 }
 
 void FlagsDOMHandler::HandleRestartBrowser(const base::ListValue* args) {
@@ -191,6 +192,7 @@
 void FlagsDOMHandler::HandleResetAllFlags(const base::ListValue* args) {
   DCHECK(flags_storage_);
   ResetAllFlags(flags_storage_.get());
+  flags_storage_->CommitPendingWrites();
 }
 
 }  // namespace
diff --git a/ios/chrome/browser/web/sad_tab_tab_helper.h b/ios/chrome/browser/web/sad_tab_tab_helper.h
index fa20dc2..1f7cffc 100644
--- a/ios/chrome/browser/web/sad_tab_tab_helper.h
+++ b/ios/chrome/browser/web/sad_tab_tab_helper.h
@@ -25,20 +25,6 @@
 
   ~SadTabTabHelper() override;
 
-  bool requires_reload_on_becoming_visible() const {
-    return requires_reload_on_becoming_visible_;
-  }
-  void set_requires_reload_on_becoming_visible(bool flag) {
-    requires_reload_on_becoming_visible_ = flag;
-  }
-
-  bool requires_reload_on_becoming_active() const {
-    return requires_reload_on_becoming_active_;
-  }
-  void set_requires_reload_on_becoming_active(bool flag) {
-    requires_reload_on_becoming_active_ = flag;
-  }
-
  private:
   // Constructs a SadTabTabHelper, assigning the helper to a web_state. A
   // default repeat_failure_interval will be used.
diff --git a/ios/chrome/test/earl_grey/chrome_test_case.mm b/ios/chrome/test/earl_grey/chrome_test_case.mm
index 8ef3af85..988c842 100644
--- a/ios/chrome/test/earl_grey/chrome_test_case.mm
+++ b/ios/chrome/test/earl_grey/chrome_test_case.mm
@@ -18,6 +18,7 @@
 #import "ios/chrome/test/app/sync_test_util.h"
 #import "ios/chrome/test/app/tab_test_util.h"
 #import "ios/web/public/test/http_server/http_server.h"
+#include "testing/coverage_util_ios.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -150,6 +151,8 @@
   [self removeAnyOpenMenusAndInfoBars];
   [self closeAllTabs];
   chrome_test_util::SetContentSettingsBlockPopups(CONTENT_SETTING_DEFAULT);
+
+  coverage_util::ConfigureCoverageReportPath();
 }
 
 // Tear down called once for the class, to shutdown mock authentication and
diff --git a/ios/tools/coverage/coverage.py b/ios/tools/coverage/coverage.py
index 0d194ba..e861358 100755
--- a/ios/tools/coverage/coverage.py
+++ b/ios/tools/coverage/coverage.py
@@ -49,7 +49,10 @@
 DEFAULT_FILTERS = ['ios/']
 
 # Only test targets with the following postfixes are considered to be valid.
-VALID_TEST_TARGET_POSTFIXES = ['unittests', 'inttests']
+VALID_TEST_TARGET_POSTFIXES = ['unittests', 'inttests', 'egtests']
+
+# Used to determine if a test target is an earl grey test.
+EARL_GREY_TEST_TARGET_POSTFIX = 'egtests'
 
 
 def CreateCoverageProfileDataForTarget(target, jobs_count=None):
@@ -112,6 +115,10 @@
       profraw_path = log.split(PROFRAW_LOG_IDENTIFIER)[1][:-1]
       return os.path.abspath(profraw_path)
 
+  assert False, ('No profraw data file is generated, did you call '
+                 'coverage_util::ConfigureCoverageReportPath() in test setup? '
+                 'Please refer to base/test/test_support_ios.mm for example.')
+
 
 def _RunTestTargetWithCoverageConfiguration(target):
   """Runs tests to generate the profraw data file.
@@ -130,8 +137,12 @@
 
   iossim_path = _GetIOSSimPath()
   application_path = _GetApplicationBundlePath(target)
-  logs_chracters = subprocess.check_output([iossim_path, application_path])
 
+  cmd = [iossim_path, application_path]
+  if _TargetIsEarlGreyTest(target):
+    cmd.append(_GetXCTestBundlePath(target))
+
+  logs_chracters = subprocess.check_output(cmd)
   return ''.join(logs_chracters).split('\n')
 
 
@@ -148,6 +159,8 @@
   Raises:
     CalledProcessError: An error occurred merging profraw data files.
   """
+  print 'Creating the profile data file'
+
   default_root = _GetSrcRootPath()
   profdata_path = os.path.join(default_root, BUILD_DIRECTORY,
                                PROFDATA_FILE_NAME)
@@ -185,6 +198,20 @@
   return os.path.join(default_root, BUILD_DIRECTORY, application_bundle_name)
 
 
+def _GetXCTestBundlePath(target):
+  """Returns the path to the xctest bundle after building.
+
+  Args:
+    target: A string representing the name of the target to be tested.
+
+  Returns:
+    A string representing the path to the generated xctest bundle.
+  """
+  application_path = _GetApplicationBundlePath(target);
+  xctest_bundle_name = target + '_module.xctest'
+  return os.path.join(application_path, 'PlugIns', xctest_bundle_name)
+
+
 def _GetIOSSimPath():
   """Returns the path to the iossim executable file after building.
 
@@ -208,6 +235,18 @@
   return settings.getboolean('goma', 'enabled')
 
 
+def _TargetIsEarlGreyTest(target):
+  """Returns true if the target is an earl grey test.
+
+  Args:
+    target: A string representing the name of the target to be tested.
+
+  Returns:
+    A boolean indicates whether the target is an earl grey test or not.
+  """
+  return target.endswith(EARL_GREY_TEST_TARGET_POSTFIX)
+
+
 def _TargetNameIsValidTestTarget(target):
   """Returns True if the target name has a valid postfix.
 
diff --git a/ios/web/net/request_tracker_impl.mm b/ios/web/net/request_tracker_impl.mm
index ec527317..639100e0 100644
--- a/ios/web/net/request_tracker_impl.mm
+++ b/ios/web/net/request_tracker_impl.mm
@@ -309,9 +309,6 @@
       case web::SECURITY_STYLE_AUTHENTICATION_BROKEN:
         sslInfo = @"Not secure ";
         break;
-      case web::SECURITY_STYLE_WARNING:
-        sslInfo = @"Security warning";
-        break;
       case web::SECURITY_STYLE_AUTHENTICATED:
         if (status_.content_status ==
             web::SSLStatus::DISPLAYED_INSECURE_CONTENT)
diff --git a/ios/web/public/security_style.h b/ios/web/public/security_style.h
index a72396c..a2fbf841 100644
--- a/ios/web/public/security_style.h
+++ b/ios/web/public/security_style.h
@@ -28,11 +28,6 @@
   // this object in an authenticated manner but were unable to do so.
   SECURITY_STYLE_AUTHENTICATION_BROKEN,
 
-  // SECURITY_STYLE_WARNING means that the object was retrieved in an
-  // authenticated manner, but there were security issues with the retrieval or
-  // the object interacted with less secure objects.
-  SECURITY_STYLE_WARNING,
-
   // SECURITY_STYLE_AUTHENTICATED indicates that we successfully retrieved this
   // object over an authenticated protocol, such as HTTPS.
   SECURITY_STYLE_AUTHENTICATED,
diff --git a/ios/web/shell/test/BUILD.gn b/ios/web/shell/test/BUILD.gn
index 49976677..e79ba5d1 100644
--- a/ios/web/shell/test/BUILD.gn
+++ b/ios/web/shell/test/BUILD.gn
@@ -70,6 +70,7 @@
     "//ios/web/public/test",
     "//ios/web/public/test/http_server",
     "//ios/web/shell",
+    "//testing/gtest:gtest",
     "//url",
   ]
 
diff --git a/ios/web/shell/test/earl_grey/web_shell_test_case.mm b/ios/web/shell/test/earl_grey/web_shell_test_case.mm
index 72de136..aec1601f 100644
--- a/ios/web/shell/test/earl_grey/web_shell_test_case.mm
+++ b/ios/web/shell/test/earl_grey/web_shell_test_case.mm
@@ -8,6 +8,7 @@
 
 #import "ios/web/public/test/http_server/http_server.h"
 #import "ios/web/shell/test/earl_grey/shell_matchers.h"
+#include "testing/coverage_util_ios.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -39,6 +40,8 @@
 + (void)setUp {
   [super setUp];
   HttpServer::GetSharedInstance().StartOrDie();
+
+  coverage_util::ConfigureCoverageReportPath();
 }
 
 // Tear down called once for the class.
diff --git a/media/gpu/vt_video_decode_accelerator_mac.cc b/media/gpu/vt_video_decode_accelerator_mac.cc
index 3d7e662b..c3292cc 100644
--- a/media/gpu/vt_video_decode_accelerator_mac.cc
+++ b/media/gpu/vt_video_decode_accelerator_mac.cc
@@ -1262,6 +1262,8 @@
     NotifyError(PLATFORM_FAILURE, SFT_PLATFORM_ERROR);
     return false;
   }
+  gfx::ColorSpace color_space = GetImageBufferColorSpace(frame.image);
+  gl_image->SetColorSpaceForYUVToRGBConversion(color_space);
 
   // Assign the new image(s) to the the picture info.
   picture_info->gl_image = gl_image;
@@ -1271,8 +1273,8 @@
   DVLOG(3) << "PictureReady(picture_id=" << picture_id << ", "
            << "bitstream_id=" << frame.bitstream_id << ")";
   client_->PictureReady(Picture(picture_id, frame.bitstream_id,
-                                gfx::Rect(frame.image_size),
-                                GetImageBufferColorSpace(frame.image), true));
+                                gfx::Rect(frame.image_size), color_space,
+                                true));
   return true;
 }
 
diff --git a/mojo/common/common_custom_types_struct_traits.h b/mojo/common/common_custom_types_struct_traits.h
index b805d00f..275cabd 100644
--- a/mojo/common/common_custom_types_struct_traits.h
+++ b/mojo/common/common_custom_types_struct_traits.h
@@ -5,6 +5,7 @@
 #ifndef MOJO_COMMON_COMMON_CUSTOM_TYPES_STRUCT_TRAITS_H_
 #define MOJO_COMMON_COMMON_CUSTOM_TYPES_STRUCT_TRAITS_H_
 
+#include "base/containers/span.h"
 #include "base/files/file.h"
 #include "base/i18n/rtl.h"
 #include "base/process/process_handle.h"
@@ -26,15 +27,15 @@
 
 template <>
 struct StructTraits<common::mojom::String16DataView, base::StringPiece16> {
-  static ConstCArray<uint16_t> data(base::StringPiece16 str) {
-    return ConstCArray<uint16_t>(reinterpret_cast<const uint16_t*>(str.data()),
-                                 str.size());
+  static base::span<const uint16_t> data(base::StringPiece16 str) {
+    return base::make_span(reinterpret_cast<const uint16_t*>(str.data()),
+                           str.size());
   }
 };
 
 template <>
 struct StructTraits<common::mojom::String16DataView, base::string16> {
-  static ConstCArray<uint16_t> data(const base::string16& str) {
+  static base::span<const uint16_t> data(const base::string16& str) {
     return StructTraits<common::mojom::String16DataView,
                         base::StringPiece16>::data(str);
   }
diff --git a/mojo/common/values_struct_traits.h b/mojo/common/values_struct_traits.h
index 6ca7289..a4f618b 100644
--- a/mojo/common/values_struct_traits.h
+++ b/mojo/common/values_struct_traits.h
@@ -5,6 +5,7 @@
 #ifndef MOJO_COMMON_VALUES_STRUCT_TRAITS_H_
 #define MOJO_COMMON_VALUES_STRUCT_TRAITS_H_
 
+#include "base/containers/span.h"
 #include "base/values.h"
 #include "mojo/common/values.mojom-shared.h"
 #include "mojo/public/cpp/bindings/array_traits.h"
@@ -179,10 +180,10 @@
     return string_piece;
   }
 
-  static mojo::ConstCArray<uint8_t> binary_value(const base::Value& value) {
+  static base::span<const uint8_t> binary_value(const base::Value& value) {
     if (!value.is_blob())
       NOTREACHED();
-    return mojo::ConstCArray<uint8_t>(
+    return base::make_span(
         reinterpret_cast<const uint8_t*>(value.GetBlob().data()),
         value.GetBlob().size());
   }
@@ -237,7 +238,7 @@
     return UnionTraits<common::mojom::ValueDataView, base::Value>::string_value(
         *value);
   }
-  static mojo::ConstCArray<uint8_t> binary_value(
+  static base::span<const uint8_t> binary_value(
       const std::unique_ptr<base::Value>& value) {
     return UnionTraits<common::mojom::ValueDataView, base::Value>::binary_value(
         *value);
diff --git a/mojo/public/cpp/bindings/array_traits_span.h b/mojo/public/cpp/bindings/array_traits_span.h
index ead2761..f4677a8 100644
--- a/mojo/public/cpp/bindings/array_traits_span.h
+++ b/mojo/public/cpp/bindings/array_traits_span.h
@@ -12,13 +12,6 @@
 
 namespace mojo {
 
-// TODO(dcheng): Remove these type aliases.
-template <typename T>
-using CArray = base::span<T>;
-
-template <typename T>
-using ConstCArray = base::span<const T>;
-
 template <typename T>
 struct ArrayTraits<base::span<T>> {
   using Element = T;
diff --git a/net/interfaces/ip_address_struct_traits.h b/net/interfaces/ip_address_struct_traits.h
index 7aa3ff6..2d08f4a 100644
--- a/net/interfaces/ip_address_struct_traits.h
+++ b/net/interfaces/ip_address_struct_traits.h
@@ -5,6 +5,7 @@
 #ifndef NET_INTERFACES_IP_ADDRESS_STRUCT_TRAITS_H_
 #define NET_INTERFACES_IP_ADDRESS_STRUCT_TRAITS_H_
 
+#include "base/containers/span.h"
 #include "mojo/public/cpp/bindings/struct_traits.h"
 #include "net/base/ip_address.h"
 #include "net/interfaces/ip_address.mojom.h"
@@ -12,10 +13,9 @@
 namespace mojo {
 template <>
 struct StructTraits<net::interfaces::IPAddressDataView, net::IPAddress> {
-  static mojo::ConstCArray<uint8_t> address_bytes(
+  static base::span<const uint8_t> address_bytes(
       const net::IPAddress& ip_address) {
-    return mojo::ConstCArray<uint8_t>(ip_address.bytes().data(),
-                                      ip_address.bytes().size());
+    return ip_address.bytes();
   }
 
   static bool Read(net::interfaces::IPAddressDataView obj, net::IPAddress* out);
diff --git a/net/spdy/core/hpack/hpack_encoder_test.cc b/net/spdy/core/hpack/hpack_encoder_test.cc
index cb7b16b..fb10c8f 100644
--- a/net/spdy/core/hpack/hpack_encoder_test.cc
+++ b/net/spdy/core/hpack/hpack_encoder_test.cc
@@ -104,7 +104,6 @@
 
 namespace {
 
-using std::map;
 using testing::ElementsAre;
 using testing::Pair;
 
@@ -219,7 +218,7 @@
   ExpectIndex(IndexOf(key_2_));
 
   SpdyHeaderBlock headers;
-  headers[key_2_->name().as_string()] = key_2_->value().as_string();
+  headers[key_2_->name()] = key_2_->value();
   CompareWithExpectedEncoding(headers);
   EXPECT_THAT(headers_observed_,
               ElementsAre(Pair(key_2_->name(), key_2_->value())));
@@ -229,7 +228,7 @@
   ExpectIndex(IndexOf(static_));
 
   SpdyHeaderBlock headers;
-  headers[static_->name().as_string()] = static_->value().as_string();
+  headers[static_->name()] = static_->value();
   CompareWithExpectedEncoding(headers);
 }
 
@@ -238,7 +237,7 @@
   ExpectIndex(IndexOf(static_));
 
   SpdyHeaderBlock headers;
-  headers[static_->name().as_string()] = static_->value().as_string();
+  headers[static_->name()] = static_->value();
   CompareWithExpectedEncoding(headers);
 
   EXPECT_EQ(0u, peer_.table_peer().dynamic_entries()->size());
@@ -248,7 +247,7 @@
   ExpectIndexedLiteral(key_2_, "value3");
 
   SpdyHeaderBlock headers;
-  headers[key_2_->name().as_string()] = "value3";
+  headers[key_2_->name()] = "value3";
   CompareWithExpectedEncoding(headers);
 
   // A new entry was inserted and added to the reference set.
@@ -290,7 +289,7 @@
   ExpectIndexedLiteral("key3", "value3");
 
   SpdyHeaderBlock headers;
-  headers[key_1_->name().as_string()] = key_1_->value().as_string();
+  headers[key_1_->name()] = key_1_->value();
   headers["key3"] = "value3";
   CompareWithExpectedEncoding(headers);
 }
diff --git a/net/spdy/core/hpack/hpack_huffman_table_test.cc b/net/spdy/core/hpack/hpack_huffman_table_test.cc
index c601c6c..1cf13fb 100644
--- a/net/spdy/core/hpack/hpack_huffman_table_test.cc
+++ b/net/spdy/core/hpack/hpack_huffman_table_test.cc
@@ -18,9 +18,6 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using testing::ElementsAreArray;
-using testing::Pointwise;
-
 namespace net {
 
 namespace test {
diff --git a/net/test/OWNERS b/net/test/OWNERS
deleted file mode 100644
index 66d66900..0000000
--- a/net/test/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# General reviewer, except sync-specific bits.
-phajdan.jr@chromium.org
diff --git a/net/url_request/url_request.cc b/net/url_request/url_request.cc
index 69628ca9..9950182 100644
--- a/net/url_request/url_request.cc
+++ b/net/url_request/url_request.cc
@@ -12,6 +12,7 @@
 #include "base/compiler_specific.h"
 #include "base/lazy_instance.h"
 #include "base/memory/singleton.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/profiler/scoped_tracker.h"
 #include "base/rand_util.h"
 #include "base/stl_util.h"
@@ -175,6 +176,13 @@
 
 URLRequest::~URLRequest() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  // Log the redirect count during destruction, to ensure that it is only
+  // recorded at the end of following all redirect chains.
+  UMA_HISTOGRAM_EXACT_LINEAR("Net.RedirectChainLength",
+                             kMaxRedirects - redirect_limit_,
+                             kMaxRedirects + 1);
+
   Cancel();
 
   if (network_delegate_) {
diff --git a/remoting/host/BUILD.gn b/remoting/host/BUILD.gn
index 048ef7f..f796127 100644
--- a/remoting/host/BUILD.gn
+++ b/remoting/host/BUILD.gn
@@ -463,6 +463,7 @@
     "daemon_process_unittest.cc",
     "desktop_process_unittest.cc",
     "desktop_session_agent_unittest.cc",
+    "file_proxy_wrapper_linux_unittest.cc",
     "gcd_rest_client_unittest.cc",
     "gcd_state_updater_unittest.cc",
     "heartbeat_sender_unittest.cc",
diff --git a/remoting/host/file_proxy_wrapper.h b/remoting/host/file_proxy_wrapper.h
index d04c281d..a674cba 100644
--- a/remoting/host/file_proxy_wrapper.h
+++ b/remoting/host/file_proxy_wrapper.h
@@ -22,19 +22,49 @@
       ErrorCallback;
   typedef base::Callback<void()> SuccessCallback;
 
-  // Creates a platforms-specific FileProxyWrapper.
+  enum State {
+    // Created, but Init() has not been called yet.
+    kUninitialized = 0,
+
+    // Init() has been called.
+    kInitialized = 1,
+
+    // CreateFile() has been called. The file may or may not exist yet.
+    kFileCreated = 2,
+
+    // Close() has been called. WriteChunk() can no longer be called, but not
+    // all chunks may have been written to disk yet. After chunks are written,
+    // the file will be moved to its target location.
+    kClosing = 3,
+
+    // Close() has been called and succeeded.
+    kClosed = 4,
+
+    // Cancel() has been called or an error occured.
+    kFailed = 5,
+  };
+
+  // Creates a platform-specific FileProxyWrapper.
   static std::unique_ptr<FileProxyWrapper> Create();
 
   FileProxyWrapper();
   virtual ~FileProxyWrapper();
 
+  // |error_callback| must not immediately destroy this FileProxyWrapper.
   virtual void Init(const ErrorCallback& error_callback) = 0;
-  virtual void CreateFile(const std::string& filename,
-                          uint64_t filesize,
+  // TODO(jarhar): Remove |success_callback| from CreateFile(), and instead
+  // allow WriteChunk() to be called immediately after CreateFile(). This also
+  // means that FileTransferMessageHandler will no longer send a
+  // FileTransferResponse with the "READY" state.
+  virtual void CreateFile(const base::FilePath& directory,
+                          const std::string& filename,
                           const SuccessCallback& success_callback) = 0;
   virtual void WriteChunk(std::unique_ptr<CompoundBuffer> buffer) = 0;
+  // TODO(jarhar): Remove |success_callback| from Close() and instead use the
+  // ErrorCallback sent to Init() to signify success after Close().
   virtual void Close(const SuccessCallback& success_callback) = 0;
   virtual void Cancel() = 0;
+  virtual State state() = 0;
 };
 
 }  // namespace remoting
diff --git a/remoting/host/file_proxy_wrapper_linux.cc b/remoting/host/file_proxy_wrapper_linux.cc
index 584ee38..b4029c2 100644
--- a/remoting/host/file_proxy_wrapper_linux.cc
+++ b/remoting/host/file_proxy_wrapper_linux.cc
@@ -4,11 +4,42 @@
 
 #include "remoting/host/file_proxy_wrapper.h"
 
+#include <memory>
+#include <queue>
+#include <string>
+#include <vector>
+
+#include "base/files/file_util.h"
 #include "base/memory/ptr_util.h"
 #include "base/sequenced_task_runner.h"
+#include "base/strings/stringprintf.h"
+#include "base/task_runner_util.h"
 #include "base/task_scheduler/post_task.h"
+#include "base/threading/thread_checker.h"
 #include "remoting/base/compound_buffer.h"
 
+namespace {
+
+void EmptyStatusCallback(base::File::Error error) {}
+
+constexpr char kTempFileExtension[] = ".crdownload";
+
+remoting::protocol::FileTransferResponse_ErrorCode FileErrorToResponseError(
+    base::File::Error file_error) {
+  switch (file_error) {
+    case base::File::FILE_ERROR_ACCESS_DENIED:
+      return remoting::protocol::
+          FileTransferResponse_ErrorCode_PERMISSIONS_ERROR;
+    case base::File::FILE_ERROR_NO_SPACE:
+      return remoting::protocol::
+          FileTransferResponse_ErrorCode_OUT_OF_DISK_SPACE;
+    default:
+      return remoting::protocol::FileTransferResponse_ErrorCode_FILE_IO_ERROR;
+  }
+}
+
+}  // namespace
+
 namespace remoting {
 
 class FileProxyWrapperLinux : public FileProxyWrapper {
@@ -18,46 +49,300 @@
 
   // FileProxyWrapper implementation.
   void Init(const ErrorCallback& error_callback) override;
-  void CreateFile(const std::string& filename,
-                  uint64_t filesize,
+  void CreateFile(const base::FilePath& directory,
+                  const std::string& filename,
                   const SuccessCallback& success_callback) override;
   void WriteChunk(std::unique_ptr<CompoundBuffer> buffer) override;
   void Close(const SuccessCallback& success_callback) override;
   void Cancel() override;
+  State state() override;
 
  private:
+  struct FileChunk {
+    int64_t write_offset;
+    std::vector<char> data;
+  };
+
+  // Callbacks for CreateFile().
+  void CreateTempFile(const SuccessCallback& success_callback,
+                      int unique_path_number);
+  void CreateTempFileCallback(const SuccessCallback& success_callback,
+                              base::File::Error error);
+
+  // Callbacks for WriteChunk().
+  void WriteFileChunk(std::unique_ptr<FileChunk> chunk);
+  void WriteCallback(base::File::Error error, int bytes_written);
+
+  // Callbacks for Close().
+  void CloseFileAndMoveToDestination();
+  void CloseCallback(base::File::Error error);
+  void MoveToDestination(int unique_path_number);
+  void MoveFileCallback(bool success);
+
+  void CancelWithError(protocol::FileTransferResponse_ErrorCode error);
+  void SetState(State state);
+
+  State state_ = kUninitialized;
   scoped_refptr<base::SequencedTaskRunner> file_task_runner_;
+  std::unique_ptr<base::FileProxy> file_proxy_;
+
+  ErrorCallback error_callback_;
+
+  base::FilePath temp_filepath_;
+  base::FilePath destination_filepath_;
+
+  int64_t next_write_file_offset_ = 0;
+  std::queue<std::unique_ptr<FileChunk>> file_chunks_;
+  // active_file_chunk_ is the chunk currently being written to disk. It is
+  // empty if nothing is being written to disk right now.
+  std::unique_ptr<FileChunk> active_file_chunk_;
+
+  SuccessCallback close_success_callback_;
+
+  base::ThreadChecker thread_checker_;
+  base::WeakPtr<FileProxyWrapperLinux> weak_ptr_;
+  base::WeakPtrFactory<FileProxyWrapperLinux> weak_factory_;
 };
 
-FileProxyWrapperLinux::FileProxyWrapperLinux()
-    : file_task_runner_(base::CreateSequencedTaskRunnerWithTraits(
-          {base::MayBlock(), base::TaskPriority::BACKGROUND})) {
-  // DCHECK(file_task_runner_);
+FileProxyWrapperLinux::FileProxyWrapperLinux() : weak_factory_(this) {
+  weak_ptr_ = weak_factory_.GetWeakPtr();
 }
 
-FileProxyWrapperLinux::~FileProxyWrapperLinux() = default;
+FileProxyWrapperLinux::~FileProxyWrapperLinux() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+}
 
 void FileProxyWrapperLinux::Init(const ErrorCallback& error_callback) {
-  // TODO(jarhar): Implement Init.
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  SetState(kInitialized);
+  error_callback_ = error_callback;
+
+  file_task_runner_ = base::CreateSequencedTaskRunnerWithTraits(
+      {base::MayBlock(), base::TaskPriority::BACKGROUND});
+  DCHECK(file_task_runner_);
+
+  file_proxy_.reset(new base::FileProxy(file_task_runner_.get()));
+
+  if (!file_task_runner_) {
+    CancelWithError(protocol::FileTransferResponse_ErrorCode_UNEXPECTED_ERROR);
+  }
 }
 
 void FileProxyWrapperLinux::CreateFile(
+    const base::FilePath& directory,
     const std::string& filename,
-    uint64_t filesize,
     const SuccessCallback& success_callback) {
-  // TODO(jarhar): Implement CreateFile.
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  SetState(kFileCreated);
+
+  destination_filepath_ = directory.Append(filename);
+  temp_filepath_ = destination_filepath_.AddExtension(kTempFileExtension);
+
+  PostTaskAndReplyWithResult(
+      file_task_runner_.get(), FROM_HERE,
+      base::BindOnce(&base::GetUniquePathNumber, temp_filepath_,
+                     base::FilePath::StringType()),
+      base::BindOnce(&FileProxyWrapperLinux::CreateTempFile, weak_ptr_,
+                     success_callback));
+}
+
+void FileProxyWrapperLinux::CreateTempFile(
+    const SuccessCallback& success_callback,
+    int unique_path_number) {
+  if (unique_path_number > 0) {
+    temp_filepath_ = temp_filepath_.InsertBeforeExtensionASCII(
+        base::StringPrintf(" (%d)", unique_path_number));
+  }
+  if (!file_proxy_->CreateOrOpen(
+          temp_filepath_, base::File::FLAG_CREATE | base::File::FLAG_WRITE,
+          base::Bind(&FileProxyWrapperLinux::CreateTempFileCallback, weak_ptr_,
+                     success_callback))) {
+    // file_proxy_ failed to post a task to file_task_runner_.
+    CancelWithError(protocol::FileTransferResponse_ErrorCode_UNEXPECTED_ERROR);
+  }
+}
+
+void FileProxyWrapperLinux::CreateTempFileCallback(
+    const SuccessCallback& success_callback,
+    base::File::Error error) {
+  if (error) {
+    LOG(ERROR) << "Creating the temp file failed with error: " << error;
+    CancelWithError(FileErrorToResponseError(error));
+  } else {
+    success_callback.Run();
+  }
 }
 
 void FileProxyWrapperLinux::WriteChunk(std::unique_ptr<CompoundBuffer> buffer) {
-  // TODO(jarhar): Implement WriteChunk.
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_EQ(state_, kFileCreated);
+
+  std::unique_ptr<FileChunk> new_file_chunk = base::WrapUnique(new FileChunk());
+  new_file_chunk->data.resize(buffer->total_bytes());
+  // This copy could be avoided if CompoundBuffer were updated to allowed us to
+  // access the individual buffers in |buffer|.
+  // TODO(jarhar): Update CompoundBuffer to allow data transfer without a
+  // memcopy.
+  buffer->CopyTo(new_file_chunk->data.data(), new_file_chunk->data.size());
+
+  new_file_chunk->write_offset = next_write_file_offset_;
+  next_write_file_offset_ += new_file_chunk->data.size();
+
+  if (active_file_chunk_) {
+    // TODO(jarhar): When flow control enabled QUIC-based WebRTC data channels
+    // are implemented, block the flow of incoming chunks here if
+    // file_chunks_ has reached a maximum size. This implementation will
+    // allow file_chunks_ to grow without limits.
+    file_chunks_.push(std::move(new_file_chunk));
+  } else {
+    WriteFileChunk(std::move(new_file_chunk));
+  }
+}
+
+void FileProxyWrapperLinux::WriteFileChunk(std::unique_ptr<FileChunk> chunk) {
+  active_file_chunk_ = std::move(chunk);
+  DCHECK(active_file_chunk_);
+  if (!file_proxy_->Write(
+          active_file_chunk_->write_offset, active_file_chunk_->data.data(),
+          active_file_chunk_->data.size(),
+          base::Bind(&FileProxyWrapperLinux::WriteCallback, weak_ptr_))) {
+    // file_proxy_ failed to post a task to file_task_runner_.
+    CancelWithError(protocol::FileTransferResponse_ErrorCode_UNEXPECTED_ERROR);
+  }
+}
+
+void FileProxyWrapperLinux::WriteCallback(base::File::Error error,
+                                          int bytes_written) {
+  if (active_file_chunk_->data.size() != (unsigned)bytes_written || error) {
+    if (!error) {
+      error = base::File::FILE_ERROR_FAILED;
+    }
+    LOG(ERROR) << "Write failed with error: " << error;
+    CancelWithError(FileErrorToResponseError(error));
+    return;
+  }
+
+  active_file_chunk_.reset();
+  if (!file_chunks_.empty()) {
+    std::unique_ptr<FileChunk> chunk_to_write = std::move(file_chunks_.front());
+    file_chunks_.pop();
+    WriteFileChunk(std::move(chunk_to_write));
+  } else if (state_ == kClosing) {
+    // All writes are complete and we have gotten the signal to move the file.
+    CloseFileAndMoveToDestination();
+  }
 }
 
 void FileProxyWrapperLinux::Close(const SuccessCallback& success_callback) {
-  // TODO(jarhar): Implement Close.
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  SetState(kClosing);
+  close_success_callback_ = success_callback;
+
+  if (!active_file_chunk_ && file_chunks_.empty()) {
+    // All writes are complete, so we can finish up now.
+    CloseFileAndMoveToDestination();
+  }
+}
+
+void FileProxyWrapperLinux::CloseFileAndMoveToDestination() {
+  DCHECK_EQ(state_, kClosing);
+  file_proxy_->Close(
+      base::Bind(&FileProxyWrapperLinux::CloseCallback, weak_ptr_));
+}
+
+void FileProxyWrapperLinux::CloseCallback(base::File::Error error) {
+  if (error) {
+    CancelWithError(FileErrorToResponseError(error));
+    return;
+  }
+
+  PostTaskAndReplyWithResult(
+      file_task_runner_.get(), FROM_HERE,
+      base::BindOnce(&base::GetUniquePathNumber, destination_filepath_,
+                     base::FilePath::StringType()),
+      base::BindOnce(&FileProxyWrapperLinux::MoveToDestination, weak_ptr_));
+}
+
+void FileProxyWrapperLinux::MoveToDestination(int unique_path_number) {
+  if (unique_path_number > 0) {
+    destination_filepath_ = destination_filepath_.InsertBeforeExtensionASCII(
+        base::StringPrintf(" (%d)", unique_path_number));
+  }
+  PostTaskAndReplyWithResult(
+      file_task_runner_.get(), FROM_HERE,
+      base::BindOnce(&base::Move, temp_filepath_, destination_filepath_),
+      base::BindOnce(&FileProxyWrapperLinux::MoveFileCallback, weak_ptr_));
+}
+
+void FileProxyWrapperLinux::MoveFileCallback(bool success) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  if (success) {
+    SetState(kClosed);
+    std::move(close_success_callback_).Run();
+  } else {
+    CancelWithError(protocol::FileTransferResponse_ErrorCode_FILE_IO_ERROR);
+  }
 }
 
 void FileProxyWrapperLinux::Cancel() {
-  // TODO(jarhar): Implement Cancel.
+  if (file_proxy_->IsValid()) {
+    file_proxy_->Close(base::Bind(&EmptyStatusCallback));
+  }
+
+  if (state_ == kFileCreated || state_ == kClosing) {
+    file_task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(base::IgnoreResult(&base::DeleteFile),
+                                  temp_filepath_, false /* recursive */));
+  }
+
+  if (state_ == kClosing || state_ == kClosed) {
+    file_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(base::IgnoreResult(&base::DeleteFile),
+                       destination_filepath_, false /* recursive */));
+  }
+
+  SetState(kFailed);
+}
+
+void FileProxyWrapperLinux::CancelWithError(
+    protocol::FileTransferResponse_ErrorCode error) {
+  Cancel();
+  std::move(error_callback_).Run(error);
+}
+
+void FileProxyWrapperLinux::SetState(State state) {
+  switch (state) {
+    case kUninitialized:
+      // No state can change to kUninitialized.
+      NOTREACHED();
+      break;
+    case kInitialized:
+      DCHECK_EQ(state_, kUninitialized);
+      break;
+    case kFileCreated:
+      DCHECK_EQ(state_, kInitialized);
+      break;
+    case kClosing:
+      DCHECK_EQ(state_, kFileCreated);
+      break;
+    case kClosed:
+      DCHECK_EQ(state_, kClosing);
+      break;
+    case kFailed:
+      // Any state can change to kFailed.
+      break;
+  }
+
+  state_ = state;
+}
+
+FileProxyWrapper::State FileProxyWrapperLinux::state() {
+  return state_;
 }
 
 // static
diff --git a/remoting/host/file_proxy_wrapper_linux_unittest.cc b/remoting/host/file_proxy_wrapper_linux_unittest.cc
new file mode 100644
index 0000000..038b240
--- /dev/null
+++ b/remoting/host/file_proxy_wrapper_linux_unittest.cc
@@ -0,0 +1,168 @@
+// 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/file_proxy_wrapper.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.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/test/scoped_task_environment.h"
+#include "net/base/io_buffer.h"
+#include "remoting/base/compound_buffer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+constexpr char kTestFilename[] = "test-file.txt";
+constexpr char kTestFilenameSecondary[] = "test-file (1).txt";
+const std::string& kTestDataOne = "this is the first test string";
+const std::string& kTestDataTwo = "this is the second test string";
+const std::string& kTestDataThree = "this is the third test string";
+
+std::unique_ptr<remoting::CompoundBuffer> ToBuffer(const std::string& data) {
+  std::unique_ptr<remoting::CompoundBuffer> buffer =
+      base::MakeUnique<remoting::CompoundBuffer>();
+  buffer->Append(new net::WrappedIOBuffer(data.data()), data.size());
+  return buffer;
+}
+
+}  // namespace
+
+namespace remoting {
+
+class FileProxyWrapperLinuxTest : public testing::Test {
+ public:
+  FileProxyWrapperLinuxTest();
+  ~FileProxyWrapperLinuxTest() override;
+
+  // testing::Test implementation.
+  void SetUp() override;
+  void TearDown() override;
+
+  const base::FilePath& TestDir() const { return dir_.GetPath(); }
+  const base::FilePath TestFilePath() const {
+    return dir_.GetPath().Append(kTestFilename);
+  }
+
+  void ErrorCallback(protocol::FileTransferResponse_ErrorCode error);
+  void CreateFileCallback();
+  void CloseCallback();
+
+ protected:
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+  base::ScopedTempDir dir_;
+
+  std::unique_ptr<FileProxyWrapper> file_proxy_wrapper_;
+  std::unique_ptr<protocol::FileTransferResponse_ErrorCode> error_;
+  bool create_callback_called_;
+  bool close_callback_called_;
+};
+
+FileProxyWrapperLinuxTest::FileProxyWrapperLinuxTest()
+    : scoped_task_environment_(
+          base::test::ScopedTaskEnvironment::MainThreadType::DEFAULT,
+          base::test::ScopedTaskEnvironment::ExecutionMode::QUEUED) {}
+FileProxyWrapperLinuxTest::~FileProxyWrapperLinuxTest() = default;
+
+void FileProxyWrapperLinuxTest::SetUp() {
+  ASSERT_TRUE(dir_.CreateUniqueTempDir());
+
+  file_proxy_wrapper_ = FileProxyWrapper::Create();
+  file_proxy_wrapper_->Init(base::Bind(
+      &FileProxyWrapperLinuxTest::ErrorCallback, base::Unretained(this)));
+
+  error_.reset();
+  create_callback_called_ = false;
+  close_callback_called_ = false;
+}
+
+void FileProxyWrapperLinuxTest::TearDown() {
+  file_proxy_wrapper_.reset();
+}
+
+void FileProxyWrapperLinuxTest::ErrorCallback(
+    protocol::FileTransferResponse_ErrorCode error) {
+  error_.reset(new protocol::FileTransferResponse_ErrorCode(error));
+}
+
+void FileProxyWrapperLinuxTest::CreateFileCallback() {
+  create_callback_called_ = true;
+}
+
+void FileProxyWrapperLinuxTest::CloseCallback() {
+  close_callback_called_ = true;
+}
+
+// Verifies that FileProxyWrapper can write three chunks to a file without
+// throwing any errors.
+TEST_F(FileProxyWrapperLinuxTest, WriteThreeChunks) {
+  file_proxy_wrapper_->CreateFile(
+      TestDir(), kTestFilename,
+      base::Bind(&FileProxyWrapperLinuxTest::CreateFileCallback,
+                 base::Unretained(this)));
+
+  scoped_task_environment_.RunUntilIdle();
+  ASSERT_FALSE(error_);
+  ASSERT_TRUE(create_callback_called_);
+
+  file_proxy_wrapper_->WriteChunk(ToBuffer(kTestDataOne));
+  file_proxy_wrapper_->WriteChunk(ToBuffer(kTestDataTwo));
+  file_proxy_wrapper_->WriteChunk(ToBuffer(kTestDataThree));
+
+  file_proxy_wrapper_->Close(base::Bind(
+      &FileProxyWrapperLinuxTest::CloseCallback, base::Unretained(this)));
+
+  scoped_task_environment_.RunUntilIdle();
+  ASSERT_FALSE(error_);
+  ASSERT_TRUE(close_callback_called_);
+
+  std::string actual_file_data;
+  ASSERT_TRUE(base::ReadFileToString(TestFilePath(), &actual_file_data));
+  ASSERT_TRUE(kTestDataOne + kTestDataTwo + kTestDataThree == actual_file_data);
+}
+
+// Verifies that calling Cancel() deletes any temporary or destination files.
+TEST_F(FileProxyWrapperLinuxTest, CancelDeletesFiles) {
+  file_proxy_wrapper_->CreateFile(
+      TestDir(), kTestFilename,
+      base::Bind(&FileProxyWrapperLinuxTest::CreateFileCallback,
+                 base::Unretained(this)));
+  scoped_task_environment_.RunUntilIdle();
+
+  file_proxy_wrapper_->WriteChunk(ToBuffer(kTestDataOne));
+  scoped_task_environment_.RunUntilIdle();
+
+  file_proxy_wrapper_->Cancel();
+  file_proxy_wrapper_.reset();
+  scoped_task_environment_.RunUntilIdle();
+
+  ASSERT_TRUE(base::IsDirectoryEmpty(TestDir()));
+}
+
+// Verifies that FileProxyWrapper will write to a file named "file (1).txt" when
+// "file.txt" already exists.
+TEST_F(FileProxyWrapperLinuxTest, FileAlreadyExists) {
+  WriteFile(TestFilePath(), kTestDataOne.data(), kTestDataOne.size());
+
+  file_proxy_wrapper_->CreateFile(
+      TestDir(), kTestFilename,
+      base::Bind(&FileProxyWrapperLinuxTest::CreateFileCallback,
+                 base::Unretained(this)));
+  scoped_task_environment_.RunUntilIdle();
+
+  file_proxy_wrapper_->WriteChunk(ToBuffer(kTestDataTwo));
+  file_proxy_wrapper_->Close(base::Bind(
+      &FileProxyWrapperLinuxTest::CloseCallback, base::Unretained(this)));
+  scoped_task_environment_.RunUntilIdle();
+
+  std::string actual_file_data;
+  base::FilePath secondary_filepath = TestDir().Append(kTestFilenameSecondary);
+  ASSERT_TRUE(base::ReadFileToString(secondary_filepath, &actual_file_data));
+  ASSERT_STREQ(kTestDataTwo.data(), actual_file_data.data());
+}
+
+}  // namespace remoting
diff --git a/remoting/proto/file_transfer.proto b/remoting/proto/file_transfer.proto
index fecff81..a9bb7228 100644
--- a/remoting/proto/file_transfer.proto
+++ b/remoting/proto/file_transfer.proto
@@ -22,6 +22,8 @@
   enum ErrorCode {
     OUT_OF_DISK_SPACE = 1;
     PERMISSIONS_ERROR = 2;
+    FILE_IO_ERROR = 3;
+    UNEXPECTED_ERROR = 4;
   }
   optional ErrorCode error = 2;
 
diff --git a/services/metrics/public/cpp/mojo_ukm_recorder.h b/services/metrics/public/cpp/mojo_ukm_recorder.h
index 1a490e9..514be3f 100644
--- a/services/metrics/public/cpp/mojo_ukm_recorder.h
+++ b/services/metrics/public/cpp/mojo_ukm_recorder.h
@@ -20,10 +20,10 @@
  *  ukm::mojom::UkmRecorderInterfacePtr interface;
  *  content::RenderThread::Get()->GetConnector()->BindInterface(
  *      content::mojom::kBrowserServiceName, mojo::MakeRequest(&interface));
- *  ukm::MojoUkmRecorder recorder(std::move(interface));
- *  std::unique_ptr<ukm::UkmEntryBuilder> builder =
- *      recorder.GetEntryBuilder(coordination_unit_id, "MyEvent");
- *  builder->AddMetric("MyMetric", metric_value);
+ *  ukm::MojoUkmRecorder ukm_recorder(std::move(interface));
+ *  ukm::builders::MyEvent(source_id)
+ *      .SetMyMetric(metric_value)
+ *      .Record(ukm_recorder);
  */
 class METRICS_EXPORT MojoUkmRecorder : public UkmRecorder {
  public:
diff --git a/services/ui/display/screen_manager_forwarding.cc b/services/ui/display/screen_manager_forwarding.cc
index 048ef05..4923d69 100644
--- a/services/ui/display/screen_manager_forwarding.cc
+++ b/services/ui/display/screen_manager_forwarding.cc
@@ -11,7 +11,7 @@
 #include "services/service_manager/public/cpp/binder_registry.h"
 #include "ui/display/screen_base.h"
 #include "ui/display/types/display_constants.h"
-#include "ui/display/types/display_snapshot_mojo.h"
+#include "ui/display/types/display_snapshot.h"
 #include "ui/display/types/fake_display_controller.h"
 #include "ui/display/types/native_display_delegate.h"
 #include "ui/ozone/public/ozone_platform.h"
@@ -247,15 +247,15 @@
     const std::vector<DisplaySnapshot*>& snapshots) {
   snapshot_map_.clear();
 
-  // Convert the DisplaySnapshots to MojoDisplaySnapshots to allow sending
-  // over Mojo. Also caches the snapshots for lookup later.
-  std::vector<std::unique_ptr<DisplaySnapshotMojo>> mojo_snapshots;
+  std::vector<std::unique_ptr<DisplaySnapshot>> snapshot_clones;
   for (auto* snapshot : snapshots) {
     snapshot_map_[snapshot->display_id()] = snapshot;
-    mojo_snapshots.push_back(DisplaySnapshotMojo::CreateFrom(*snapshot));
+
+    // Clone display snapshots to send over IPC.
+    snapshot_clones.push_back(snapshot->Clone());
   }
 
-  callback.Run(std::move(mojo_snapshots));
+  callback.Run(std::move(snapshot_clones));
 }
 
 void ScreenManagerForwarding::ForwardConfigure(
diff --git a/services/ui/ws/BUILD.gn b/services/ui/ws/BUILD.gn
index 785c099..5d265c4 100644
--- a/services/ui/ws/BUILD.gn
+++ b/services/ui/ws/BUILD.gn
@@ -256,6 +256,7 @@
     "transient_windows_unittest.cc",
     "user_activity_monitor_unittest.cc",
     "user_display_manager_unittest.cc",
+    "window_coordinate_conversions_unittest.cc",
     "window_finder_unittest.cc",
     "window_manager_state_unittest.cc",
     "window_tree_client_unittest.cc",
diff --git a/services/ui/ws/event_dispatcher.cc b/services/ui/ws/event_dispatcher.cc
index bbe2cba..b6acc43 100644
--- a/services/ui/ws/event_dispatcher.cc
+++ b/services/ui/ws/event_dispatcher.cc
@@ -357,7 +357,7 @@
   updated_target.deepest_window.window =
       location_target.deepest_window.window
           ? delegate_->GetFallbackTargetForEventBlockedByModal(
-                location_target.deepest_window.window->GetRoot())
+                location_target.deepest_window.window->GetRootForDrawn())
           : nullptr;
   return updated_target;
 }
@@ -691,7 +691,9 @@
 void EventDispatcher::DispatchToClient(ServerWindow* window,
                                        ClientSpecificId client_id,
                                        const ui::LocatedEvent& event) {
-  gfx::Point location = ConvertPointFromRoot(window, event.location());
+  gfx::Point location = ConvertPointFromRootForEventDispatch(
+      delegate_->GetRootWindowForEventDispatch(window), window,
+      event.location());
   std::unique_ptr<ui::Event> clone = ui::Event::Clone(event);
   clone->AsLocatedEvent()->set_location(location);
   // TODO(jonross): add post-target accelerator support once accelerators
@@ -799,7 +801,7 @@
   //   sending exit as necessary.
   // http://crbug.com/613646 .
   if (!new_parent || !new_parent->IsDrawn() ||
-      new_parent->GetRoot() != old_parent->GetRoot()) {
+      new_parent->GetRootForDrawn() != old_parent->GetRootForDrawn()) {
     CancelPointerEventsToTarget(window);
   }
 }
diff --git a/services/ui/ws/event_dispatcher_delegate.h b/services/ui/ws/event_dispatcher_delegate.h
index 217a335..c128b15 100644
--- a/services/ui/ws/event_dispatcher_delegate.h
+++ b/services/ui/ws/event_dispatcher_delegate.h
@@ -96,6 +96,10 @@
   virtual ServerWindow* GetRootWindowContaining(gfx::Point* location_in_display,
                                                 int64_t* display_id) = 0;
 
+  // Returns the root of |window| that is used for event dispatch. The returned
+  // value is used for coordinate conversion.
+  virtual ServerWindow* GetRootWindowForEventDispatch(ServerWindow* window) = 0;
+
   // Called when event dispatch could not find a target. OnAccelerator may still
   // be called.
   virtual void OnEventTargetNotFound(const ui::Event& event,
diff --git a/services/ui/ws/event_dispatcher_unittest.cc b/services/ui/ws/event_dispatcher_unittest.cc
index 29725bf..ebafea0 100644
--- a/services/ui/ws/event_dispatcher_unittest.cc
+++ b/services/ui/ws/event_dispatcher_unittest.cc
@@ -202,6 +202,9 @@
     }
     return nullptr;
   }
+  ServerWindow* GetRootWindowForEventDispatch(ServerWindow* window) override {
+    return window->GetRootForDrawn();
+  }
 
   Delegate* delegate_;
   ServerWindow* focused_window_;
diff --git a/services/ui/ws/focus_controller_unittest.cc b/services/ui/ws/focus_controller_unittest.cc
index 119d86bc..13dc7773 100644
--- a/services/ui/ws/focus_controller_unittest.cc
+++ b/services/ui/ws/focus_controller_unittest.cc
@@ -365,7 +365,7 @@
   viz::HostFrameSinkManager* GetHostFrameSinkManager() override {
     return nullptr;
   }
-  ServerWindow* GetRootWindow(const ServerWindow* window) override {
+  ServerWindow* GetRootWindowForDrawn(const ServerWindow* window) override {
     const ServerWindow* root = window;
     while (root && root->parent())
       root = root->parent();
diff --git a/services/ui/ws/modal_window_controller.cc b/services/ui/ws/modal_window_controller.cc
index 1cfba19..989272c 100644
--- a/services/ui/ws/modal_window_controller.cc
+++ b/services/ui/ws/modal_window_controller.cc
@@ -278,7 +278,7 @@
 bool ModalWindowController::IsWindowInSystemModalContainer(
     const ServerWindow* window) const {
   DCHECK(window->IsDrawn());
-  const ServerWindow* root = window->GetRoot();
+  const ServerWindow* root = window->GetRootForDrawn();
   DCHECK(root);
   for (auto& blocking_containers : all_blocking_containers_) {
     if (blocking_containers->IsInDisplayWithRoot(root))
@@ -292,7 +292,7 @@
 const ServerWindow* ModalWindowController::GetMinContainer(
     const ServerWindow* window) const {
   DCHECK(window->IsDrawn());
-  const ServerWindow* root = window->GetRoot();
+  const ServerWindow* root = window->GetRootForDrawn();
   DCHECK(root);
   for (auto& blocking_containers : all_blocking_containers_) {
     if (blocking_containers->IsInDisplayWithRoot(root))
diff --git a/services/ui/ws/server_window.cc b/services/ui/ws/server_window.cc
index 9871199..c00f068 100644
--- a/services/ui/ws/server_window.cc
+++ b/services/ui/ws/server_window.cc
@@ -231,8 +231,8 @@
   accepts_drops_ = accepts_drops;
 }
 
-const ServerWindow* ServerWindow::GetRoot() const {
-  return delegate_->GetRootWindow(this);
+const ServerWindow* ServerWindow::GetRootForDrawn() const {
+  return delegate_->GetRootWindowForDrawn(this);
 }
 
 ServerWindow* ServerWindow::GetChildWindow(const WindowId& window_id) {
@@ -414,7 +414,7 @@
 }
 
 bool ServerWindow::IsDrawn() const {
-  const ServerWindow* root = delegate_->GetRootWindow(this);
+  const ServerWindow* root = delegate_->GetRootWindowForDrawn(this);
   if (!root || !root->visible())
     return false;
   const ServerWindow* window = this;
diff --git a/services/ui/ws/server_window.h b/services/ui/ws/server_window.h
index f23a390..3ed98e9 100644
--- a/services/ui/ws/server_window.h
+++ b/services/ui/ws/server_window.h
@@ -113,12 +113,14 @@
   const ServerWindow* parent() const { return parent_; }
   ServerWindow* parent() { return parent_; }
 
+  // Returns the root window used in checking drawn status. This is not
+  // necessarily the same as the root window used in event dispatch.
   // NOTE: this returns null if the window does not have an ancestor associated
   // with a display.
-  const ServerWindow* GetRoot() const;
-  ServerWindow* GetRoot() {
+  const ServerWindow* GetRootForDrawn() const;
+  ServerWindow* GetRootForDrawn() {
     return const_cast<ServerWindow*>(
-        const_cast<const ServerWindow*>(this)->GetRoot());
+        const_cast<const ServerWindow*>(this)->GetRootForDrawn());
   }
 
   const Windows& children() const { return children_; }
diff --git a/services/ui/ws/server_window_delegate.h b/services/ui/ws/server_window_delegate.h
index bdc4c0b..0945d71 100644
--- a/services/ui/ws/server_window_delegate.h
+++ b/services/ui/ws/server_window_delegate.h
@@ -26,7 +26,9 @@
 
   // Returns the root of the window tree to which this |window| is attached.
   // Returns null if this window is not attached up through to a root window.
-  virtual ServerWindow* GetRootWindow(const ServerWindow* window) = 0;
+  // The returned root is used for drawn checks and may differ from that used
+  // for event dispatch purposes.
+  virtual ServerWindow* GetRootWindowForDrawn(const ServerWindow* window) = 0;
 
   // Called when a CompositorFrame with a new SurfaceId activates for the first
   // time for |window|.
diff --git a/services/ui/ws/server_window_drawn_tracker.cc b/services/ui/ws/server_window_drawn_tracker.cc
index 35724897..011916e 100644
--- a/services/ui/ws/server_window_drawn_tracker.cc
+++ b/services/ui/ws/server_window_drawn_tracker.cc
@@ -125,8 +125,8 @@
     if (window->visible()) {
       will_change = false;
     } else {
-      bool is_drawn = (window->GetRoot() == window) ||
-          (window->parent() && window->parent()->IsDrawn());
+      bool is_drawn = (window->GetRootForDrawn() == window) ||
+                      (window->parent() && window->parent()->IsDrawn());
       if (is_drawn) {
         for (ServerWindow* w = window_; is_drawn && w != window;
              w = w->parent())
diff --git a/services/ui/ws/test_server_window_delegate.cc b/services/ui/ws/test_server_window_delegate.cc
index e2362c0..ac61737 100644
--- a/services/ui/ws/test_server_window_delegate.cc
+++ b/services/ui/ws/test_server_window_delegate.cc
@@ -17,7 +17,7 @@
   return nullptr;
 }
 
-ServerWindow* TestServerWindowDelegate::GetRootWindow(
+ServerWindow* TestServerWindowDelegate::GetRootWindowForDrawn(
     const ServerWindow* window) {
   return root_window_;
 }
diff --git a/services/ui/ws/test_server_window_delegate.h b/services/ui/ws/test_server_window_delegate.h
index 953f7af..9dbdef2 100644
--- a/services/ui/ws/test_server_window_delegate.h
+++ b/services/ui/ws/test_server_window_delegate.h
@@ -21,7 +21,7 @@
  private:
   // ServerWindowDelegate:
   viz::HostFrameSinkManager* GetHostFrameSinkManager() override;
-  ServerWindow* GetRootWindow(const ServerWindow* window) override;
+  ServerWindow* GetRootWindowForDrawn(const ServerWindow* window) override;
   void OnFirstSurfaceActivation(const viz::SurfaceInfo& surface_info,
                                 ServerWindow* window) override;
 
diff --git a/services/ui/ws/window_coordinate_conversions.cc b/services/ui/ws/window_coordinate_conversions.cc
index e6bab98f4..264b7c1a 100644
--- a/services/ui/ws/window_coordinate_conversions.cc
+++ b/services/ui/ws/window_coordinate_conversions.cc
@@ -14,11 +14,8 @@
 namespace ws {
 namespace {
 
-gfx::Transform GetTransformToRoot(const ServerWindow* window) {
-  // This code should only be called when |window| is connected to a display.
-  const ServerWindow* root = window->GetRoot();
-  DCHECK(root);
-
+gfx::Transform GetTransformToRoot(const ServerWindow* root,
+                                  const ServerWindow* window) {
   gfx::Transform transform;
   const ServerWindow* w = window;
   for (; w && w != root; w = w->parent()) {
@@ -29,16 +26,30 @@
       transform.ConcatTransform(w->transform());
     transform.ConcatTransform(translation);
   }
+  // Allow the root to also have a transform. This mirrors how
+  // WindowManagerDisplayRoot works. Ash sets the transform for rotation as well
+  // as ui scale.
+  if (w == root && !w->transform().IsIdentity())
+    transform.ConcatTransform(w->transform());
   return transform;
 }
 
 }  // namespace
 
-gfx::Point ConvertPointFromRoot(const ServerWindow* window,
-                                const gfx::Point& location_in_root) {
-  const gfx::Transform transform = GetTransformToRoot(window);
+gfx::Point ConvertPointFromRootForEventDispatch(
+    const ServerWindow* root,
+    const ServerWindow* window,
+    const gfx::Point& location_in_root) {
+  // This code should only be called when |window| is connected to a display.
+  DCHECK(root);
+
+  if (root == window)
+    return location_in_root;
+
+  const gfx::Transform transform = GetTransformToRoot(root, window);
   gfx::Point3F location_in_root3(gfx::PointF{location_in_root});
   transform.TransformPointReverse(&location_in_root3);
+
   return gfx::ToFlooredPoint(location_in_root3.AsPointF());
 }
 
diff --git a/services/ui/ws/window_coordinate_conversions.h b/services/ui/ws/window_coordinate_conversions.h
index 1c687c1..f871bb6 100644
--- a/services/ui/ws/window_coordinate_conversions.h
+++ b/services/ui/ws/window_coordinate_conversions.h
@@ -15,8 +15,9 @@
 class ServerWindow;
 
 // Converts |point|, in the coordinates of the root, to that of |window|.
-gfx::Point ConvertPointFromRoot(const ServerWindow* window,
-                                const gfx::Point& point);
+gfx::Point ConvertPointFromRootForEventDispatch(const ServerWindow* root,
+                                                const ServerWindow* window,
+                                                const gfx::Point& point);
 
 }  // namespace ws
 }  // namespace ui
diff --git a/services/ui/ws/window_coordinate_conversions_unittest.cc b/services/ui/ws/window_coordinate_conversions_unittest.cc
new file mode 100644
index 0000000..09bf4c7
--- /dev/null
+++ b/services/ui/ws/window_coordinate_conversions_unittest.cc
@@ -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.
+
+#include "services/ui/ws/window_coordinate_conversions.h"
+
+#include "services/ui/ws/server_window.h"
+#include "services/ui/ws/test_server_window_delegate.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ui {
+namespace ws {
+
+TEST(WindowCoordinateConversions, Transform) {
+  TestServerWindowDelegate window_delegate;
+  ServerWindow root(&window_delegate, WindowId(1, 2));
+  root.set_event_targeting_policy(
+      mojom::EventTargetingPolicy::DESCENDANTS_ONLY);
+  root.SetVisible(true);
+  root.SetBounds(gfx::Rect(0, 0, 100, 100), base::nullopt);
+  ServerWindow child(&window_delegate, WindowId(1, 3));
+  root.Add(&child);
+  child.SetVisible(true);
+  child.SetBounds(gfx::Rect(0, 0, 20, 20), base::nullopt);
+  // Make the root |child|, and set a transform on |child|, which mirrors
+  // how WindowManagerState and EventDispatcher work together.
+  window_delegate.set_root_window(&child);
+  gfx::Transform transform;
+  transform.Scale(SkIntToMScalar(2), SkIntToMScalar(2));
+  child.SetTransform(transform);
+
+  ServerWindow child_child(&window_delegate, WindowId(1, 4));
+  child.Add(&child_child);
+  child_child.SetVisible(true);
+  child_child.SetBounds(gfx::Rect(4, 6, 12, 24), base::nullopt);
+
+  const gfx::Point converted_point = ConvertPointFromRootForEventDispatch(
+      &child, &child_child, gfx::Point(14, 20));
+  EXPECT_EQ(gfx::Point(3, 4), converted_point);
+}
+
+}  // namespace ws
+}  // namespace ui
diff --git a/services/ui/ws/window_finder.cc b/services/ui/ws/window_finder.cc
index 92b2154f..b8efd8d 100644
--- a/services/ui/ws/window_finder.cc
+++ b/services/ui/ws/window_finder.cc
@@ -73,14 +73,16 @@
 
 gfx::Transform TransformFromParent(const ServerWindow* window,
                                    const gfx::Transform& current_transform) {
-  gfx::Transform transform = current_transform;
+  gfx::Transform result = current_transform;
+  if (window->bounds().origin() != gfx::Point()) {
+    gfx::Transform translation;
+    translation.Translate(static_cast<float>(window->bounds().x()),
+                          static_cast<float>(window->bounds().y()));
+    result.PreconcatTransform(translation);
+  }
   if (!window->transform().IsIdentity())
-    transform.ConcatTransform(window->transform());
-  gfx::Transform translation;
-  translation.Translate(static_cast<float>(window->bounds().x()),
-                        static_cast<float>(window->bounds().y()));
-  transform.ConcatTransform(translation);
-  return transform;
+    result.PreconcatTransform(window->transform());
+  return result;
 }
 
 bool FindDeepestVisibleWindowForLocationImpl(
@@ -152,9 +154,19 @@
 DeepestWindow FindDeepestVisibleWindowForLocation(ServerWindow* root_window,
                                                   EventSource event_source,
                                                   const gfx::Point& location) {
+  gfx::Point initial_location = location;
+  gfx::Transform root_transform = root_window->transform();
+  if (!root_transform.IsIdentity()) {
+    gfx::Point3F transformed_location(gfx::PointF{initial_location});
+    root_transform.TransformPointReverse(&transformed_location);
+    initial_location = gfx::ToFlooredPoint(transformed_location.AsPointF());
+  }
   DeepestWindow result;
+  // Allow the root to have a transform, which mirrors what happens with
+  // WindowManagerDisplayRoot.
   FindDeepestVisibleWindowForLocationImpl(root_window, event_source, location,
-                                          location, gfx::Transform(), &result);
+                                          initial_location, root_transform,
+                                          &result);
   return result;
 }
 
diff --git a/services/ui/ws/window_finder_unittest.cc b/services/ui/ws/window_finder_unittest.cc
index d3189fcd..1476a4fa 100644
--- a/services/ui/ws/window_finder_unittest.cc
+++ b/services/ui/ws/window_finder_unittest.cc
@@ -209,6 +209,18 @@
                          &root, EventSource::MOUSE, gfx::Point(9, 9))
                          .window);
 
+  ServerWindow child_child(&window_delegate, WindowId(1, 4));
+  child.Add(&child_child);
+  child_child.SetVisible(true);
+  child_child.SetBounds(gfx::Rect(12, 12, 4, 4), base::nullopt);
+
+  EXPECT_EQ(&child, FindDeepestVisibleWindowForLocation(
+                        &root, EventSource::MOUSE, gfx::Point(30, 30))
+                        .window);
+  EXPECT_EQ(&child_child, FindDeepestVisibleWindowForLocation(
+                              &root, EventSource::MOUSE, gfx::Point(35, 35))
+                              .window);
+
   // Verify extended hit test with transform is picked up.
   root.set_extended_hit_test_regions_for_children(gfx::Insets(-2, -2, -2, -2),
                                                   gfx::Insets(-2, -2, -2, -2));
@@ -220,5 +232,31 @@
                          .window);
 }
 
+TEST(WindowFinderTest, FindDeepestVisibleWindowWithTransformOnParent) {
+  TestServerWindowDelegate window_delegate;
+  ServerWindow root(&window_delegate, WindowId(1, 2));
+  root.set_event_targeting_policy(
+      mojom::EventTargetingPolicy::DESCENDANTS_ONLY);
+  root.SetVisible(true);
+  root.SetBounds(gfx::Rect(0, 0, 100, 100), base::nullopt);
+  ServerWindow child(&window_delegate, WindowId(1, 3));
+  root.Add(&child);
+  child.SetVisible(true);
+  child.SetBounds(gfx::Rect(10, 10, 10, 10), base::nullopt);
+  // Make the root child, but the transform is set on the parent. This mirrors
+  // how WindowManagerState and EventDispatcher work together.
+  window_delegate.set_root_window(&child);
+  gfx::Transform transform;
+  transform.Scale(SkIntToMScalar(2), SkIntToMScalar(2));
+  root.SetTransform(transform);
+
+  EXPECT_EQ(&child, FindDeepestVisibleWindowForLocation(
+                        &root, EventSource::MOUSE, gfx::Point(25, 25))
+                        .window);
+  EXPECT_EQ(nullptr, FindDeepestVisibleWindowForLocation(
+                         &root, EventSource::MOUSE, gfx::Point(52, 52))
+                         .window);
+}
+
 }  // namespace ws
 }  // namespace ui
diff --git a/services/ui/ws/window_manager_access_policy.cc b/services/ui/ws/window_manager_access_policy.cc
index 826c297b..400d1f8 100644
--- a/services/ui/ws/window_manager_access_policy.cc
+++ b/services/ui/ws/window_manager_access_policy.cc
@@ -83,7 +83,7 @@
   if (WasCreatedByThisClient(window))
     return true;
   // The WindowManager can change the visibility of the WindowManager root.
-  const ServerWindow* root = window->GetRoot();
+  const ServerWindow* root = window->GetRootForDrawn();
   return root && window->parent() == root;
 }
 
diff --git a/services/ui/ws/window_manager_state.cc b/services/ui/ws/window_manager_state.cc
index 3988789..393c9744 100644
--- a/services/ui/ws/window_manager_state.cc
+++ b/services/ui/ws/window_manager_state.cc
@@ -806,6 +806,18 @@
   return target_display_root->GetClientVisibleRoot();
 }
 
+ServerWindow* WindowManagerState::GetRootWindowForEventDispatch(
+    ServerWindow* window) {
+  for (auto& display_root_ptr : window_manager_display_roots_) {
+    ServerWindow* client_visible_root =
+        display_root_ptr->GetClientVisibleRoot();
+    if (client_visible_root->Contains(window))
+      return client_visible_root;
+  }
+  NOTREACHED();
+  return nullptr;
+}
+
 void WindowManagerState::OnEventTargetNotFound(const ui::Event& event,
                                                int64_t display_id) {
   window_server()->SendToPointerWatchers(event, user_id(), nullptr, /* window */
diff --git a/services/ui/ws/window_manager_state.h b/services/ui/ws/window_manager_state.h
index 1e15d0014..2b1e762 100644
--- a/services/ui/ws/window_manager_state.h
+++ b/services/ui/ws/window_manager_state.h
@@ -288,6 +288,7 @@
                                           bool in_nonclient_area) override;
   ServerWindow* GetRootWindowContaining(gfx::Point* location_in_display,
                                         int64_t* display_id) override;
+  ServerWindow* GetRootWindowForEventDispatch(ServerWindow* window) override;
   void OnEventTargetNotFound(const Event& event, int64_t display_id) override;
   ServerWindow* GetFallbackTargetForEventBlockedByModal(
       ServerWindow* window) override;
diff --git a/services/ui/ws/window_server.cc b/services/ui/ws/window_server.cc
index 62f9701..4e5ec19 100644
--- a/services/ui/ws/window_server.cc
+++ b/services/ui/ws/window_server.cc
@@ -731,7 +731,7 @@
       std::move(frame_sink_manager));
 }
 
-ServerWindow* WindowServer::GetRootWindow(const ServerWindow* window) {
+ServerWindow* WindowServer::GetRootWindowForDrawn(const ServerWindow* window) {
   Display* display = display_manager_->GetDisplayContaining(window);
   return display ? display->root_window() : nullptr;
 }
diff --git a/services/ui/ws/window_server.h b/services/ui/ws/window_server.h
index 3b1a771..7553519c 100644
--- a/services/ui/ws/window_server.h
+++ b/services/ui/ws/window_server.h
@@ -313,7 +313,7 @@
   void CreateFrameSinkManager();
 
   // Overridden from ServerWindowDelegate:
-  ServerWindow* GetRootWindow(const ServerWindow* window) override;
+  ServerWindow* GetRootWindowForDrawn(const ServerWindow* window) override;
 
   // Overridden from ServerWindowObserver:
   void OnWindowDestroyed(ServerWindow* window) override;
diff --git a/services/viz/public/cpp/compositing/quads_struct_traits.cc b/services/viz/public/cpp/compositing/quads_struct_traits.cc
index d3d13d57..87737b2 100644
--- a/services/viz/public/cpp/compositing/quads_struct_traits.cc
+++ b/services/viz/public/cpp/compositing/quads_struct_traits.cc
@@ -162,7 +162,7 @@
     return false;
   }
   quad->background_color = data.background_color();
-  CArray<float> vertex_opacity_array(quad->vertex_opacity);
+  base::span<float> vertex_opacity_array(quad->vertex_opacity);
   if (!data.ReadVertexOpacity(&vertex_opacity_array))
     return false;
 
diff --git a/services/viz/public/cpp/compositing/quads_struct_traits.h b/services/viz/public/cpp/compositing/quads_struct_traits.h
index 450b7385..dd84f82 100644
--- a/services/viz/public/cpp/compositing/quads_struct_traits.h
+++ b/services/viz/public/cpp/compositing/quads_struct_traits.h
@@ -5,6 +5,7 @@
 #ifndef SERVICES_VIZ_PUBLIC_CPP_COMPOSITING_QUADS_STRUCT_TRAITS_H_
 #define SERVICES_VIZ_PUBLIC_CPP_COMPOSITING_QUADS_STRUCT_TRAITS_H_
 
+#include "base/containers/span.h"
 #include "base/logging.h"
 #include "cc/ipc/filter_operation_struct_traits.h"
 #include "cc/ipc/filter_operations_struct_traits.h"
@@ -281,7 +282,7 @@
     return quad->background_color;
   }
 
-  static ConstCArray<float> vertex_opacity(const cc::DrawQuad& input) {
+  static base::span<const float> vertex_opacity(const cc::DrawQuad& input) {
     const cc::TextureDrawQuad* quad = cc::TextureDrawQuad::MaterialCast(&input);
     return quad->vertex_opacity;
   }
diff --git a/skia/public/interfaces/bitmap_skbitmap_struct_traits.h b/skia/public/interfaces/bitmap_skbitmap_struct_traits.h
index 7e45bb2..8113a1ee 100644
--- a/skia/public/interfaces/bitmap_skbitmap_struct_traits.h
+++ b/skia/public/interfaces/bitmap_skbitmap_struct_traits.h
@@ -5,6 +5,7 @@
 #ifndef SKIA_PUBLIC_INTERFACES_BITMAP_SKBITMAP_STRUCT_TRAITS_H_
 #define SKIA_PUBLIC_INTERFACES_BITMAP_SKBITMAP_STRUCT_TRAITS_H_
 
+#include "base/containers/span.h"
 #include "mojo/public/cpp/bindings/array_traits.h"
 #include "skia/public/interfaces/bitmap.mojom.h"
 #include "third_party/skia/include/core/SkBitmap.h"
@@ -12,7 +13,7 @@
 namespace mojo {
 
 // A buffer used to read pixel data directly from BitmapDataView to SkBitmap.
-using BitmapBuffer = CArray<uint8_t>;
+using BitmapBuffer = base::span<uint8_t>;
 
 // Struct traits to use SkBitmap for skia::mojom::Bitmap in Chrome C++ code.
 template <>
diff --git a/testing/buildbot/chromium.json b/testing/buildbot/chromium.json
index 951cb6b..0953b7f 100644
--- a/testing/buildbot/chromium.json
+++ b/testing/buildbot/chromium.json
@@ -37,5 +37,16 @@
         "script": "checkbins.py"
       }
     ]
+  },
+  "Win x64": {
+    "additional_compile_targets": [
+      "all"
+    ],
+    "scripts": [
+      {
+        "name": "checkbins",
+        "script": "checkbins.py"
+      }
+    ]
   }
 }
diff --git a/testing/buildbot/filters/fuchsia.base_unittests.filter b/testing/buildbot/filters/fuchsia.base_unittests.filter
index 7cd0a22d..768ba29 100644
--- a/testing/buildbot/filters/fuchsia.base_unittests.filter
+++ b/testing/buildbot/filters/fuchsia.base_unittests.filter
@@ -20,7 +20,6 @@
 -FileUtilTest.FileToFILE
 -NativeLibraryTest.LoadLibrary
 -NativeLibraryTest.LoadLibraryPreferOwnSymbols
--PathServiceTest.Get
 -PlatformThreadTest.ThreadPriorityCurrentThread
 -ProcessMemoryDumpTest.CountResidentBytes
 -ProcessMemoryDumpTest.CountResidentBytesInSharedMemory
diff --git a/testing/buildbot/filters/fuchsia.content_unittests.filter b/testing/buildbot/filters/fuchsia.content_unittests.filter
index 97729c73..47a9e15 100644
--- a/testing/buildbot/filters/fuchsia.content_unittests.filter
+++ b/testing/buildbot/filters/fuchsia.content_unittests.filter
@@ -1,7 +1,5 @@
 # Being ported, https://crbug.com/754861.
 
--AsyncResourceHandlerTest.OneChunkLengths
--AsyncResourceHandlerTest.TwoChunksLengths
 -AudioRendererMixerManagerTest.MixerParamsLatencyRtc
 -AudioRendererSinkCacheTest.SmokeTest
 -BackgroundSyncManagerTest.DeleteAndStartOverServiceWorkerContext
@@ -30,6 +28,7 @@
 -GpuDataManagerImplPrivateTest.GpuSideBlacklistingWebGL
 -GpuDataManagerImplPrivateTest.GpuSideExceptions
 -GpuDataManagerImplPrivateTest.UpdateActiveGpu
+-LegacyInputRouterImplTest.AckedTouchEventState
 -MediaStreamManagerTest.MakeAndCancelMultipleRequests
 -MediaStreamManagerTest.MakeMediaAccessRequest
 -MediaStreamManagerTest.MakeMultipleRequests
@@ -60,13 +59,6 @@
 -PageStateSerializationTest.BackwardsCompat_v22
 -PageStateSerializationTest.BackwardsCompat_v23
 -PageStateSerializationTest.BackwardsCompat_v24
--PresentationConnectionProxyTest.TestControllerConnectionCallsClose
--PresentationConnectionProxyTest.TestSendArrayBuffer
--PresentationConnectionProxyTest.TestSendString
--QuotaPolicyCookieStoreTest.ForceKeepSessionState
--QuotaPolicyCookieStoreTest.TestDestroyOnBackgroundThread
--QuotaPolicyCookieStoreTest.TestPersistence
--QuotaPolicyCookieStoreTest.TestPolicy
 -RenderWidgetHostViewAuraCopyRequestTest.DedupeFrameSubscriberRequests
 -RenderWidgetHostViewAuraCopyRequestTest.DestroyedAfterCopyRequest
 -RenderWidgetHostViewAuraCopyRequestTest.PresentTime
@@ -89,13 +81,6 @@
 -URLLoaderImplTest.Empty
 -URLLoaderImplTest.SSLSentOnlyWhenRequested
 -URLLoaderImplTest.SniffMimeType
--V8ValueConverterImplTest.DetectCycles
--V8ValueConverterImplTest.MaxRecursionDepth
--V8ValueConverterImplTest.NegativeZero
--V8ValueConverterImplTest.RecursiveObjects
--V8ValueConverterImplTest.StrategyBypass
--V8ValueConverterImplTest.StripNullFromObjects
--V8ValueConverterImplTest.WeirdProperties
 -WebContentsAudioInputStreamTest.MirroringOneStreamAfterTargetChange/0
 
 
@@ -109,6 +94,7 @@
 -AppCacheStorageImplTest.Reinitialize1
 -AppCacheStorageImplTest.Reinitialize2
 -AppCacheStorageImplTest.Reinitialize3
+-AppCacheUpdateJobTest.AlreadyChecking/1
 -ChromeAppCacheServiceTest.KeepOnDestruction
 -ChromeAppCacheServiceTest.SaveSessionState
 -DOMStorageAreaTest.DeleteOrigin
@@ -135,3 +121,7 @@
 -IndexedDBFactoryTest.DataFormatVersion
 -IndexedDBFactoryTest.MemoryBackingStoreLifetime
 -LocalStorageContextMojoTest.Migration
+-QuotaPolicyCookieStoreTest.ForceKeepSessionState
+-QuotaPolicyCookieStoreTest.TestDestroyOnBackgroundThread
+-QuotaPolicyCookieStoreTest.TestPersistence
+-QuotaPolicyCookieStoreTest.TestPolicy
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index c1632fa..f18e0ee 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -3520,6 +3520,26 @@
             ]
         }
     ],
+    "WheelScrollLatchingAndAsyncWheelEvents": [
+        {
+            "platforms": [
+                "android",
+                "chromeos",
+                "linux",
+                "mac",
+                "win"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "AsyncWheelEvents",
+                        "TouchpadAndWheelScrollLatching"
+                    ]
+                }
+            ]
+        }
+    ],
     "XGEOVisibleNetworks": [
         {
             "platforms": [
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
index 2b95ecb..9c95114 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
@@ -4371,7 +4371,6 @@
 crbug.com/591099 fast/block/positioning/inline-block-relposition.html [ Failure Pass ]
 crbug.com/591099 fast/block/positioning/leftmargin-topmargin.html [ Failure ]
 crbug.com/591099 fast/block/positioning/move-with-auto-width.html [ Failure ]
-crbug.com/591099 fast/block/positioning/offsetLeft-offsetTop-multicolumn.html [ Failure ]
 crbug.com/591099 fast/block/positioning/offsetLeft-relative-iframe.html [ Crash Failure ]
 crbug.com/591099 fast/block/positioning/offsetLeft-relative-td.html [ Crash Failure ]
 crbug.com/591099 fast/block/positioning/padding-percent.html [ Crash Failure ]
@@ -9504,7 +9503,6 @@
 crbug.com/757767 fast/multicol/float-with-margin-moved-by-child-line-and-unbreakable.html [ Timeout ]
 crbug.com/757767 fast/multicol/float-with-margin-moved-by-child-line.html [ Timeout ]
 crbug.com/757767 fast/multicol/float-with-margin-moved-unbreakable.html [ Timeout Failure ]
-crbug.com/591099 fast/multicol/focus-outline.html [ Failure ]
 crbug.com/757767 fast/multicol/forced-break-after-block-with-spanner.html [ Timeout ]
 crbug.com/757767 fast/multicol/forced-break-after-empty-block-after-spanner.html [ Timeout ]
 crbug.com/757767 fast/multicol/forced-break-after-last-block-before-spanner.html [ Timeout ]
@@ -9629,6 +9627,7 @@
 crbug.com/591099 fast/multicol/newmulticol/unresolvable-percent-max-height-2.html [ Failure ]
 crbug.com/591099 fast/multicol/newmulticol/unresolvable-percent-max-height.html [ Failure ]
 crbug.com/591099 fast/multicol/null-lastFloat-in-removeFloatingObjectsBelow.html [ Failure ]
+crbug.com/591099 fast/multicol/one-column-with-break.html [ Failure ]
 crbug.com/591099 fast/multicol/orphaned-line-at-exact-top-of-column.html [ Failure ]
 crbug.com/591099 fast/multicol/orphans-relayout.html [ Failure ]
 crbug.com/591099 fast/multicol/out-of-flow/abspos-auto-left-right.html [ Failure ]
@@ -9639,12 +9638,15 @@
 crbug.com/591099 fast/multicol/out-of-flow/abspos-auto-position.html [ Failure ]
 crbug.com/591099 fast/multicol/out-of-flow/abspos-auto-top-bottom.html [ Failure ]
 crbug.com/757767 fast/multicol/out-of-flow/nested-multicol.html [ Crash ]
+crbug.com/591099 fast/multicol/out-of-flow/nested-with-abspos-inside-relpos.html [ Failure ]
 crbug.com/591099 fast/multicol/out-of-flow/offset-properties.html [ Failure ]
 crbug.com/757767 fast/multicol/outlines-at-column-boundaries.html [ Crash ]
 crbug.com/591099 fast/multicol/overflow-across-columns.html [ Failure ]
+crbug.com/591099 fast/multicol/overflow-content.html [ Failure ]
 crbug.com/591099 fast/multicol/overflow-into-columngap.html [ Failure ]
 crbug.com/757767 fast/multicol/overflow-unsplittable.html [ Timeout ]
 crbug.com/591099 fast/multicol/overflowing-columns-large-gaps.html [ Failure ]
+crbug.com/591099 fast/multicol/pageLogicalOffset-vertical.html [ Failure ]
 crbug.com/757767 fast/multicol/paged-becomes-multicol-auto-height.html [ Crash ]
 crbug.com/757767 fast/multicol/paged-becomes-multicol-fixed-height.html [ Crash ]
 crbug.com/591099 fast/multicol/paged-becomes-multicol-with-spanner.html [ Failure ]
@@ -9663,8 +9665,6 @@
 crbug.com/591099 fast/multicol/remove-all-children.html [ Failure ]
 crbug.com/591099 fast/multicol/remove-style-multicol-with-nested-layers.html [ Failure ]
 crbug.com/757767 fast/multicol/renderer-positioned-assert-crash.html [ Crash ]
-crbug.com/591099 fast/multicol/resize-container-with-column-rule-child.html [ Failure ]
-crbug.com/591099 fast/multicol/resize-with-column-rule.html [ Failure ]
 crbug.com/591099 fast/multicol/ruby-crash.html [ Failure ]
 crbug.com/757767 fast/multicol/rule-in-nested-with-too-tall-line.html [ Crash ]
 crbug.com/591099 fast/multicol/rule-thicker-than-gap.html [ Failure ]
@@ -9849,7 +9849,6 @@
 crbug.com/757767 fast/multicol/vertical-rl/offset-top-and-left-at-boundaries-nested.html [ Timeout ]
 crbug.com/757767 fast/multicol/vertical-rl/offset-top-and-left-at-boundaries.html [ Timeout ]
 crbug.com/757767 fast/multicol/vertical-rl/offset-top-and-left-nested.html [ Timeout ]
-crbug.com/591099 fast/multicol/vertical-rl/rule-style.html [ Failure ]
 crbug.com/591099 fast/multicol/vertical-rl/rules-with-border-before.html [ Failure ]
 crbug.com/591099 fast/multicol/vertical-rl/unsplittable-inline-block.html [ Failure ]
 crbug.com/757767 fast/multicol/very-tall-block-crash.html [ Timeout Failure ]
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService b/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService
index 63d55ef..72eaf6e 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService
@@ -414,6 +414,7 @@
 crbug.com/721408 http/tests/inspector-protocol/network/request-interception-mock302.js [ Failure ]
 crbug.com/721408 http/tests/inspector-protocol/network/request-interception-mock404.js [ Timeout ]
 crbug.com/721408 http/tests/inspector-protocol/network/request-interception-modify-get-to-post.js [ Failure ]
+crbug.com/721408 http/tests/inspector-protocol/network/request-interception-patterns.js [ Failure ]
 crbug.com/721408 http/tests/inspector-protocol/network/xhr-interception-auth-fail.js [ Failure ]
 crbug.com/721408 http/tests/inspector-protocol/network/xhr-interception.js [ Timeout ]
 crbug.com/721408 http/tests/inspector-protocol/page/frameScheduledNavigation.js [ Crash ]
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2 b/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
index 406537a..cf7db01 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
@@ -744,6 +744,7 @@
 Bug(none) fast/multicol/out-of-flow/abspos-auto-position-small-on-line-at-boundary.html [ Failure ]
 Bug(none) fast/multicol/out-of-flow/abspos-auto-position.html [ Failure ]
 Bug(none) fast/multicol/out-of-flow/nested-multicol.html [ Failure ]
+Bug(none) fast/multicol/out-of-flow/nested-with-abspos-inside-relpos.html [ Failure ]
 Bug(none) fast/multicol/outlines-at-column-boundaries.html [ Failure ]
 Bug(none) fast/multicol/overflow-across-columns.html [ Failure ]
 Bug(none) fast/multicol/overflow-into-columngap.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index eb8fa98..8f553022 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -92,11 +92,8 @@
 # Fails consistently on WebKit Mac10.10, WebKit Mac10.11 (retina) and mac10.10_blink_rel tryserver, but not on other Mac bots.
 crbug.com/614910 [ Mac ] virtual/gpu-rasterization/images/pixel-crack-image-background-webkit-transform-scale.html [ Pass Failure ]
 
-crbug.com/671048 virtual/color_space/fast/canvas/color-space/display_linear-rgb.html [ Pass Failure Crash ]
-
 crbug.com/693568 virtual/gpu/fast/canvas/canvas-imageSmoothingQuality.html [ Failure ]
 
-
 # Looks like a filuare to get a paint on time. Test could be changed to use runAfterLayoutAndPaint
 # instead of a timeout, which may fix things.
 crbug.com/713049 images/color-profile-reflection.html [ Failure Pass ]
@@ -502,7 +499,7 @@
 crbug.com/635619 virtual/layout_ng/fast/block/float/br-with-clear-2.html [ Failure ]
 crbug.com/635619 virtual/layout_ng/fast/block/float/centered-float-avoidance-complexity.html [ Failure ]
 crbug.com/635619 virtual/layout_ng/fast/block/float/checkbox-and-radio-avoid-floats.html [ Failure ]
-crbug.com/635619 virtual/layout_ng/fast/block/float/clear-intruding-floats-when-moving-to-inline-parent-3.html [ Failure Crash ]
+crbug.com/635619 virtual/layout_ng/fast/block/float/clear-intruding-floats-when-moving-to-inline-parent-3.html [ Failure Crash Pass ]
 crbug.com/635619 virtual/layout_ng/fast/block/float/crash-on-absolute-positioning.html [ Failure ]
 crbug.com/635619 virtual/layout_ng/fast/block/float/crash-replaced-display-block.html [ Failure ]
 crbug.com/635619 virtual/layout_ng/fast/block/float/element-clears-float-without-clearance.html [ Failure ]
@@ -513,15 +510,15 @@
 crbug.com/635619 virtual/layout_ng/fast/block/float/float-in-float-hit-testing.html [ Failure ]
 crbug.com/635619 virtual/layout_ng/fast/block/float/float-in-float-painting.html [ Failure ]
 crbug.com/635619 virtual/layout_ng/fast/block/float/float-inserted-into-clean-line.html [ Failure ]
-crbug.com/635619 virtual/layout_ng/fast/block/float/float-list-changed-before-layout-crash.html [ Crash ]
+crbug.com/635619 virtual/layout_ng/fast/block/float/float-list-changed-before-layout-crash.html [ Crash Pass ]
 crbug.com/635619 virtual/layout_ng/fast/block/float/float-not-removed-from-first-letter.html [ Crash Failure ]
 crbug.com/635619 virtual/layout_ng/fast/block/float/float-not-removed-from-next-sibling-crash.html [ Failure ]
-crbug.com/635619 virtual/layout_ng/fast/block/float/float-not-removed-from-next-sibling4.html [ Crash ]
+crbug.com/635619 virtual/layout_ng/fast/block/float/float-not-removed-from-next-sibling4.html [ Crash Pass ]
 crbug.com/635619 virtual/layout_ng/fast/block/float/float-not-removed-from-next-sibling5.html [ Failure ]
 crbug.com/635619 virtual/layout_ng/fast/block/float/float-on-empty-line.html [ Failure ]
 crbug.com/635619 virtual/layout_ng/fast/block/float/float-on-zero-height-line.html [ Failure ]
 crbug.com/635619 virtual/layout_ng/fast/block/float/float-overflow-hidden-containing-block-width.html [ Failure ]
-crbug.com/635619 virtual/layout_ng/fast/block/float/float-reparent-during-detach-crash.html [ Crash ]
+crbug.com/635619 virtual/layout_ng/fast/block/float/float-reparent-during-detach-crash.html [ Crash Pass ]
 crbug.com/635619 virtual/layout_ng/fast/block/float/floats-and-text-indent-rl.html [ Failure ]
 crbug.com/635619 virtual/layout_ng/fast/block/float/floats-and-text-indent.html [ Failure ]
 crbug.com/757767 virtual/layout_ng/fast/block/float/floats-do-not-overhang-from-block-formatting-context.html [ Crash Timeout Failure ]
diff --git a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
index d63dabac9..3788e47 100644
--- a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
+++ b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
@@ -273540,7 +273540,7 @@
    "support"
   ],
   "payment-request/payment-request-ctor-currency-code-checks.https.html": [
-   "7efdfa3457fff90fc3f66054505dcf7a7ff4645b",
+   "29a226caa4f91f0b92f4ed8bd99d40e241884107",
    "testharness"
   ],
   "payment-request/payment-request-ctor-pmi-handling.https-expected.txt": [
diff --git a/third_party/WebKit/LayoutTests/external/wpt/common/performance-timeline-utils.js b/third_party/WebKit/LayoutTests/external/wpt/common/performance-timeline-utils.js
index 268bb11..3beb28e 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/common/performance-timeline-utils.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/common/performance-timeline-utils.js
@@ -31,10 +31,12 @@
 function test_entries(actualEntries, expectedEntries) {
   test_equals(actualEntries.length, expectedEntries.length)
   expectedEntries.forEach(function (expectedEntry) {
-    test_true(!!actualEntries.find(function (actualEntry) {
+    var foundEntry = actualEntries.find(function (actualEntry) {
       return typeof Object.keys(expectedEntry).find(function (key) {
             return actualEntry[key] !== expectedEntry[key]
           }) === 'undefined'
-    }))
+    })
+    test_true(!!foundEntry)
+    assert_object_equals(foundEntry.toJSON(), expectedEntry)
   })
 }
diff --git a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/README.md b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/README.md
index 695a5326..775a354 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/feature-policy/README.md
+++ b/third_party/WebKit/LayoutTests/external/wpt/feature-policy/README.md
@@ -1,9 +1,7 @@
 # Feature Policy Guide
 ## How to Test a New Feature with Feature Policy
 
-This directory contains a framework to test features with feature policy. Please
-refer to `/content/common/feature_policy/README.md` for more details on feature
-policy.
+This directory contains a framework to test features with feature policy.
 
 When adding a new feature to feature policy, the following cases should be tested:
 * feature enabled by header policy [HTTP tests]
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/browsers/history/the-history-interface/history_properties_only_fully_active.html b/third_party/WebKit/LayoutTests/external/wpt/html/browsers/history/the-history-interface/history_properties_only_fully_active.html
new file mode 100644
index 0000000..0404a6b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/html/browsers/history/the-history-interface/history_properties_only_fully_active.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<title>history properties should throw SecurityError when not in a fully active Document</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+  <iframe id="child"></iframe>
+</body>
+<script>
+  test(function(t) {
+    var ifr = document.getElementById("child");
+    var cached_history = ifr.contentWindow.history;
+    ifr.remove();
+    assert_throws("SecurityError", function() { cached_history.length; });
+    assert_throws("SecurityError", function() { cached_history.scrollRestoration; });
+    assert_throws("SecurityError", function() { cached_history.state; });
+    assert_throws("SecurityError", function() { cached_history.go(0); });
+    assert_throws("SecurityError", function() { cached_history.back(); });
+    assert_throws("SecurityError", function() { cached_history.forward(); });
+    assert_throws("SecurityError", function() { cached_history.pushState(1, document.title, "?x=1"); });
+    assert_throws("SecurityError", function() { cached_history.replaceState(2, document.title, "?x=2"); });
+  });
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/payment-request/payment-request-ctor-currency-code-checks.https.html b/third_party/WebKit/LayoutTests/external/wpt/payment-request/payment-request-ctor-currency-code-checks.https.html
index 69a375c1..3a5e0e7 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/payment-request/payment-request-ctor-currency-code-checks.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/payment-request/payment-request-ctor-currency-code-checks.https.html
@@ -162,7 +162,7 @@
     };
     const displayItem = {
       amount,
-      label: "valid currency",
+      label: "invalid currency",
     };
     const details = {
       total: defaultTotal,
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/blue.png.sub.headers b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/blue.png.sub.headers
index 5f3e5431..23988ed 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/blue.png.sub.headers
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/blue.png.sub.headers
@@ -1 +1 @@
-Server-Timing: metric2=3.4;blue.png
+Server-Timing: metric2=2.1;blue.png
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/green.png.sub.headers b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/green.png.sub.headers
index 1c6d7451..d559754 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/green.png.sub.headers
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/green.png.sub.headers
@@ -1 +1 @@
-Server-Timing: metric3=5.6;green.png
+Server-Timing: metric3=3.1;green.png
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/test_server_timing.html b/third_party/WebKit/LayoutTests/external/wpt/server-timing/test_server_timing.html
index 6a6c8977..fa100301 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/test_server_timing.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/test_server_timing.html
@@ -9,14 +9,18 @@
       setup({explicit_done: true})
 
       window.addEventListener('load', function() {
-        // there should be exactly two server-timing entries, 1 for document, 1 for img#one
+        // there should be exactly three server-timing entries, 2 for document, 1 for img#one
         test_entries(performance.getEntriesByType('navigation')[0].serverTiming, [{
+          duration: 1.1,
+          name: 'metric1',
+          description: 'document',
+        }, {
           duration: 1.2,
           name: 'metric1',
           description: 'document',
         }])
         test_entries(performance.getEntriesByName(document.querySelector('img#one').src)[0].serverTiming, [{
-          duration: 3.4,
+          duration: 2.1,
           name: 'metric2',
           description: 'blue.png',
         }])
@@ -24,7 +28,7 @@
         new PerformanceObserver(function(entryList, observer) {
           // there should be exactly one server-timing entry, 1 for img#two
           test_entries(entryList.getEntriesByName(document.querySelector('img#two').src)[0].serverTiming, [{
-            duration: 5.6,
+            duration: 3.1,
             name: 'metric3',
             description: 'green.png',
           }])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/test_server_timing.html.sub.headers b/third_party/WebKit/LayoutTests/external/wpt/server-timing/test_server_timing.html.sub.headers
index ddff591c..c539669 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/test_server_timing.html.sub.headers
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/test_server_timing.html.sub.headers
@@ -1 +1 @@
-Server-Timing: metric1=1.2;document
+Server-Timing: metric1=1.1;document, metric1=1.2;document
diff --git a/third_party/WebKit/LayoutTests/fast/dom/Window/property-access-on-cached-properties-after-frame-navigated-expected.txt b/third_party/WebKit/LayoutTests/fast/dom/Window/property-access-on-cached-properties-after-frame-navigated-expected.txt
index 5e95577..a3eef918 100644
--- a/third_party/WebKit/LayoutTests/fast/dom/Window/property-access-on-cached-properties-after-frame-navigated-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/dom/Window/property-access-on-cached-properties-after-frame-navigated-expected.txt
@@ -1,4 +1,4 @@
-CONSOLE WARNING: line 118: 'window.webkitStorageInfo' is deprecated. Please use 'navigator.webkitTemporaryStorage' or 'navigator.webkitPersistentStorage' instead.
+CONSOLE WARNING: line 122: 'window.webkitStorageInfo' is deprecated. Please use 'navigator.webkitTemporaryStorage' or 'navigator.webkitPersistentStorage' instead.
 Tests access of cached DOMWindow properties after the associated frame is navigated. Test should not crash and properties should be set to sane defaults.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
@@ -13,9 +13,6 @@
 PASS window.cached_applicationCache.onprogress is null
 PASS window.cached_applicationCache.onupdateready is null
 PASS window.cached_applicationCache.status is 0
-PASS window.cached_history.length is 0
-PASS window.cached_history.scrollRestoration is 'auto'
-PASS window.cached_history.state is null
 FAIL window.cached_location.hash should be  (of type string). Was undefined (of type undefined).
 FAIL window.cached_location.host should be  (of type string). Was undefined (of type undefined).
 FAIL window.cached_location.hostname should be  (of type string). Was undefined (of type undefined).
diff --git a/third_party/WebKit/LayoutTests/fast/dom/Window/property-access-on-cached-properties-after-frame-removed-and-gced-expected.txt b/third_party/WebKit/LayoutTests/fast/dom/Window/property-access-on-cached-properties-after-frame-removed-and-gced-expected.txt
index 8f06f92..0c524bba 100644
--- a/third_party/WebKit/LayoutTests/fast/dom/Window/property-access-on-cached-properties-after-frame-removed-and-gced-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/dom/Window/property-access-on-cached-properties-after-frame-removed-and-gced-expected.txt
@@ -1,4 +1,4 @@
-CONSOLE WARNING: line 118: 'window.webkitStorageInfo' is deprecated. Please use 'navigator.webkitTemporaryStorage' or 'navigator.webkitPersistentStorage' instead.
+CONSOLE WARNING: line 122: 'window.webkitStorageInfo' is deprecated. Please use 'navigator.webkitTemporaryStorage' or 'navigator.webkitPersistentStorage' instead.
 Tests access of cached DOMWindow properties after the associated frame is removed from a web page and garbage collected. Test should not crash and properties should be set to sane defaults.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
@@ -13,9 +13,6 @@
 PASS window.cached_applicationCache.onprogress is null
 PASS window.cached_applicationCache.onupdateready is null
 PASS window.cached_applicationCache.status is 0
-PASS window.cached_history.length is 0
-PASS window.cached_history.scrollRestoration is 'auto'
-PASS window.cached_history.state is null
 FAIL window.cached_location.hash should be  (of type string). Was undefined (of type undefined).
 FAIL window.cached_location.host should be  (of type string). Was undefined (of type undefined).
 FAIL window.cached_location.hostname should be  (of type string). Was undefined (of type undefined).
diff --git a/third_party/WebKit/LayoutTests/fast/dom/Window/property-access-on-cached-properties-after-frame-removed-expected.txt b/third_party/WebKit/LayoutTests/fast/dom/Window/property-access-on-cached-properties-after-frame-removed-expected.txt
index 9194474..ff49816 100644
--- a/third_party/WebKit/LayoutTests/fast/dom/Window/property-access-on-cached-properties-after-frame-removed-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/dom/Window/property-access-on-cached-properties-after-frame-removed-expected.txt
@@ -1,4 +1,4 @@
-CONSOLE WARNING: line 118: 'window.webkitStorageInfo' is deprecated. Please use 'navigator.webkitTemporaryStorage' or 'navigator.webkitPersistentStorage' instead.
+CONSOLE WARNING: line 122: 'window.webkitStorageInfo' is deprecated. Please use 'navigator.webkitTemporaryStorage' or 'navigator.webkitPersistentStorage' instead.
 Tests access of cached DOMWindow properties after the associated frame is no longer in a web page. Test should not crash and properties should be set to sane defaults.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
@@ -13,9 +13,6 @@
 PASS window.cached_applicationCache.onprogress is null
 PASS window.cached_applicationCache.onupdateready is null
 PASS window.cached_applicationCache.status is 0
-PASS window.cached_history.length is 0
-PASS window.cached_history.scrollRestoration is 'auto'
-PASS window.cached_history.state is null
 FAIL window.cached_location.hash should be  (of type string). Was undefined (of type undefined).
 FAIL window.cached_location.host should be  (of type string). Was undefined (of type undefined).
 FAIL window.cached_location.hostname should be  (of type string). Was undefined (of type undefined).
diff --git a/third_party/WebKit/LayoutTests/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt b/third_party/WebKit/LayoutTests/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt
index cd4c9f86..f6ae968 100644
--- a/third_party/WebKit/LayoutTests/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt
@@ -1,4 +1,4 @@
-CONSOLE WARNING: line 118: 'window.webkitStorageInfo' is deprecated. Please use 'navigator.webkitTemporaryStorage' or 'navigator.webkitPersistentStorage' instead.
+CONSOLE WARNING: line 122: 'window.webkitStorageInfo' is deprecated. Please use 'navigator.webkitTemporaryStorage' or 'navigator.webkitPersistentStorage' instead.
 Tests property access on a cached DOMWindow after the associated frame is navigated. Test should not crash and properties read from the cached DOMWindow should be identical to properties through the 'current' DOMWindow.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
@@ -17,9 +17,6 @@
 PASS oldChildWindow.defaultStatus is newChildWindow.defaultStatus
 PASS oldChildWindow.defaultstatus is newChildWindow.defaultstatus
 PASS oldChildWindow.devicePixelRatio is newChildWindow.devicePixelRatio
-PASS oldChildWindow.history.length is newChildWindow.history.length
-PASS oldChildWindow.history.scrollRestoration is newChildWindow.history.scrollRestoration
-PASS oldChildWindow.history.state is newChildWindow.history.state
 PASS oldChildWindow.innerHeight is newChildWindow.innerHeight
 PASS oldChildWindow.innerWidth is newChildWindow.innerWidth
 PASS oldChildWindow.isSecureContext is newChildWindow.isSecureContext
diff --git a/third_party/WebKit/LayoutTests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-and-gced-expected.txt b/third_party/WebKit/LayoutTests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-and-gced-expected.txt
index 07c86248d..0ddd635c 100644
--- a/third_party/WebKit/LayoutTests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-and-gced-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-and-gced-expected.txt
@@ -1,4 +1,4 @@
-CONSOLE WARNING: line 118: 'window.webkitStorageInfo' is deprecated. Please use 'navigator.webkitTemporaryStorage' or 'navigator.webkitPersistentStorage' instead.
+CONSOLE WARNING: line 122: 'window.webkitStorageInfo' is deprecated. Please use 'navigator.webkitTemporaryStorage' or 'navigator.webkitPersistentStorage' instead.
 Tests property access on a cached DOMWindow after the associated frame is removed from a web page and garbage collected. Test should not crash and properties should be set to sane defaults.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
@@ -8,9 +8,6 @@
 PASS childWindow.defaultStatus is ''
 PASS childWindow.defaultstatus is ''
 PASS childWindow.devicePixelRatio is 0
-PASS childWindow.history.length is 0
-PASS childWindow.history.scrollRestoration is 'auto'
-PASS childWindow.history.state is null
 PASS childWindow.innerHeight is 0
 PASS childWindow.innerWidth is 0
 PASS childWindow.isSecureContext is false
diff --git a/third_party/WebKit/LayoutTests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-expected.txt b/third_party/WebKit/LayoutTests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-expected.txt
index dd98bda..ca90a90 100644
--- a/third_party/WebKit/LayoutTests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-expected.txt
@@ -1,4 +1,4 @@
-CONSOLE WARNING: line 118: 'window.webkitStorageInfo' is deprecated. Please use 'navigator.webkitTemporaryStorage' or 'navigator.webkitPersistentStorage' instead.
+CONSOLE WARNING: line 122: 'window.webkitStorageInfo' is deprecated. Please use 'navigator.webkitTemporaryStorage' or 'navigator.webkitPersistentStorage' instead.
 Tests property access on a cached DOMWindow after the associated frame is no longer in a web page. Test should not crash and properties should be set to sane defaults.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
@@ -8,9 +8,6 @@
 PASS childWindow.defaultStatus is ''
 PASS childWindow.defaultstatus is ''
 PASS childWindow.devicePixelRatio is 0
-PASS childWindow.history.length is 0
-PASS childWindow.history.scrollRestoration is 'auto'
-PASS childWindow.history.state is null
 PASS childWindow.innerHeight is 0
 PASS childWindow.innerWidth is 0
 PASS childWindow.isSecureContext is false
diff --git a/third_party/WebKit/LayoutTests/fast/dom/Window/resources/window-property-collector.js b/third_party/WebKit/LayoutTests/fast/dom/Window/resources/window-property-collector.js
index 4fb3c34..cf80433 100644
--- a/third_party/WebKit/LayoutTests/fast/dom/Window/resources/window-property-collector.js
+++ b/third_party/WebKit/LayoutTests/fast/dom/Window/resources/window-property-collector.js
@@ -32,6 +32,10 @@
         return;
     }
 
+    // Skip history, which throws SecurityErrors and is covered by web-platform-tests.
+    if (path[0] == 'history')
+        return;
+
     // FIXME: Skip MemoryInfo for now, since it's not implemented as a DOMWindowProperty, and has
     // no way of knowing when it's detached. Eventually this should have the same behavior.
     if (path.length >= 2 && (path[0] == 'console' || path[0] == 'performance') && path[1] == 'memory')
diff --git a/third_party/WebKit/LayoutTests/fast/encoding/supported-encodings-expected.txt b/third_party/WebKit/LayoutTests/fast/encoding/supported-encodings-expected.txt
index d810d00..6d6f1c77 100644
--- a/third_party/WebKit/LayoutTests/fast/encoding/supported-encodings-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/encoding/supported-encodings-expected.txt
@@ -1,263 +1,232 @@
 This is a testharness.js-based test.
-PASS Supported label: iso_8859-8 
-PASS Supported label: cp1254 
-PASS Supported label: iso-ir-126 
-FAIL Supported label: windows-1254-html assert_true: windows-1254-html should only be supported if it is specified expected true got false
-PASS Supported label: x-user-defined 
-PASS Supported label: iso-ir-157 
-FAIL Supported label: iso-8859-13-html assert_true: iso-8859-13-html should only be supported if it is specified expected true got false
-PASS Supported label: csisolatinarabic 
-PASS Supported label: csiso58gb231280 
-PASS Supported label: iso88595 
-PASS Supported label: latin2 
-PASS Supported label: csisolatinhebrew 
-PASS Supported label: iso-8859-6 
-PASS Supported label: replacement 
-PASS Supported label: cp1256 
-PASS Supported label: gb_2312-80 
-PASS Supported label: koi 
-FAIL Supported label: unicodefeff assert_true: unicodefeff should only be supported if it is specified expected true got false
-PASS Supported label: latin4 
-PASS Supported label: x-sjis 
-PASS Supported label: iso_8859-9:1989 
-PASS Supported label: x-x-big5 
-PASS Supported label: iso8859-9 
-PASS Supported label: utf-16be 
-PASS Supported label: iso_8859-4 
-PASS Supported label: iso_8859-7:1987 
-PASS Supported label: iso8859-8 
-PASS Supported label: cp1251 
-PASS Supported label: windows-1255 
-PASS Supported label: iso8859-10 
-PASS Supported label: iso8859-15 
-PASS Supported label: iso_8859-9 
-PASS Supported label: x-cp1250 
-PASS Supported label: gb2312 
-PASS Supported label: csisolatin4 
-FAIL Supported label: euc-jp-html assert_true: euc-jp-html should only be supported if it is specified expected true got false
-PASS Supported label: hebrew 
-PASS Supported label: koi8-ru 
-PASS Supported label: iso8859-13 
-PASS Supported label: x-cp1251 
-PASS Supported label: cp1255 
-PASS Supported label: iso885910 
-PASS Supported label: windows-1250 
-PASS Supported label: iso88594 
-PASS Supported label: ks_c_5601-1987 
-PASS Supported label: asmo-708 
-PASS Supported label: x-gbk 
-PASS Supported label: unicode-1-1-utf-8 
-PASS Supported label: iso8859-7 
-PASS Supported label: x-cp1256 
-PASS Supported label: ecma-114 
-PASS Supported label: cp866 
-PASS Supported label: iso_8859-3 
-PASS Supported label: koi8_r 
-PASS Supported label: windows-31j 
-PASS Supported label: iso_8859-4:1988 
-PASS Supported label: iso88597 
-PASS Supported label: shift-jis 
-PASS Supported label: iso-ir-148 
-PASS Supported label: tis-620 
-FAIL Supported label: windows-1253-html assert_true: windows-1253-html should only be supported if it is specified expected true got false
-PASS Supported label: iso-8859-10 
-FAIL Supported label: iso-10646-ucs-2 assert_true: iso-10646-ucs-2 should only be supported if it is specified expected true got false
-FAIL Supported label: windows-1251-html assert_true: windows-1251-html should only be supported if it is specified expected true got false
-PASS Supported label: iso-2022-cn 
-FAIL Supported label: windows-1257-html assert_true: windows-1257-html should only be supported if it is specified expected true got false
-PASS Supported label: windows-1257 
-PASS Supported label: arabic 
-PASS Supported label: windows-1251 
-PASS Supported label: csisolatincyrillic 
-FAIL Supported label: iso-8859-10-html assert_true: iso-8859-10-html should only be supported if it is specified expected true got false
-PASS Supported label: iso_8859-2:1987 
-PASS Supported label: ks_c_5601-1989 
-PASS Supported label: iso885911 
-FAIL Supported label: iso-8859-8-html assert_true: iso-8859-8-html should only be supported if it is specified expected true got false
-PASS Supported label: iso-2022-kr 
-PASS Supported label: x-mac-ukrainian 
-PASS Supported label: logical 
-PASS Supported label: koi8-r 
-PASS Supported label: windows-1252 
-PASS Supported label: csiso2022jp 
-PASS Supported label: elot_928 
-PASS Supported label: koi8 
-PASS Supported label: windows-1256 
-PASS Supported label: csisolatin1 
-PASS Supported label: windows-874 
-PASS Supported label: iso8859-5 
-FAIL Supported label: iso-8859-5-html assert_true: iso-8859-5-html should only be supported if it is specified expected true got false
-PASS Supported label: iso-ir-110 
-PASS Supported label: ksc5601 
-PASS Supported label: cp1253 
-PASS Supported label: csiso88598e 
-PASS Supported label: iso_8859-2 
-PASS Supported label: iso-8859-11 
-PASS Supported label: iso-ir-149 
-PASS Supported label: iso88598 
-PASS Supported label: iso-8859-2 
-PASS Supported label: iso-8859-9 
-PASS Supported label: x-cp1257 
-FAIL Supported label: ucs-2 assert_true: ucs-2 should only be supported if it is specified expected true got false
-PASS Supported label: csisolatin6 
-PASS Supported label: iso885913 
-PASS Supported label: iso-8859-8 
-PASS Supported label: ms_kanji 
-PASS Supported label: iso_8859-6:1987 
 PASS Supported label: 866 
-PASS Supported label: iso-ir-138 
-PASS Supported label: cseucpkdfmtjapanese 
-FAIL Supported label: euc-kr-html assert_true: euc-kr-html should only be supported if it is specified expected true got false
-PASS Supported label: ms932 
-PASS Supported label: big5-hkscs 
-PASS Supported label: mac 
-FAIL Supported label: windows-1258-html assert_true: windows-1258-html should only be supported if it is specified expected true got false
-PASS Supported label: iso_8859-1 
-PASS Supported label: x-cp1254 
-PASS Supported label: iso8859-2 
-PASS Supported label: iso8859-11 
-PASS Supported label: big5 
-FAIL Supported label: iso-8859-4-html assert_true: iso-8859-4-html should only be supported if it is specified expected true got false
-PASS Supported label: iso-8859-1 
-PASS Supported label: latin6 
-FAIL Supported label: iso-8859-16-html assert_true: iso-8859-16-html should only be supported if it is specified expected true got false
-PASS Supported label: csisolatin2 
-PASS Supported label: iso-8859-7 
-PASS Supported label: iso-8859-8-i 
-PASS Supported label: iso88591 
-PASS Supported label: windows-1258 
-PASS Supported label: iso_8859-8:1988 
-PASS Supported label: iso-8859-3 
-PASS Supported label: csisolatingreek 
-PASS Supported label: x-cp1255 
-PASS Supported label: shift_jis 
-FAIL Supported label: iso-8859-7-html assert_true: iso-8859-7-html should only be supported if it is specified expected true got false
-FAIL Supported label: koi8-r-html assert_true: koi8-r-html should only be supported if it is specified expected true got false
-PASS Supported label: iso-8859-4 
-FAIL Supported label: windows-1252-html assert_true: windows-1252-html should only be supported if it is specified expected true got false
-PASS Supported label: iso-ir-144 
-PASS Supported label: iso-ir-109 
-PASS Supported label: csiso88596i 
-PASS Supported label: csksc56011987 
-PASS Supported label: iso-8859-14 
-PASS Supported label: iso885914 
-PASS Supported label: l6 
-PASS Supported label: iso_8859-5 
-PASS Supported label: gbk 
-PASS Supported label: l5 
-PASS Supported label: iso-ir-100 
-FAIL Supported label: x-unicode20utf8 assert_true: x-unicode20utf8 should only be supported if it is specified expected true got false
-PASS Supported label: csiso2022kr 
-FAIL Supported label: csunicode assert_true: csunicode should only be supported if it is specified expected true got false
-FAIL Supported label: unicode20utf8 assert_true: unicode20utf8 should only be supported if it is specified expected true got false
-PASS Supported label: cyrillic 
-FAIL Supported label: iso-8859-3-html assert_true: iso-8859-3-html should only be supported if it is specified expected true got false
-PASS Supported label: euc-jp 
-PASS Supported label: greek8 
-PASS Supported label: hz-gb-2312 
-PASS Supported label: iso8859-14 
-PASS Supported label: iso-2022-cn-ext 
-PASS Supported label: csgb2312 
-PASS Supported label: iso-8859-8-e 
-PASS Supported label: csisolatin5 
-PASS Supported label: sun_eu_greek 
-PASS Supported label: iso88599 
-FAIL Supported label: unicode assert_true: unicode should only be supported if it is specified expected true got false
-FAIL Supported label: windows-1255-html assert_true: windows-1255-html should only be supported if it is specified expected true got false
-PASS Supported label: csibm866 
-FAIL Supported label: ibm866-html assert_true: ibm866-html should only be supported if it is specified expected true got false
-PASS Supported label: iso8859-3 
-PASS Supported label: iso-8859-6-e 
-FAIL Supported label: unicodefffe assert_true: unicodefffe should only be supported if it is specified expected true got false
-PASS Supported label: macintosh 
-PASS Supported label: iso-2022-jp 
-PASS Supported label: l9 
-PASS Supported label: iso-8859-5 
-PASS Supported label: iso-ir-101 
-PASS Supported label: iso-ir-127 
-PASS Supported label: iso8859-4 
-PASS Supported label: windows-1253 
-FAIL Supported label: windows-936-2000 assert_true: windows-936-2000 should only be supported if it is specified expected true got false
-PASS Supported label: iso-8859-13 
-PASS Supported label: ksc_5601 
-PASS Supported label: cn-big5 
-PASS Supported label: chinese 
-PASS Supported label: latin1 
-PASS Supported label: ibm866 
-FAIL Supported label: windows-1256-html assert_true: windows-1256-html should only be supported if it is specified expected true got false
-PASS Supported label: iso_8859-6 
-PASS Supported label: x-euc-jp 
-PASS Supported label: euc-kr 
-PASS Supported label: iso88596 
-PASS Supported label: latin5 
-PASS Supported label: l4 
-FAIL Supported label: iso-8859-15-html assert_true: iso-8859-15-html should only be supported if it is specified expected true got false
-PASS Supported label: l1 
-PASS Supported label: utf-16 
-PASS Supported label: iso885915 
-PASS Supported label: cp1252 
-PASS Supported label: iso8859-6 
-PASS Supported label: cseuckr 
-PASS Supported label: x-cp1253 
-PASS Supported label: iso88593 
-PASS Supported label: csisolatin3 
-PASS Supported label: iso-8859-6-i 
-FAIL Supported label: windows-874-html assert_true: windows-874-html should only be supported if it is specified expected true got false
-FAIL Supported label: macintosh-html assert_true: macintosh-html should only be supported if it is specified expected true got false
-PASS Supported label: iso_8859-1:1987 
-PASS Supported label: csiso88598i 
-PASS Supported label: utf-16le 
-PASS Supported label: utf8 
-PASS Supported label: sjis 
-PASS Supported label: iso-8859-16 
-PASS Supported label: iso_8859-7 
-FAIL Supported label: iso-8859-14-html assert_true: iso-8859-14-html should only be supported if it is specified expected true got false
-PASS Supported label: csisolatin9 
-PASS Supported label: dos-874 
-PASS Supported label: cskoi8r 
-PASS Supported label: gb_2312 
-PASS Supported label: visual 
-PASS Supported label: x-cp1258 
-FAIL Supported label: shift_jis-html assert_true: shift_jis-html should only be supported if it is specified expected true got false
-PASS Supported label: x-cp1252 
-PASS Supported label: utf-8 
-FAIL Supported label: koi8-u-html assert_true: koi8-u-html should only be supported if it is specified expected true got false
-PASS Supported label: cp1258 
-PASS Supported label: iso_8859-15 
-PASS Supported label: iso_8859-3:1988 
-FAIL Supported label: x-mac-cyrillic-html assert_true: x-mac-cyrillic-html should only be supported if it is specified expected true got false
 PASS Supported label: ansi_x3.4-1968 
 PASS Supported label: ascii 
-PASS Supported label: koi8-u 
-PASS Supported label: l3 
-FAIL Supported label: unicode11utf8 assert_true: unicode11utf8 should only be supported if it is specified expected true got false
-FAIL Supported label: iso-8859-2-html assert_true: iso-8859-2-html should only be supported if it is specified expected true got false
+PASS Supported label: big5 
+PASS Supported label: big5-hkscs 
+PASS Supported label: euc-jp 
+PASS Supported label: euc-kr 
+PASS Supported label: gb2312 
+PASS Supported label: gbk 
+PASS Supported label: gb_2312-80 
 PASS Supported label: ibm819 
-PASS Supported label: korean 
-PASS Supported label: iso88592 
-PASS Supported label: csmacintosh 
-PASS Supported label: iso_8859-5:1988 
-PASS Supported label: x-mac-roman 
-PASS Supported label: csiso88596e 
-FAIL Supported label: big5-html assert_true: big5-html should only be supported if it is specified expected true got false
-PASS Supported label: greek 
-FAIL Supported label: iso-8859-6-html assert_true: iso-8859-6-html should only be supported if it is specified expected true got false
-PASS Supported label: l2 
-PASS Supported label: cp819 
-PASS Supported label: windows-949 
-PASS Supported label: csshiftjis 
-PASS Supported label: ecma-118 
+PASS Supported label: ibm866 
+FAIL Supported label: iso-10646-ucs-2 assert_true: iso-10646-ucs-2 should only be supported if it is specified expected true got false
+PASS Supported label: iso-2022-jp 
+PASS Supported label: iso-8859-1 
+PASS Supported label: iso-8859-10 
+PASS Supported label: iso-8859-13 
+PASS Supported label: iso-8859-14 
 PASS Supported label: iso-8859-15 
-PASS Supported label: windows-1254 
+PASS Supported label: iso-8859-16 
+PASS Supported label: iso-8859-2 
+PASS Supported label: iso-8859-3 
+PASS Supported label: iso-8859-4 
+PASS Supported label: iso-8859-5 
+PASS Supported label: iso-8859-6 
+PASS Supported label: iso-8859-7 
+PASS Supported label: iso-8859-8 
+PASS Supported label: iso-8859-8-e 
+PASS Supported label: iso-8859-8-i 
+PASS Supported label: koi8-r 
+PASS Supported label: koi8-u 
+PASS Supported label: shift_jis 
+FAIL Supported label: ucs-2 assert_true: ucs-2 should only be supported if it is specified expected true got false
 PASS Supported label: us-ascii 
-PASS Supported label: latin3 
-PASS Supported label: iso-ir-58 
+PASS Supported label: utf-16 
+PASS Supported label: utf-16be 
+PASS Supported label: utf-16le 
+PASS Supported label: utf-8 
+FAIL Supported label: unicode assert_true: unicode should only be supported if it is specified expected true got false
+PASS Supported label: arabic 
+PASS Supported label: asmo-708 
+PASS Supported label: chinese 
+PASS Supported label: cn-big5 
 PASS Supported label: cp1250 
-PASS Supported label: csbig5 
+PASS Supported label: cp1251 
+PASS Supported label: cp1252 
+PASS Supported label: cp1253 
+PASS Supported label: cp1254 
+PASS Supported label: cp1255 
+PASS Supported label: cp1256 
 PASS Supported label: cp1257 
-PASS Supported label: x-mac-cyrillic 
-FAIL Supported label: windows-1250-html assert_true: windows-1250-html should only be supported if it is specified expected true got false
-PASS Supported label: iso8859-1 
+PASS Supported label: cp1258 
+PASS Supported label: cp819 
+PASS Supported label: cp866 
+PASS Supported label: csgb2312 
+PASS Supported label: csiso88598i 
+PASS Supported label: csisolatin1 
+FAIL Supported label: csunicode assert_true: csunicode should only be supported if it is specified expected true got false
+PASS Supported label: csbig5 
+PASS Supported label: cseuckr 
+PASS Supported label: cseucpkdfmtjapanese 
+PASS Supported label: csibm866 
+PASS Supported label: csiso2022jp 
+PASS Supported label: csiso2022kr 
+PASS Supported label: csiso58gb231280 
+PASS Supported label: csiso88596e 
+PASS Supported label: csiso88596i 
+PASS Supported label: csiso88598e 
+PASS Supported label: csisolatin2 
+PASS Supported label: csisolatin3 
+PASS Supported label: csisolatin4 
+PASS Supported label: csisolatin5 
+PASS Supported label: csisolatin6 
+PASS Supported label: csisolatin9 
+PASS Supported label: csisolatinarabic 
+PASS Supported label: csisolatincyrillic 
+PASS Supported label: csisolatingreek 
+PASS Supported label: csisolatinhebrew 
+PASS Supported label: cskoi8r 
+PASS Supported label: csksc56011987 
+PASS Supported label: csmacintosh 
+PASS Supported label: csshiftjis 
+PASS Supported label: cyrillic 
+PASS Supported label: dos-874 
+PASS Supported label: ecma-114 
+PASS Supported label: ecma-118 
+PASS Supported label: elot_928 
 PASS Supported label: gb18030 
+PASS Supported label: gb_2312 
+PASS Supported label: greek 
+PASS Supported label: greek8 
+PASS Supported label: hebrew 
+PASS Supported label: hz-gb-2312 
+PASS Supported label: iso-2022-cn 
+PASS Supported label: iso-2022-cn-ext 
+PASS Supported label: iso-2022-kr 
+PASS Supported label: iso-8859-11 
+PASS Supported label: iso-8859-6-e 
+PASS Supported label: iso-8859-6-i 
+PASS Supported label: iso-8859-9 
+PASS Supported label: iso-ir-100 
+PASS Supported label: iso-ir-101 
+PASS Supported label: iso-ir-109 
+PASS Supported label: iso-ir-110 
+PASS Supported label: iso-ir-126 
+PASS Supported label: iso-ir-127 
+PASS Supported label: iso-ir-138 
+PASS Supported label: iso-ir-144 
+PASS Supported label: iso-ir-148 
+PASS Supported label: iso-ir-149 
+PASS Supported label: iso-ir-157 
+PASS Supported label: iso-ir-58 
+PASS Supported label: iso8859-1 
+PASS Supported label: iso8859-10 
+PASS Supported label: iso8859-11 
+PASS Supported label: iso8859-13 
+PASS Supported label: iso8859-14 
+PASS Supported label: iso8859-15 
+PASS Supported label: iso8859-2 
+PASS Supported label: iso8859-3 
+PASS Supported label: iso8859-4 
+PASS Supported label: iso8859-5 
+PASS Supported label: iso8859-6 
+PASS Supported label: iso8859-7 
+PASS Supported label: iso8859-8 
+PASS Supported label: iso8859-9 
+PASS Supported label: iso88591 
+PASS Supported label: iso885910 
+PASS Supported label: iso885911 
+PASS Supported label: iso885913 
+PASS Supported label: iso885914 
+PASS Supported label: iso885915 
+PASS Supported label: iso88592 
+PASS Supported label: iso88593 
+PASS Supported label: iso88594 
+PASS Supported label: iso88595 
+PASS Supported label: iso88596 
+PASS Supported label: iso88597 
+PASS Supported label: iso88598 
+PASS Supported label: iso88599 
+PASS Supported label: iso_8859-1 
+PASS Supported label: iso_8859-15 
+PASS Supported label: iso_8859-1:1987 
+PASS Supported label: iso_8859-2 
+PASS Supported label: iso_8859-2:1987 
+PASS Supported label: iso_8859-3 
+PASS Supported label: iso_8859-3:1988 
+PASS Supported label: iso_8859-4 
+PASS Supported label: iso_8859-4:1988 
+PASS Supported label: iso_8859-5 
+PASS Supported label: iso_8859-5:1988 
+PASS Supported label: iso_8859-6 
+PASS Supported label: iso_8859-6:1987 
+PASS Supported label: iso_8859-7 
+PASS Supported label: iso_8859-7:1987 
+PASS Supported label: iso_8859-8 
+PASS Supported label: iso_8859-8:1988 
+PASS Supported label: iso_8859-9 
+PASS Supported label: iso_8859-9:1989 
+PASS Supported label: koi 
+PASS Supported label: koi8 
+PASS Supported label: koi8-ru 
+PASS Supported label: koi8_r 
+PASS Supported label: korean 
+PASS Supported label: ks_c_5601-1987 
+PASS Supported label: ks_c_5601-1989 
+PASS Supported label: ksc5601 
+PASS Supported label: ksc_5601 
+PASS Supported label: l1 
+PASS Supported label: l2 
+PASS Supported label: l3 
+PASS Supported label: l4 
+PASS Supported label: l5 
+PASS Supported label: l6 
+PASS Supported label: l9 
+PASS Supported label: latin1 
+PASS Supported label: latin2 
+PASS Supported label: latin3 
+PASS Supported label: latin4 
+PASS Supported label: latin5 
+PASS Supported label: latin6 
+PASS Supported label: logical 
+PASS Supported label: mac 
+PASS Supported label: macintosh 
+PASS Supported label: ms932 
+PASS Supported label: ms_kanji 
+PASS Supported label: replacement 
+PASS Supported label: shift-jis 
+PASS Supported label: sjis 
+PASS Supported label: sun_eu_greek 
+PASS Supported label: tis-620 
+PASS Supported label: unicode-1-1-utf-8 
+FAIL Supported label: unicode11utf8 assert_true: unicode11utf8 should only be supported if it is specified expected true got false
+FAIL Supported label: unicode20utf8 assert_true: unicode20utf8 should only be supported if it is specified expected true got false
+FAIL Supported label: unicodefeff assert_true: unicodefeff should only be supported if it is specified expected true got false
+FAIL Supported label: unicodefffe assert_true: unicodefffe should only be supported if it is specified expected true got false
+PASS Supported label: utf8 
+PASS Supported label: visual 
+PASS Supported label: windows-1250 
+PASS Supported label: windows-1251 
+PASS Supported label: windows-1252 
+PASS Supported label: windows-1253 
+PASS Supported label: windows-1254 
+PASS Supported label: windows-1255 
+PASS Supported label: windows-1256 
+PASS Supported label: windows-1257 
+PASS Supported label: windows-1258 
+PASS Supported label: windows-31j 
+PASS Supported label: windows-874 
+FAIL Supported label: windows-936-2000 assert_true: windows-936-2000 should only be supported if it is specified expected true got false
+PASS Supported label: windows-949 
+PASS Supported label: x-cp1250 
+PASS Supported label: x-cp1251 
+PASS Supported label: x-cp1252 
+PASS Supported label: x-cp1253 
+PASS Supported label: x-cp1254 
+PASS Supported label: x-cp1255 
+PASS Supported label: x-cp1256 
+PASS Supported label: x-cp1257 
+PASS Supported label: x-cp1258 
+PASS Supported label: x-euc-jp 
+PASS Supported label: x-gbk 
+PASS Supported label: x-mac-cyrillic 
+PASS Supported label: x-mac-roman 
+PASS Supported label: x-mac-ukrainian 
+PASS Supported label: x-sjis 
+FAIL Supported label: x-unicode20utf8 assert_true: x-unicode20utf8 should only be supported if it is specified expected true got false
+PASS Supported label: x-user-defined 
+PASS Supported label: x-x-big5 
 Harness: the test ran to completion.
 
diff --git a/third_party/WebKit/LayoutTests/fast/encoding/supported-encodings.html b/third_party/WebKit/LayoutTests/fast/encoding/supported-encodings.html
index 1e4079a..fc83ba1 100644
--- a/third_party/WebKit/LayoutTests/fast/encoding/supported-encodings.html
+++ b/third_party/WebKit/LayoutTests/fast/encoding/supported-encodings.html
@@ -9,7 +9,7 @@
 // use of the "internals" API for testing and therefore can not be
 // upstreamed to web-platform-tests.
 
-let supported_labels = internals.supportedTextEncodingLabels();
+let supported_labels = internals.supportedTextEncodingLabels().sort();
 
 // Specified labels from: https://encoding.spec.whatwg.org/
 
diff --git a/third_party/WebKit/LayoutTests/fast/multicol/out-of-flow/nested-with-abspos-inside-relpos-expected.html b/third_party/WebKit/LayoutTests/fast/multicol/out-of-flow/nested-with-abspos-inside-relpos-expected.html
new file mode 100644
index 0000000..4080881
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/multicol/out-of-flow/nested-with-abspos-inside-relpos-expected.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<p>There should be a hotpink square to the left of a blue rectangle.</p>
+<div style="float:left; width:70px; height:70px; background:hotpink;"></div>
+<div style="float:left; margin-left:80px; width:70px; height:40px; background:blue;"></div>
diff --git a/third_party/WebKit/LayoutTests/fast/multicol/out-of-flow/nested-with-abspos-inside-relpos.html b/third_party/WebKit/LayoutTests/fast/multicol/out-of-flow/nested-with-abspos-inside-relpos.html
new file mode 100644
index 0000000..6df411bc
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/multicol/out-of-flow/nested-with-abspos-inside-relpos.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<p>There should be a hotpink square to the left of a blue rectangle.</p>
+<div style="columns:2; column-gap:0; column-fill:auto; width:300px; height:70px; line-height:40px; orphans:1; widows:1;">
+    <div style="position:relative;">
+        <div style="columns:2;">
+            <div style="position:absolute; width:70px; background:hotpink;">
+                <br>
+                <div style="background:blue;"><br></div>
+            </div>
+        </div>
+    </div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/sdk/network-interception-wildcard-pattern-matching-expected.txt b/third_party/WebKit/LayoutTests/http/tests/devtools/sdk/network-interception-wildcard-pattern-matching-expected.txt
new file mode 100644
index 0000000..4c5fbd7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/sdk/network-interception-wildcard-pattern-matching-expected.txt
@@ -0,0 +1,99 @@
+Tests that wild card pattern matching works.
+
+Checking Pattern: *a*a*
+Input: asdfasdf
+Result: true
+
+Checking Pattern: a*a*
+Input: asdfasdf
+Result: true
+
+Checking Pattern: a*a
+Input: asdfasdf
+Result: false
+
+Checking Pattern: a*a*f
+Input: asdfasdf
+Result: true
+
+Checking Pattern: af
+Input: asdfasdf
+Result: false
+
+Checking Pattern: *df
+Input: asdfasdf
+Result: true
+
+Checking Pattern: *
+Input: asdfasdf
+Result: true
+
+Checking Pattern: *
+Input: 
+Result: true
+
+Checking Pattern: 
+Input: 
+Result: false
+
+Checking Pattern: 
+Input: asdfasdf
+Result: false
+
+Checking Pattern: a*c
+Input: ac
+Result: true
+
+Checking Pattern: a**c
+Input: ac
+Result: true
+
+Checking Pattern: a**c
+Input: abc
+Result: true
+
+Checking Pattern: a*c
+Input: abc
+Result: true
+
+Checking Pattern: *ac*
+Input: ac
+Result: true
+
+Checking Pattern: ac
+Input: abc
+Result: false
+
+Checking Pattern: a\*c
+Input: a*c
+Result: true
+
+Checking Pattern: a\\*c
+Input: a\c
+Result: true
+
+Checking Pattern: a\*c
+Input: ac
+Result: false
+
+Checking Pattern: a\*c
+Input: a**c
+Result: false
+
+Checking Pattern: a\*\\*c
+Input: a*\*c
+Result: true
+
+Checking Pattern: a\*\\\*c
+Input: a*\*c
+Result: true
+
+Checking Pattern: a?c
+Input: a?c
+Result: true
+
+Checking Pattern: a?c
+Input: acc
+Result: false
+
+
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/sdk/network-interception-wildcard-pattern-matching.js b/third_party/WebKit/LayoutTests/http/tests/devtools/sdk/network-interception-wildcard-pattern-matching.js
new file mode 100644
index 0000000..82ec15b8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/sdk/network-interception-wildcard-pattern-matching.js
@@ -0,0 +1,41 @@
+// 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.
+
+(async function() {
+  TestRunner.addResult("Tests that wild card pattern matching works.\n");
+
+  checkPattern('*a*a*', 'asdfasdf');
+  checkPattern('a*a*', 'asdfasdf');
+  checkPattern('a*a', 'asdfasdf');
+  checkPattern('a*a*f', 'asdfasdf');
+  checkPattern('af', 'asdfasdf');
+  checkPattern('*df', 'asdfasdf');
+  checkPattern('*', 'asdfasdf');
+  checkPattern('*', '');
+  checkPattern('', '');
+  checkPattern('', 'asdfasdf');
+  checkPattern('a*c', 'ac');
+  checkPattern('a**c', 'ac');
+  checkPattern('a**c', 'abc');
+  checkPattern('a*c', 'abc');
+  checkPattern('*ac*', 'ac');
+  checkPattern('ac', 'abc');
+  checkPattern('a\\*c', 'a*c');
+  checkPattern('a\\\\*c', 'a\\c');
+  checkPattern('a\\*c', 'ac');
+  checkPattern('a\\*c', 'a**c');
+  checkPattern('a\\*\\\\*c', 'a*\\*c');
+  checkPattern('a\\*\\\\\\*c', 'a*\\*c');
+  checkPattern('a?c', 'a?c');
+  checkPattern('a?c', 'acc');
+
+  TestRunner.completeTest();
+
+  function checkPattern(pattern, input) {
+    TestRunner.addResult("Checking Pattern: " + pattern);
+    TestRunner.addResult("Input: " + input);
+    TestRunner.addResult("Result: " + SDK.RequestInterceptor.patternMatchesUrl(pattern, input));
+    TestRunner.addResult("");
+  }
+})();
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/page/page-lifecycleEvents.js b/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/page/page-lifecycleEvents.js
index e5b07e7..b2e9459 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/page/page-lifecycleEvents.js
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/page/page-lifecycleEvents.js
@@ -14,5 +14,5 @@
     }
   });
 
-  dp.Page.navigate({url: testRunner.url('../resources/image.html')});
+  dp.Page.navigate({url: 'data:,Hello!'});
 })
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/security/security-state-comparator-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/security/security-state-comparator-expected.txt
index 7f5c898..804f3fc71 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/security/security-state-comparator-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/security/security-state-comparator-expected.txt
@@ -3,37 +3,26 @@
 Sign of SecurityStateComparator("info","info"): 0 (expected: 0)
 Sign of SecurityStateComparator("info","insecure"): -1 (expected: -1)
 Sign of SecurityStateComparator("info","neutral"): -1 (expected: -1)
-Sign of SecurityStateComparator("info","warning"): -1 (expected: -1)
 Sign of SecurityStateComparator("info","secure"): -1 (expected: -1)
 Sign of SecurityStateComparator("info","unknown"): -1 (expected: -1)
 Sign of SecurityStateComparator("insecure","info"): 1 (expected: 1)
 Sign of SecurityStateComparator("insecure","insecure"): 0 (expected: 0)
 Sign of SecurityStateComparator("insecure","neutral"): -1 (expected: -1)
-Sign of SecurityStateComparator("insecure","warning"): -1 (expected: -1)
 Sign of SecurityStateComparator("insecure","secure"): -1 (expected: -1)
 Sign of SecurityStateComparator("insecure","unknown"): -1 (expected: -1)
 Sign of SecurityStateComparator("neutral","info"): 1 (expected: 1)
 Sign of SecurityStateComparator("neutral","insecure"): 1 (expected: 1)
 Sign of SecurityStateComparator("neutral","neutral"): 0 (expected: 0)
-Sign of SecurityStateComparator("neutral","warning"): -1 (expected: -1)
 Sign of SecurityStateComparator("neutral","secure"): -1 (expected: -1)
 Sign of SecurityStateComparator("neutral","unknown"): -1 (expected: -1)
-Sign of SecurityStateComparator("warning","info"): 1 (expected: 1)
-Sign of SecurityStateComparator("warning","insecure"): 1 (expected: 1)
-Sign of SecurityStateComparator("warning","neutral"): 1 (expected: 1)
-Sign of SecurityStateComparator("warning","warning"): 0 (expected: 0)
-Sign of SecurityStateComparator("warning","secure"): -1 (expected: -1)
-Sign of SecurityStateComparator("warning","unknown"): -1 (expected: -1)
 Sign of SecurityStateComparator("secure","info"): 1 (expected: 1)
 Sign of SecurityStateComparator("secure","insecure"): 1 (expected: 1)
 Sign of SecurityStateComparator("secure","neutral"): 1 (expected: 1)
-Sign of SecurityStateComparator("secure","warning"): 1 (expected: 1)
 Sign of SecurityStateComparator("secure","secure"): 0 (expected: 0)
 Sign of SecurityStateComparator("secure","unknown"): -1 (expected: -1)
 Sign of SecurityStateComparator("unknown","info"): 1 (expected: 1)
 Sign of SecurityStateComparator("unknown","insecure"): 1 (expected: 1)
 Sign of SecurityStateComparator("unknown","neutral"): 1 (expected: 1)
-Sign of SecurityStateComparator("unknown","warning"): 1 (expected: 1)
 Sign of SecurityStateComparator("unknown","secure"): 1 (expected: 1)
 Sign of SecurityStateComparator("unknown","unknown"): 0 (expected: 0)
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/security/security-state-comparator.html b/third_party/WebKit/LayoutTests/http/tests/inspector/security/security-state-comparator.html
index 23d07e39..63c36f4 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/security/security-state-comparator.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/security/security-state-comparator.html
@@ -6,8 +6,8 @@
 function test() {
   var ordering = [
     Protocol.Security.SecurityState.Info, Protocol.Security.SecurityState.Insecure,
-    Protocol.Security.SecurityState.Neutral, Protocol.Security.SecurityState.Warning,
-    Protocol.Security.SecurityState.Secure, Protocol.Security.SecurityState.Unknown
+    Protocol.Security.SecurityState.Neutral, Protocol.Security.SecurityState.Secure,
+    Protocol.Security.SecurityState.Unknown
   ];
 
   TestRunner.assertEquals(ordering.length, Object.keys(Protocol.Security.SecurityState).length);
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 cf876ce..9030ee98 100644
--- a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
@@ -4947,6 +4947,7 @@
     getter duration
     getter name
     method constructor
+    method toJSON
 interface PerformanceTiming
     attribute @@toStringTag
     getter connectEnd
diff --git a/third_party/WebKit/Source/core/dom/Document.cpp b/third_party/WebKit/Source/core/dom/Document.cpp
index bf200bc..a7d6891 100644
--- a/third_party/WebKit/Source/core/dom/Document.cpp
+++ b/third_party/WebKit/Source/core/dom/Document.cpp
@@ -1995,6 +1995,7 @@
   }
 
   ScrollSnapType snap_type = overflow_style->GetScrollSnapType();
+  ScrollBehavior scroll_behavior = document_element_style->GetScrollBehavior();
 
   RefPtr<ComputedStyle> viewport_style;
   if (change == kForce || !GetLayoutViewItem().Style()) {
@@ -2012,7 +2013,8 @@
         old_style.OverflowY() == overflow_y &&
         old_style.HasNormalColumnGap() == column_gap_normal &&
         old_style.ColumnGap() == column_gap &&
-        old_style.GetScrollSnapType() == snap_type) {
+        old_style.GetScrollSnapType() == snap_type &&
+        old_style.GetScrollBehavior() == scroll_behavior) {
       return;
     }
     viewport_style = ComputedStyle::Clone(old_style);
@@ -2030,6 +2032,7 @@
   else
     viewport_style->SetColumnGap(column_gap);
   viewport_style->SetScrollSnapType(snap_type);
+  viewport_style->SetScrollBehavior(scroll_behavior);
   GetLayoutViewItem().SetStyle(viewport_style);
   SetupFontBuilder(*viewport_style);
 }
diff --git a/third_party/WebKit/Source/core/exported/WebFrameTest.cpp b/third_party/WebKit/Source/core/exported/WebFrameTest.cpp
index 21a4c49d..354a374 100644
--- a/third_party/WebKit/Source/core/exported/WebFrameTest.cpp
+++ b/third_party/WebKit/Source/core/exported/WebFrameTest.cpp
@@ -11678,4 +11678,63 @@
   tester.ExpectTotalCount(histogramName, 3);
 }
 
+// TODO(pdr): Create a version of this test for SPV2 (crbug.com.758028).
+TEST_P(ParameterizedWebFrameTest, DidScrollCallbackAfterScrollableAreaChanges) {
+  FrameTestHelpers::WebViewHelper web_view_helper;
+  web_view_helper.Initialize();
+  web_view_helper.Resize(WebSize(200, 200));
+  WebViewImpl* web_view = web_view_helper.WebView();
+
+  InitializeWithHTML(*web_view->MainFrameImpl()->GetFrame(),
+                     "<style>"
+                     "  #scrollable {"
+                     "    height: 100px;"
+                     "    width: 100px;"
+                     "    overflow: scroll;"
+                     "    will-change: transform;"
+                     "  }"
+                     "  #forceScroll { height: 120px; width: 50px; }"
+                     "</style>"
+                     "<div id='scrollable'>"
+                     "  <div id='forceScroll'></div>"
+                     "</div>");
+
+  web_view->UpdateAllLifecyclePhases();
+
+  Document* document = web_view->MainFrameImpl()->GetFrame()->GetDocument();
+  Element* scrollable = document->getElementById("scrollable");
+
+  auto* scrollable_area =
+      ToLayoutBox(scrollable->GetLayoutObject())->GetScrollableArea();
+  EXPECT_NE(nullptr, scrollable_area);
+
+  // We should have a composited layer for scrolling due to will-change.
+  WebLayer* web_scroll_layer =
+      scrollable_area->LayerForScrolling()->PlatformLayer();
+  EXPECT_NE(nullptr, web_scroll_layer);
+
+  EXPECT_EQ(ScrollOffset(), scrollable_area->GetScrollOffset());
+  web_scroll_layer->SetScrollOffsetFromImplSideForTesting(
+      gfx::ScrollOffset(0, 1));
+  web_view->UpdateAllLifecyclePhases();
+  EXPECT_EQ(ScrollOffset(0, 1), scrollable_area->GetScrollOffset());
+
+  // Make the scrollable area non-scrollable.
+  scrollable->setAttribute(HTMLNames::styleAttr, "overflow: visible");
+
+  // Update layout without updating compositing state.
+  WebLocalFrame* frame = web_view_helper.LocalMainFrame();
+  frame->ExecuteScript(
+      WebScriptSource("var forceLayoutFromScript = scrollable.offsetTop;"));
+  EXPECT_EQ(document->Lifecycle().GetState(), DocumentLifecycle::kLayoutClean);
+
+  EXPECT_EQ(nullptr,
+            ToLayoutBox(scrollable->GetLayoutObject())->GetScrollableArea());
+
+  // The web scroll layer has not been deleted yet and we should be able to
+  // apply impl-side offsets without crashing.
+  web_scroll_layer->SetScrollOffsetFromImplSideForTesting(
+      gfx::ScrollOffset(0, 3));
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/exported/WebRemoteFrameImpl.cpp b/third_party/WebKit/Source/core/exported/WebRemoteFrameImpl.cpp
index 0559c4d..81e8a7a2 100644
--- a/third_party/WebKit/Source/core/exported/WebRemoteFrameImpl.cpp
+++ b/third_party/WebKit/Source/core/exported/WebRemoteFrameImpl.cpp
@@ -124,11 +124,6 @@
   return GetFrame()->GetPage()->GetChromeClient().GetWebView();
 }
 
-WebPerformance WebRemoteFrameImpl::Performance() const {
-  NOTREACHED();
-  return WebPerformance();
-}
-
 void WebRemoteFrameImpl::StopLoading() {
   // TODO(dcheng,japhet): Calling this method should stop loads
   // in all subframes, both remote and local.
diff --git a/third_party/WebKit/Source/core/exported/WebRemoteFrameImpl.h b/third_party/WebKit/Source/core/exported/WebRemoteFrameImpl.h
index 7782cb1..f0c3987 100644
--- a/third_party/WebKit/Source/core/exported/WebRemoteFrameImpl.h
+++ b/third_party/WebKit/Source/core/exported/WebRemoteFrameImpl.h
@@ -38,7 +38,6 @@
   void SetName(const WebString&) override;
   WebRect VisibleContentRect() const override;
   WebView* View() const override;
-  WebPerformance Performance() const override;
   void StopLoading() override;
 
   // WebRemoteFrame methods:
diff --git a/third_party/WebKit/Source/core/frame/History.cpp b/third_party/WebKit/Source/core/frame/History.cpp
index 8caa267c..a3edf95 100644
--- a/third_party/WebKit/Source/core/frame/History.cpp
+++ b/third_party/WebKit/Source/core/frame/History.cpp
@@ -63,13 +63,23 @@
   DOMWindowClient::Trace(visitor);
 }
 
-unsigned History::length() const {
-  if (!GetFrame() || !GetFrame()->Client())
+unsigned History::length(ExceptionState& exception_state) const {
+  if (!GetFrame() || !GetFrame()->Client()) {
+    exception_state.ThrowSecurityError(
+        "May not use a History object associated with a Document that is not "
+        "fully active");
     return 0;
+  }
   return GetFrame()->Client()->BackForwardLength();
 }
 
-SerializedScriptValue* History::state() {
+SerializedScriptValue* History::state(ExceptionState& exception_state) {
+  if (!GetFrame()) {
+    exception_state.ThrowSecurityError(
+        "May not use a History object associated with a Document that is not "
+        "fully active");
+    return 0;
+  }
   last_state_object_requested_ = StateInternal();
   return last_state_object_requested_.Get();
 }
@@ -86,10 +96,15 @@
   return 0;
 }
 
-void History::setScrollRestoration(const String& value) {
+void History::setScrollRestoration(const String& value,
+                                   ExceptionState& exception_state) {
   DCHECK(value == "manual" || value == "auto");
-  if (!GetFrame() || !GetFrame()->Client())
+  if (!GetFrame() || !GetFrame()->Client()) {
+    exception_state.ThrowSecurityError(
+        "May not use a History object associated with a Document that is not "
+        "fully active");
     return;
+  }
 
   HistoryScrollRestorationType scroll_restoration =
       value == "manual" ? kScrollRestorationManual : kScrollRestorationAuto;
@@ -103,7 +118,13 @@
   }
 }
 
-String History::scrollRestoration() {
+String History::scrollRestoration(ExceptionState& exception_state) {
+  if (!GetFrame() || !GetFrame()->Client()) {
+    exception_state.ThrowSecurityError(
+        "May not use a History object associated with a Document that is not "
+        "fully active");
+    return "auto";
+  }
   return ScrollRestorationInternal() == kScrollRestorationManual ? "manual"
                                                                  : "auto";
 }
@@ -146,17 +167,24 @@
   return state == StateInternal();
 }
 
-void History::back(ScriptState* script_state) {
-  go(script_state, -1);
+void History::back(ScriptState* script_state, ExceptionState& exception_state) {
+  go(script_state, -1, exception_state);
 }
 
-void History::forward(ScriptState* script_state) {
-  go(script_state, 1);
+void History::forward(ScriptState* script_state,
+                      ExceptionState& exception_state) {
+  go(script_state, 1, exception_state);
 }
 
-void History::go(ScriptState* script_state, int delta) {
-  if (!GetFrame() || !GetFrame()->Client())
+void History::go(ScriptState* script_state,
+                 int delta,
+                 ExceptionState& exception_state) {
+  if (!GetFrame() || !GetFrame()->Client()) {
+    exception_state.ThrowSecurityError(
+        "May not use a History object associated with a Document that is not "
+        "fully active");
     return;
+  }
 
   DCHECK(IsMainThread());
   Document* active_document = ToDocument(ExecutionContext::From(script_state));
@@ -235,8 +263,12 @@
                                FrameLoadType type,
                                ExceptionState& exception_state) {
   if (!GetFrame() || !GetFrame()->GetPage() ||
-      !GetFrame()->Loader().GetDocumentLoader())
+      !GetFrame()->Loader().GetDocumentLoader()) {
+    exception_state.ThrowSecurityError(
+        "May not use a History object associated with a Document that is not "
+        "fully active");
     return;
+  }
 
   KURL full_url = UrlForState(url_string);
   if (!CanChangeToUrl(full_url, GetFrame()->GetDocument()->GetSecurityOrigin(),
diff --git a/third_party/WebKit/Source/core/frame/History.h b/third_party/WebKit/Source/core/frame/History.h
index 7170288..e18e738 100644
--- a/third_party/WebKit/Source/core/frame/History.h
+++ b/third_party/WebKit/Source/core/frame/History.h
@@ -53,12 +53,12 @@
  public:
   static History* Create(LocalFrame* frame) { return new History(frame); }
 
-  unsigned length() const;
-  SerializedScriptValue* state();
+  unsigned length(ExceptionState&) const;
+  SerializedScriptValue* state(ExceptionState&);
 
-  void back(ScriptState*);
-  void forward(ScriptState*);
-  void go(ScriptState*, int delta);
+  void back(ScriptState*, ExceptionState&);
+  void forward(ScriptState*, ExceptionState&);
+  void go(ScriptState*, int delta, ExceptionState&);
 
   void pushState(RefPtr<SerializedScriptValue>,
                  const String& title,
@@ -73,8 +73,8 @@
                      kFrameLoadTypeReplaceCurrentItem, exception_state);
   }
 
-  void setScrollRestoration(const String& value);
-  String scrollRestoration();
+  void setScrollRestoration(const String& value, ExceptionState&);
+  String scrollRestoration(ExceptionState&);
 
   bool stateChanged() const;
   bool IsSameAsCurrentState(SerializedScriptValue*) const;
diff --git a/third_party/WebKit/Source/core/frame/History.idl b/third_party/WebKit/Source/core/frame/History.idl
index 81588c2..376d6c5f 100644
--- a/third_party/WebKit/Source/core/frame/History.idl
+++ b/third_party/WebKit/Source/core/frame/History.idl
@@ -28,13 +28,13 @@
 enum ScrollRestoration {"auto", "manual"};
 
 interface History {
-    readonly attribute unsigned long length;
-    [Measure] attribute ScrollRestoration scrollRestoration;
+    [RaisesException] readonly attribute unsigned long length;
+    [Measure, RaisesException] attribute ScrollRestoration scrollRestoration;
     // TODO(foolip): The SerializedScriptValue type should be any.
-    [CachedAttribute=stateChanged] readonly attribute SerializedScriptValue state;
-    [CallWith=ScriptState] void go(optional long delta = 0);
-    [CallWith=ScriptState] void back();
-    [CallWith=ScriptState] void forward();
+    [CachedAttribute=stateChanged, RaisesException] readonly attribute SerializedScriptValue state;
+    [CallWith=ScriptState, RaisesException] void go(optional long delta = 0);
+    [CallWith=ScriptState, RaisesException] void back();
+    [CallWith=ScriptState, RaisesException] void forward();
     [RaisesException] void pushState(SerializedScriptValue data, DOMString title, optional DOMString? url = null);
     [RaisesException] void replaceState(SerializedScriptValue data, DOMString title, optional DOMString? url = null);
 };
diff --git a/third_party/WebKit/Source/core/frame/LocalFrameView.cpp b/third_party/WebKit/Source/core/frame/LocalFrameView.cpp
index c719397..e9ef4ff 100644
--- a/third_party/WebKit/Source/core/frame/LocalFrameView.cpp
+++ b/third_party/WebKit/Source/core/frame/LocalFrameView.cpp
@@ -4430,6 +4430,21 @@
     SetScrollOffset(clamped, kClampingScroll);
 }
 
+ScrollableArea* LocalFrameView::ScrollableAreaWithElementId(
+    const CompositorElementId& id) {
+  if (id == GetCompositorElementId())
+    return this;
+  if (scrollable_areas_) {
+    // This requires iterating over all scrollable areas. We may want to store a
+    // map of ElementId to ScrollableArea if this is an issue for performance.
+    for (ScrollableArea* scrollable_area : *scrollable_areas_) {
+      if (id == scrollable_area->GetCompositorElementId())
+        return scrollable_area;
+    }
+  }
+  return nullptr;
+}
+
 void LocalFrameView::ScrollContentsIfNeeded() {
   if (pending_scroll_delta_.IsZero())
     return;
diff --git a/third_party/WebKit/Source/core/frame/LocalFrameView.h b/third_party/WebKit/Source/core/frame/LocalFrameView.h
index f2950a3..c3fda3f 100644
--- a/third_party/WebKit/Source/core/frame/LocalFrameView.h
+++ b/third_party/WebKit/Source/core/frame/LocalFrameView.h
@@ -848,6 +848,11 @@
 
   size_t PaintFrameCount() const { return paint_frame_count_; };
 
+  // Return the ScrollableArea in a FrameView with the given ElementId, if any.
+  // This is not recursive and will only return ScrollableAreas owned by this
+  // LocalFrameView (or possibly the LocalFrameView itself).
+  ScrollableArea* ScrollableAreaWithElementId(const CompositorElementId&);
+
  protected:
   // Scroll the content via the compositor.
   bool ScrollContentsFastPath(const IntSize& scroll_delta);
diff --git a/third_party/WebKit/Source/core/frame/PerformanceMonitor.cpp b/third_party/WebKit/Source/core/frame/PerformanceMonitor.cpp
index d9561ca..b84cfcd0 100644
--- a/third_party/WebKit/Source/core/frame/PerformanceMonitor.cpp
+++ b/third_party/WebKit/Source/core/frame/PerformanceMonitor.cpp
@@ -14,6 +14,7 @@
 #include "core/frame/Frame.h"
 #include "core/frame/LocalFrame.h"
 #include "core/html/parser/HTMLDocumentParser.h"
+#include "core/loader/DocumentLoader.h"
 #include "core/probe/CoreProbes.h"
 #include "platform/Histogram.h"
 #include "platform/wtf/CurrentTime.h"
@@ -24,6 +25,7 @@
 
 namespace {
 static const double kLongTaskSubTaskThresholdInSeconds = 0.012;
+static const double kNetworkQuietWindowSeconds = 0.5;
 }  // namespace
 
 // static
@@ -249,6 +251,74 @@
   DCHECK(user_callback_ != &probe);
 }
 
+void PerformanceMonitor::WillSendRequest(ExecutionContext*,
+                                         unsigned long,
+                                         DocumentLoader* loader,
+                                         ResourceRequest&,
+                                         const ResourceResponse&,
+                                         const FetchInitiatorInfo&) {
+  if (loader->GetFrame() != local_root_)
+    return;
+  int request_count = loader->Fetcher()->ActiveRequestCount();
+  // If we are above the allowed number of active requests, reset timers.
+  if (network_2_quiet_ >= 0 && request_count > 2)
+    network_2_quiet_ = 0;
+  if (network_0_quiet_ >= 0 && request_count > 0)
+    network_0_quiet_ = 0;
+}
+
+void PerformanceMonitor::DidFailLoading(unsigned long identifier,
+                                        DocumentLoader* loader,
+                                        const ResourceError&) {
+  if (loader->GetFrame() != local_root_)
+    return;
+  DidLoadResource();
+}
+
+void PerformanceMonitor::DidFinishLoading(unsigned long,
+                                          DocumentLoader* loader,
+                                          double,
+                                          int64_t,
+                                          int64_t) {
+  if (loader->GetFrame() != local_root_)
+    return;
+  DidLoadResource();
+}
+
+void PerformanceMonitor::DidLoadResource() {
+  // If we already reported quiet time, bail out.
+  if (network_0_quiet_ < 0 && network_2_quiet_ < 0)
+    return;
+
+  int request_count =
+      local_root_->GetDocument()->Fetcher()->ActiveRequestCount();
+  // If we did not achieve either 0 or 2 active connections, bail out.
+  if (request_count > 2)
+    return;
+
+  double timestamp = MonotonicallyIncreasingTime();
+  // Arriving at =2 updates the quiet_2 base timestamp.
+  // Arriving at <2 sets the quiet_2 base timestamp only if
+  // it was not already set.
+  if (request_count == 2 && network_2_quiet_ >= 0)
+    network_2_quiet_ = timestamp;
+  else if (request_count < 2 && network_2_quiet_ == 0)
+    network_2_quiet_ = timestamp;
+
+  if (request_count == 0 && network_0_quiet_ >= 0)
+    network_0_quiet_ = timestamp;
+}
+
+void PerformanceMonitor::DomContentLoadedEventFired(LocalFrame* frame) {
+  if (frame != local_root_)
+    return;
+  // Reset idle timers upon DOMContentLoaded, look at current active
+  // connections.
+  network_2_quiet_ = 0;
+  network_0_quiet_ = 0;
+  DidLoadResource();
+}
+
 void PerformanceMonitor::DocumentWriteFetchScript(Document* document) {
   if (!enabled_)
     return;
@@ -257,6 +327,22 @@
 }
 
 void PerformanceMonitor::WillProcessTask(double start_time) {
+  // If we have idle time and we are kNetworkQuietWindowSeconds seconds past it,
+  // emit idle signals.
+  if (network_2_quiet_ > 0 &&
+      start_time - network_2_quiet_ > kNetworkQuietWindowSeconds) {
+    probe::lifecycleEvent(local_root_->GetDocument(), "networkAlmostIdle",
+                          start_time);
+    network_2_quiet_ = -1;
+  }
+
+  if (network_0_quiet_ > 0 &&
+      start_time - network_0_quiet_ > kNetworkQuietWindowSeconds) {
+    probe::lifecycleEvent(local_root_->GetDocument(), "networkIdle",
+                          start_time);
+    network_0_quiet_ = -1;
+  }
+
   // Reset m_taskExecutionContext. We don't clear this in didProcessTask
   // as it is needed in ReportTaskTime which occurs after didProcessTask.
   task_execution_context_ = nullptr;
@@ -275,6 +361,12 @@
 }
 
 void PerformanceMonitor::DidProcessTask(double start_time, double end_time) {
+  // Shift idle timestamps with the duration of the task, we were not idle.
+  if (network_2_quiet_ > 0)
+    network_2_quiet_ += end_time - start_time;
+  if (network_0_quiet_ > 0)
+    network_0_quiet_ += end_time - start_time;
+
   if (!enabled_)
     return;
   double layout_threshold = thresholds_[kLongLayout];
diff --git a/third_party/WebKit/Source/core/frame/PerformanceMonitor.h b/third_party/WebKit/Source/core/frame/PerformanceMonitor.h
index b58bbbc..6ed1112 100644
--- a/third_party/WebKit/Source/core/frame/PerformanceMonitor.h
+++ b/third_party/WebKit/Source/core/frame/PerformanceMonitor.h
@@ -9,6 +9,7 @@
 #include "core/dom/Document.h"
 #include "core/frame/LocalFrame.h"
 #include "core/timing/SubTaskAttribution.h"
+#include "platform/Timer.h"
 #include "platform/heap/Handle.h"
 #include "platform/scheduler/base/task_time_observer.h"
 #include "platform/wtf/text/AtomicString.h"
@@ -95,6 +96,20 @@
   void Will(const probe::V8Compile&);
   void Did(const probe::V8Compile&);
 
+  void WillSendRequest(ExecutionContext*,
+                       unsigned long,
+                       DocumentLoader*,
+                       ResourceRequest&,
+                       const ResourceResponse&,
+                       const FetchInitiatorInfo&);
+  void DidFailLoading(unsigned long, DocumentLoader*, const ResourceError&);
+  void DidFinishLoading(unsigned long,
+                        DocumentLoader*,
+                        double,
+                        int64_t,
+                        int64_t);
+  void DomContentLoadedEventFired(LocalFrame*);
+
   void DocumentWriteFetchScript(Document*);
 
   // Direct API for core.
@@ -126,8 +141,10 @@
   void WillProcessTask(double start_time) override;
   void DidProcessTask(double start_time, double end_time) override;
   void OnBeginNestedRunLoop() override {}
+
   void WillExecuteScript(ExecutionContext*);
   void DidExecuteScript();
+  void DidLoadResource();
 
   std::pair<String, DOMWindow*> SanitizedAttribution(
       const HeapHashSet<Member<Frame>>& frame_contexts,
@@ -154,6 +171,8 @@
               typename DefaultHash<size_t>::Hash,
               WTF::UnsignedWithZeroKeyHashTraits<size_t>>
       subscriptions_;
+  double network_0_quiet_ = 0;
+  double network_2_quiet_ = 0;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/frame/VisualViewport.h b/third_party/WebKit/Source/core/frame/VisualViewport.h
index 559fbb74..256d6131 100644
--- a/third_party/WebKit/Source/core/frame/VisualViewport.h
+++ b/third_party/WebKit/Source/core/frame/VisualViewport.h
@@ -217,6 +217,11 @@
       IncludeScrollbarsInRect = kExcludeScrollbars) const override;
   RefPtr<WebTaskRunner> GetTimerTaskRunner() const final;
 
+  // VisualViewport scrolling may involve pinch zoom and gets routed through
+  // WebViewImpl explicitly rather than via ScrollingCoordinator::DidScroll
+  // since it needs to be set in tandem with the page scale delta.
+  void DidScroll(const gfx::ScrollOffset&) final { NOTREACHED(); }
+
   // Visual Viewport API implementation.
   double OffsetLeft() const;
   double OffsetTop() const;
diff --git a/third_party/WebKit/Source/core/input/MouseEventManager.cpp b/third_party/WebKit/Source/core/input/MouseEventManager.cpp
index 8586a2a7..9b87ccb 100644
--- a/third_party/WebKit/Source/core/input/MouseEventManager.cpp
+++ b/third_party/WebKit/Source/core/input/MouseEventManager.cpp
@@ -793,10 +793,14 @@
   if (!mouse_pressed_)
     return WebInputEventResult::kNotHandled;
 
-  // We disable the drag and drop actions on pen input.
-  if (!is_pen && HandleDrag(event, DragInitiator::kMouse)) {
+  // We disable the drag and drop actions on pen input on windows.
+  bool should_handle_drag = true;
+#if defined(OS_WIN)
+  should_handle_drag = !is_pen;
+#endif
+
+  if (should_handle_drag && HandleDrag(event, DragInitiator::kMouse))
     return WebInputEventResult::kHandledSystem;
-  }
 
   Node* target_node = event.InnerNode();
   if (!target_node)
diff --git a/third_party/WebKit/Source/core/inspector/InspectorNetworkAgent.cpp b/third_party/WebKit/Source/core/inspector/InspectorNetworkAgent.cpp
index bfa1c034..592adf4f 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorNetworkAgent.cpp
+++ b/third_party/WebKit/Source/core/inspector/InspectorNetworkAgent.cpp
@@ -31,6 +31,8 @@
 #include "core/inspector/InspectorNetworkAgent.h"
 
 #include <memory>
+#include <utility>
+
 #include "bindings/core/v8/ExceptionState.h"
 #include "bindings/core/v8/SourceLocation.h"
 #include "core/dom/Document.h"
@@ -414,9 +416,6 @@
     case ResourceResponse::kSecurityStyleAuthenticationBroken:
       security_state = protocol::Security::SecurityStateEnum::Insecure;
       break;
-    case ResourceResponse::kSecurityStyleWarning:
-      security_state = protocol::Security::SecurityStateEnum::Warning;
-      break;
     case ResourceResponse::kSecurityStyleAuthenticated:
       security_state = protocol::Security::SecurityStateEnum::Secure;
       break;
@@ -872,6 +871,7 @@
 }
 
 void InspectorNetworkAgent::DidFailLoading(unsigned long identifier,
+                                           DocumentLoader*,
                                            const ResourceError& error) {
   String request_id = IdentifiersFactory::RequestId(identifier);
   bool canceled = error.IsCancellation();
diff --git a/third_party/WebKit/Source/core/inspector/InspectorNetworkAgent.h b/third_party/WebKit/Source/core/inspector/InspectorNetworkAgent.h
index ae88889..af1f17a 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorNetworkAgent.h
+++ b/third_party/WebKit/Source/core/inspector/InspectorNetworkAgent.h
@@ -113,7 +113,9 @@
                                       DocumentLoader*,
                                       const ResourceResponse&,
                                       Resource*);
-  void DidFailLoading(unsigned long identifier, const ResourceError&);
+  void DidFailLoading(unsigned long identifier,
+                      DocumentLoader*,
+                      const ResourceError&);
   void DidCommitLoad(LocalFrame*, DocumentLoader*);
   void ScriptImported(unsigned long identifier, const String& source_string);
   void DidReceiveScriptResponse(unsigned long identifier);
diff --git a/third_party/WebKit/Source/core/inspector/InspectorPageAgent.cpp b/third_party/WebKit/Source/core/inspector/InspectorPageAgent.cpp
index 7e32c9d6..a9c007a 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorPageAgent.cpp
+++ b/third_party/WebKit/Source/core/inspector/InspectorPageAgent.cpp
@@ -731,9 +731,9 @@
     FinishReload();
     script_to_evaluate_on_load_once_ = pending_script_to_evaluate_on_load_once_;
     pending_script_to_evaluate_on_load_once_ = String();
+    GetFrontend()->lifecycleEvent("commit", MonotonicallyIncreasingTime());
   }
   GetFrontend()->frameNavigated(BuildObjectForFrame(loader->GetFrame()));
-  GetFrontend()->lifecycleEvent("commit", MonotonicallyIncreasingTime());
 }
 
 void InspectorPageAgent::FrameAttachedToParent(LocalFrame* frame) {
diff --git a/third_party/WebKit/Source/core/inspector/InspectorTraceEvents.cpp b/third_party/WebKit/Source/core/inspector/InspectorTraceEvents.cpp
index efdf4e7..3808d43 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorTraceEvents.cpp
+++ b/third_party/WebKit/Source/core/inspector/InspectorTraceEvents.cpp
@@ -165,6 +165,7 @@
 }
 
 void InspectorTraceEvents::DidFailLoading(unsigned long identifier,
+                                          DocumentLoader* loader,
                                           const ResourceError&) {
   TRACE_EVENT_INSTANT1(
       "devtools.timeline", "ResourceFinish", TRACE_EVENT_SCOPE_THREAD, "data",
diff --git a/third_party/WebKit/Source/core/inspector/InspectorTraceEvents.h b/third_party/WebKit/Source/core/inspector/InspectorTraceEvents.h
index 80b8e97..bd4e0681 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorTraceEvents.h
+++ b/third_party/WebKit/Source/core/inspector/InspectorTraceEvents.h
@@ -94,7 +94,9 @@
                         double monotonic_finish_time,
                         int64_t encoded_data_length,
                         int64_t decoded_body_length);
-  void DidFailLoading(unsigned long identifier, const ResourceError&);
+  void DidFailLoading(unsigned long identifier,
+                      DocumentLoader*,
+                      const ResourceError&);
 
   void Will(const probe::ExecuteScript&);
   void Did(const probe::ExecuteScript&);
diff --git a/third_party/WebKit/Source/core/inspector/browser_protocol-1.2.json b/third_party/WebKit/Source/core/inspector/browser_protocol-1.2.json
index c0f3788..d6ac8d0 100644
--- a/third_party/WebKit/Source/core/inspector/browser_protocol-1.2.json
+++ b/third_party/WebKit/Source/core/inspector/browser_protocol-1.2.json
@@ -823,7 +823,7 @@
             {
                 "id": "SecurityState",
                 "type": "string",
-                "enum": ["unknown", "neutral", "insecure", "warning", "secure", "info"],
+                "enum": ["unknown", "neutral", "insecure", "secure", "info"],
                 "description": "The security level of a page or resource."
             },
             {
diff --git a/third_party/WebKit/Source/core/inspector/browser_protocol.json b/third_party/WebKit/Source/core/inspector/browser_protocol.json
index 58743a2..e725fcf 100644
--- a/third_party/WebKit/Source/core/inspector/browser_protocol.json
+++ b/third_party/WebKit/Source/core/inspector/browser_protocol.json
@@ -1114,7 +1114,7 @@
             {
                 "id": "SecurityState",
                 "type": "string",
-                "enum": ["unknown", "neutral", "insecure", "warning", "secure", "info"],
+                "enum": ["unknown", "neutral", "insecure", "secure", "info"],
                 "description": "The security level of a page or resource."
             },
             {
diff --git a/third_party/WebKit/Source/core/layout/LayoutFlowThread.cpp b/third_party/WebKit/Source/core/layout/LayoutFlowThread.cpp
index c71c3ffea..ecba205 100644
--- a/third_party/WebKit/Source/core/layout/LayoutFlowThread.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutFlowThread.cpp
@@ -49,27 +49,18 @@
       return nullptr;
     if (curr->IsLayoutFlowThread())
       return ToLayoutFlowThread(curr);
-    LayoutObject* container = curr->Container();
+    curr = curr->Container();
+    // Bail when we fall out of the outermost flow thread.
+    if (!curr || !curr->IsInsideFlowThread())
+      return nullptr;
     // If we're inside something strictly unbreakable (due to having scrollbars
     // or being writing mode roots, for instance), it's also strictly
     // unbreakable in any outer fragmentation context. As such, what goes on
     // inside any fragmentation context on the inside of this is completely
     // opaque to ancestor fragmentation contexts.
-    if (constraint == kIsolateUnbreakableContainers && container &&
-        container->IsBox() &&
-        ToLayoutBox(container)->GetPaginationBreakability() == kForbidBreaks)
+    if (constraint == kIsolateUnbreakableContainers && curr->IsBox() &&
+        ToLayoutBox(curr)->GetPaginationBreakability() == kForbidBreaks)
       return nullptr;
-    curr = curr->Parent();
-    while (curr != container) {
-      if (curr->IsLayoutFlowThread()) {
-        // The nearest ancestor flow thread isn't in our containing block chain.
-        // Then we aren't really part of any flow thread, and we should stop
-        // looking. This happens when there are out-of-flow objects or column
-        // spanners.
-        return nullptr;
-      }
-      curr = curr->Parent();
-    }
   }
   return nullptr;
 }
diff --git a/third_party/WebKit/Source/core/layout/LayoutMultiColumnFlowThread.cpp b/third_party/WebKit/Source/core/layout/LayoutMultiColumnFlowThread.cpp
index 6206a6cd..3da0cbf 100644
--- a/third_party/WebKit/Source/core/layout/LayoutMultiColumnFlowThread.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutMultiColumnFlowThread.cpp
@@ -895,6 +895,19 @@
   return false;
 }
 
+bool LayoutMultiColumnFlowThread::IsNearestAncestorFlowThreadOf(
+    LayoutObject* descendant) const {
+  while (descendant != this) {
+    if (descendant->IsLayoutFlowThread()) {
+      // The nearest ancestor flow thread may not be in our containing block
+      // chain when there are out-of-flow objects or column spanners involved.
+      return false;
+    }
+    descendant = descendant->Parent();
+  }
+  return true;
+}
+
 void LayoutMultiColumnFlowThread::AddColumnSetToThread(
     LayoutMultiColumnSet* column_set) {
   if (LayoutMultiColumnSet* next_set =
@@ -1006,6 +1019,8 @@
   // spanner) where needed.
   if (ShouldSkipInsertedOrRemovedChild(this, *descendant))
     return;
+  if (!IsNearestAncestorFlowThreadOf(descendant))
+    return;
   LayoutObject* object_after_subtree =
       NextInPreOrderAfterChildrenSkippingOutOfFlow(this, descendant);
   LayoutObject* next;
@@ -1065,6 +1080,8 @@
     return;
   if (ShouldSkipInsertedOrRemovedChild(this, *descendant))
     return;
+  if (!IsNearestAncestorFlowThreadOf(descendant))
+    return;
   bool had_containing_placeholder =
       ContainingColumnSpannerPlaceholder(descendant);
   bool processed_something = false;
diff --git a/third_party/WebKit/Source/core/layout/LayoutMultiColumnFlowThread.h b/third_party/WebKit/Source/core/layout/LayoutMultiColumnFlowThread.h
index 61e16f8..7c1cc3a5 100644
--- a/third_party/WebKit/Source/core/layout/LayoutMultiColumnFlowThread.h
+++ b/third_party/WebKit/Source/core/layout/LayoutMultiColumnFlowThread.h
@@ -306,6 +306,7 @@
       LayoutObject* inserted_before_in_flow_thread);
   void DestroySpannerPlaceholder(LayoutMultiColumnSpannerPlaceholder*);
   virtual bool DescendantIsValidColumnSpanner(LayoutObject* descendant) const;
+  bool IsNearestAncestorFlowThreadOf(LayoutObject* descendant) const;
 
   void AddColumnSetToThread(LayoutMultiColumnSet*) override;
   void WillBeRemovedFromTree() override;
diff --git a/third_party/WebKit/Source/core/layout/line/BreakingContextInlineHeaders.h b/third_party/WebKit/Source/core/layout/line/BreakingContextInlineHeaders.h
index 686c7e8..bc5addd 100644
--- a/third_party/WebKit/Source/core/layout/line/BreakingContextInlineHeaders.h
+++ b/third_party/WebKit/Source/core/layout/line/BreakingContextInlineHeaders.h
@@ -156,6 +156,7 @@
   bool ShouldMidWordBreak(UChar,
                           LineLayoutText,
                           const Font&,
+                          unsigned& next_grapheme_cluster,
                           float& char_width,
                           float& width_from_last_breaking_opportunity,
                           bool break_all,
@@ -165,7 +166,7 @@
   bool RewindToFirstMidWordBreak(LineLayoutText,
                                  const ComputedStyle&,
                                  const Font&,
-                                 bool break_all,
+                                 const LazyLineBreakIterator&,
                                  WordMeasurement&);
   bool RewindToMidWordBreak(LineLayoutText,
                             const ComputedStyle&,
@@ -701,6 +702,7 @@
     UChar c,
     LineLayoutText layout_text,
     const Font& font,
+    unsigned& next_grapheme_cluster,
     float& char_width,
     float& width_from_last_breaking_opportunity,
     bool break_all,
@@ -718,12 +720,19 @@
   float overflow_allowance = 4 * font.GetFontDescription().ComputedSize();
 
   width_from_last_breaking_opportunity += char_width;
-  bool mid_word_break_is_before_surrogate_pair =
-      U16_IS_LEAD(c) && current_.Offset() + 1 < layout_text.TextLength() &&
-      U16_IS_TRAIL(layout_text.UncheckedCharacterAt(current_.Offset() + 1));
+  unsigned char_len;
+  if (layout_text.Is8Bit()) {
+    // For 8-bit string, code unit == grapheme cluster.
+    char_len = 1;
+  } else {
+    NonSharedCharacterBreakIterator iterator(layout_text.GetText());
+    int next = iterator.Following(current_.Offset());
+    next_grapheme_cluster =
+        next != kTextBreakDone ? next : layout_text.TextLength();
+    char_len = next_grapheme_cluster - current_.Offset();
+  }
   char_width =
-      TextWidth(layout_text, current_.Offset(),
-                mid_word_break_is_before_surrogate_pair ? 2 : 1, font,
+      TextWidth(layout_text, current_.Offset(), char_len, font,
                 width_.CommittedWidth() + width_from_last_breaking_opportunity,
                 collapse_white_space_);
   if (width_.CommittedWidth() + width_from_last_breaking_opportunity +
@@ -742,24 +751,6 @@
   return true;
 }
 
-ALWAYS_INLINE int LastBreakablePositionForBreakAll(LineLayoutText text,
-                                                   const ComputedStyle& style,
-                                                   int start,
-                                                   int end) {
-  LazyLineBreakIterator line_break_iterator(text.GetText(),
-                                            style.LocaleForLineBreakIterator());
-  int last_breakable_position = 0, next_breakable_position = -1;
-  for (int i = start;; i = next_breakable_position + 1) {
-    line_break_iterator.IsBreakable(i, next_breakable_position,
-                                    LineBreakType::kBreakAll);
-    if (next_breakable_position == end)
-      return end;
-    if (next_breakable_position < 0 || next_breakable_position > end)
-      return last_breakable_position;
-    last_breakable_position = next_breakable_position;
-  }
-}
-
 ALWAYS_INLINE bool BreakingContext::RewindToMidWordBreak(
     WordMeasurement& word_measurement,
     int end,
@@ -801,20 +792,12 @@
     LineLayoutText text,
     const ComputedStyle& style,
     const Font& font,
-    bool break_all,
+    const LazyLineBreakIterator& break_iterator,
     WordMeasurement& word_measurement) {
   int start = word_measurement.start_offset;
-  int end = CanMidWordBreakBefore(text) ? start : start + 1;
-  if (break_all) {
-    LazyLineBreakIterator line_break_iterator(
-        text.GetText(), style.LocaleForLineBreakIterator());
-    int next_breakable = -1;
-    line_break_iterator.IsBreakable(end, next_breakable,
-                                    LineBreakType::kBreakAll);
-    if (next_breakable < 0)
-      return false;
-    end = next_breakable;
-  }
+  int end = CanMidWordBreakBefore(text)
+                ? start
+                : break_iterator.NextBreakOpportunity(start + 1);
   if (end >= word_measurement.end_offset)
     return false;
 
@@ -837,10 +820,14 @@
   int len = word_measurement.end_offset - start;
   if (!len)
     return false;
+
+  LazyLineBreakIterator break_iterator(
+      text.GetText(), style.LocaleForLineBreakIterator(),
+      break_all ? LineBreakType::kBreakAll : LineBreakType::kBreakCharacter);
   float x_pos_to_break = width_.AvailableWidth() - width_.CurrentWidth();
   if (x_pos_to_break <= LayoutUnit::Epsilon()) {
     // There were no space left. Skip computing how many characters can fit.
-    return RewindToFirstMidWordBreak(text, style, font, break_all,
+    return RewindToFirstMidWordBreak(text, style, font, break_iterator,
                                      word_measurement);
   }
 
@@ -853,19 +840,18 @@
   if (run.Rtl())
     x_pos_to_break = word_measurement.width - x_pos_to_break;
   len = font.OffsetForPosition(run, x_pos_to_break, false);
+  int end = start + len;
+  if (len) {
+    end = break_iterator.PreviousBreakOpportunity(end, start);
+    len = end - start;
+  }
   if (!len) {
     // No characters can fit in the available space.
-    return RewindToFirstMidWordBreak(text, style, font, break_all,
+    // Break at the first mid-word break opportunity and let the line overflow.
+    return RewindToFirstMidWordBreak(text, style, font, break_iterator,
                                      word_measurement);
   }
 
-  int end = start + len;
-  if (break_all) {
-    end = LastBreakablePositionForBreakAll(text, style, start, end);
-    if (!end)
-      return false;
-    len = end - start;
-  }
   FloatRect rect = font.SelectionRectForText(run, FloatPoint(), 0, 0, len);
   return RewindToMidWordBreak(word_measurement, end, rect.Width());
 }
@@ -944,6 +930,7 @@
   const Font& font = style.GetFont();
 
   unsigned last_space = current_.Offset();
+  unsigned next_grapheme_cluster = 0;
   float word_spacing = current_style_->WordSpacing();
   float last_space_word_spacing = 0;
   float word_spacing_for_word_measurement = 0;
@@ -1037,11 +1024,13 @@
     bool apply_word_spacing = false;
 
     // Determine if we should try breaking in the middle of a word.
-    if (can_break_mid_word && !mid_word_break && !U16_IS_TRAIL(c))
+    if (can_break_mid_word && !mid_word_break &&
+        current_.Offset() >= next_grapheme_cluster) {
       mid_word_break =
-          ShouldMidWordBreak(c, layout_text, font, char_width,
-                             width_from_last_breaking_opportunity, break_all,
-                             next_breakable_position_for_break_all);
+          ShouldMidWordBreak(c, layout_text, font, next_grapheme_cluster,
+                             char_width, width_from_last_breaking_opportunity,
+                             break_all, next_breakable_position_for_break_all);
+    }
 
     // Determine if we are in the whitespace between words.
     int next_breakable_position = current_.NextBreakablePosition();
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_block_break_token.cc b/third_party/WebKit/Source/core/layout/ng/ng_block_break_token.cc
index 7607150..16fecf5 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_block_break_token.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_block_break_token.cc
@@ -15,7 +15,9 @@
   child_break_tokens_.swap(child_break_tokens);
 }
 
-NGBlockBreakToken::NGBlockBreakToken(NGLayoutInputNode node)
-    : NGBreakToken(kBlockBreakToken, kFinished, node) {}
+NGBlockBreakToken::NGBlockBreakToken(NGLayoutInputNode node,
+                                     LayoutUnit used_block_size)
+    : NGBreakToken(kBlockBreakToken, kFinished, node),
+      used_block_size_(used_block_size) {}
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_block_break_token.h b/third_party/WebKit/Source/core/layout/ng/ng_block_break_token.h
index c580ceb..72323d5 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_block_break_token.h
+++ b/third_party/WebKit/Source/core/layout/ng/ng_block_break_token.h
@@ -31,8 +31,9 @@
   }
 
   // Creates a break token for a node which cannot produce any more fragments.
-  static RefPtr<NGBlockBreakToken> Create(NGLayoutInputNode node) {
-    return AdoptRef(new NGBlockBreakToken(node));
+  static RefPtr<NGBlockBreakToken> Create(NGLayoutInputNode node,
+                                          LayoutUnit used_block_size) {
+    return AdoptRef(new NGBlockBreakToken(node, used_block_size));
   }
 
   // Represents the amount of block size used in previous fragments.
@@ -59,7 +60,7 @@
                     LayoutUnit used_block_size,
                     Vector<RefPtr<NGBreakToken>>& child_break_tokens);
 
-  explicit NGBlockBreakToken(NGLayoutInputNode node);
+  NGBlockBreakToken(NGLayoutInputNode node, LayoutUnit used_block_size);
 
   LayoutUnit used_block_size_;
   Vector<RefPtr<NGBreakToken>> child_break_tokens_;
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_block_child_iterator_test.cc b/third_party/WebKit/Source/core/layout/ng/ng_block_child_iterator_test.cc
index 795b086..2ec30e5 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_block_child_iterator_test.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_block_child_iterator_test.cc
@@ -64,7 +64,7 @@
   NGLayoutInputNode node3 = node2.NextSibling();
 
   Vector<RefPtr<NGBreakToken>> child_break_tokens;
-  child_break_tokens.push_back(NGBlockBreakToken::Create(node1));
+  child_break_tokens.push_back(NGBlockBreakToken::Create(node1, LayoutUnit()));
   RefPtr<NGBlockBreakToken> parent_token =
       NGBlockBreakToken::Create(container, LayoutUnit(50), child_break_tokens);
 
@@ -75,7 +75,7 @@
   ASSERT_EQ(NGBlockChildIterator::Entry(nullptr, nullptr),
             iterator.NextChild());
 
-  child_break_tokens.push_back(NGBlockBreakToken::Create(node2));
+  child_break_tokens.push_back(NGBlockBreakToken::Create(node2, LayoutUnit()));
   parent_token =
       NGBlockBreakToken::Create(container, LayoutUnit(50), child_break_tokens);
 
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc
index a0750de..af7989c 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc
@@ -925,6 +925,7 @@
     // need to prepare a break token.
     container_builder_.SetUsedBlockSize(std::min(space_left, block_size) +
                                         used_block_size);
+    container_builder_.SetDidBreak();
     container_builder_.SetBlockSize(std::min(space_left, block_size));
     container_builder_.SetBlockOverflow(space_left);
     return;
@@ -933,12 +934,14 @@
   if (block_size > space_left) {
     // Need a break inside this block.
     container_builder_.SetUsedBlockSize(space_left + used_block_size);
+    container_builder_.SetDidBreak();
     container_builder_.SetBlockSize(space_left);
     container_builder_.SetBlockOverflow(space_left);
     return;
   }
 
   // The end of the block fits in the current fragmentainer.
+  container_builder_.SetUsedBlockSize(used_block_size + block_size);
   container_builder_.SetBlockSize(block_size);
   container_builder_.SetBlockOverflow(content_size_);
 }
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_block_node.cc b/third_party/WebKit/Source/core/layout/ng/ng_block_node.cc
index 66bbfdd..71accc5f 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_block_node.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_block_node.cc
@@ -46,32 +46,63 @@
   return layout_object && layout_object->IsFloating() && fragment.IsBox();
 }
 
-void UpdateLegacyMultiColumnFlowThread(LayoutBox* layout_box,
-                                       const NGPhysicalBoxFragment* fragment) {
+void UpdateLegacyMultiColumnFlowThread(
+    LayoutBox* layout_box,
+    const NGConstraintSpace& constraint_space,
+    const NGPhysicalBoxFragment* fragment) {
   LayoutBlockFlow* multicol = ToLayoutBlockFlow(layout_box);
   LayoutMultiColumnFlowThread* flow_thread = multicol->MultiColumnFlowThread();
   if (!flow_thread)
     return;
+
+  NGWritingMode writing_mode = constraint_space.WritingMode();
+  LayoutUnit column_inline_size;
+  LayoutUnit flow_end;
+
+  // Stitch the columns together.
+  for (const RefPtr<NGPhysicalFragment> child : fragment->Children()) {
+    NGBoxFragment child_fragment(writing_mode,
+                                 ToNGPhysicalBoxFragment(child.Get()));
+    flow_end += child_fragment.BlockSize();
+    column_inline_size = child_fragment.InlineSize();
+  }
+
   if (LayoutMultiColumnSet* column_set = flow_thread->FirstMultiColumnSet()) {
-    column_set->SetWidth(fragment->Size().width);
-    column_set->SetHeight(fragment->Size().height);
-
-    // TODO(mstensho): This value has next to nothing to do with the flow thread
-    // portion size, but at least it's usually better than zero.
-    column_set->EndFlow(fragment->Size().height);
-
+    NGBoxFragment logical_fragment(writing_mode, fragment);
+    column_set->SetLogicalWidth(logical_fragment.InlineSize());
+    column_set->SetLogicalHeight(logical_fragment.BlockSize());
+    column_set->EndFlow(flow_end);
     column_set->UpdateFromNG();
   }
-  // TODO(mstensho): Fix the relatively nonsensical values here (the content box
-  // size of the multicol container has very little to do with the price of
-  // eggs).
-  flow_thread->SetWidth(fragment->Size().width);
-  flow_thread->SetHeight(fragment->Size().height);
+  // TODO(mstensho): Update all column boxes, not just the first column set
+  // (like we do above). This is needed to support column-span:all.
+  for (LayoutBox* column_box = flow_thread->FirstMultiColumnBox(); column_box;
+       column_box = column_box->NextSiblingMultiColumnBox())
+    column_box->ClearNeedsLayout();
 
   flow_thread->ValidateColumnSets();
+  flow_thread->SetLogicalWidth(column_inline_size);
+  flow_thread->SetLogicalHeight(flow_end);
   flow_thread->ClearNeedsLayout();
 }
 
+// Return true if the specified fragment is the first generated fragment of
+// some node.
+bool IsFirstFragment(const NGConstraintSpace& constraint_space,
+                     const NGPhysicalBoxFragment& fragment) {
+  const auto* break_token = ToNGBlockBreakToken(fragment.BreakToken());
+  if (!break_token)
+    return true;
+  NGBoxFragment logical_fragment(constraint_space.WritingMode(), &fragment);
+  return break_token->UsedBlockSize() <= logical_fragment.BlockSize();
+}
+
+// Return true if the specified fragment is the final fragment of some node.
+bool IsLastFragment(const NGPhysicalBoxFragment& fragment) {
+  const auto* break_token = fragment.BreakToken();
+  return !break_token || break_token->IsFinished();
+}
+
 }  // namespace
 
 NGBlockNode::NGBlockNode(LayoutBox* box) : NGLayoutInputNode(box, kBlock) {}
@@ -196,21 +227,38 @@
 void NGBlockNode::CopyFragmentDataToLayoutBox(
     const NGConstraintSpace& constraint_space,
     NGLayoutResult* layout_result) {
-  NGPhysicalBoxFragment* physical_fragment =
+  const NGPhysicalBoxFragment* physical_fragment =
       ToNGPhysicalBoxFragment(layout_result->PhysicalFragment().Get());
-  if (box_->Style()->SpecifiesColumns())
-    UpdateLegacyMultiColumnFlowThread(box_, physical_fragment);
-  box_->SetWidth(physical_fragment->Size().width);
-  box_->SetHeight(physical_fragment->Size().height);
+  if (box_->Style()->SpecifiesColumns()) {
+    UpdateLegacyMultiColumnFlowThread(box_, constraint_space,
+                                      physical_fragment);
+  }
+  NGBoxFragment fragment(constraint_space.WritingMode(), physical_fragment);
+  // For each fragment we process, we'll accumulate the logical height and
+  // logical intrinsic content box height. We reset it at the first fragment,
+  // and accumulate at each method call for fragments belonging to the same
+  // layout object. Logical width will only be set at the first fragment and is
+  // expected to remain the same throughout all subsequent fragments, since
+  // legacy layout doesn't support non-uniform fragmentainer widths.
+  LayoutUnit logical_height;
+  LayoutUnit intrinsic_content_logical_height;
+  if (IsFirstFragment(constraint_space, *physical_fragment)) {
+    box_->SetLogicalWidth(fragment.InlineSize());
+  } else {
+    DCHECK_EQ(box_->LogicalWidth(), fragment.InlineSize())
+        << "Variable fragment inline size not supported";
+    logical_height = box_->LogicalHeight();
+    intrinsic_content_logical_height = box_->IntrinsicContentLogicalHeight();
+  }
+  logical_height += fragment.BlockSize();
+  intrinsic_content_logical_height += fragment.OverflowSize().block_size;
   NGBoxStrut border_scrollbar_padding =
       ComputeBorders(constraint_space, Style()) +
       ComputePadding(constraint_space, Style()) + GetScrollbarSizes(box_);
-  LayoutUnit intrinsic_logical_height =
-      box_->Style()->IsHorizontalWritingMode()
-          ? physical_fragment->OverflowSize().height
-          : physical_fragment->OverflowSize().width;
-  intrinsic_logical_height -= border_scrollbar_padding.BlockSum();
-  box_->SetIntrinsicContentLogicalHeight(intrinsic_logical_height);
+  if (IsLastFragment(*physical_fragment))
+    intrinsic_content_logical_height -= border_scrollbar_padding.BlockSum();
+  box_->SetLogicalHeight(logical_height);
+  box_->SetIntrinsicContentLogicalHeight(intrinsic_content_logical_height);
 
   // LayoutBox::Margin*() should be used value, while we set computed value
   // here. This is not entirely correct, but these values are not used for
@@ -247,7 +295,9 @@
         }
       }
     } else {
-      CopyChildFragmentPosition(ToNGPhysicalBoxFragment(*child_fragment));
+      const auto& box_fragment = *ToNGPhysicalBoxFragment(child_fragment.Get());
+      if (IsFirstFragment(constraint_space, box_fragment))
+        CopyChildFragmentPosition(box_fragment);
 
       if (child_fragment->GetLayoutObject()->IsLayoutBlockFlow())
         ToLayoutBlockFlow(child_fragment->GetLayoutObject())
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.cc b/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.cc
index 9fb3957..4b3519e 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.cc
@@ -196,7 +196,7 @@
       break_token = NGBlockBreakToken::Create(node_, used_block_size_,
                                               child_break_tokens_);
     } else {
-      break_token = NGBlockBreakToken::Create(node_);
+      break_token = NGBlockBreakToken::Create(node_, used_block_size_);
     }
   }
 
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.h b/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.h
index 1e699827..6039007 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.h
+++ b/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.h
@@ -87,12 +87,16 @@
 
   NGFragmentBuilder& AddOutOfFlowDescendant(NGOutOfFlowPositionedDescendant);
 
-  // Sets how much of the block size we've used so far for this box.
-  //
-  // This will result in a fragment which has an unfinished break token, which
-  // contains this information.
+  // Set how much of the block size we've used so far for this box.
   NGFragmentBuilder& SetUsedBlockSize(LayoutUnit used_block_size) {
     used_block_size_ = used_block_size;
+    return *this;
+  }
+
+  // Specify that we broke.
+  //
+  // This will result in a fragment which has an unfinished break token.
+  NGFragmentBuilder& SetDidBreak() {
     did_break_ = true;
     return *this;
   }
diff --git a/third_party/WebKit/Source/core/loader/FrameFetchContext.cpp b/third_party/WebKit/Source/core/loader/FrameFetchContext.cpp
index 694b616..515f954e 100644
--- a/third_party/WebKit/Source/core/loader/FrameFetchContext.cpp
+++ b/third_party/WebKit/Source/core/loader/FrameFetchContext.cpp
@@ -556,7 +556,8 @@
     return;
 
   GetFrame()->Loader().Progress().CompleteProgress(identifier);
-  probe::didFailLoading(GetFrame()->GetDocument(), identifier, error);
+  probe::didFailLoading(GetFrame()->GetDocument(), identifier,
+                        MasterDocumentLoader(), error);
   // Notification to FrameConsole should come AFTER InspectorInstrumentation
   // call, DevTools front-end relies on this.
   if (!is_internal_request)
diff --git a/third_party/WebKit/Source/core/loader/WorkerFetchContext.cpp b/third_party/WebKit/Source/core/loader/WorkerFetchContext.cpp
index 105c7837..e0a2ad97 100644
--- a/third_party/WebKit/Source/core/loader/WorkerFetchContext.cpp
+++ b/third_party/WebKit/Source/core/loader/WorkerFetchContext.cpp
@@ -309,7 +309,7 @@
                                          const ResourceError& error,
                                          int64_t encoded_data_length,
                                          bool is_internal_request) {
-  probe::didFailLoading(global_scope_, identifier, error);
+  probe::didFailLoading(global_scope_, identifier, nullptr, error);
 }
 
 void WorkerFetchContext::AddResourceTiming(const ResourceTimingInfo& info) {
diff --git a/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.cpp b/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.cpp
index 05743c6..3ec9b65 100644
--- a/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.cpp
+++ b/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.cpp
@@ -51,7 +51,6 @@
 #include "platform/exported/WebScrollbarThemeGeometryNative.h"
 #include "platform/geometry/Region.h"
 #include "platform/geometry/TransformState.h"
-#include "platform/graphics/CompositorElementId.h"
 #include "platform/graphics/GraphicsLayer.h"
 #include "platform/instrumentation/tracing/TraceEvent.h"
 #if defined(OS_MACOSX)
@@ -174,6 +173,29 @@
   scroll_gesture_region_is_dirty_ = true;
 }
 
+void ScrollingCoordinator::DidScroll(const gfx::ScrollOffset& offset,
+                                     const CompositorElementId& element_id) {
+  for (auto* frame = page_->MainFrame(); frame;
+       frame = frame->Tree().TraverseNext()) {
+    // Remote frames will receive DidScroll callbacks from their own compositor.
+    if (!frame->IsLocalFrame())
+      continue;
+
+    // Find the associated scrollable area using the element id and notify it
+    // of the compositor-side scroll. We explicitly do not check the
+    // VisualViewport which handles scroll offset differently (see:
+    // VisualViewport::didScroll).
+    if (LocalFrameView* view = ToLocalFrame(frame)->View()) {
+      if (auto* scrollable = view->ScrollableAreaWithElementId(element_id)) {
+        scrollable->DidScroll(offset);
+        return;
+      }
+    }
+  }
+  // The ScrollableArea with matching ElementId may have been deleted and we can
+  // safely ignore the DidScroll callback.
+}
+
 void ScrollingCoordinator::UpdateAfterCompositingChangeIfNeeded() {
   if (!page_->MainFrame()->IsLocalFrame())
     return;
@@ -302,12 +324,6 @@
 
 void ScrollingCoordinator::WillDestroyScrollableArea(
     ScrollableArea* scrollable_area) {
-  {
-    // Remove any callback from |scroll_layer| to this object.
-    DisableCompositingQueryAsserts disabler;
-    if (GraphicsLayer* scroll_layer = scrollable_area->LayerForScrolling())
-      scroll_layer->SetScrollableArea(nullptr, false);
-  }
   RemoveWebScrollbarLayer(scrollable_area, kHorizontalScrollbar);
   RemoveWebScrollbarLayer(scrollable_area, kVerticalScrollbar);
 }
@@ -483,12 +499,8 @@
     return false;
 
   GraphicsLayer* scroll_layer = scrollable_area->LayerForScrolling();
-
-  if (scroll_layer) {
-    bool is_for_visual_viewport =
-        scrollable_area == &page_->GetVisualViewport();
-    scroll_layer->SetScrollableArea(scrollable_area, is_for_visual_viewport);
-  }
+  if (scroll_layer)
+    scroll_layer->SetScrollableArea(scrollable_area);
 
   UpdateUserInputScrollable(scrollable_area);
 
@@ -499,8 +511,12 @@
     FloatPoint scroll_position(scrollable_area->ScrollOrigin() +
                                scrollable_area->GetScrollOffset());
     web_layer->SetScrollPosition(scroll_position);
-
     web_layer->SetBounds(scrollable_area->ContentsSize());
+    // VisualViewport scrolling may involve pinch zoom and gets routed through
+    // WebViewImpl explicitly rather than via ScrollingCoordinator::DidScroll
+    // since it needs to be set in tandem with the page scale delta.
+    if (scrollable_area != &page_->GetVisualViewport())
+      web_layer->SetScrollClient(this);
   }
   if (WebScrollbarLayer* scrollbar_layer =
           GetWebScrollbarLayer(scrollable_area, kHorizontalScrollbar)) {
diff --git a/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.h b/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.h
index 770f210..66a83ed2 100644
--- a/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.h
+++ b/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.h
@@ -30,11 +30,13 @@
 #include "core/CoreExport.h"
 #include "core/paint/LayerHitTestRects.h"
 #include "platform/geometry/IntRect.h"
+#include "platform/graphics/CompositorElementId.h"
 #include "platform/heap/Handle.h"
 #include "platform/scroll/MainThreadScrollingReason.h"
 #include "platform/scroll/ScrollTypes.h"
 #include "platform/wtf/Noncopyable.h"
 #include "platform/wtf/text/WTFString.h"
+#include "public/platform/WebLayerScrollClient.h"
 
 namespace blink {
 using MainThreadScrollingReasons = uint32_t;
@@ -63,7 +65,8 @@
 // compositor, as well as creating and managing scrollbar layers.
 
 class CORE_EXPORT ScrollingCoordinator final
-    : public GarbageCollectedFinalized<ScrollingCoordinator> {
+    : public GarbageCollectedFinalized<ScrollingCoordinator>,
+      public WebLayerScrollClient {
   WTF_MAKE_NONCOPYABLE(ScrollingCoordinator);
 
  public:
@@ -146,6 +149,9 @@
     return programmatic_scroll_animator_timeline_.get();
   }
 
+  // Callback for compositor-side layer scrolls.
+  void DidScroll(const gfx::ScrollOffset&, const CompositorElementId&) final;
+
   // For testing purposes only. This ScrollingCoordinator is reused between
   // layout test, and must be reset for the results to be valid.
   void Reset();
diff --git a/third_party/WebKit/Source/core/page/scrolling/SmoothScrollTest.cpp b/third_party/WebKit/Source/core/page/scrolling/SmoothScrollTest.cpp
index 481607f02..4d4d8cb8 100644
--- a/third_party/WebKit/Source/core/page/scrolling/SmoothScrollTest.cpp
+++ b/third_party/WebKit/Source/core/page/scrolling/SmoothScrollTest.cpp
@@ -387,6 +387,35 @@
   ASSERT_EQ(container->scrollTop(), 0);
 }
 
+TEST_F(SmoothScrollTest, ApplyRootElementScrollBehaviorToViewport) {
+  v8::HandleScope HandleScope(v8::Isolate::GetCurrent());
+  WebView().Resize(WebSize(800, 600));
+  SimRequest request("https://example.com/test.html", "text/html");
+  LoadURL("https://example.com/test.html");
+  request.Complete("<html style='scroll-behavior: smooth'>"
+      "<div id='space' style='height: 1000px'></div>"
+      "<div id='content' style='height: 1000px'></div></html>");
+
+  Element* content = GetDocument().getElementById("content");
+  ScrollIntoViewOptionsOrBoolean arg;
+  ScrollIntoViewOptions options;
+  options.setBlock("start");
+  arg.setScrollIntoViewOptions(options);
+  Compositor().BeginFrame();
+  ASSERT_EQ(Window().scrollY(), 0);
+
+  content->scrollIntoView(arg);
+  // Scrolling the container
+  Compositor().BeginFrame();  // update run_state_.
+  Compositor().BeginFrame();  // Set start_time = now.
+  Compositor().BeginFrame(0.2);
+  ASSERT_EQ(Window().scrollY(), 299);
+
+  // Finish scrolling the container
+  Compositor().BeginFrame(1);
+  ASSERT_EQ(Window().scrollY(), content->OffsetTop());
+}
+
 }  // namespace
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/paint/FirstMeaningfulPaintDetector.cpp b/third_party/WebKit/Source/core/paint/FirstMeaningfulPaintDetector.cpp
index a0db648..a599b9b 100644
--- a/third_party/WebKit/Source/core/paint/FirstMeaningfulPaintDetector.cpp
+++ b/third_party/WebKit/Source/core/paint/FirstMeaningfulPaintDetector.cpp
@@ -159,8 +159,6 @@
       !paint_timing_->FirstContentfulPaintRendered())
     return;
   network0_quiet_reached_ = true;
-  probe::lifecycleEvent(GetDocument(), "networkIdle",
-                        MonotonicallyIncreasingTime());
 
   if (provisional_first_meaningful_paint_) {
     // Enforce FirstContentfulPaint <= FirstMeaningfulPaint.
@@ -176,8 +174,6 @@
       !paint_timing_->FirstContentfulPaintRendered())
     return;
   network2_quiet_reached_ = true;
-  probe::lifecycleEvent(GetDocument(), "networkAlmostIdle",
-                        MonotonicallyIncreasingTime());
 
   if (provisional_first_meaningful_paint_) {
     // If there's only been one contentful paint, then there won't have been
diff --git a/third_party/WebKit/Source/core/paint/PaintInvalidationCapableScrollableArea.cpp b/third_party/WebKit/Source/core/paint/PaintInvalidationCapableScrollableArea.cpp
index 38da685..d071c02 100644
--- a/third_party/WebKit/Source/core/paint/PaintInvalidationCapableScrollableArea.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintInvalidationCapableScrollableArea.cpp
@@ -274,11 +274,8 @@
 
 CompositorElementId
 PaintInvalidationCapableScrollableArea::GetCompositorElementId() const {
-  return GetLayoutBox()
-      ->FirstFragment()
-      ->PaintProperties()
-      ->ScrollTranslation()
-      ->GetCompositorElementId();
+  return CompositorElementIdFromLayoutObjectId(
+      GetLayoutBox()->UniqueId(), CompositorElementIdNamespace::kScroll);
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp b/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp
index 058eceb6..4c69713 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp
@@ -155,8 +155,7 @@
 
 void PaintLayerScrollableArea::DidScroll(const gfx::ScrollOffset& offset) {
   ScrollableArea::DidScroll(offset);
-  // Ensure that there is no DidScroll callback from the composited scrolling
-  // cc::Layer to this object.
+  // This should be alive if it receives composited scroll callbacks.
   CHECK(!has_been_disposed_);
 }
 
diff --git a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
index 4f1754ff..1cd5c6f 100644
--- a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
@@ -16,6 +16,8 @@
 #include "core/layout/svg/SVGLayoutSupport.h"
 #include "core/layout/svg/SVGResources.h"
 #include "core/layout/svg/SVGResourcesCache.h"
+#include "core/page/Page.h"
+#include "core/page/scrolling/ScrollingCoordinator.h"
 #include "core/paint/FindPaintOffsetAndVisualRectNeedingUpdate.h"
 #include "core/paint/FindPropertiesNeedingUpdate.h"
 #include "core/paint/ObjectPaintProperties.h"
@@ -194,7 +196,7 @@
       full_context.force_subtree_update |= UpdateScroll(
           frame_view, context.current.scroll, scroll_clip, scroll_bounds,
           user_scrollable_horizontal, user_scrollable_vertical, reasons,
-          frame_view.GetScrollableArea());
+          frame_view.GetPage()->GetScrollingCoordinator());
     } else if (frame_view.ScrollNode()) {
       // Ensure pre-existing properties are cleared if there is no scrolling.
       frame_view.SetScrollNode(nullptr);
@@ -1018,14 +1020,14 @@
           force_subtree_update = true;
       }
 
-      auto element_id = CompositorElementIdFromLayoutObjectId(
-          object.UniqueId(), CompositorElementIdNamespace::kScroll);
+      auto element_id = scrollable_area->GetCompositorElementId();
 
       // TODO(pdr): Set the correct compositing reasons here.
       auto result = properties.UpdateScroll(
           context.current.scroll, bounds_offset, container_bounds,
           scroll_bounds, user_scrollable_horizontal, user_scrollable_vertical,
-          reasons, element_id, scrollable_area);
+          reasons, element_id,
+          object.GetFrameView()->GetPage()->GetScrollingCoordinator());
       force_subtree_update |= result.NewNodeCreated();
     } else {
       // Ensure pre-existing properties are cleared.
diff --git a/third_party/WebKit/Source/core/paint/TableSectionPainter.cpp b/third_party/WebKit/Source/core/paint/TableSectionPainter.cpp
index b3e59fed..77452357 100644
--- a/third_party/WebKit/Source/core/paint/TableSectionPainter.cpp
+++ b/third_party/WebKit/Source/core/paint/TableSectionPainter.cpp
@@ -28,11 +28,6 @@
     return;
 
   LayoutTable* table = layout_table_section_.Table();
-  // TODO(crbug.com/757947): This shouldn't be possible but happens to
-  // column-spanners in nested multi-col contexts.
-  if (!table->IsPageLogicalHeightKnown())
-    return;
-
   LayoutPoint pagination_offset = paint_offset;
   LayoutUnit page_height = table->PageLogicalHeightForOffset(LayoutUnit());
 
@@ -91,11 +86,6 @@
   // Work out the top position of the table so we can decide
   // which page to paint the first footer on.
   LayoutTable* table = layout_table_section_.Table();
-  // TODO(crbug.com/757947): This shouldn't be possible but happens to
-  // column-spanners in nested multi-col contexts.
-  if (!table->IsPageLogicalHeightKnown())
-    return;
-
   LayoutRect sections_rect(LayoutPoint(), table->Size());
   table->SubtractCaptionRect(sections_rect);
   LayoutUnit page_height = table->PageLogicalHeightForOffset(LayoutUnit());
diff --git a/third_party/WebKit/Source/core/probe/CoreProbes.json5 b/third_party/WebKit/Source/core/probe/CoreProbes.json5
index 69f20b2..546e68e 100644
--- a/third_party/WebKit/Source/core/probe/CoreProbes.json5
+++ b/third_party/WebKit/Source/core/probe/CoreProbes.json5
@@ -150,12 +150,16 @@
       include_path: "core/frame",
       probes: [
         "CallFunction",
-        "documentWriteFetchScript",
         "ExecuteScript",
         "RecalculateStyle",
         "UpdateLayout",
         "UserCallback",
         "V8Compile",
+        "didFailLoading",
+        "didFinishLoading",
+        "documentWriteFetchScript",
+        "domContentLoadedEventFired",
+        "willSendRequest",
       ]
     },
     PerformanceMetrics: {
diff --git a/third_party/WebKit/Source/core/probe/CoreProbes.pidl b/third_party/WebKit/Source/core/probe/CoreProbes.pidl
index 4a271c3..bac545c 100644
--- a/third_party/WebKit/Source/core/probe/CoreProbes.pidl
+++ b/third_party/WebKit/Source/core/probe/CoreProbes.pidl
@@ -96,7 +96,7 @@
   void didReceiveEncodedDataLength(ExecutionContext*, unsigned long identifier, int encodedDataLength);
   void didFinishLoading(ExecutionContext*, unsigned long identifier, DocumentLoader*, double finishTime, int64_t encodedDataLength, int64_t decodedBodyLength);
   void didReceiveCORSRedirectResponse(ExecutionContext*, unsigned long identifier, DocumentLoader*, const ResourceResponse&, Resource*);
-  void didFailLoading(ExecutionContext*, unsigned long identifier, const ResourceError&);
+  void didFailLoading(ExecutionContext*, unsigned long identifier, DocumentLoader*, const ResourceError&);
   void documentThreadableLoaderStartedLoadingForClient(ExecutionContext*, unsigned long identifier, ThreadableLoaderClient* client);
   void documentThreadableLoaderFailedToStartLoadingForClient(ExecutionContext*, ThreadableLoaderClient* client);
   void willSendEventSourceRequest(ExecutionContext*, ThreadableLoaderClient* eventSource);
diff --git a/third_party/WebKit/Source/core/timing/PerformanceServerTiming.idl b/third_party/WebKit/Source/core/timing/PerformanceServerTiming.idl
index 5026f3a..e1ce24a 100644
--- a/third_party/WebKit/Source/core/timing/PerformanceServerTiming.idl
+++ b/third_party/WebKit/Source/core/timing/PerformanceServerTiming.idl
@@ -10,4 +10,5 @@
     readonly attribute DOMString name;
     readonly attribute DOMHighResTimeStamp duration;
     readonly attribute DOMString description;
+    serializer = {attribute};
 };
diff --git a/third_party/WebKit/Source/devtools/front_end/platform/utilities.js b/third_party/WebKit/Source/devtools/front_end/platform/utilities.js
index 87159e46..dca1766 100644
--- a/third_party/WebKit/Source/devtools/front_end/platform/utilities.js
+++ b/third_party/WebKit/Source/devtools/front_end/platform/utilities.js
@@ -1227,6 +1227,8 @@
    */
   delete: function(key, value) {
     var values = this.get(key);
+    if (!values)
+      return false;
     var result = values.delete(value);
     if (!values.size)
       this._map.delete(key);
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/NetworkManager.js b/third_party/WebKit/Source/devtools/front_end/sdk/NetworkManager.js
index bf78a55b..f955e51 100644
--- a/third_party/WebKit/Source/devtools/front_end/sdk/NetworkManager.js
+++ b/third_party/WebKit/Source/devtools/front_end/sdk/NetworkManager.js
@@ -664,7 +664,9 @@
   requestIntercepted(
       interceptionId, request, resourceType, isNavigationRequest, redirectHeaders, redirectStatusCode, redirectUrl,
       authChallenge) {
-    // Stub implementation.  Event not currently used by the frontend.
+    SDK.multitargetNetworkManager._requestIntercepted(new SDK.InterceptedRequest(
+        this._manager.target().networkAgent(), interceptionId, request, resourceType, isNavigationRequest,
+        redirectHeaders, redirectStatusCode, redirectUrl, authChallenge));
   }
 
   /**
@@ -757,12 +759,18 @@
     this._agents = new Set();
     /** @type {!SDK.NetworkManager.Conditions} */
     this._networkConditions = SDK.NetworkManager.NoThrottlingConditions;
+    /** @type {?Promise} */
+    this._updatingInterceptionPatternsPromise = null;
 
+    // TODO(allada) Remove these and merge it with request interception.
     this._blockingEnabledSetting = Common.moduleSetting('requestBlockingEnabled');
     this._blockedPatternsSetting = Common.settings.createSetting('networkBlockedPatterns', []);
     this._effectiveBlockedURLs = [];
     this._updateBlockedPatterns();
 
+    /** @type {!Multimap<string, !SDK.RequestInterceptor>} */
+    this._requestInterceptorMap = new Multimap();
+
     SDK.targetManager.observeTargets(this, SDK.Target.Capability.Network);
   }
 
@@ -791,6 +799,8 @@
       networkAgent.setUserAgentOverride(this._currentUserAgent());
     if (this._effectiveBlockedURLs.length)
       networkAgent.setBlockedURLs(this._effectiveBlockedURLs);
+    if (this.isIntercepting())
+      networkAgent.setRequestInterceptionEnabled(true, this._requestInterceptorMap.keysArray());
     this._agents.add(networkAgent);
     if (this.isThrottling())
       this._updateNetworkConditions(networkAgent);
@@ -899,6 +909,7 @@
     this._updateUserAgentOverride();
   }
 
+  // TODO(allada) Move all request blocking into interception and let view manage blocking.
   /**
    * @return {!Array<!SDK.NetworkManager.BlockedPattern>}
    */
@@ -956,6 +967,77 @@
       agent.setBlockedURLs(this._effectiveBlockedURLs);
   }
 
+  /**
+   * @return {boolean}
+   */
+  isIntercepting() {
+    return !!this._requestInterceptorMap.size;
+  }
+
+  /**
+   * @param {!SDK.RequestInterceptor} requestInterceptor
+   * @param {!Set<string>=} oldPatterns
+   * @return {!Promise}
+   */
+  _registerRequestInterceptor(requestInterceptor, oldPatterns) {
+    var newPatterns = requestInterceptor.enabled() ? requestInterceptor.patterns() : new Set();
+    var patternsToUnregister = new Set(oldPatterns);
+
+    for (var pattern of newPatterns) {
+      if (patternsToUnregister.has(pattern)) {
+        patternsToUnregister.delete(pattern);
+        continue;
+      }
+      this._requestInterceptorMap.set(pattern, requestInterceptor);
+    }
+    for (var pattern of patternsToUnregister)
+      this._requestInterceptorMap.delete(pattern, requestInterceptor);
+    return this._updateInterceptionPatternsOnNextTick();
+  }
+
+  /**
+   * @return {!Promise}
+   */
+  _updateInterceptionPatternsOnNextTick() {
+    // This is used so we can register and unregister patterns in loops without sending lots of protocol messages.
+    if (!this._updatingInterceptionPatternsPromise)
+      this._updatingInterceptionPatternsPromise = Promise.resolve().then(this._updateInterceptionPatterns.bind(this));
+    return this._updatingInterceptionPatternsPromise;
+  }
+
+  /**
+   * @return {!Promise}
+   */
+  _updateInterceptionPatterns() {
+    this._updatingInterceptionPatternsPromise = null;
+    var promises = /** @type {!Array<!Promise>} */ ([]);
+    for (var agent of this._agents) {
+      // We do not allow '?' as a single character wild card for now.
+      var patterns = this._requestInterceptorMap.keysArray().map(pattern => pattern.replace(/([\\?])/g, '\\$1'));
+      promises.push(agent.setRequestInterceptionEnabled(this.isIntercepting(), patterns));
+    }
+    this.dispatchEventToListeners(SDK.MultitargetNetworkManager.Events.InterceptorsChanged);
+    return Promise.all(promises);
+  }
+
+  /**
+   * @param {!SDK.InterceptedRequest} interceptedRequest
+   */
+  async _requestIntercepted(interceptedRequest) {
+    for (var pattern of this._requestInterceptorMap.keysArray()) {
+      if (!SDK.RequestInterceptor.patternMatchesUrl(pattern, interceptedRequest.request.url))
+        continue;
+      for (var requestInterceptor of this._requestInterceptorMap.get(pattern)) {
+        console.assert(requestInterceptor.enabled());
+        await requestInterceptor.handle(interceptedRequest);
+        if (interceptedRequest.hasResponded())
+          return;
+      }
+    }
+    if (!interceptedRequest.hasResponded())
+      interceptedRequest.continueRequestWithoutChange();
+  }
+
   clearBrowserCache() {
     for (var agent of this._agents)
       agent.clearBrowserCache();
@@ -997,10 +1079,195 @@
 SDK.MultitargetNetworkManager.Events = {
   BlockedPatternsChanged: Symbol('BlockedPatternsChanged'),
   ConditionsChanged: Symbol('ConditionsChanged'),
-  UserAgentChanged: Symbol('UserAgentChanged')
+  UserAgentChanged: Symbol('UserAgentChanged'),
+  InterceptorsChanged: Symbol('InterceptorsChanged')
 };
 
 /**
  * @type {!SDK.MultitargetNetworkManager}
  */
 SDK.multitargetNetworkManager;
+
+SDK.RequestInterceptor = class {
+  /**
+   * @param {boolean} enabled
+   * @param {!Set<string>=} patterns
+   */
+  constructor(enabled, patterns) {
+    this._enabled = enabled;
+    this._patterns = patterns || new Set();
+    if (enabled && this._patterns.size)
+      SDK.multitargetNetworkManager._registerRequestInterceptor(this);
+  }
+
+  /**
+   * @param {string} pattern
+   * @param {string} url
+   * @return {boolean}
+   */
+  static patternMatchesUrl(pattern, url) {
+    if (!pattern.length)
+      return false;
+    var parts = [];
+    var prevIndex = 0;
+    var index = indexOfWildOrEscape(0);
+    for (; index >= 0; index = indexOfWildOrEscape(index + 1)) {
+      if (pattern[index] === '\\') {
+        // Since we are removing the slash, there is no need to skip next character.
+        pattern = pattern.substring(0, index) + pattern.substring(index + 1);
+        continue;
+      }
+      parts.push(pattern.substring(prevIndex, index));
+      prevIndex = index + 1;
+    }
+    parts.push(pattern.substring(prevIndex));
+    // If a pattern is a wild card only it'll be an empty string.
+    var firstPart = parts.shift();
+    if (firstPart && !url.startsWith(firstPart))
+      return false;
+
+    // Check ending of url against pattern.
+    if (parts.length) {
+      var lastPart = parts.pop();
+      if (lastPart && !url.endsWith(lastPart))
+        return false;
+      url = url.substring(0, url.length - lastPart.length);
+    }
+
+    var pos = firstPart.length;
+    for (var part of parts) {
+      if (!part.length)
+        continue;
+      pos = url.indexOf(part, pos);
+      if (pos === -1)
+        return false;
+      pos += part.length;
+    }
+    return true;
+
+    /**
+     * @param {number} fromPosition
+     * @return {number}
+     */
+    function indexOfWildOrEscape(fromPosition) {
+      var wildPos = pattern.indexOf('*', fromPosition);
+      var slashPos = pattern.indexOf('\\', fromPosition);
+      if (wildPos === -1 || slashPos === -1)
+        return Math.max(wildPos, slashPos);
+      return Math.min(wildPos, slashPos);
+    }
+  }
+
+  /**
+   * @param {boolean} enabled
+   * @return {!Promise}
+   */
+  setEnabled(enabled) {
+    if (this._enabled === enabled)
+      return Promise.resolve();
+    this._enabled = enabled;
+    var oldPatterns = !enabled ? this._patterns : undefined;
+    return SDK.multitargetNetworkManager._registerRequestInterceptor(this, oldPatterns);
+  }
+
+  /**
+   * @param {!Set<string>} patterns
+   * @return {!Promise}
+   */
+  setPatterns(patterns) {
+    var oldPatterns = this._patterns;
+    this._patterns = patterns;
+    return SDK.multitargetNetworkManager._registerRequestInterceptor(this, oldPatterns);
+  }
+
+  /**
+   * @return {boolean}
+   */
+  enabled() {
+    return this._enabled;
+  }
+
+  /**
+   * @return {!Set<string>}
+   */
+  patterns() {
+    return this._patterns;
+  }
+
+  /**
+   * @param {!SDK.InterceptedRequest} interceptedRequest
+   * @return {!Promise}
+   */
+  handle(interceptedRequest) {
+    throw 'Not implemented.';
+  }
+};
+
+SDK.InterceptedRequest = class {
+  /**
+   * @param {!Protocol.NetworkAgent} networkAgent
+   * @param {!Protocol.Network.InterceptionId} interceptionId
+   * @param {!Protocol.Network.Request} request
+   * @param {!Protocol.Page.ResourceType} resourceType
+   * @param {boolean} isNavigationRequest
+   * @param {!Protocol.Network.Headers=} redirectHeaders
+   * @param {number=} redirectStatusCode
+   * @param {string=} redirectUrl
+   * @param {!Protocol.Network.AuthChallenge=} authChallenge
+   */
+  constructor(
+      networkAgent, interceptionId, request, resourceType, isNavigationRequest, redirectHeaders, redirectStatusCode,
+      redirectUrl, authChallenge) {
+    this._networkAgent = networkAgent;
+    this._interceptionId = interceptionId;
+    this._hasResponded = false;
+
+    this.request = request;
+    this.resourceType = resourceType;
+    this.isNavigationRequest = isNavigationRequest;
+    this.redirectHeaders = redirectHeaders;
+    this.redirectStatusCode = redirectStatusCode;
+    this.redirectUrl = redirectUrl;
+    this.authChallenge = authChallenge;
+  }
+
+  /**
+   * @return {boolean}
+   */
+  hasResponded() {
+    return this._hasResponded;
+  }
+
+  /**
+   * @param {string} content
+   * @param {string} mimeType
+   */
+  continueRequestWithContent(content, mimeType) {
+    this._hasResponded = true;
+    var headers = [
+      'HTTP/1.1 200 OK',
+      'Date: ' + (new Date()).toUTCString(),
+      'Server: Chrome Devtools Request Interceptor',
+      'Connection: closed',
+      'Content-Length: ' + content.length,
+      'Content-Type: ' + mimeType,
+    ];
+    var encodedResponse = (headers.join('\r\n') + '\r\n\r\n' + content).toBase64();
+    this._networkAgent.continueInterceptedRequest(this._interceptionId, undefined, encodedResponse);
+  }
+
+  continueRequestWithoutChange() {
+    console.assert(!this._hasResponded);
+    this._hasResponded = true;
+    this._networkAgent.continueInterceptedRequest(this._interceptionId);
+  }
+
+  /**
+   * @param {!Protocol.Network.ErrorReason} errorReason
+   */
+  continueRequestWithError(errorReason) {
+    console.assert(!this._hasResponded);
+    this._hasResponded = true;
+    this._networkAgent.continueInterceptedRequest(this._interceptionId, errorReason);
+  }
+};
diff --git a/third_party/WebKit/Source/devtools/front_end/security/SecurityModel.js b/third_party/WebKit/Source/devtools/front_end/security/SecurityModel.js
index a90a173e..89c26ee 100644
--- a/third_party/WebKit/Source/devtools/front_end/security/SecurityModel.js
+++ b/third_party/WebKit/Source/devtools/front_end/security/SecurityModel.js
@@ -43,8 +43,7 @@
       securityStateMap = new Map();
       var ordering = [
         Protocol.Security.SecurityState.Info, Protocol.Security.SecurityState.Insecure,
-        Protocol.Security.SecurityState.Neutral, Protocol.Security.SecurityState.Warning,
-        Protocol.Security.SecurityState.Secure,
+        Protocol.Security.SecurityState.Neutral, Protocol.Security.SecurityState.Secure,
         // Unknown is max so that failed/cancelled requests don't overwrite the origin security state for successful requests,
         // and so that failed/cancelled requests appear at the bottom of the origins list.
         Protocol.Security.SecurityState.Unknown
diff --git a/third_party/WebKit/Source/devtools/front_end/unit_test_runner.json b/third_party/WebKit/Source/devtools/front_end/unit_test_runner.json
index 9034d72..7396047 100644
--- a/third_party/WebKit/Source/devtools/front_end/unit_test_runner.json
+++ b/third_party/WebKit/Source/devtools/front_end/unit_test_runner.json
@@ -15,10 +15,10 @@
         { "name": "test_runner", "type": "autostart" },
         { "name": "quick_open" },
         { "name": "inline_editor" },
-        { "name": "sdk" },
-        { "name": "protocol" },
+        { "name": "sdk", "type": "autostart" },
+        { "name": "protocol", "type": "autostart" },
         { "name": "data_grid" },
-        { "name": "product_registry" },
+        { "name": "product_registry", "type": "autostart" },
         { "name": "product_registry_impl" },
         { "name": "formatter" }
     ],
diff --git a/third_party/WebKit/Source/devtools/front_end/workspace_diff/WorkspaceDiff.js b/third_party/WebKit/Source/devtools/front_end/workspace_diff/WorkspaceDiff.js
index 3d40fd1..5ad7377 100644
--- a/third_party/WebKit/Source/devtools/front_end/workspace_diff/WorkspaceDiff.js
+++ b/third_party/WebKit/Source/devtools/front_end/workspace_diff/WorkspaceDiff.js
@@ -234,7 +234,7 @@
 
     if (current === null || baseline === null)
       return null;
-    return Diff.Diff.lineDiff(baseline.split('\n'), current.split('\n'));
+    return Diff.Diff.lineDiff(baseline.split(/\r\n|\n|\r/), current.split(/\r\n|\n|\r/));
   }
 };
 
diff --git a/third_party/WebKit/Source/modules/background_fetch/BackgroundFetchManager.cpp b/third_party/WebKit/Source/modules/background_fetch/BackgroundFetchManager.cpp
index bcb5622..43f99c59 100644
--- a/third_party/WebKit/Source/modules/background_fetch/BackgroundFetchManager.cpp
+++ b/third_party/WebKit/Source/modules/background_fetch/BackgroundFetchManager.cpp
@@ -82,6 +82,11 @@
           kInvalidStateError,
           "There already is a registration for the given id."));
       return;
+    case mojom::blink::BackgroundFetchError::STORAGE_ERROR:
+      DCHECK(!registration);
+      resolver->Reject(DOMException::Create(
+          kAbortError, "Failed to store registration due to I/O error."));
+      return;
     case mojom::blink::BackgroundFetchError::INVALID_ARGUMENT:
     case mojom::blink::BackgroundFetchError::INVALID_ID:
       // Not applicable for this callback.
@@ -179,6 +184,11 @@
     case mojom::blink::BackgroundFetchError::INVALID_ID:
       resolver->Resolve(registration);
       return;
+    case mojom::blink::BackgroundFetchError::STORAGE_ERROR:
+      DCHECK(!registration);
+      resolver->Reject(DOMException::Create(
+          kAbortError, "Failed to get registration due to I/O error."));
+      return;
     case mojom::blink::BackgroundFetchError::DUPLICATED_ID:
     case mojom::blink::BackgroundFetchError::INVALID_ARGUMENT:
       // Not applicable for this callback.
@@ -213,6 +223,11 @@
     case mojom::blink::BackgroundFetchError::NONE:
       resolver->Resolve(ids);
       return;
+    case mojom::blink::BackgroundFetchError::STORAGE_ERROR:
+      DCHECK(ids.IsEmpty());
+      resolver->Reject(DOMException::Create(
+          kAbortError, "Failed to get registration IDs due to I/O error."));
+      return;
     case mojom::blink::BackgroundFetchError::DUPLICATED_ID:
     case mojom::blink::BackgroundFetchError::INVALID_ARGUMENT:
     case mojom::blink::BackgroundFetchError::INVALID_ID:
diff --git a/third_party/WebKit/Source/modules/background_fetch/BackgroundFetchRegistration.cpp b/third_party/WebKit/Source/modules/background_fetch/BackgroundFetchRegistration.cpp
index 45be653..b904985 100644
--- a/third_party/WebKit/Source/modules/background_fetch/BackgroundFetchRegistration.cpp
+++ b/third_party/WebKit/Source/modules/background_fetch/BackgroundFetchRegistration.cpp
@@ -4,6 +4,7 @@
 
 #include "modules/background_fetch/BackgroundFetchRegistration.h"
 
+#include "core/dom/DOMException.h"
 #include "modules/background_fetch/BackgroundFetchBridge.h"
 #include "modules/background_fetch/IconDefinition.h"
 #include "modules/serviceworkers/ServiceWorkerRegistration.h"
@@ -67,6 +68,10 @@
     case mojom::blink::BackgroundFetchError::INVALID_ID:
       resolver->Resolve(false /* success */);
       return;
+    case mojom::blink::BackgroundFetchError::STORAGE_ERROR:
+      resolver->Reject(DOMException::Create(
+          kAbortError, "Failed to abort registration due to I/O error."));
+      return;
     case mojom::blink::BackgroundFetchError::DUPLICATED_ID:
     case mojom::blink::BackgroundFetchError::INVALID_ARGUMENT:
       // Not applicable for this callback.
diff --git a/third_party/WebKit/Source/modules/background_fetch/BackgroundFetchedEvent.cpp b/third_party/WebKit/Source/modules/background_fetch/BackgroundFetchedEvent.cpp
index bd48c2b9..33899a0e 100644
--- a/third_party/WebKit/Source/modules/background_fetch/BackgroundFetchedEvent.cpp
+++ b/third_party/WebKit/Source/modules/background_fetch/BackgroundFetchedEvent.cpp
@@ -76,6 +76,10 @@
     case mojom::blink::BackgroundFetchError::INVALID_ID:
       resolver->Resolve();
       return;
+    case mojom::blink::BackgroundFetchError::STORAGE_ERROR:
+      resolver->Reject(DOMException::Create(
+          kAbortError, "Failed to update UI due to I/O error."));
+      return;
     case mojom::blink::BackgroundFetchError::DUPLICATED_ID:
     case mojom::blink::BackgroundFetchError::INVALID_ARGUMENT:
       // Not applicable for this callback.
diff --git a/third_party/WebKit/Source/modules/canvas2d/CanvasRenderingContext2DTest.cpp b/third_party/WebKit/Source/modules/canvas2d/CanvasRenderingContext2DTest.cpp
index 6672834f..3f7d00d 100644
--- a/third_party/WebKit/Source/modules/canvas2d/CanvasRenderingContext2DTest.cpp
+++ b/third_party/WebKit/Source/modules/canvas2d/CanvasRenderingContext2DTest.cpp
@@ -1117,7 +1117,7 @@
     std::swap(src_pixel[0], src_pixel[2]);
 
   // Create and test the ImageBitmap objects.
-  Optional<IntRect> crop_rect = IntRect(0, 0, 300, 150);
+  Optional<IntRect> crop_rect = IntRect(0, 0, 10, 10);
   sk_sp<SkColorSpace> color_space = nullptr;
   SkColorType color_type = SkColorType::kN32_SkColorType;
   SkColorSpaceXform::ColorFormat color_format32 =
diff --git a/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.h b/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.h
index c29bd10d..ed3783ff 100644
--- a/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.h
+++ b/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.h
@@ -377,6 +377,7 @@
 
  private:
   friend class BaseAudioContextAutoplayTest;
+  friend class DISABLED_BaseAudioContextAutoplayTest;
 
   // Do not change the order of this enum, it is used for metrics.
   enum AutoplayStatus {
diff --git a/third_party/WebKit/Source/modules/webaudio/BaseAudioContextTest.cpp b/third_party/WebKit/Source/modules/webaudio/BaseAudioContextTest.cpp
index e00611ed..0b90ac2 100644
--- a/third_party/WebKit/Source/modules/webaudio/BaseAudioContextTest.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/BaseAudioContextTest.cpp
@@ -5,6 +5,7 @@
 #include "modules/webaudio/BaseAudioContext.h"
 
 #include "bindings/core/v8/V8BindingForCore.h"
+#include "build/build_config.h"
 #include "core/dom/Document.h"
 #include "core/dom/UserGestureIndicator.h"
 #include "core/frame/FrameOwner.h"
@@ -94,7 +95,14 @@
 
 }  // anonymous namespace
 
-class BaseAudioContextAutoplayTest
+#if defined(OS_ANDROID) && defined(ADDRESS_SANITIZER)
+// Often times out with Android ASAN: https://crbug.com/758934.
+#define MAYBE_BaseAudioContextAutoplayTest DISABLED_BaseAudioContextAutoplayTest
+#else
+#define MAYBE_BaseAudioContextAutoplayTest BaseAudioContextAutoplayTest
+#endif
+
+class MAYBE_BaseAudioContextAutoplayTest
     : public ::testing::TestWithParam<AutoplayPolicy::Type> {
  protected:
   using AutoplayStatus = BaseAudioContext::AutoplayStatus;
@@ -162,7 +170,8 @@
 };
 
 // Creates an AudioContext without a gesture inside a x-origin child frame.
-TEST_P(BaseAudioContextAutoplayTest, AutoplayMetrics_CreateNoGesture_Child) {
+TEST_P(MAYBE_BaseAudioContextAutoplayTest,
+       AutoplayMetrics_CreateNoGesture_Child) {
   BaseAudioContext* audio_context = BaseAudioContext::Create(
       ChildDocument(), AudioContextOptions(), ASSERT_NO_EXCEPTION);
   RecordAutoplayStatus(audio_context);
@@ -186,7 +195,8 @@
 }
 
 // Creates an AudioContext without a gesture inside a main frame.
-TEST_P(BaseAudioContextAutoplayTest, AutoplayMetrics_CreateNoGesture_Main) {
+TEST_P(MAYBE_BaseAudioContextAutoplayTest,
+       AutoplayMetrics_CreateNoGesture_Main) {
   BaseAudioContext* audio_context = BaseAudioContext::Create(
       GetDocument(), AudioContextOptions(), ASSERT_NO_EXCEPTION);
   RecordAutoplayStatus(audio_context);
@@ -209,7 +219,7 @@
 
 // Creates an AudioContext then call resume without a gesture in a x-origin
 // child frame.
-TEST_P(BaseAudioContextAutoplayTest,
+TEST_P(MAYBE_BaseAudioContextAutoplayTest,
        AutoplayMetrics_CallResumeNoGesture_Child) {
   ScriptState::Scope scope(GetScriptStateFrom(ChildDocument()));
 
@@ -238,7 +248,8 @@
 }
 
 // Creates an AudioContext then call resume without a gesture in a main frame.
-TEST_P(BaseAudioContextAutoplayTest, AutoplayMetrics_CallResumeNoGesture_Main) {
+TEST_P(MAYBE_BaseAudioContextAutoplayTest,
+       AutoplayMetrics_CallResumeNoGesture_Main) {
   ScriptState::Scope scope(GetScriptStateFrom(GetDocument()));
 
   BaseAudioContext* audio_context = BaseAudioContext::Create(
@@ -264,7 +275,8 @@
 }
 
 // Creates an AudioContext with a user gesture inside a x-origin child frame.
-TEST_P(BaseAudioContextAutoplayTest, AutoplayMetrics_CreateGesture_Child) {
+TEST_P(MAYBE_BaseAudioContextAutoplayTest,
+       AutoplayMetrics_CreateGesture_Child) {
   std::unique_ptr<UserGestureIndicator> user_gesture_scope =
       LocalFrame::CreateUserGesture(ChildDocument().GetFrame(),
                                     UserGestureToken::kNewGesture);
@@ -293,7 +305,7 @@
 }
 
 // Creates an AudioContext with a user gesture inside a main frame.
-TEST_P(BaseAudioContextAutoplayTest, AutoplayMetrics_CreateGesture_Main) {
+TEST_P(MAYBE_BaseAudioContextAutoplayTest, AutoplayMetrics_CreateGesture_Main) {
   std::unique_ptr<UserGestureIndicator> user_gesture_scope =
       LocalFrame::CreateUserGesture(GetDocument().GetFrame(),
                                     UserGestureToken::kNewGesture);
@@ -320,7 +332,8 @@
 
 // Creates an AudioContext then calls resume with a user gesture inside a
 // x-origin child frame.
-TEST_P(BaseAudioContextAutoplayTest, AutoplayMetrics_CallResumeGesture_Child) {
+TEST_P(MAYBE_BaseAudioContextAutoplayTest,
+       AutoplayMetrics_CallResumeGesture_Child) {
   ScriptState::Scope scope(GetScriptStateFrom(ChildDocument()));
 
   BaseAudioContext* audio_context = BaseAudioContext::Create(
@@ -355,7 +368,8 @@
 
 // Creates an AudioContext then calls resume with a user gesture inside a main
 // frame.
-TEST_P(BaseAudioContextAutoplayTest, AutoplayMetrics_CallResumeGesture_Main) {
+TEST_P(MAYBE_BaseAudioContextAutoplayTest,
+       AutoplayMetrics_CallResumeGesture_Main) {
   ScriptState::Scope scope(GetScriptStateFrom(GetDocument()));
 
   BaseAudioContext* audio_context = BaseAudioContext::Create(
@@ -387,7 +401,8 @@
 
 // Creates an AudioContext then calls start on a node without a gesture inside a
 // x-origin child frame.
-TEST_P(BaseAudioContextAutoplayTest, AutoplayMetrics_NodeStartNoGesture_Child) {
+TEST_P(MAYBE_BaseAudioContextAutoplayTest,
+       AutoplayMetrics_NodeStartNoGesture_Child) {
   BaseAudioContext* audio_context = BaseAudioContext::Create(
       ChildDocument(), AudioContextOptions(), ASSERT_NO_EXCEPTION);
   audio_context->MaybeRecordStartAttempt();
@@ -413,7 +428,8 @@
 
 // Creates an AudioContext then calls start on a node without a gesture inside a
 // main frame.
-TEST_P(BaseAudioContextAutoplayTest, AutoplayMetrics_NodeStartNoGesture_Main) {
+TEST_P(MAYBE_BaseAudioContextAutoplayTest,
+       AutoplayMetrics_NodeStartNoGesture_Main) {
   BaseAudioContext* audio_context = BaseAudioContext::Create(
       GetDocument(), AudioContextOptions(), ASSERT_NO_EXCEPTION);
   audio_context->MaybeRecordStartAttempt();
@@ -437,7 +453,8 @@
 
 // Creates an AudioContext then calls start on a node with a gesture inside a
 // x-origin child frame.
-TEST_P(BaseAudioContextAutoplayTest, AutoplayMetrics_NodeStartGesture_Child) {
+TEST_P(MAYBE_BaseAudioContextAutoplayTest,
+       AutoplayMetrics_NodeStartGesture_Child) {
   BaseAudioContext* audio_context = BaseAudioContext::Create(
       ChildDocument(), AudioContextOptions(), ASSERT_NO_EXCEPTION);
 
@@ -468,7 +485,8 @@
 
 // Creates an AudioContext then calls start on a node with a gesture inside a
 // main frame.
-TEST_P(BaseAudioContextAutoplayTest, AutoplayMetrics_NodeStartGesture_Main) {
+TEST_P(MAYBE_BaseAudioContextAutoplayTest,
+       AutoplayMetrics_NodeStartGesture_Main) {
   BaseAudioContext* audio_context = BaseAudioContext::Create(
       GetDocument(), AudioContextOptions(), ASSERT_NO_EXCEPTION);
 
@@ -496,7 +514,7 @@
 
 // Creates an AudioContext then calls start on a node without a gesture and
 // finally allows the AudioContext to produce sound inside x-origin child frame.
-TEST_P(BaseAudioContextAutoplayTest,
+TEST_P(MAYBE_BaseAudioContextAutoplayTest,
        AutoplayMetrics_NodeStartNoGestureThenSuccess_Child) {
   ScriptState::Scope scope(GetScriptStateFrom(ChildDocument()));
 
@@ -532,7 +550,7 @@
 
 // Creates an AudioContext then calls start on a node without a gesture and
 // finally allows the AudioContext to produce sound inside a main frame.
-TEST_P(BaseAudioContextAutoplayTest,
+TEST_P(MAYBE_BaseAudioContextAutoplayTest,
        AutoplayMetrics_NodeStartNoGestureThenSuccess_Main) {
   ScriptState::Scope scope(GetScriptStateFrom(GetDocument()));
 
@@ -565,7 +583,7 @@
 
 // Creates an AudioContext then calls start on a node with a gesture and
 // finally allows the AudioContext to produce sound inside x-origin child frame.
-TEST_P(BaseAudioContextAutoplayTest,
+TEST_P(MAYBE_BaseAudioContextAutoplayTest,
        AutoplayMetrics_NodeStartGestureThenSucces_Child) {
   ScriptState::Scope scope(GetScriptStateFrom(ChildDocument()));
 
@@ -601,7 +619,7 @@
 
 // Creates an AudioContext then calls start on a node with a gesture and
 // finally allows the AudioContext to produce sound inside a main frame.
-TEST_P(BaseAudioContextAutoplayTest,
+TEST_P(MAYBE_BaseAudioContextAutoplayTest,
        AutoplayMetrics_NodeStartGestureThenSucces_Main) {
   ScriptState::Scope scope(GetScriptStateFrom(GetDocument()));
 
@@ -634,7 +652,7 @@
 
 // Attempts to autoplay an AudioContext in a x-origin child frame when the
 // document previous received a user gesture.
-TEST_P(BaseAudioContextAutoplayTest,
+TEST_P(MAYBE_BaseAudioContextAutoplayTest,
        AutoplayMetrics_DocumentReceivedGesture_Child) {
   ChildDocument().GetFrame()->UpdateUserActivationInFrameTree();
 
@@ -670,7 +688,7 @@
 
 // Attempts to autoplay an AudioContext in a main child frame when the
 // document previous received a user gesture.
-TEST_P(BaseAudioContextAutoplayTest,
+TEST_P(MAYBE_BaseAudioContextAutoplayTest,
        AutoplayMetrics_DocumentReceivedGesture_Main) {
   GetDocument().GetFrame()->UpdateUserActivationInFrameTree();
 
@@ -696,7 +714,7 @@
 
 // Attempts to autoplay an AudioContext in a main child frame when the
 // document received a user gesture before navigation.
-TEST_P(BaseAudioContextAutoplayTest,
+TEST_P(MAYBE_BaseAudioContextAutoplayTest,
        AutoplayMetrics_DocumentReceivedGesture_BeforeNavigation) {
   GetDocument().GetFrame()->SetDocumentHasReceivedUserGestureBeforeNavigation(
       true);
@@ -722,8 +740,8 @@
 }
 
 INSTANTIATE_TEST_CASE_P(
-    BaseAudioContextAutoplayTest,
-    BaseAudioContextAutoplayTest,
+    MAYBE_BaseAudioContextAutoplayTest,
+    MAYBE_BaseAudioContextAutoplayTest,
     ::testing::Values(AutoplayPolicy::Type::kNoUserGestureRequired,
                       AutoplayPolicy::Type::kUserGestureRequired,
                       AutoplayPolicy::Type::kUserGestureRequiredForCrossOrigin,
diff --git a/third_party/WebKit/Source/platform/graphics/GraphicsLayer.cpp b/third_party/WebKit/Source/platform/graphics/GraphicsLayer.cpp
index f457dc5..2f3a7a6 100644
--- a/third_party/WebKit/Source/platform/graphics/GraphicsLayer.cpp
+++ b/third_party/WebKit/Source/platform/graphics/GraphicsLayer.cpp
@@ -1138,20 +1138,10 @@
   UpdateChildList();
 }
 
-void GraphicsLayer::SetScrollableArea(ScrollableArea* scrollable_area,
-                                      bool is_visual_viewport) {
+void GraphicsLayer::SetScrollableArea(ScrollableArea* scrollable_area) {
   if (scrollable_area_ == scrollable_area)
     return;
-
   scrollable_area_ = scrollable_area;
-
-  // VisualViewport scrolling may involve pinch zoom and gets routed through
-  // WebViewImpl explicitly rather than via ScrollableArea::didScroll since it
-  // needs to be set in tandem with the page scale delta.
-  if (is_visual_viewport)
-    layer_->Layer()->SetScrollClient(nullptr);
-  else
-    layer_->Layer()->SetScrollClient(scrollable_area);
 }
 
 std::unique_ptr<base::trace_event::ConvertableToTraceFormat>
diff --git a/third_party/WebKit/Source/platform/graphics/GraphicsLayer.h b/third_party/WebKit/Source/platform/graphics/GraphicsLayer.h
index 1284233..0befa1df 100644
--- a/third_party/WebKit/Source/platform/graphics/GraphicsLayer.h
+++ b/third_party/WebKit/Source/platform/graphics/GraphicsLayer.h
@@ -243,7 +243,7 @@
   unsigned NumLinkHighlights() { return link_highlights_.size(); }
   LinkHighlight* GetLinkHighlight(int i) { return link_highlights_[i]; }
 
-  void SetScrollableArea(ScrollableArea*, bool is_visual_viewport);
+  void SetScrollableArea(ScrollableArea*);
   ScrollableArea* GetScrollableArea() const { return scrollable_area_; }
 
   WebContentLayer* ContentLayer() const { return layer_.get(); }
diff --git a/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositorTest.cpp b/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositorTest.cpp
index 66765ce..eb12cb70 100644
--- a/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositorTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositorTest.cpp
@@ -769,9 +769,10 @@
  public:
   FakeScrollClient() : did_scroll_count(0) {}
 
-  void DidScroll(const gfx::ScrollOffset& scroll_offset) final {
+  void DidScroll(const gfx::ScrollOffset& offset,
+                 const CompositorElementId&) final {
     did_scroll_count++;
-    last_scroll_offset = scroll_offset;
+    last_scroll_offset = offset;
   };
 
   gfx::ScrollOffset last_scroll_offset;
diff --git a/third_party/WebKit/Source/platform/graphics/paint/ScrollPaintPropertyNode.h b/third_party/WebKit/Source/platform/graphics/paint/ScrollPaintPropertyNode.h
index 267cf3cb..047f433 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/ScrollPaintPropertyNode.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/ScrollPaintPropertyNode.h
@@ -191,8 +191,8 @@
   // The scrolling element id is stored directly on the scroll node and not on
   // the associated TransformPaintPropertyNode used for scroll offset.
   CompositorElementId compositor_element_id_;
-  // TODO(pdr): This can be destroyed and be not safe to use. Refactor this to
-  // use ElementIds for safety (crbug.com/758360).
+  // TODO(pdr): This is the same on all scroll nodes. Refactor this to be stored
+  // in a more central place.
   WebLayerScrollClient* scroll_client_;
 };
 
diff --git a/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp b/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp
index dbd6f13..0523153 100644
--- a/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp
@@ -237,6 +237,9 @@
       if (previous_frame_index == kNotFound) {
         previous_frame_index = required_previous_frame_index;
         Decode(previous_frame_index);
+        if (Failed()) {
+          return;
+        }
       }
 
       // We try to reuse |previous_frame| as starting state to avoid copying.
diff --git a/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoderTest.cpp b/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoderTest.cpp
index 2c81611..7be1aef 100644
--- a/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoderTest.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoderTest.cpp
@@ -430,4 +430,43 @@
   EXPECT_FALSE(frame->HasAlpha());
 }
 
+TEST(GIFImageDecoderTest, recursiveDecodeFailure) {
+  auto data = ReadFile(kLayoutTestResourcesDir, "count-down-color-test.gif");
+  ASSERT_TRUE(data.Get());
+
+  {
+    auto decoder = CreateDecoder();
+    decoder->SetData(data.Get(), true);
+    for (size_t i = 0; i <= 3; ++i) {
+      ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(i);
+      ASSERT_NE(frame, nullptr);
+      ASSERT_EQ(frame->GetStatus(), ImageFrame::kFrameComplete);
+    }
+  }
+
+  // Modify data to have an error in frame 2.
+  const size_t kErrorOffset = 15302u;
+  RefPtr<SharedBuffer> modified_data =
+      SharedBuffer::Create(data->Data(), kErrorOffset);
+  modified_data->Append("A", 1u);
+  modified_data->Append(data->Data() + kErrorOffset + 1,
+                        data->size() - kErrorOffset - 1);
+  {
+    auto decoder = CreateDecoder();
+    decoder->SetData(modified_data.Get(), true);
+    decoder->DecodeFrameBufferAtIndex(2);
+    ASSERT_TRUE(decoder->Failed());
+  }
+
+  {
+    // Decode frame 3, recursively decoding frame 2, which 3 depends on.
+    auto decoder = CreateDecoder();
+    decoder->SetData(modified_data.Get(), true);
+    ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(3);
+    EXPECT_TRUE(decoder->Failed());
+    ASSERT_NE(frame, nullptr);
+    ASSERT_EQ(frame->RequiredPreviousFrameIndex(), 2u);
+  }
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/loader/fetch/ResourceFetcher.cpp b/third_party/WebKit/Source/platform/loader/fetch/ResourceFetcher.cpp
index f484bd7..873946f 100644
--- a/third_party/WebKit/Source/platform/loader/fetch/ResourceFetcher.cpp
+++ b/third_party/WebKit/Source/platform/loader/fetch/ResourceFetcher.cpp
@@ -1219,6 +1219,10 @@
   return non_blocking_loaders_.size();
 }
 
+int ResourceFetcher::ActiveRequestCount() const {
+  return loaders_.size() + non_blocking_loaders_.size();
+}
+
 void ResourceFetcher::EnableIsPreloadedForTest() {
   if (preloaded_urls_for_test_)
     return;
diff --git a/third_party/WebKit/Source/platform/loader/fetch/ResourceFetcher.h b/third_party/WebKit/Source/platform/loader/fetch/ResourceFetcher.h
index 319bf517..d440641 100644
--- a/third_party/WebKit/Source/platform/loader/fetch/ResourceFetcher.h
+++ b/third_party/WebKit/Source/platform/loader/fetch/ResourceFetcher.h
@@ -105,6 +105,7 @@
 
   int BlockingRequestCount() const;
   int NonblockingRequestCount() const;
+  int ActiveRequestCount() const;
 
   enum ClearPreloadsPolicy {
     kClearAllPreloads,
diff --git a/third_party/WebKit/Source/platform/loader/fetch/ResourceResponse.h b/third_party/WebKit/Source/platform/loader/fetch/ResourceResponse.h
index a570945..eb99edd 100644
--- a/third_party/WebKit/Source/platform/loader/fetch/ResourceResponse.h
+++ b/third_party/WebKit/Source/platform/loader/fetch/ResourceResponse.h
@@ -27,6 +27,9 @@
 #ifndef ResourceResponse_h
 #define ResourceResponse_h
 
+#include <memory>
+#include <utility>
+
 #include "platform/PlatformExport.h"
 #include "platform/blob/BlobData.h"
 #include "platform/loader/fetch/ResourceLoadInfo.h"
@@ -70,7 +73,6 @@
     kSecurityStyleUnknown,
     kSecurityStyleUnauthenticated,
     kSecurityStyleAuthenticationBroken,
-    kSecurityStyleWarning,
     kSecurityStyleAuthenticated
   };
 
diff --git a/third_party/WebKit/Source/platform/mojo/CommonCustomTypesStructTraits.cpp b/third_party/WebKit/Source/platform/mojo/CommonCustomTypesStructTraits.cpp
index 15f496fc8..bb7e974 100644
--- a/third_party/WebKit/Source/platform/mojo/CommonCustomTypesStructTraits.cpp
+++ b/third_party/WebKit/Source/platform/mojo/CommonCustomTypesStructTraits.cpp
@@ -23,19 +23,20 @@
 }
 
 // static
-ConstCArray<uint16_t> StructTraits<common::mojom::String16DataView,
-                                   WTF::String>::data(const WTF::String& input,
-                                                      void* context) {
+base::span<const uint16_t>
+StructTraits<common::mojom::String16DataView, WTF::String>::data(
+    const WTF::String& input,
+    void* context) {
   auto contextObject = static_cast<base::string16*>(context);
   DCHECK_EQ(input.Is8Bit(), !!contextObject);
 
   if (contextObject) {
-    return ConstCArray<uint16_t>(
+    return base::make_span(
         reinterpret_cast<const uint16_t*>(contextObject->data()),
         contextObject->size());
   }
 
-  return ConstCArray<uint16_t>(
+  return base::make_span(
       reinterpret_cast<const uint16_t*>(input.Characters16()), input.length());
 }
 
diff --git a/third_party/WebKit/Source/platform/mojo/CommonCustomTypesStructTraits.h b/third_party/WebKit/Source/platform/mojo/CommonCustomTypesStructTraits.h
index 45562a00..37df946 100644
--- a/third_party/WebKit/Source/platform/mojo/CommonCustomTypesStructTraits.h
+++ b/third_party/WebKit/Source/platform/mojo/CommonCustomTypesStructTraits.h
@@ -5,6 +5,7 @@
 #ifndef CommonCustomTypesStructTraits_h
 #define CommonCustomTypesStructTraits_h
 
+#include "base/containers/span.h"
 #include "base/strings/string16.h"
 #include "mojo/common/string16.mojom-blink.h"
 #include "platform/PlatformExport.h"
@@ -22,7 +23,8 @@
     delete static_cast<base::string16*>(context);
   }
 
-  static ConstCArray<uint16_t> data(const WTF::String& input, void* context);
+  static base::span<const uint16_t> data(const WTF::String& input,
+                                         void* context);
   static bool Read(common::mojom::String16DataView, WTF::String* out);
 };
 
diff --git a/third_party/WebKit/Source/platform/mojo/DEPS b/third_party/WebKit/Source/platform/mojo/DEPS
index aee38e5..289dba40 100644
--- a/third_party/WebKit/Source/platform/mojo/DEPS
+++ b/third_party/WebKit/Source/platform/mojo/DEPS
@@ -2,6 +2,7 @@
     # To whitelist base/ stuff Blink is allowed to include, we list up all
     # directories and files instead of writing 'base/'.
     "+base/callback.h",
+    "+base/containers/span.h",
     "+base/message_loop/message_loop.h",
     "+base/strings/string16.h",
     "+mojo/public/cpp/bindings/binding.h",
diff --git a/third_party/WebKit/Source/platform/scroll/ScrollableArea.h b/third_party/WebKit/Source/platform/scroll/ScrollableArea.h
index 8889f20..44f01a1 100644
--- a/third_party/WebKit/Source/platform/scroll/ScrollableArea.h
+++ b/third_party/WebKit/Source/platform/scroll/ScrollableArea.h
@@ -38,7 +38,6 @@
 #include "platform/wtf/MathExtras.h"
 #include "platform/wtf/Noncopyable.h"
 #include "platform/wtf/Vector.h"
-#include "public/platform/WebLayerScrollClient.h"
 
 namespace blink {
 
@@ -61,8 +60,7 @@
   kIncludeScrollbars,
 };
 
-class PLATFORM_EXPORT ScrollableArea : public GarbageCollectedMixin,
-                                       public WebLayerScrollClient {
+class PLATFORM_EXPORT ScrollableArea : public GarbageCollectedMixin {
   WTF_MAKE_NONCOPYABLE(ScrollableArea);
 
  public:
@@ -379,7 +377,7 @@
   virtual RefPtr<WebTaskRunner> GetTimerTaskRunner() const = 0;
 
   // Callback for compositor-side scrolling.
-  void DidScroll(const gfx::ScrollOffset&) override;
+  virtual void DidScroll(const gfx::ScrollOffset&);
 
   virtual void ScrollbarFrameRectChanged() {}
 
diff --git a/third_party/WebKit/Source/platform/text/TextBreakIterator.cpp b/third_party/WebKit/Source/platform/text/TextBreakIterator.cpp
index 51da80f..5d3b9918 100644
--- a/third_party/WebKit/Source/platform/text/TextBreakIterator.cpp
+++ b/third_party/WebKit/Source/platform/text/TextBreakIterator.cpp
@@ -382,6 +382,7 @@
 unsigned LazyLineBreakIterator::NextBreakOpportunity(unsigned offset) const {
   int next_break = -1;
   IsBreakable(offset, next_break);
+  DCHECK_GE(next_break, 0);
   return next_break;
 }
 
diff --git a/third_party/WebKit/Source/platform/text/TextBreakIteratorTest.cpp b/third_party/WebKit/Source/platform/text/TextBreakIteratorTest.cpp
index 8684740b..6dc6565 100644
--- a/third_party/WebKit/Source/platform/text/TextBreakIteratorTest.cpp
+++ b/third_party/WebKit/Source/platform/text/TextBreakIteratorTest.cpp
@@ -174,4 +174,15 @@
   MATCH_BREAK_AFTER_SPACE(LineBreakType::kBreakAll, {1, 2, 4, 8, 9, 10, 11});
 }
 
+TEST_F(TextBreakIteratorTest, NextBreakOpportunityAtEnd) {
+  LineBreakType break_types[] = {
+      LineBreakType::kNormal, LineBreakType::kBreakAll,
+      LineBreakType::kBreakCharacter, LineBreakType::kKeepAll};
+  for (const auto break_type : break_types) {
+    LazyLineBreakIterator break_iterator(String("1"));
+    break_iterator.SetBreakType(break_type);
+    EXPECT_EQ(1u, break_iterator.NextBreakOpportunity(1));
+  }
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/wtf/text/TextCodecICU.cpp b/third_party/WebKit/Source/platform/wtf/text/TextCodecICU.cpp
index 1999a4a0..96c8f63 100644
--- a/third_party/WebKit/Source/platform/wtf/text/TextCodecICU.cpp
+++ b/third_party/WebKit/Source/platform/wtf/text/TextCodecICU.cpp
@@ -56,6 +56,23 @@
   return WTF::WrapUnique(new TextCodecICU(encoding));
 }
 
+namespace {
+bool IncludeAlias(const char* alias) {
+#if !defined(USING_SYSTEM_ICU)
+  // Chromium's build of ICU includes *-html aliases to manage the encoding
+  // labels defined in the Encoding Standard, but these must not be
+  // web-exposed.
+  const char* kSuffix = "-html";
+  const size_t kSuffixLength = 5;
+  size_t alias_length = strlen(alias);
+  if ((alias_length >= kSuffixLength) &&
+      !strcmp(alias + alias_length - kSuffixLength, kSuffix))
+    return false;
+#endif
+  return true;
+}
+}  // namespace
+
 void TextCodecICU::RegisterEncodingNames(EncodingNameRegistrar registrar) {
   // We register Hebrew with logical ordering using a separate name.
   // Otherwise, this would share the same canonical name as the
@@ -131,7 +148,7 @@
         error = U_ZERO_ERROR;
         const char* alias = ucnv_getAlias(name, j, &error);
         DCHECK(U_SUCCESS(error));
-        if (U_SUCCESS(error) && alias != standard_name)
+        if (U_SUCCESS(error) && alias != standard_name && IncludeAlias(alias))
           registrar(alias, standard_name);
       }
   }
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/chromium_exportable_commits.py b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/chromium_exportable_commits.py
index 2bb4f42..10bd021 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/chromium_exportable_commits.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/chromium_exportable_commits.py
@@ -11,15 +11,13 @@
 
 
 def exportable_commits_over_last_n_commits(
-        host, local_wpt, wpt_github, number=DEFAULT_COMMIT_HISTORY_WINDOW, clean=True):
+        host, local_wpt, wpt_github, number=DEFAULT_COMMIT_HISTORY_WINDOW, require_clean=True):
     """Lists exportable commits after a certain point.
 
     Exportable commits contain changes in the wpt directory and have not been
     exported (no corresponding closed PRs on GitHub). Commits made by importer
     are ignored. Exportable commits may or may not apply cleanly against the
-    wpt HEAD. If the optional argument clean is True (default), only those that
-    can be applied cleanly (when tested individually) and produce non-empty diff
-    are returnd.
+    wpt HEAD (see argument require_clean).
 
     Args:
         host: A Host object.
@@ -28,9 +26,9 @@
         wpt_github: A WPTGitHub instance, used to check whether PRs are closed.
         number: The number of commits back to look. The commits to check will
             include all commits starting from the commit before HEAD~n, up
-            to and including HEAD. (Default is 5000.)
-        clean: Whether to only return exportable commits that can be applied
-            cleanly and produce non-empty diff. (Default is True.)
+            to and including HEAD.
+        require_clean: Whether to only return exportable commits that can be
+            applied cleanly and produce non-empty diff when tested individually.
 
     Returns:
         (exportable_commits, errors) where exportable_commits is a list of
@@ -39,10 +37,10 @@
         cleanly, both in chronological order.
     """
     start_commit = 'HEAD~{}'.format(number + 1)
-    return _exportable_commits_since(start_commit, host, local_wpt, wpt_github, clean)
+    return _exportable_commits_since(start_commit, host, local_wpt, wpt_github, require_clean)
 
 
-def _exportable_commits_since(chromium_commit_hash, host, local_wpt, wpt_github, clean=True):
+def _exportable_commits_since(chromium_commit_hash, host, local_wpt, wpt_github, require_clean=True):
     """Lists exportable commits after the given commit.
 
     Args:
@@ -65,7 +63,7 @@
     errors = []
     for commit in chromium_commits:
         state, error = get_commit_export_state(commit, local_wpt, wpt_github)
-        if clean:
+        if require_clean:
             success = state == CommitExportState.EXPORTABLE_CLEAN
         else:
             success = state in (CommitExportState.EXPORTABLE_CLEAN, CommitExportState.EXPORTABLE_DIRTY)
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/chromium_exportable_commits_unittest.py b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/chromium_exportable_commits_unittest.py
index 72bcbcd0..d89e732 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/chromium_exportable_commits_unittest.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/chromium_exportable_commits_unittest.py
@@ -27,6 +27,23 @@
         return self.patch_success, self.patch_error
 
 
+class MultiResponseMockLocalWPT(object):
+
+    def __init__(self, test_results):
+        """A mock LocalWPT with canned responses of test_patch.
+
+        Args:
+            test_results: a list of (success, error).
+        """
+        self.test_results = test_results
+        self.count = 0
+
+    def test_patch(self, _):
+        success, error = self.test_results[self.count]
+        self.count += 1
+        return success, error
+
+
 class ChromiumExportableCommitsTest(unittest.TestCase):
 
     # TODO(qyearsley): Add a test for exportable_commits_over_last_n_commits.
@@ -42,7 +59,7 @@
             'diff-tree': 'some\nfiles',
             'format-patch': 'hey I\'m a patch',
             'footers': 'cr-rev-position',
-        })
+        }, strict=True)
 
         commits, _ = _exportable_commits_since(
             'beefcafe', host, MockLocalWPT(), MockWPTGitHub(pull_requests=[]))
@@ -59,6 +76,46 @@
             ['git', 'format-patch', '-1', '--stdout', 'add087a97844f4b9e307d9a216940582d96db306', '--', 'some', 'files'],
         ])
 
+    def test_exportable_commits_since_require_clean_by_default(self):
+        host = MockHost()
+        host.executive = mock_git_commands({
+            'diff-tree': 'some\nfiles',
+            'footers': 'cr-rev-position',
+            'format-patch': 'hey I\'m a patch',
+            'rev-list': 'add087a97844f4b9e307d9a216940582d96db306\n'
+                        'add087a97844f4b9e307d9a216940582d96db307\n'
+                        'add087a97844f4b9e307d9a216940582d96db308\n'
+        })
+        local_wpt = MultiResponseMockLocalWPT([
+            (True, ''),
+            (False, 'patch failure'),
+            (True, ''),
+        ])
+
+        commits, _ = _exportable_commits_since(
+            'beefcafe', host, local_wpt, MockWPTGitHub(pull_requests=[]))
+        self.assertEqual(len(commits), 2)
+
+    def test_exportable_commits_since_without_require_clean(self):
+        host = MockHost()
+        host.executive = mock_git_commands({
+            'diff-tree': 'some\nfiles',
+            'footers': 'cr-rev-position',
+            'format-patch': 'hey I\'m a patch',
+            'rev-list': 'add087a97844f4b9e307d9a216940582d96db306\n'
+                        'add087a97844f4b9e307d9a216940582d96db307\n'
+                        'add087a97844f4b9e307d9a216940582d96db308\n'
+        })
+        local_wpt = MultiResponseMockLocalWPT([
+            (True, ''),
+            (False, 'patch failure'),
+            (True, ''),
+        ])
+
+        commits, _ = _exportable_commits_since(
+            'beefcafe', host, local_wpt, MockWPTGitHub(pull_requests=[]), require_clean=False)
+        self.assertEqual(len(commits), 3)
+
     def test_get_commit_export_state(self):
         commit = MockChromiumCommit(MockHost())
         github = MockWPTGitHub(pull_requests=[])
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_exporter.py b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_exporter.py
index 04eb0fe..7c156196e 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_exporter.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_exporter.py
@@ -2,6 +2,9 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+"""Exports Chromium changes to web-platform-tests."""
+
+import argparse
 import logging
 
 from webkitpy.w3c.local_wpt import LocalWPT
@@ -10,7 +13,8 @@
     WPT_GH_URL,
     WPT_REVISION_FOOTER,
     EXPORT_PR_LABEL,
-    PROVISIONAL_PR_LABEL
+    PROVISIONAL_PR_LABEL,
+    read_credentials
 )
 from webkitpy.w3c.gerrit import GerritAPI, GerritCL
 from webkitpy.w3c.wpt_github import WPTGitHub, MergeError
@@ -20,22 +24,34 @@
 
 class TestExporter(object):
 
-    def __init__(self, host, gh_user, gh_token, gerrit_user, gerrit_token, dry_run=False):
+    def __init__(self, host):
         self.host = host
-        self.wpt_github = WPTGitHub(host, gh_user, gh_token)
+        self.wpt_github = None
+        self.gerrit = None
+        self.dry_run = False
+        self.local_wpt = None
 
-        self.gerrit = GerritAPI(self.host, gerrit_user, gerrit_token)
-
-        self.dry_run = dry_run
-        self.local_wpt = LocalWPT(self.host, gh_token)
-        self.local_wpt.fetch()
-
-    def run(self):
+    def main(self, argv=None):
         """Creates PRs for in-flight CLs and merges changes that land on master.
 
         Returns:
             A boolean: True if success, False if there were any patch failures.
         """
+        args = self.parse_args(argv)
+        self.dry_run = args.dry_run
+        credentials = read_credentials(self.host, args.credentials_json)
+
+        if not (credentials['GH_USER'] and credentials['GH_TOKEN']):
+            _log.error('Must provide both user and token for GitHub.')
+            return False
+
+        self.wpt_github = self.wpt_github or WPTGitHub(self.host, credentials['GH_USER'], credentials['GH_TOKEN'])
+        self.gerrit = self.gerrit or GerritAPI(self.host, credentials['GERRIT_USER'], credentials['GERRIT_TOKEN'])
+        self.local_wpt = self.local_wpt or LocalWPT(self.host, credentials['GH_TOKEN'])
+        self.local_wpt.fetch()
+
+        logging.basicConfig(level=logging.INFO, format='%(message)s')
+
         open_gerrit_cls = self.gerrit.query_exportable_open_cls()
         self.process_gerrit_cls(open_gerrit_cls)
 
@@ -46,6 +62,18 @@
 
         return not bool(errors)
 
+    def parse_args(self, argv):
+        parser = argparse.ArgumentParser(description=__doc__)
+        parser.add_argument(
+            '--dry-run', action='store_true',
+            help='See what would be done without actually creating or merging '
+                 'any pull requests.')
+        parser.add_argument(
+            '--credentials-json', required=True,
+            help='A JSON file with an object containing zero or more of the '
+                 'following keys: GH_USER, GH_TOKEN, GERRIT_USER, GERRIT_TOKEN')
+        return parser.parse_args(argv)
+
     def process_gerrit_cls(self, gerrit_cls):
         for cl in gerrit_cls:
             self.process_gerrit_cl(cl)
@@ -109,8 +137,17 @@
             self.create_or_update_pull_request_from_commit(commit)
 
     def get_exportable_commits(self):
+        """Gets exportable commits that can apply cleanly and independently.
+
+        Returns:
+            A list of ChromiumCommit for clean exportable commits, and a list
+            of error messages for other exportable commits that fail to apply.
+        """
+        # Exportable commits that cannot apply cleanly are logged, and will be
+        # retried next time. A common case is that a commit depends on an
+        # earlier commit, and can only be exported after the earlier one.
         return exportable_commits_over_last_n_commits(
-            self.host, self.local_wpt, self.wpt_github)
+            self.host, self.local_wpt, self.wpt_github, require_clean=True)
 
     def remove_provisional_pr_label(self, pull_request):
         if self.dry_run:
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_exporter_unittest.py b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_exporter_unittest.py
index b0f2e9e..72157867 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_exporter_unittest.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_exporter_unittest.py
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 import base64
+import json
 
 from webkitpy.common.host_mock import MockHost
 from webkitpy.common.system.executive_mock import MockExecutive, mock_git_commands
@@ -18,23 +19,34 @@
 
 class TestExporterTest(LoggingTestCase):
 
-    def test_dry_run_stops_before_creating_pr(self):
+    def setUp(self):
+        super(TestExporterTest, self).setUp()
         host = MockHost()
-        host.executive = mock_git_commands({
+        host.filesystem.write_text_file(
+            '/tmp/credentials.json',
+            json.dumps({
+                'GH_USER': 'github-username',
+                'GH_TOKEN': 'github-token',
+                'GERRIT_USER': 'gerrit-username',
+                'GERRIT_TOKEN': 'gerrit-token',
+            }))
+        self.host = host
+
+    def test_dry_run_stops_before_creating_pr(self):
+        self.host.executive = mock_git_commands({
             'crrev-parse': 'c2087acb00eee7960339a0be34ea27d6b20e1131',
         })
-        test_exporter = TestExporter(host, 'gh-username', 'gh-token', gerrit_user=None,
-                                     gerrit_token=None, dry_run=True)
+        test_exporter = TestExporter(self.host)
         test_exporter.wpt_github = MockWPTGitHub(pull_requests=[
             PullRequest(title='title1', number=1234, body='', state='open', labels=[]),
         ])
-        test_exporter.gerrit = MockGerritAPI(host, 'gerrit-username', 'gerrit-token')
+        test_exporter.gerrit = MockGerritAPI(self.host, 'gerrit-username', 'gerrit-token')
         test_exporter.get_exportable_commits = lambda: ([
-            ChromiumCommit(host, position='refs/heads/master@{#458475}'),
-            ChromiumCommit(host, position='refs/heads/master@{#458476}'),
-            ChromiumCommit(host, position='refs/heads/master@{#458477}'),
+            ChromiumCommit(self.host, position='refs/heads/master@{#458475}'),
+            ChromiumCommit(self.host, position='refs/heads/master@{#458476}'),
+            ChromiumCommit(self.host, position='refs/heads/master@{#458477}'),
         ], [])
-        success = test_exporter.run()
+        success = test_exporter.main(['--credentials-json', '/tmp/credentials.json', '--dry-run'])
 
         self.assertTrue(success)
         self.assertEqual(test_exporter.wpt_github.calls, [
@@ -44,7 +56,6 @@
         ])
 
     def test_creates_pull_request_for_all_exportable_commits(self):
-        host = MockHost()
 
         # TODO(robertma): Use MockChromiumCommit instead.
         def mock_command(args):
@@ -70,11 +81,11 @@
                     return 'newest fake text'
             return canned_git_outputs.get(args[1], '')
 
-        host.executive = MockExecutive(run_command_fn=mock_command)
-        test_exporter = TestExporter(host, 'gh-username', 'gh-token', gerrit_user=None, gerrit_token=None)
+        self.host.executive = MockExecutive(run_command_fn=mock_command)
+        test_exporter = TestExporter(self.host)
         test_exporter.wpt_github = MockWPTGitHub(pull_requests=[], create_pr_fail_index=1)
-        test_exporter.gerrit = MockGerritAPI(host, 'gerrit-username', 'gerrit-token')
-        success = test_exporter.run()
+        test_exporter.gerrit = MockGerritAPI(self.host, 'gerrit-username', 'gerrit-token')
+        success = test_exporter.main(['--credentials-json', '/tmp/credentials.json'])
 
         self.assertTrue(success)
         self.assertEqual(test_exporter.wpt_github.calls, [
@@ -107,12 +118,11 @@
         # 3. #458477 has a closed PR associated with it and should be skipped.
         # 4. #458478 has an in-flight PR associated with it and should be merged successfully.
         # 5. #458479 has an in-flight PR associated with it but can not be merged.
-        host = MockHost()
-        host.executive = mock_git_commands({
+        self.host.executive = mock_git_commands({
             'show': 'git show text\nCr-Commit-Position: refs/heads/master@{#458476}\nChange-Id: I0476',
             'crrev-parse': 'c2087acb00eee7960339a0be34ea27d6b20e1131',
         })
-        test_exporter = TestExporter(host, 'gh-username', 'gh-token', gerrit_user=None, gerrit_token=None)
+        test_exporter = TestExporter(self.host)
         test_exporter.wpt_github = MockWPTGitHub(pull_requests=[
             PullRequest(
                 title='Open PR',
@@ -143,15 +153,15 @@
                 labels=[]  # It's important that this is empty.
             ),
         ], unsuccessful_merge_index=3)  # Mark the last PR as unmergable.
-        test_exporter.gerrit = MockGerritAPI(host, 'gerrit-username', 'gerrit-token')
+        test_exporter.gerrit = MockGerritAPI(self.host, 'gerrit-username', 'gerrit-token')
         test_exporter.get_exportable_commits = lambda: ([
-            MockChromiumCommit(host, position='refs/heads/master@{#458475}', change_id='I0005'),
-            MockChromiumCommit(host, position='refs/heads/master@{#458476}', change_id='I0476'),
-            MockChromiumCommit(host, position='refs/heads/master@{#458477}', change_id='Idead'),
-            MockChromiumCommit(host, position='refs/heads/master@{#458478}', change_id='I0118'),
-            MockChromiumCommit(host, position='refs/heads/master@{#458479}', change_id='I0147'),
+            MockChromiumCommit(self.host, position='refs/heads/master@{#458475}', change_id='I0005'),
+            MockChromiumCommit(self.host, position='refs/heads/master@{#458476}', change_id='I0476'),
+            MockChromiumCommit(self.host, position='refs/heads/master@{#458477}', change_id='Idead'),
+            MockChromiumCommit(self.host, position='refs/heads/master@{#458478}', change_id='I0118'),
+            MockChromiumCommit(self.host, position='refs/heads/master@{#458479}', change_id='I0147'),
         ], [])
-        success = test_exporter.run()
+        success = test_exporter.main(['--credentials-json', '/tmp/credentials.json'])
 
         self.assertTrue(success)
         self.assertEqual(test_exporter.wpt_github.calls, [
@@ -185,12 +195,10 @@
         self.assertEqual(test_exporter.wpt_github.pull_requests_merged, [3456])
 
     def test_new_gerrit_cl(self):
-        host = MockHost()
-        test_exporter = TestExporter(host, 'gh-username', 'gh-token', gerrit_user=None,
-                                     gerrit_token=None, dry_run=False)
+        test_exporter = TestExporter(self.host)
         test_exporter.wpt_github = MockWPTGitHub(pull_requests=[])
         test_exporter.get_exportable_commits = lambda: ([], [])
-        test_exporter.gerrit = MockGerritAPI(host, 'gerrit-username', 'gerrit-token')
+        test_exporter.gerrit = MockGerritAPI(self.host, 'gerrit-username', 'gerrit-token')
         test_exporter.gerrit.get = lambda path, raw: base64.b64encode('sample diff')  # pylint: disable=unused-argument
         test_exporter.gerrit.query_exportable_open_cls = lambda: [
             GerritCL(data={
@@ -205,7 +213,7 @@
                 'owner': {'email': 'test@chromium.org'},
             }, api=test_exporter.gerrit),
         ]
-        test_exporter.run()
+        test_exporter.main(['--credentials-json', '/tmp/credentials.json'])
 
         self.assertEqual(test_exporter.wpt_github.calls, [
             'pr_with_change_id',
@@ -221,16 +229,14 @@
         self.assertEqual(test_exporter.wpt_github.pull_requests_merged, [])
 
     def test_gerrit_cl_no_update_if_pr_with_same_revision(self):
-        host = MockHost()
-        test_exporter = TestExporter(host, 'gh-username', 'gh-token', gerrit_user=None,
-                                     gerrit_token=None, dry_run=False)
+        test_exporter = TestExporter(self.host)
         test_exporter.wpt_github = MockWPTGitHub(pull_requests=[
             PullRequest(title='title1', number=1234,
                         body='description\nWPT-Export-Revision: 1',
                         state='open', labels=[]),
         ])
         test_exporter.get_exportable_commits = lambda: ([], [])
-        test_exporter.gerrit = MockGerritAPI(host, 'gerrit-username', 'gerrit-token')
+        test_exporter.gerrit = MockGerritAPI(self.host, 'gerrit-username', 'gerrit-token')
         test_exporter.gerrit.query_exportable_open_cls = lambda: [
             GerritCL(data={
                 'change_id': '1',
@@ -244,7 +250,7 @@
                 'owner': {'email': 'test@chromium.org'},
             }, api=test_exporter.gerrit),
         ]
-        success = test_exporter.run()
+        success = test_exporter.main(['--credentials-json', '/tmp/credentials.json'])
 
         self.assertTrue(success)
         self.assertEqual(test_exporter.wpt_github.calls, [
@@ -254,16 +260,14 @@
         self.assertEqual(test_exporter.wpt_github.pull_requests_merged, [])
 
     def test_gerrit_cl_updates_if_cl_has_new_revision(self):
-        host = MockHost()
-        test_exporter = TestExporter(host, 'gh-username', 'gh-token', gerrit_user=None,
-                                     gerrit_token=None, dry_run=False)
+        test_exporter = TestExporter(self.host)
         test_exporter.wpt_github = MockWPTGitHub(pull_requests=[
             PullRequest(title='title1', number=1234,
                         body='description\nWPT-Export-Revision: 1',
                         state='open', labels=[]),
         ])
         test_exporter.get_exportable_commits = lambda: ([], [])
-        test_exporter.gerrit = MockGerritAPI(host, 'gerrit-username', 'gerrit-token')
+        test_exporter.gerrit = MockGerritAPI(self.host, 'gerrit-username', 'gerrit-token')
         test_exporter.gerrit.get = lambda path, raw: base64.b64encode('sample diff')  # pylint: disable=unused-argument
         test_exporter.gerrit.query_exportable_open_cls = lambda: [
             GerritCL(data={
@@ -285,7 +289,7 @@
                 'owner': {'email': 'test@chromium.org'},
             }, api=test_exporter.gerrit),
         ]
-        test_exporter.run()
+        test_exporter.main(['--credentials-json', '/tmp/credentials.json'])
 
         self.assertEqual(test_exporter.wpt_github.calls, [
             'pr_with_change_id',
@@ -294,21 +298,19 @@
         self.assertEqual(test_exporter.wpt_github.pull_requests_created, [])
         self.assertEqual(test_exporter.wpt_github.pull_requests_merged, [])
 
-    def test_updates_landed_gerrit_cl(self):
-        host = MockHost()
-        test_exporter = TestExporter(host, 'gh-username', 'gh-token', gerrit_user=None,
-                                     gerrit_token=None, dry_run=False)
+    def test_attempts_to_merge_landed_gerrit_cl(self):
+        test_exporter = TestExporter(self.host)
         test_exporter.wpt_github = MockWPTGitHub(pull_requests=[
             PullRequest(title='title1', number=1234,
                         body='description\nWPT-Export-Revision: 9\nChange-Id: decafbad',
                         state='open', labels=['do not merge yet']),
         ])
         test_exporter.get_exportable_commits = lambda: ([
-            MockChromiumCommit(host, change_id='decafbad'),
+            MockChromiumCommit(self.host, change_id='decafbad'),
         ], [])
-        test_exporter.gerrit = MockGerritAPI(host, 'gerrit-username', 'gerrit-token')
+        test_exporter.gerrit = MockGerritAPI(self.host, 'gerrit-username', 'gerrit-token')
         test_exporter.gerrit.query_exportable_open_cls = lambda: []
-        success = test_exporter.run()
+        success = test_exporter.main(['--credentials-json', '/tmp/credentials.json'])
 
         self.assertTrue(success)
         self.assertEqual(test_exporter.wpt_github.calls, [
@@ -321,20 +323,18 @@
         self.assertEqual(test_exporter.wpt_github.pull_requests_merged, [])
 
     def test_merges_non_provisional_pr(self):
-        host = MockHost()
-        test_exporter = TestExporter(host, 'gh-username', 'gh-token', gerrit_user=None,
-                                     gerrit_token=None, dry_run=False)
+        test_exporter = TestExporter(self.host)
         test_exporter.wpt_github = MockWPTGitHub(pull_requests=[
             PullRequest(title='title1', number=1234,
                         body='description\nWPT-Export-Revision: 9\nChange-Id: decafbad',
                         state='open', labels=['']),
         ])
         test_exporter.get_exportable_commits = lambda: ([
-            MockChromiumCommit(host, change_id='decafbad'),
+            MockChromiumCommit(self.host, change_id='decafbad'),
         ], [])
-        test_exporter.gerrit = MockGerritAPI(host, 'gerrit-username', 'gerrit-token')
+        test_exporter.gerrit = MockGerritAPI(self.host, 'gerrit-username', 'gerrit-token')
         test_exporter.gerrit.query_exportable_open_cls = lambda: []
-        success = test_exporter.run()
+        success = test_exporter.main(['--credentials-json', '/tmp/credentials.json'])
 
         self.assertTrue(success)
         self.assertEqual(test_exporter.wpt_github.calls, [
@@ -347,12 +347,10 @@
         self.assertEqual(test_exporter.wpt_github.pull_requests_merged, [1234])
 
     def test_does_not_create_pr_if_cl_review_has_not_started(self):
-        host = MockHost()
-        test_exporter = TestExporter(host, 'gh-username', 'gh-token', gerrit_user=None,
-                                     gerrit_token=None, dry_run=False)
+        test_exporter = TestExporter(self.host)
         test_exporter.wpt_github = MockWPTGitHub(pull_requests=[])
         test_exporter.get_exportable_commits = lambda: ([], [])
-        test_exporter.gerrit = MockGerritAPI(host, 'gerrit-username', 'gerrit-token')
+        test_exporter.gerrit = MockGerritAPI(self.host, 'gerrit-username', 'gerrit-token')
         test_exporter.gerrit.get = lambda path, raw: base64.b64encode('sample diff')  # pylint: disable=unused-argument
         test_exporter.gerrit.query_exportable_open_cls = lambda: [
             GerritCL(data={
@@ -374,7 +372,7 @@
                 'owner': {'email': 'test@chromium.org'},
             }, api=test_exporter.gerrit),
         ]
-        success = test_exporter.run()
+        success = test_exporter.main(['--credentials-json', '/tmp/credentials.json'])
 
         self.assertTrue(success)
         self.assertEqual(test_exporter.wpt_github.calls, [])
@@ -382,14 +380,12 @@
         self.assertEqual(test_exporter.wpt_github.pull_requests_merged, [])
 
     def test_run_returns_false_on_patch_failure(self):
-        host = MockHost()
-        test_exporter = TestExporter(host, 'gh-username', 'gh-token', gerrit_user=None,
-                                     gerrit_token=None, dry_run=False)
+        test_exporter = TestExporter(self.host)
         test_exporter.wpt_github = MockWPTGitHub(pull_requests=[])
         test_exporter.get_exportable_commits = lambda: ([], ['There was an error with the rutabaga.'])
-        test_exporter.gerrit = MockGerritAPI(host, 'gerrit-username', 'gerrit-token')
+        test_exporter.gerrit = MockGerritAPI(self.host, 'gerrit-username', 'gerrit-token')
         test_exporter.gerrit.get = lambda path, raw: base64.b64encode('sample diff')  # pylint: disable=unused-argument
-        success = test_exporter.run()
+        success = test_exporter.main(['--credentials-json', '/tmp/credentials.json'])
 
         self.assertFalse(success)
         self.assertLog(['INFO: Cloning GitHub w3c/web-platform-tests into /tmp/wpt\n',
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_importer.py b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_importer.py
index fea111a..aca9b969c 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_importer.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_importer.py
@@ -17,6 +17,7 @@
 from webkitpy.common.net.buildbot import current_build_link
 from webkitpy.common.net.git_cl import GitCL
 from webkitpy.common.path_finder import PathFinder
+from webkitpy.common.system.log_utils import configure_logging
 from webkitpy.layout_tests.models.test_expectations import TestExpectations, TestExpectationParser
 from webkitpy.layout_tests.port.base import Port
 from webkitpy.w3c.chromium_exportable_commits import exportable_commits_over_last_n_commits
@@ -52,7 +53,7 @@
 
         self.verbose = options.verbose
         log_level = logging.DEBUG if self.verbose else logging.INFO
-        logging.basicConfig(level=log_level, format='%(message)s')
+        configure_logging(logging_level=log_level, include_time=True)
 
         if not self.checkout_is_okay():
             return 1
@@ -241,8 +242,7 @@
             A list of commits applied (could be empty), or None if any
             of the patches could not be applied cleanly.
         """
-        # Ignore patch failures when importing. Exporter will report them.
-        commits, _ = self.exportable_but_not_exported_commits(local_wpt)
+        commits = self.exportable_but_not_exported_commits(local_wpt)
         for commit in commits:
             _log.info('Applying exportable commit locally:')
             _log.info(commit.url())
@@ -257,6 +257,8 @@
                 _log.warning('No pull request found.')
             error = local_wpt.apply_patch(commit.format_patch())
             if error:
+                _log.error('Commit cannot be applied cleanly:')
+                _log.error(error)
                 return None
             self.run(
                 ['git', 'commit', '--all', '-F', '-'],
@@ -265,9 +267,17 @@
         return commits
 
     def exportable_but_not_exported_commits(self, local_wpt):
-        """Returns a list of commits that would be clobbered by importer
-        and a list of error messages for patches failing to apply cleanly."""
-        return exportable_commits_over_last_n_commits(self.host, local_wpt, self.wpt_github)
+        """Returns a list of commits that would be clobbered by importer.
+
+        The list contains all exportable but not exported commits, not filtered
+        by whether they can apply cleanly.
+        """
+        # The errors returned by exportable_commits_over_last_n_commits are
+        # irrelevant and ignored here, because it tests patches *individually*
+        # while the importer tries to reapply these patches *cumulatively*.
+        commits, _ = exportable_commits_over_last_n_commits(
+            self.host, local_wpt, self.wpt_github, require_clean=False)
+        return commits
 
     def _generate_manifest(self):
         """Generates MANIFEST.json for imported tests.
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_importer_unittest.py b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_importer_unittest.py
index 83129c58..2a3ecee 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_importer_unittest.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_importer_unittest.py
@@ -116,7 +116,7 @@
                 '+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-ui-3/outline-004.html\n'
                 '@@ -20,7 +20,7 @@\n'
                 '...'))
-        importer.exportable_but_not_exported_commits = lambda _: ([fake_commit], [])
+        importer.exportable_but_not_exported_commits = lambda _: [fake_commit]
         applied = importer.apply_exportable_commits_locally(LocalWPT(host))
         self.assertEqual(applied, [fake_commit])
         self.assertEqual(host.executive.full_calls, [
@@ -145,7 +145,7 @@
         wpt_github = MockWPTGitHub(pull_requests=[])
         importer = TestImporter(host, wpt_github=wpt_github)
         commit = MockChromiumCommit(host, subject='My fake commit')
-        importer.exportable_but_not_exported_commits = lambda _: ([commit], [])
+        importer.exportable_but_not_exported_commits = lambda _: [commit]
         local_wpt = LocalWPT(host)
         local_wpt.apply_patch = lambda _: 'Failed'  # Failure to apply patch.
         applied = importer.apply_exportable_commits_locally(local_wpt)
diff --git a/third_party/WebKit/Tools/Scripts/wpt-export b/third_party/WebKit/Tools/Scripts/wpt-export
index 87f2b845..88c267c 100755
--- a/third_party/WebKit/Tools/Scripts/wpt-export
+++ b/third_party/WebKit/Tools/Scripts/wpt-export
@@ -3,60 +3,24 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Exports local changes to web-platform-tests in Chromium to upstream repo.
+"""Pushes changes to web-platform-tests inside Chromium to the upstream repo."""
 
-This script checks LayoutTests/external/wpt for changes that can be exported,
-then creates a patch, and creates and lands a pull request for the upstream
-repository.
-
-For this script to be effective it needs to be run on a regular
-interval (e.g. every 10 mins).
-"""
-
-import argparse
-import json
-import logging
-import sys
-
+from webkitpy.common import exit_codes
 from webkitpy.common.host import Host
+
 from webkitpy.w3c.test_exporter import TestExporter
-from webkitpy.w3c.common import read_credentials
-
-
-_log = logging.getLogger(__name__)
 
 
 def main():
-    logging.basicConfig(level=logging.INFO, format='%(message)s')
-
-    parser = argparse.ArgumentParser(description=__doc__)
-    parser.add_argument(
-        '--dry-run', action='store_true',
-        help='See what would be done without actually creating or merging '
-             'any pull requests.')
-    parser.add_argument(
-        '--credentials-json', required=True,
-        help='A JSON file with an object containing zero or more of the '
-             'following keys: GH_USER, GH_TOKEN, GERRIT_USER, GERRIT_TOKEN')
-    args = parser.parse_args()
-
     host = Host()
-    credentials = read_credentials(host, args.credentials_json)
-
-    if not (credentials['GH_USER'] and credentials['GH_TOKEN']):
-        parser.error('Must provide both gh_user and gh_token for GitHub.')
-
-    success = TestExporter(
-        host=host,
-        gh_user=credentials['GH_USER'],
-        gh_token=credentials['GH_TOKEN'],
-        gerrit_user=credentials['GERRIT_USER'],
-        gerrit_token=credentials['GERRIT_TOKEN'],
-        dry_run=args.dry_run
-    ).run()
-
-    return 0 if success else 1
+    exporter = TestExporter(host)
+    try:
+        success = exporter.main()
+        host.exit(0 if success else 1)
+    except KeyboardInterrupt:
+        host.print_('Interrupted, exiting')
+        host.exit(exit_codes.INTERRUPTED_EXIT_STATUS)
 
 
 if __name__ == '__main__':
-    sys.exit(main())
+    main()
diff --git a/third_party/WebKit/Tools/Scripts/wpt-import b/third_party/WebKit/Tools/Scripts/wpt-import
index e90d8c6d..6d95943 100755
--- a/third_party/WebKit/Tools/Scripts/wpt-import
+++ b/third_party/WebKit/Tools/Scripts/wpt-import
@@ -3,15 +3,15 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Pulls the latest revisions of the web-platform-tests or csswg-test repos."""
+"""Pulls the latest revisions of the web-platform-tests."""
 
 from webkitpy.common import exit_codes
-from webkitpy.common import version_check
 from webkitpy.common.host import Host
+
 from webkitpy.w3c.test_importer import TestImporter
 
 
-if __name__ == '__main__':
+def main():
     host = Host()
     importer = TestImporter(host)
     try:
@@ -19,3 +19,7 @@
     except KeyboardInterrupt:
         host.print_("Interrupted, exiting")
         host.exit(exit_codes.INTERRUPTED_EXIT_STATUS)
+
+
+if __name__ == '__main__':
+    main()
diff --git a/third_party/WebKit/public/platform/WebLayer.h b/third_party/WebKit/public/platform/WebLayer.h
index 00a04a22..b9886a2 100644
--- a/third_party/WebKit/public/platform/WebLayer.h
+++ b/third_party/WebKit/public/platform/WebLayer.h
@@ -215,6 +215,11 @@
   // deleting the scroll client.
   virtual void SetScrollClient(WebLayerScrollClient*) = 0;
 
+  // Sets a synthetic impl-side scroll offset which will end up reporting this
+  // call back to blink via the |WebLayerScrollClient| callback.
+  virtual void SetScrollOffsetFromImplSideForTesting(
+      const gfx::ScrollOffset&) = 0;
+
   // The scroll-boundary-behavior allows developers to specify whether the
   // scroll should be propagated to its ancestors at the beginning of the
   // scroll, and whether the overscroll should cause UI affordance such as
diff --git a/third_party/WebKit/public/platform/WebLayerScrollClient.h b/third_party/WebKit/public/platform/WebLayerScrollClient.h
index 0f426575..40e6193 100644
--- a/third_party/WebKit/public/platform/WebLayerScrollClient.h
+++ b/third_party/WebKit/public/platform/WebLayerScrollClient.h
@@ -27,12 +27,20 @@
 
 #include "WebCommon.h"
 
+namespace gfx {
+class ScrollOffset;
+}
+
+namespace cc {
+struct ElementId;
+}
+
 namespace blink {
 
 // A client that is notified of scrolling on a WebLayer.
 class BLINK_PLATFORM_EXPORT WebLayerScrollClient {
  public:
-  virtual void DidScroll(const gfx::ScrollOffset&) = 0;
+  virtual void DidScroll(const gfx::ScrollOffset&, const cc::ElementId&) = 0;
 
  protected:
   virtual ~WebLayerScrollClient() {}
diff --git a/third_party/WebKit/public/platform/WebSecurityStyle.h b/third_party/WebKit/public/platform/WebSecurityStyle.h
index a5fa203..2d67d09 100644
--- a/third_party/WebKit/public/platform/WebSecurityStyle.h
+++ b/third_party/WebKit/public/platform/WebSecurityStyle.h
@@ -10,7 +10,6 @@
   kWebSecurityStyleUnknown,
   kWebSecurityStyleNeutral,
   kWebSecurityStyleInsecure,
-  kWebSecurityStyleWarning,
   kWebSecurityStyleSecure,
   kWebSecurityStyleLast = kWebSecurityStyleSecure
 };
diff --git a/third_party/WebKit/public/platform/modules/background_fetch/background_fetch.mojom b/third_party/WebKit/public/platform/modules/background_fetch/background_fetch.mojom
index 3d5e49c1..605ea065 100644
--- a/third_party/WebKit/public/platform/modules/background_fetch/background_fetch.mojom
+++ b/third_party/WebKit/public/platform/modules/background_fetch/background_fetch.mojom
@@ -11,7 +11,8 @@
   NONE,
   DUPLICATED_ID,
   INVALID_ARGUMENT,
-  INVALID_ID
+  INVALID_ID,
+  STORAGE_ERROR
 };
 
 // Represents the definition of an icon developers can optionally provide with a
diff --git a/third_party/WebKit/public/web/WebFrame.h b/third_party/WebKit/public/web/WebFrame.h
index 41cffaa..bd94657 100644
--- a/third_party/WebKit/public/web/WebFrame.h
+++ b/third_party/WebKit/public/web/WebFrame.h
@@ -49,7 +49,6 @@
 class Visitor;
 class WebElement;
 class WebLocalFrame;
-class WebPerformance;
 class WebRemoteFrame;
 class WebSecurityOrigin;
 class WebString;
@@ -174,10 +173,6 @@
   // Returns the next frame in "frame traversal order".
   WebFrame* TraverseNext() const;
 
-  // Content ------------------------------------------------------------
-
-  virtual WebPerformance Performance() const = 0;
-
   // Scripting ----------------------------------------------------------
 
   // Returns the global proxy object.
diff --git a/third_party/WebKit/public/web/WebLocalFrame.h b/third_party/WebKit/public/web/WebLocalFrame.h
index 3130b9d..d3372d7 100644
--- a/third_party/WebKit/public/web/WebLocalFrame.h
+++ b/third_party/WebKit/public/web/WebLocalFrame.h
@@ -42,6 +42,7 @@
 class WebFrameWidget;
 class WebFrameScheduler;
 class WebInputMethodController;
+class WebPerformance;
 class WebRange;
 class WebSecurityOrigin;
 class WebScriptExecutionCallback;
@@ -788,6 +789,10 @@
   // checkbox, radio etc.)
   virtual void AdvanceFocusInForm(WebFocusType) = 0;
 
+  // Performance --------------------------------------------------------
+
+  virtual WebPerformance Performance() const = 0;
+
   // Testing ------------------------------------------------------------------
 
   // Dumps the layer tree, used by the accelerated compositor, in
diff --git a/tools/battor_agent/battor_agent.cc b/tools/battor_agent/battor_agent.cc
index 3319713..2a623892 100644
--- a/tools/battor_agent/battor_agent.cc
+++ b/tools/battor_agent/battor_agent.cc
@@ -386,6 +386,11 @@
   }
 }
 
+void BattOrAgent::OnFlushComplete(bool success) {
+  // TODO(charliea): Wire up the BattOrAgent so that it calls Flush() when a
+  // read fails and before retrying the command.
+}
+
 void BattOrAgent::PerformAction(Action action) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
diff --git a/tools/battor_agent/battor_agent.h b/tools/battor_agent/battor_agent.h
index fa39fee..c8c66cc 100644
--- a/tools/battor_agent/battor_agent.h
+++ b/tools/battor_agent/battor_agent.h
@@ -69,6 +69,7 @@
   void OnMessageRead(bool success,
                      BattOrMessageType type,
                      std::unique_ptr<std::vector<char>> bytes) override;
+  void OnFlushComplete(bool success) override;
 
  protected:
   // The connection that knows how to communicate with the BattOr in terms of
diff --git a/tools/battor_agent/battor_connection.h b/tools/battor_agent/battor_connection.h
index ced2c00f..23b9b6e 100644
--- a/tools/battor_agent/battor_connection.h
+++ b/tools/battor_agent/battor_connection.h
@@ -37,6 +37,7 @@
     virtual void OnMessageRead(bool success,
                                BattOrMessageType type,
                                std::unique_ptr<std::vector<char>> bytes) = 0;
+    virtual void OnFlushComplete(bool success) = 0;
   };
 
   BattOrConnection(Listener* listener);
@@ -66,7 +67,10 @@
   // Cancels the current message read operation.
   virtual void CancelReadMessage() = 0;
 
-  // Flushes the serial connection to the BattOr.
+  // Flushes the serial connection to the BattOr, reading and throwing away
+  // bytes from the serial connection until the connection is quiet for a
+  // sufficiently long time. This also discards any trailing bytes from past
+  // successful reads.
   virtual void Flush() = 0;
 
  protected:
diff --git a/tools/battor_agent/battor_connection_impl.cc b/tools/battor_agent/battor_connection_impl.cc
index c1fcf5a4..01b177e 100644
--- a/tools/battor_agent/battor_connection_impl.cc
+++ b/tools/battor_agent/battor_connection_impl.cc
@@ -4,13 +4,15 @@
 
 #include "tools/battor_agent/battor_connection_impl.h"
 
+#include <memory>
+
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/command_line.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "base/time/time.h"
+#include "base/time/default_tick_clock.h"
 #include "device/serial/buffer.h"
 #include "device/serial/serial_io_handler.h"
 #include "net/base/io_buffer.h"
@@ -39,6 +41,10 @@
 const bool kBattOrHasCtsFlowControl = true;
 // The maximum BattOr message is 50kB long.
 const size_t kMaxMessageSizeBytes = 50000;
+const size_t kFlushBufferSize = 50000;
+// The length of time that must pass without receiving any bytes in order for a
+// flush to be considered complete.
+const uint16_t kFlushQuietPeriodThresholdMs = 50;
 
 // Returns the maximum number of bytes that could be required to read a message
 // of the specified type.
@@ -74,14 +80,13 @@
     serial_log_.open(serial_log_path.c_str(),
                      std::fstream::out | std::fstream::trunc);
   }
+  tick_clock_ = std::make_unique<base::DefaultTickClock>();
 }
 
 BattOrConnectionImpl::~BattOrConnectionImpl() {}
 
 void BattOrConnectionImpl::Open() {
   if (io_handler_) {
-    // Opening new connection so flush serial data from old connection.
-    Flush();
     OnOpened(true);
     return;
   }
@@ -190,8 +195,9 @@
 }
 
 void BattOrConnectionImpl::Flush() {
-  io_handler_->Flush();
   already_read_buffer_.clear();
+  flush_quiet_period_start_ = tick_clock_->NowTicks();
+  BeginReadBytesForFlush();
 }
 
 scoped_refptr<device::SerialIoHandler> BattOrConnectionImpl::CreateIoHandler() {
@@ -199,8 +205,8 @@
 }
 
 void BattOrConnectionImpl::BeginReadBytesForMessage(size_t max_bytes_to_read) {
-  LogSerial(
-      StringPrintf("Starting read of up to %zu bytes.", max_bytes_to_read));
+  LogSerial(StringPrintf("(message) Starting read of up to %zu bytes.",
+                         max_bytes_to_read));
 
   pending_read_buffer_ =
       make_scoped_refptr(new net::IOBuffer(max_bytes_to_read));
@@ -216,7 +222,7 @@
     device::mojom::SerialReceiveError error) {
   if (error != device::mojom::SerialReceiveError::NONE) {
     LogSerial(StringPrintf(
-        "Read failed due to serial read failure with error code: %d.",
+        "(message) Read failed due to serial read failure with error code: %d.",
         static_cast<int>(error)));
     EndReadBytesForMessage(false, BATTOR_MESSAGE_TYPE_CONTROL, nullptr);
     return;
@@ -229,10 +235,10 @@
     // exacerbates a problem on Mac wherein we can't process sample frames
     // quickly enough to prevent the serial buffer from overflowing, causing us
     // to drop frames.
-    LogSerial(StringPrintf("%d more bytes read.", bytes_read));
+    LogSerial(StringPrintf("(message) %d more bytes read.", bytes_read));
   } else {
     LogSerial(StringPrintf(
-        "%d more bytes read: %s.", bytes_read,
+        "(message) %d more bytes read: %s.", bytes_read,
         CharArrayToString(pending_read_buffer_->data(), bytes_read).c_str()));
   }
 
@@ -250,26 +256,29 @@
   if (parse_message_error == ParseMessageError::NOT_ENOUGH_BYTES) {
     if (already_read_buffer_.size() >= message_max_bytes) {
       LogSerial(
-          "Read failed due to no complete message after max read length.");
+          "(message) Read failed due to no complete message after max read "
+          "length.");
       EndReadBytesForMessage(false, BATTOR_MESSAGE_TYPE_CONTROL, nullptr);
       return;
     }
 
-    LogSerial("(Message still incomplete: reading more bytes.)");
+    LogSerial("(message) Still incomplete: reading more bytes.)");
     BeginReadBytesForMessage(message_max_bytes - already_read_buffer_.size());
     return;
   }
 
   if (parse_message_error != ParseMessageError::NONE) {
-    LogSerial(StringPrintf(
-        "Read failed due to the message containing an irrecoverable error: %d.",
-        parse_message_error));
+    LogSerial(
+        StringPrintf("(message) Read failed due to the message containing an "
+                     "irrecoverable error: %d.",
+                     parse_message_error));
     EndReadBytesForMessage(false, BATTOR_MESSAGE_TYPE_CONTROL, nullptr);
     return;
   }
 
   if (type != pending_read_message_type_) {
-    LogSerial("Read failed due to receiving a message of the wrong type.");
+    LogSerial(
+        "(message) Read failed due to receiving a message of the wrong type.");
     EndReadBytesForMessage(false, BATTOR_MESSAGE_TYPE_CONTROL, nullptr);
     return;
   }
@@ -281,7 +290,7 @@
     bool success,
     BattOrMessageType type,
     std::unique_ptr<vector<char>> bytes) {
-  LogSerial(StringPrintf("Read finished with success: %d.", success));
+  LogSerial(StringPrintf("(message) Read finished with success: %d.", success));
 
   pending_read_buffer_ = nullptr;
   base::ThreadTaskRunnerHandle::Get()->PostTask(
@@ -290,6 +299,81 @@
                  type, base::Passed(std::move(bytes))));
 }
 
+void BattOrConnectionImpl::BeginReadBytesForFlush() {
+  base::TimeDelta quiet_period_duration =
+      tick_clock_->NowTicks() - flush_quiet_period_start_;
+  LogSerial(
+      StringPrintf("(flush) Starting read (quiet period has lasted %f ms).",
+                   quiet_period_duration.InMillisecondsF()));
+
+  pending_read_buffer_ =
+      make_scoped_refptr(new net::IOBuffer(kFlushBufferSize));
+
+  io_handler_->Read(base::MakeUnique<device::ReceiveBuffer>(
+      pending_read_buffer_, static_cast<uint32_t>(kFlushBufferSize),
+      base::BindOnce(&BattOrConnectionImpl::OnBytesReadForFlush,
+                     base::Unretained(this))));
+  SetFlushReadTimeout();
+}
+
+void BattOrConnectionImpl::SetFlushReadTimeout() {
+  flush_timeout_callback_.Reset(
+      base::Bind(&BattOrConnectionImpl::CancelReadMessage, AsWeakPtr()));
+  base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+      FROM_HERE, flush_timeout_callback_.callback(),
+      base::TimeDelta::FromMilliseconds(kFlushQuietPeriodThresholdMs));
+}
+
+void BattOrConnectionImpl::OnBytesReadForFlush(
+    int bytes_read,
+    device::mojom::SerialReceiveError error) {
+  flush_timeout_callback_.Cancel();
+
+  if (error != device::mojom::SerialReceiveError::NONE &&
+      error != device::mojom::SerialReceiveError::TIMEOUT) {
+    LogSerial(StringPrintf(
+        "(flush) Read failed due to serial read failure with error code: %d.",
+        static_cast<int>(error)));
+    pending_read_buffer_ = nullptr;
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::Bind(&Listener::OnFlushComplete,
+                              base::Unretained(listener_), false));
+    return;
+  }
+
+  LogSerial(StringPrintf("(flush) %i additional bytes read.", bytes_read));
+  if (bytes_read == 0 || error == device::mojom::SerialReceiveError::TIMEOUT) {
+    // Reading zero bytes or a serial read timeout both indicate that the
+    // connection was quiet.
+    base::TimeDelta quiet_period_duration =
+        tick_clock_->NowTicks() - flush_quiet_period_start_;
+    if (quiet_period_duration >=
+        base::TimeDelta::FromMilliseconds(kFlushQuietPeriodThresholdMs)) {
+      LogSerial("(flush) Quiet period has finished.");
+      pending_read_buffer_ = nullptr;
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::Bind(&Listener::OnFlushComplete,
+                                base::Unretained(listener_), true));
+      return;
+    }
+
+    // If we didn't receive bytes but the quiet period hasn't elapsed, try to
+    // read again after a delay.
+    LogSerial(StringPrintf("(flush) Reading more bytes after %u ms delay.",
+                           kFlushQuietPeriodThresholdMs));
+    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+        FROM_HERE,
+        base::BindOnce(&BattOrConnectionImpl::BeginReadBytesForFlush,
+                       AsWeakPtr()),
+        base::TimeDelta::FromMilliseconds(kFlushQuietPeriodThresholdMs));
+    return;
+  }
+
+  // We received additional bytes: restart the quiet period and read more bytes.
+  flush_quiet_period_start_ = tick_clock_->NowTicks();
+  BeginReadBytesForFlush();
+}
+
 BattOrConnectionImpl::ParseMessageError BattOrConnectionImpl::ParseMessage(
     BattOrMessageType* type,
     vector<char>* bytes) {
diff --git a/tools/battor_agent/battor_connection_impl.h b/tools/battor_agent/battor_connection_impl.h
index 3f7bdba..4d98eaf 100644
--- a/tools/battor_agent/battor_connection_impl.h
+++ b/tools/battor_agent/battor_connection_impl.h
@@ -6,13 +6,16 @@
 #define TOOLS_BATTOR_AGENT_BATTOR_CONNECTION_IMPL_H_
 
 #include <fstream>
+#include <memory>
 #include <vector>
 
 #include "base/callback_forward.h"
+#include "base/cancelable_callback.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/single_thread_task_runner.h"
+#include "base/time/tick_clock.h"
 #include "services/device/public/interfaces/serial.mojom.h"
 #include "tools/battor_agent/battor_connection.h"
 #include "tools/battor_agent/battor_error.h"
@@ -54,6 +57,8 @@
   // IO handler capable of reading and writing from the serial connection.
   scoped_refptr<device::SerialIoHandler> io_handler_;
 
+  std::unique_ptr<base::TickClock> tick_clock_;
+
  private:
   void OnOpened(bool success);
 
@@ -70,6 +75,11 @@
                               BattOrMessageType type,
                               std::unique_ptr<std::vector<char>> data);
 
+  void BeginReadBytesForFlush();
+  void OnBytesReadForFlush(int bytes_read,
+                           device::mojom::SerialReceiveError error);
+  void SetFlushReadTimeout();
+
   // Pulls off the next complete message from already_read_buffer_, returning
   // its type and contents through out parameters and any error that occurred
   // through the return value.
@@ -104,6 +114,13 @@
   // The total number of bytes that we're expecting to send.
   size_t pending_write_length_;
 
+  // The start of the period over which no bytes must be read from the serial
+  // connection in order for Flush() to be considered complete.
+  base::TimeTicks flush_quiet_period_start_;
+
+  // The timeout that will trigger a timeout at the end of a flush quiet period.
+  base::CancelableClosure flush_timeout_callback_;
+
   // Threads needed for serial communication.
   scoped_refptr<base::SingleThreadTaskRunner> ui_thread_task_runner_;
 
diff --git a/tools/battor_agent/battor_connection_impl_unittest.cc b/tools/battor_agent/battor_connection_impl_unittest.cc
index ac2546c4e..5909ee4 100644
--- a/tools/battor_agent/battor_connection_impl_unittest.cc
+++ b/tools/battor_agent/battor_connection_impl_unittest.cc
@@ -4,11 +4,14 @@
 
 #include "tools/battor_agent/battor_connection_impl.h"
 
+#include <memory>
+
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/weak_ptr.h"
-#include "base/test/test_simple_task_runner.h"
+#include "base/test/simple_test_tick_clock.h"
+#include "base/test/test_mock_time_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "device/serial/test_serial_io_handler.h"
 #include "services/device/public/interfaces/serial.mojom.h"
@@ -27,8 +30,11 @@
 // TestableBattOrConnection uses a fake serial connection be testable.
 class TestableBattOrConnection : public BattOrConnectionImpl {
  public:
-  TestableBattOrConnection(BattOrConnection::Listener* listener)
-      : BattOrConnectionImpl("/dev/test", listener, nullptr) {}
+  TestableBattOrConnection(BattOrConnection::Listener* listener,
+                           std::unique_ptr<base::TickClock> tick_clock)
+      : BattOrConnectionImpl("/dev/test", listener, nullptr) {
+    tick_clock_ = std::move(tick_clock);
+  }
   scoped_refptr<device::SerialIoHandler> CreateIoHandler() override {
     return device::TestSerialIoHandler::Create();
   }
@@ -44,7 +50,7 @@
                                  public BattOrConnection::Listener {
  public:
   BattOrConnectionImplTest()
-      : task_runner_(new base::TestSimpleTaskRunner()),
+      : task_runner_(new base::TestMockTimeTaskRunner()),
         thread_task_runner_handle_(task_runner_) {}
 
   void OnConnectionOpened(bool success) override { open_success_ = success; };
@@ -57,10 +63,15 @@
     read_type_ = type;
     read_bytes_ = std::move(bytes);
   }
+  void OnFlushComplete(bool success) override {
+    is_slow_flush_complete_ = true;
+    slow_flush_success_ = success;
+  }
 
  protected:
   void SetUp() override {
-    connection_.reset(new TestableBattOrConnection(this));
+    connection_.reset(
+        new TestableBattOrConnection(this, task_runner_->GetMockTickClock()));
     task_runner_->ClearPendingTasks();
   }
 
@@ -75,6 +86,12 @@
     task_runner_->RunUntilIdle();
   }
 
+  void Flush() {
+    is_slow_flush_complete_ = false;
+    connection_->Flush();
+    task_runner_->RunUntilIdle();
+  }
+
   // Reads the specified number of bytes directly from the serial connection.
   scoped_refptr<net::IOBuffer> ReadMessageRaw(int bytes_to_read) {
     scoped_refptr<net::IOBuffer> buffer(
@@ -96,9 +113,8 @@
     task_runner_->RunUntilIdle();
   }
 
-  void ForceReadTimeout() {
-    connection_->GetIoHandler()->ForceReceiveError(
-        device::mojom::SerialReceiveError::TIMEOUT);
+  void ForceReceiveError(device::mojom::SerialReceiveError error) {
+    connection_->GetIoHandler()->ForceReceiveError(error);
     task_runner_->RunUntilIdle();
   }
 
@@ -110,17 +126,23 @@
     task_runner_->RunUntilIdle();
   }
 
+  void AdvanceTickClock(base::TimeDelta delta) {
+    task_runner_->FastForwardBy(delta);
+  }
+
   bool GetOpenSuccess() { return open_success_; }
   bool GetSendSuccess() { return send_success_; }
   bool IsReadComplete() { return is_read_complete_; }
   bool GetReadSuccess() { return read_success_; }
   BattOrMessageType GetReadType() { return read_type_; }
   std::vector<char>* GetReadMessage() { return read_bytes_.get(); }
+  bool IsFlushComplete() { return is_slow_flush_complete_; }
+  bool GetFlushSuccess() { return slow_flush_success_; }
 
  private:
   std::unique_ptr<TestableBattOrConnection> connection_;
 
-  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+  scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
   base::ThreadTaskRunnerHandle thread_task_runner_handle_;
 
   // Result from the last connect command.
@@ -132,6 +154,9 @@
   bool read_success_;
   BattOrMessageType read_type_;
   std::unique_ptr<std::vector<char>> read_bytes_;
+  // Results from the last slow flush command.
+  bool is_slow_flush_complete_;
+  bool slow_flush_success_;
 };
 
 TEST_F(BattOrConnectionImplTest, InitSendsCorrectBytes) {
@@ -240,7 +265,7 @@
   // The first read should recognize that a second read is necessary.
   ASSERT_FALSE(IsReadComplete());
 
-  ForceReadTimeout();
+  ForceReceiveError(device::mojom::SerialReceiveError::TIMEOUT);
 
   // The second read should fail due to the time out.
   ASSERT_TRUE(IsReadComplete());
@@ -267,7 +292,7 @@
   // The first read should recognize that a second read is necessary.
   ASSERT_FALSE(IsReadComplete());
 
-  ForceReadTimeout();
+  ForceReceiveError(device::mojom::SerialReceiveError::TIMEOUT);
 
   // The second read should fail due to the time out.
   ASSERT_TRUE(IsReadComplete());
@@ -340,10 +365,10 @@
   SendControlMessage(BATTOR_CONTROL_MESSAGE_TYPE_INIT, 5, 8);
 
   // When reading sample frames, we're forced to read lots because each frame
-  // could be up to 50kB long. By reading a really short sample frame (like the
-  // zero-length one above), the BattOrConnection is forced to store whatever
-  // extra data it finds in the serial stream - in this case, the init control
-  // message that we sent.
+  // could be up to 50kB long. By reading a really short sample frame (like
+  // the zero-length one above), the BattOrConnection is forced to store
+  // whatever extra data it finds in the serial stream - in this case, the
+  // init control message that we sent.
   ReadMessage(BATTOR_MESSAGE_TYPE_SAMPLES);
 
   ASSERT_TRUE(IsReadComplete());
@@ -414,4 +439,98 @@
   ASSERT_FALSE(GetReadSuccess());
 }
 
+TEST_F(BattOrConnectionImplTest, FlushSucceedsAfterTimeout) {
+  OpenConnection();
+
+  Flush();
+  AdvanceTickClock(base::TimeDelta::FromMilliseconds(50));
+
+  ASSERT_TRUE(IsFlushComplete());
+  ASSERT_TRUE(GetFlushSuccess());
+}
+
+TEST_F(BattOrConnectionImplTest, FlushClearsOverreadBuffer) {
+  OpenConnection();
+
+  // Send two data frames and only read one of them. When reading data frames,
+  // we try to read a large chunk from the wire due to the large potential size
+  // of a data frame (~100kB). By sending two tiny data frames on the wire and
+  // reading back one of them, we know that we read past the end of the first
+  // message and all of the second message because the data frames were so
+  // small. These extra bytes that were unnecesssary for the first message were
+  // storied internally by BattOrConnectionImpl, and we want to ensure that
+  // Flush() clears this internal data.
+  const char data[] = {
+      BATTOR_CONTROL_BYTE_START,
+      BATTOR_MESSAGE_TYPE_SAMPLES,
+      0x02,
+      0x00,
+      0x02,
+      0x00,
+      0x02,
+      0x00,
+      BATTOR_CONTROL_BYTE_END,
+  };
+  SendBytesRaw(data, 9);
+  SendBytesRaw(data, 9);
+  ReadMessage(BATTOR_MESSAGE_TYPE_SAMPLES);
+  Flush();
+
+  AdvanceTickClock(base::TimeDelta::FromMilliseconds(50));
+
+  ASSERT_TRUE(IsFlushComplete());
+  ASSERT_TRUE(GetFlushSuccess());
+
+  ReadMessage(BATTOR_MESSAGE_TYPE_SAMPLES);
+
+  // The read should be incomplete due to no data being on the wire - the second
+  // control message was cleared by the slow flush.
+  ASSERT_FALSE(IsReadComplete());
+}
+
+TEST_F(BattOrConnectionImplTest, FlushClearsMultipleReadsOfData) {
+  OpenConnection();
+
+  // Send 10 full flush buffers worth of data.
+  char data[50000];
+  for (size_t i = 0; i < 50000; i++)
+    data[i] = '0';
+  for (int i = 0; i < 10; i++)
+    SendBytesRaw(data, 50000);
+
+  Flush();
+  AdvanceTickClock(base::TimeDelta::FromMilliseconds(50));
+
+  ASSERT_TRUE(IsFlushComplete());
+  ASSERT_TRUE(GetFlushSuccess());
+
+  SendControlMessage(BATTOR_CONTROL_MESSAGE_TYPE_RESET, 4, 7);
+  ReadMessage(BATTOR_MESSAGE_TYPE_CONTROL);
+
+  // Even though 500kB of garbage data was sent before the valid control
+  // message on the serial connection, the slow flush should have cleared it
+  // all, resulting in a successful read.
+  ASSERT_TRUE(IsReadComplete());
+  ASSERT_TRUE(GetReadSuccess());
+}
+
+TEST_F(BattOrConnectionImplTest, FlushIncompleteBeforeTimeout) {
+  OpenConnection();
+
+  Flush();
+  AdvanceTickClock(base::TimeDelta::FromMilliseconds(49));
+
+  ASSERT_FALSE(IsFlushComplete());
+}
+
+TEST_F(BattOrConnectionImplTest, FlushFailsWithNonTimeoutError) {
+  OpenConnection();
+
+  Flush();
+  ForceReceiveError(device::mojom::SerialReceiveError::DISCONNECTED);
+
+  ASSERT_TRUE(IsFlushComplete());
+  ASSERT_FALSE(GetFlushSuccess());
+}
+
 }  // namespace battor
diff --git a/tools/chrome_proxy/webdriver/client_config.py b/tools/chrome_proxy/webdriver/client_config.py
new file mode 100644
index 0000000..70eb26c
--- /dev/null
+++ b/tools/chrome_proxy/webdriver/client_config.py
@@ -0,0 +1,96 @@
+# 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 common
+from common import TestDriver
+from common import IntegrationTest
+from decorators import ChromeVersionEqualOrAfterM
+import json
+
+
+class ClientConfig(IntegrationTest):
+  # Ensure client config is fetched at the start of the Chrome session, and the
+  # session ID is correctly set in the chrome-proxy request header.
+  def testClientConfig(self):
+    with TestDriver() as t:
+      t.AddChromeArg('--enable-spdy-proxy-auth')
+      t.SleepUntilHistogramHasEntry(
+        'DataReductionProxy.ConfigService.FetchResponseCode')
+      t.LoadURL('http://check.googlezip.net/test.html')
+      responses = t.GetHTTPResponses()
+      self.assertEqual(2, len(responses))
+      for response in responses:
+        chrome_proxy_header = response.request_headers['chrome-proxy']
+        header_values = [v.strip(' ') for v in chrome_proxy_header.split(',')]
+        self.assertTrue(any(v[:2] == 's=' for v in header_values))
+        self.assertFalse(any(v[:3] == 'ps=' for v in header_values))
+        self.assertFalse(any(v[:4] == 'sid=' for v in header_values))
+        # Verify that the proxy server honored the session ID.
+        self.assertHasChromeProxyViaHeader(response)
+        self.assertEqual(200, response.status)
+
+
+  # Ensure client config is fetched at the start of the Chrome session, and the
+  # variations ID is set in the request.
+  @ChromeVersionEqualOrAfterM(62)
+  def testClientConfigVariationsHeader(self):
+    with TestDriver() as t:
+      t.AddChromeArg('--log-net-log=chrome.netlog.json')
+      t.AddChromeArg('--enable-spdy-proxy-auth')
+      # Force set the variations ID, so they are send along with the client
+      # config fetch request.
+      t.AddChromeArg('--force-variation-ids=42')
+
+      t.LoadURL('http://check.googlezip.net/test.html')
+      t._StopDriver()
+
+      variation_header_count = 0
+
+      # Look for the request made to data saver client config server.
+      with open('chrome.netlog.json') as data_file:
+        data = json.load(data_file)
+      for i in data["events"]:
+       dumped_event = json.dumps(i)
+       if dumped_event.find("datasaver.googleapis.com") !=-1 and\
+        dumped_event.find("clientConfigs") != -1 and\
+        dumped_event.find("headers") != -1 and\
+        dumped_event.find("accept-encoding") != -1 and\
+        dumped_event.find("x-client-data") !=-1:
+          variation_header_count = variation_header_count + 1
+
+      # Variation IDs are set. x-client-data should be present in the request
+      # headers.
+      self.assertLessEqual(1, variation_header_count)
+
+  # Ensure client config is fetched at the start of the Chrome session, and the
+  # variations ID is not set in the request.
+  @ChromeVersionEqualOrAfterM(62)
+  def testClientConfigNoVariationsHeader(self):
+    with TestDriver() as t:
+      t.AddChromeArg('--log-net-log=chrome.netlog.json')
+      t.AddChromeArg('--enable-spdy-proxy-auth')
+
+      t.LoadURL('http://check.googlezip.net/test.html')
+      t._StopDriver()
+
+      variation_header_count = 0
+
+      # Look for the request made to data saver client config server.
+      with open('chrome.netlog.json') as data_file:
+        data = json.load(data_file)
+      for i in data["events"]:
+       dumped_event = json.dumps(i)
+       if dumped_event.find("datasaver.googleapis.com") !=-1 and\
+        dumped_event.find("clientConfigs") != -1 and\
+        dumped_event.find("headers") != -1 and\
+        dumped_event.find("accept-encoding") != -1 and\
+        dumped_event.find("x-client-data") !=-1:
+          variation_header_count = variation_header_count + 1
+
+      # Variation IDs are not set. x-client-data should not be present in the
+      # request headers.
+      self.assertEqual(0, variation_header_count)
+
+if __name__ == '__main__':
+  IntegrationTest.RunAllTests()
\ No newline at end of file
diff --git a/tools/chrome_proxy/webdriver/cross_origin_push.py b/tools/chrome_proxy/webdriver/cross_origin_push.py
new file mode 100644
index 0000000..395c9434
--- /dev/null
+++ b/tools/chrome_proxy/webdriver/cross_origin_push.py
@@ -0,0 +1,58 @@
+# 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 common
+from common import TestDriver
+from common import IntegrationTest
+from decorators import ChromeVersionEqualOrAfterM
+import json
+
+
+class CrossOriginPush(IntegrationTest):
+  # Ensure cross origin push from trusted proxy server is adopted by Chromium.
+  @ChromeVersionEqualOrAfterM(62)
+  def testClientConfigVariationsHeader(self):
+    with TestDriver() as t:
+      t.AddChromeArg('--log-net-log=chrome.netlog.json')
+      t.AddChromeArg('--enable-spdy-proxy-auth')
+      t.AddChromeArg(
+          '--force-fieldtrial-params=DataReductionProxyServerExperiments'
+          '.TestNanoRedirectPush:exp/test_nano_redirect_push')
+      t.AddChromeArg(
+          '--force-fieldtrials=DataReductionProxyServerExperiments'
+          '/TestNanoRedirectPush')
+
+      t.LoadURL('http://googleweblight.com/i?u='
+        'http://check.googlezip.net/test.html')
+
+    promised_stream_count = 0
+    adopted_stream_count = 0
+
+    # Look for the request made to data saver client config server.
+    with open('chrome.netlog.json') as data_file:
+      data = json.load(data_file)
+
+    mapped_const = data["constants"]["logEventTypes"]\
+      ["HTTP2_STREAM_ADOPTED_PUSH_STREAM"]
+    self.assertLess(0, mapped_const)
+
+    for i in data["events"]:
+     dumped_event = json.dumps(i)
+     if dumped_event.find("chrome-proxy") != -1 and\
+      dumped_event.find("check.googlezip.net/test.html") != -1 and\
+      dumped_event.find("promised_stream_id") !=-1:
+        promised_stream_count = promised_stream_count + 1
+
+     if dumped_event.find(str(mapped_const)) != -1 and\
+      dumped_event.find("check.googlezip.net/test.html") != -1 and\
+      dumped_event.find("stream_id") !=-1:
+        adopted_stream_count = adopted_stream_count + 1
+
+    # Verify that the stream was pushed and adopted.
+    self.assertEqual(1, promised_stream_count)
+    self.assertEqual(1, adopted_stream_count)
+
+
+if __name__ == '__main__':
+  IntegrationTest.RunAllTests()
\ No newline at end of file
diff --git a/tools/chrome_proxy/webdriver/smoke.py b/tools/chrome_proxy/webdriver/smoke.py
index b27524d1..4534aca 100644
--- a/tools/chrome_proxy/webdriver/smoke.py
+++ b/tools/chrome_proxy/webdriver/smoke.py
@@ -82,25 +82,6 @@
       succeeded = t.GetHistogram('DataReductionProxy.Pingback.Succeeded')
       self.assertEqual(1, succeeded['count'])
 
-  # Ensure client config is fetched at the start of the Chrome session, and the
-  # session ID is correctly set in the chrome-proxy request header.
-  def testClientConfig(self):
-    with TestDriver() as t:
-      t.AddChromeArg('--enable-spdy-proxy-auth')
-      t.SleepUntilHistogramHasEntry(
-        'DataReductionProxy.ConfigService.FetchResponseCode')
-      t.LoadURL('http://check.googlezip.net/test.html')
-      responses = t.GetHTTPResponses()
-      self.assertEqual(2, len(responses))
-      for response in responses:
-        chrome_proxy_header = response.request_headers['chrome-proxy']
-        self.assertIn('s=', chrome_proxy_header)
-        self.assertNotIn('ps=', chrome_proxy_header)
-        self.assertNotIn('sid=', chrome_proxy_header)
-        # Verify that the proxy server honored the session ID.
-        self.assertHasChromeProxyViaHeader(response)
-        self.assertEqual(200, response.status)
-
   # Verify unique page IDs are sent in the Chrome-Proxy header.
   @ChromeVersionEqualOrAfterM(59)
   def testPageID(self):
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 77a5719..5102142 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -388,6 +388,13 @@
   <int value="2" label="Use resource from cache"/>
 </enum>
 
+<enum name="ActivationList">
+  <int value="0" label="No match"/>
+  <int value="1" label="Social Eng Ads Interstitial"/>
+  <int value="2" label="Phishing Interstitial"/>
+  <int value="3" label="Subresource Filter"/>
+</enum>
+
 <enum name="ActiveWindowShowType">
   <int value="0" label="No Active Window"/>
   <int value="1" label="Other"/>
@@ -27988,6 +27995,92 @@
   </int>
 </enum>
 
+<enum name="OfflinePrefetchArchiveActualSizeVsExpected">
+  <summary>
+    Percentage ranges for the ratios of actual downloaded size of a prefetched
+    offline page archive versus its expected size.
+  </summary>
+  <int value="0" label="archive_body_length = 0"/>
+  <int value="1" label="0% to 10%"/>
+  <int value="2" label="10% to 20%"/>
+  <int value="3" label="20% to 30%"/>
+  <int value="4" label="30% to 40%"/>
+  <int value="5" label="40% to 50%"/>
+  <int value="6" label="50% to 60%"/>
+  <int value="7" label="60% to 70%"/>
+  <int value="8" label="70% to 80%"/>
+  <int value="9" label="80% to 90%"/>
+  <int value="10" label="90% to 100%"/>
+  <int value="11" label="size matches (100%)"/>
+  <int value="12" label="100% to 110%"/>
+  <int value="13" label="110% to 120%"/>
+  <int value="14" label="120% to 130%"/>
+  <int value="15" label="130% to 140%"/>
+  <int value="16" label="140% to 150%"/>
+  <int value="17" label="150% to 160%"/>
+  <int value="18" label="160% to 170%"/>
+  <int value="19" label="170% to 180%"/>
+  <int value="20" label="180% to 190%"/>
+  <int value="21" label="190% to 200%"/>
+  <int value="22" label="above 200%"/>
+</enum>
+
+<enum name="OfflinePrefetchItemErrorCode">
+  <summary>
+    Error codes representing the reason why a prefetch item was finalized.
+  </summary>
+  <int value="0" label="SUCCESS">
+    The entry had gone through the pipeline and successfully completed
+    prefetching. Explicitly setting to 0 as that is the default value for the
+    respective SQLite column.
+  </int>
+  <int value="100" label="TOO_MANY_NEW_URLS">
+    Got too many URLs from suggestions, canceled this one. See kMaxUrlsToSend
+    defined in GeneratePageBundleTask.
+  </int>
+  <int value="200" label="DOWNLOAD_ERROR">
+    An error happened while attempting to download the archive file.
+  </int>
+  <int value="300" label="IMPORT_ERROR">
+    An error happened while importing the downloaded archive file int the
+    Offline Pages system.
+  </int>
+  <int value="400" label="ARCHIVING_FAILED">
+    Got a failure result from GetOperation (or the GeneratePageBundle metadata).
+  </int>
+  <int value="500" label="ARCHIVING_LIMIT_EXCEEDED">
+    Got a failure result from GetOperation or GeneratePageBundle that a
+    server-side limit on the page was exceeded.
+  </int>
+  <int value="600" label="STALE_AT_NEW_REQUEST">
+    Entry became stale while at the NEW_REQUEST state.
+  </int>
+  <int value="700" label="STALE_AT_AWAITING_GCM">
+    Entry became stale while at the AWAITING_GCM state.
+  </int>
+  <int value="800" label="STALE_AT_RECEIVED_GCM">
+    Entry became stale while at the RECEIVED_GCM state.
+  </int>
+  <int value="900" label="STALE_AT_RECEIVED_BUNDLE">
+    Entry became stale while at the RECEIVED_BUNDLE state.
+  </int>
+  <int value="1000" label="STALE_AT_DOWNLOADING">
+    Entry became stale while at the DOWNLOADING state.
+  </int>
+  <int value="1100" label="STALE_AT_UNKNOWN">
+    Entry became stale at an unknown state.
+  </int>
+  <int value="1200" label="GET_OPERATION_MAX_ATTEMPTS_REACHED">
+    Exceeding maximum retries for get operation request.
+  </int>
+  <int value="1300" label="GENERATE_PAGE_BUNDLE_REQUEST_MAX_ATTEMPTS_REACHED">
+    Exceeded maximum retries limit for generate page bundle request.
+  </int>
+  <int value="1400" label="DOWNLOAD_MAX_ATTEMPTS_REACHED">
+    Exceeded maximum retries for download.
+  </int>
+</enum>
+
 <enum name="OfflineStatus">
   <obsolete>
     Deprecated 4/2015.
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 11cc435..8ee4c35 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -42222,6 +42222,16 @@
   </summary>
 </histogram>
 
+<histogram name="Net.RedirectChainLength" units="redirects">
+  <owner>csharrison@chromium.org</owner>
+  <owner>rsleevi@chromium.org</owner>
+  <summary>
+    The total number of redirects encountered during processing a URLRequest.
+    This includes redirects generated by servers (for example, 302 Found) and
+    redirects generated internally (for example, HSTS redirects or error pages).
+  </summary>
+</histogram>
+
 <histogram name="Net.RedirectWithUnadvertisedContentEncoding" enum="Boolean">
   <owner>eustas@chromium.org</owner>
   <summary>
@@ -50199,6 +50209,40 @@
   <summary>Size of the saved copy of an offline page.</summary>
 </histogram>
 
+<histogram name="OfflinePages.Prefetching.ActionRetryAttempts" units="retries">
+  <owner>carlosk@chromium.org</owner>
+  <summary>
+    Number of attempts to perform a specific retriable pipeline action for each
+    finished prefetch item.
+  </summary>
+</histogram>
+
+<histogram name="OfflinePages.Prefetching.DownloadedArchiveSizeVsExpected"
+    enum="OfflinePrefetchArchiveActualSizeVsExpected">
+  <owner>carlosk@chromium.org</owner>
+  <summary>
+    Reports differences between the expected size of a prefetch archive -- as
+    reported by the service -- and the actual downloaded file size.
+  </summary>
+</histogram>
+
+<histogram name="OfflinePages.Prefetching.FinishedItemErrorCode"
+    enum="OfflinePrefetchItemErrorCode">
+  <owner>carlosk@chromium.org</owner>
+  <summary>
+    Error code representing the reason why each offline prefetch item was
+    finalized.
+  </summary>
+</histogram>
+
+<histogram name="OfflinePages.Prefetching.ItemLifetime" units="seconds">
+  <owner>carlosk@chromium.org</owner>
+  <summary>
+    The lifetime of an offline prefetch item, from creation until
+    &quot;zombie-fication&quot; (after metrics collection).
+  </summary>
+</histogram>
+
 <histogram name="OfflinePages.RedirectResult" enum="OfflinePagesRedirectResult">
   <obsolete>
     Deprecated 8/2016. Use OfflinePages.RequestResult instead.
@@ -78607,6 +78651,15 @@
   </summary>
 </histogram>
 
+<histogram name="SubresourceFilter.PageLoad.ActivationList"
+    enum="ActivationList">
+  <owner>csharrison@chromium.org</owner>
+  <summary>
+    The ActivationList or NONE that the main frame navigation matched with.
+    Recorded at NavigationThrottle destruction (i.e. the end of the navigation).
+  </summary>
+</histogram>
+
 <histogram name="SubresourceFilter.PageLoad.ActivationState"
     enum="SubresourceFilterActivationState">
   <owner>csharrison@chromium.org</owner>
@@ -78618,6 +78671,9 @@
 
 <histogram name="SubresourceFilter.PageLoad.FinalURLMatch"
     enum="BooleanMatched">
+  <obsolete>
+    Deprecated in favor of SubresourceFilter.PageLoad.ActivationList.
+  </obsolete>
   <owner>melandory@chromium.org</owner>
   <summary>
     Records, for each main frame navigation, whether the last URL in the
@@ -97720,6 +97776,22 @@
   <affected-histogram name="OfflinePages.SavePageTime"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="OfflinePrefechItemFinalOutcome" separator=".">
+  <suffix name="Successful" label="For items that were completed sucessfully."/>
+  <suffix name="Failed"
+      label="For items that failed being prefetched for any reason."/>
+  <affected-histogram name="OfflinePages.Prefetching.ItemLifetime"/>
+</histogram_suffixes>
+
+<histogram_suffixes name="OfflinePrefechRetriableAction" separator=".">
+  <suffix name="GeneratePageBundle"
+      label="Action: GeneratePageBundle request."/>
+  <suffix name="GetOperation" label="Action: GetOperation request."/>
+  <suffix name="DownloadInitiation"
+      label="Action: start downloading an archive."/>
+  <affected-histogram name="OfflinePages.Prefetching.ActionRetryAttempts"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="OmniboxPageContext" separator=".">
   <suffix name="INVALID_SPEC" label="invalid spec; shouldn't happen"/>
   <suffix name="NTP"
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index 7c0635c..0da7eef 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -526,6 +526,7 @@
     media playbacks, the total number of visits, the calculated engagement score
     and the new number of significant media playbacks that occured this visit.
   </summary>
+  <metric name="Engagement.IsHigh"/>
   <metric name="Engagement.Score"/>
   <metric name="Playbacks.Delta"/>
   <metric name="Playbacks.Total"/>
@@ -959,6 +960,14 @@
     interactions with them. No events are created for pages that don't contain
     password forms.
   </summary>
+  <metric name="PageLevelUserAction">
+    <summary>
+      Records if the user interacts with the password manager in a way that
+      cannot (reliably) be attributed to a specific PasswordFormManager. Values
+      correspond to the enum
+      PasswordManagerMetricsRecorder::PageLevelUserAction.
+    </summary>
+  </metric>
   <metric name="ProvisionalSaveFailure">
     <summary>
       Records a provisional save failure in case the password manager cannot
diff --git a/tools/perf/contrib/vr_benchmarks/vr_page_sets/shared_android_vr_page_state.py b/tools/perf/contrib/vr_benchmarks/vr_page_sets/shared_android_vr_page_state.py
index e54fbb0a..5d42d95 100644
--- a/tools/perf/contrib/vr_benchmarks/vr_page_sets/shared_android_vr_page_state.py
+++ b/tools/perf/contrib/vr_benchmarks/vr_page_sets/shared_android_vr_page_state.py
@@ -5,15 +5,16 @@
 import os
 from core import path_util
 path_util.AddAndroidPylibToPath()
+from page_sets import android_screen_restoration_shared_state as screen_state
 from pylib.utils import shared_preference_utils
 from telemetry.core import android_platform
 from telemetry.core import platform
 from telemetry.core import util
-from telemetry.page import shared_page_state
 from telemetry.internal.platform import android_device
 
 
-class SharedAndroidVrPageState(shared_page_state.SharedPageState):
+class SharedAndroidVrPageState(
+    screen_state.AndroidScreenRestorationSharedState):
   """SharedPageState for VR Telemetry tests.
 
   Performs the same functionality as SharedPageState, but with two main
@@ -21,6 +22,9 @@
   1. It is currently restricted to Android
   2. It performs VR-specific setup such as installing and configuring
      additional APKs that are necessary for testing
+
+  Also ensures that the screen is on before the test starts by inheriting from
+  AndroidScreenRestorationSharedState.
   """
   def __init__(self, test, finder_options, story_set):
     # TODO(bsheedy): See about making this a cross-platform SharedVrPageState -
diff --git a/ui/accessibility/platform/ax_platform_node_win.cc b/ui/accessibility/platform/ax_platform_node_win.cc
index a85bd50..c85b0945 100644
--- a/ui/accessibility/platform/ax_platform_node_win.cc
+++ b/ui/accessibility/platform/ax_platform_node_win.cc
@@ -2476,9 +2476,8 @@
   // If this is a web area for a presentational iframe, give it a role of
   // something other than DOCUMENT so that the fact that it's a separate doc
   // is not exposed to AT.
-  if (IsWebAreaForPresentationalIframe()) {
+  if (IsWebAreaForPresentationalIframe())
     return ROLE_SYSTEM_GROUPING;
-  }
 
   switch (GetData().role) {
     case AX_ROLE_ALERT:
@@ -2646,10 +2645,14 @@
     case AX_ROLE_MENU_ITEM_RADIO:
       return ROLE_SYSTEM_MENUITEM;
 
-    case AX_ROLE_MENU_LIST_POPUP:
+    case ui::AX_ROLE_MENU_LIST_POPUP:
+      if (IsAncestorComboBox())
+        return ROLE_SYSTEM_LIST;
       return ROLE_SYSTEM_MENUPOPUP;
 
-    case AX_ROLE_MENU_LIST_OPTION:
+    case ui::AX_ROLE_MENU_LIST_OPTION:
+      if (IsAncestorComboBox())
+        return ROLE_SYSTEM_LISTITEM;
       return ROLE_SYSTEM_MENUITEM;
 
     case AX_ROLE_METER:
@@ -3640,4 +3643,15 @@
   return S_OK;
 }
 
+// TODO(dmazzoni): Remove this function once combo box refactoring is complete.
+bool AXPlatformNodeWin::IsAncestorComboBox() {
+  auto* parent =
+      static_cast<AXPlatformNodeWin*>(FromNativeViewAccessible(GetParent()));
+  if (!parent)
+    return false;
+  if (parent->MSAARole() == ROLE_SYSTEM_COMBOBOX)
+    return true;
+  return parent->IsAncestorComboBox();
+}
+
 }  // namespace ui
diff --git a/ui/accessibility/platform/ax_platform_node_win.h b/ui/accessibility/platform/ax_platform_node_win.h
index 4be65635..ecf7e80 100644
--- a/ui/accessibility/platform/ax_platform_node_win.h
+++ b/ui/accessibility/platform/ax_platform_node_win.h
@@ -745,6 +745,8 @@
                                      LONG** selected,
                                      LONG* n_selected);
 
+  bool IsAncestorComboBox();
+
   // Relationships between this node and other nodes.
   std::vector<AXPlatformNodeRelationWin*> relations_;
 };
diff --git a/ui/app_list/views/app_list_view_unittest.cc b/ui/app_list/views/app_list_view_unittest.cc
index 692a54e..5d8df24 100644
--- a/ui/app_list/views/app_list_view_unittest.cc
+++ b/ui/app_list/views/app_list_view_unittest.cc
@@ -477,125 +477,6 @@
   ASSERT_EQ(view_->GetAppsPaginationModel()->selected_page(), 0);
 }
 
-// TODO(weidong): Remove expand arrow corresponding tests in StartPageView.
-// http://crbug.com/757704.
-// Tests the focus change in search box view and start page view triggered by
-// tab key.
-TEST_F(AppListViewFullscreenTest, DISABLED_StartPageTabFocusTest) {
-  constexpr size_t apps_num = 5u;
-  InitializeStartPageView(apps_num);
-  ui::KeyEvent tab(ui::ET_KEY_PRESSED, ui::VKEY_TAB, ui::EF_NONE);
-  TestStartPageViewForwardFocusOnKey(&tab, apps_num);
-}
-
-// TODO(weidong): Remove expand arrow corresponding tests in StartPageView.
-// http://crbug.com/757704.
-// Tests the focus change in search box view and start page view triggered by
-// shift+tab key.
-TEST_F(AppListViewFullscreenTest, DISABLED_StartPageShiftTabFocusTest) {
-  constexpr size_t apps_num = 5u;
-  InitializeStartPageView(apps_num);
-  ui::KeyEvent shift_tab(ui::ET_KEY_PRESSED, ui::VKEY_TAB, ui::EF_SHIFT_DOWN);
-  TestStartPageViewBackwardFocusOnKey(&shift_tab, apps_num);
-}
-
-// TODO(weidong): Remove expand arrow corresponding tests in StartPageView.
-// http://crbug.com/757704.
-// Tests the focus change in search box view and start page view triggered by
-// right arrow key.
-TEST_F(AppListViewFullscreenTest, DISABLED_StartPageRightArrowFocusTest) {
-  constexpr size_t apps_num = 5u;
-  InitializeStartPageView(apps_num);
-  ui::KeyEvent right(ui::ET_KEY_PRESSED, ui::VKEY_RIGHT, ui::EF_NONE);
-  TestStartPageViewForwardFocusOnKey(&right, apps_num);
-}
-
-// TODO(weidong): Remove expand arrow corresponding tests in StartPageView.
-// http://crbug.com/757704.
-// Tests the focus change in search box view and start page view triggered by
-// left arrow key.
-TEST_F(AppListViewFullscreenTest, DISABLED_StartPageLeftArrowFocusTest) {
-  constexpr size_t apps_num = 5u;
-  InitializeStartPageView(apps_num);
-  ui::KeyEvent left(ui::ET_KEY_PRESSED, ui::VKEY_LEFT, ui::EF_NONE);
-  TestStartPageViewBackwardFocusOnKey(&left, apps_num);
-}
-
-// TODO(weidong): Remove expand arrow corresponding tests in StartPageView.
-// http://crbug.com/757704.
-// Tests the focus change in search box view and start page view triggered by
-// down arrow key.
-TEST_F(AppListViewFullscreenTest, DISABLED_StartPageDownArrowFocusTest) {
-  constexpr size_t apps_num = 5u;
-  InitializeStartPageView(apps_num);
-
-  // Hitting down key sets focus on search box.
-  ui::KeyEvent down(ui::ET_KEY_PRESSED, ui::VKEY_DOWN, ui::EF_NONE);
-  search_box_view()->search_box()->OnKeyEvent(&down);
-  EXPECT_EQ(FOCUS_SEARCH_BOX, search_box_view()->get_focused_view_for_test());
-  EXPECT_EQ(StartPageView::kNoSelection,
-            start_page_view()->GetSelectedIndexForTest());
-
-  // Hitting down key when focus is on search box moves focus to the suggestion
-  // app in the middle.
-  search_box_view()->search_box()->OnKeyEvent(&down);
-  EXPECT_EQ(FOCUS_CONTENTS_VIEW,
-            search_box_view()->get_focused_view_for_test());
-  EXPECT_EQ(static_cast<int>(apps_num / 2),
-            start_page_view()->GetSelectedIndexForTest());
-
-  // Hitting down key when focus is on suggestion app moves focus to expand
-  // arrow.
-  search_box_view()->search_box()->OnKeyEvent(&down);
-  EXPECT_EQ(FOCUS_CONTENTS_VIEW,
-            search_box_view()->get_focused_view_for_test());
-  EXPECT_EQ(StartPageView::kExpandArrowSelection,
-            start_page_view()->GetSelectedIndexForTest());
-
-  // Hitting down key when focus is on expand arrow clears focus.
-  search_box_view()->search_box()->OnKeyEvent(&down);
-  EXPECT_EQ(FOCUS_NONE, search_box_view()->get_focused_view_for_test());
-  EXPECT_EQ(StartPageView::kNoSelection,
-            start_page_view()->GetSelectedIndexForTest());
-}
-
-// TODO(weidong): Remove expand arrow corresponding tests in StartPageView.
-// http://crbug.com/757704.
-// Tests the focus change in search box view and start page view triggered by
-// up arrow key.
-TEST_F(AppListViewFullscreenTest, DISABLED_StartPageUpArrowFocusTest) {
-  constexpr size_t apps_num = 5u;
-  InitializeStartPageView(apps_num);
-
-  // Hitting up key sets focus on expand arrow.
-  ui::KeyEvent up(ui::ET_KEY_PRESSED, ui::VKEY_UP, ui::EF_NONE);
-  search_box_view()->search_box()->OnKeyEvent(&up);
-  EXPECT_EQ(FOCUS_CONTENTS_VIEW,
-            search_box_view()->get_focused_view_for_test());
-  EXPECT_EQ(StartPageView::kExpandArrowSelection,
-            start_page_view()->GetSelectedIndexForTest());
-
-  // Hitting up key when focus is on expand arrow moves focus to the suggestion
-  // app in the middle.
-  search_box_view()->search_box()->OnKeyEvent(&up);
-  EXPECT_EQ(FOCUS_CONTENTS_VIEW,
-            search_box_view()->get_focused_view_for_test());
-  EXPECT_EQ(static_cast<int>(apps_num / 2),
-            start_page_view()->GetSelectedIndexForTest());
-
-  // Hitting up key when focus is on suggestion app moves focus to search box.
-  search_box_view()->search_box()->OnKeyEvent(&up);
-  EXPECT_EQ(FOCUS_SEARCH_BOX, search_box_view()->get_focused_view_for_test());
-  EXPECT_EQ(StartPageView::kNoSelection,
-            start_page_view()->GetSelectedIndexForTest());
-
-  // Hitting up key when focus is on search box clears focus.
-  search_box_view()->search_box()->OnKeyEvent(&up);
-  EXPECT_EQ(FOCUS_NONE, search_box_view()->get_focused_view_for_test());
-  EXPECT_EQ(StartPageView::kNoSelection,
-            start_page_view()->GetSelectedIndexForTest());
-}
-
 // Tests that when a click or tap event propagates to the AppListView, if the
 // event location is within the bounds of AppsGridView, do not close the
 // AppListView.
diff --git a/ui/app_list/views/apps_grid_view.cc b/ui/app_list/views/apps_grid_view.cc
index c14d157a..c1411e88 100644
--- a/ui/app_list/views/apps_grid_view.cc
+++ b/ui/app_list/views/apps_grid_view.cc
@@ -470,6 +470,10 @@
     selected_view_->SchedulePaint();
     selected_view_ = nullptr;
   }
+  if (suggestions_container_)
+    suggestions_container_->ClearSelectedIndex();
+  if (expand_arrow_view_)
+    expand_arrow_view_->SetSelected(false);
 }
 
 bool AppsGridView::IsSelectedView(const AppListItemView* view) const {
@@ -929,18 +933,15 @@
 
 bool AppsGridView::OnKeyPressed(const ui::KeyEvent& event) {
   bool handled = false;
-  if (is_fullscreen_app_list_enabled_ &&
+  if (suggestions_container_ &&
       suggestions_container_->selected_index() != -1) {
     int selected_suggested_index = suggestions_container_->selected_index();
     handled = suggestions_container_->GetTileItemView(selected_suggested_index)
                   ->OnKeyPressed(event);
   }
 
-  // Only suggestions apps can be selected in STATE_START state.
-  if (is_fullscreen_app_list_enabled_ &&
-      contents_view_->GetActiveState() == AppListModel::STATE_START) {
-    ClearSelectedView(selected_view_);
-  }
+  if (expand_arrow_view_ && expand_arrow_view_->selected())
+    handled = expand_arrow_view_->OnKeyPressed(event);
 
   if (selected_view_)
     handled = static_cast<views::View*>(selected_view_)->OnKeyPressed(event);
@@ -955,8 +956,11 @@
         MoveSelected(0, forward_dir, 0);
         return true;
       case ui::VKEY_UP:
-        if (selected_view_)  // Don't initiate selection with UP
+        if (is_fullscreen_app_list_enabled_ || selected_view_) {
+          // Don't initiate selection with UP in non-fullscreen app list. In
+          // fullscreen app list, UP is already handled by SearchBoxView.
           MoveSelected(0, 0, -1);
+        }
         return true;
       case ui::VKEY_DOWN:
         MoveSelected(0, 0, 1);
@@ -1213,11 +1217,16 @@
 void AppsGridView::MoveSelected(int page_delta,
                                 int slot_x_delta,
                                 int slot_y_delta) {
+  if (expand_arrow_view_ && expand_arrow_view_->selected() &&
+      HandleExpandArrowMove(page_delta, slot_x_delta, slot_y_delta)) {
+    return;
+  }
+
   if (!selected_view_) {
     // If fullscreen app list is enabled and we are on the first page, moving
     // selected should consider suggested apps tiles before all apps tiles.
     int current_page = pagination_model_.selected_page();
-    if (!is_fullscreen_app_list_enabled_ || current_page != 0)
+    if (!suggestions_container_ || current_page != 0)
       return SetSelectedItemByIndex(Index(current_page, 0));
     if (HandleSuggestionsMove(page_delta, slot_x_delta, slot_y_delta))
       return;
@@ -1315,6 +1324,9 @@
 bool AppsGridView::HandleSuggestionsMove(int page_delta,
                                          int slot_x_delta,
                                          int slot_y_delta) {
+  DCHECK(suggestions_container_);
+  DCHECK(expand_arrow_view_);
+
   if (suggestions_container_->selected_index() == -1) {
     suggestions_container_->SetSelectedIndex(0);
     return true;
@@ -1325,21 +1337,36 @@
 
   if (slot_x_delta != 0) {
     int new_index = suggestions_container_->selected_index() + slot_x_delta;
-    // Moving right out of |suggestions_container_| should give focus to the
-    // first tile of all apps.
     if (new_index == suggestions_container_->num_results()) {
       suggestions_container_->ClearSelectedIndex();
-      SetSelectedItemByIndex(Index(0, 0));
+      if (contents_view_->GetActiveState() == AppListModel::STATE_START) {
+        // In state start, moving right out of |suggestions_container_| should
+        // give selection to the expand arrow.
+        expand_arrow_view_->SetSelected(true);
+      } else {
+        DCHECK(contents_view_->GetActiveState() == AppListModel::STATE_APPS);
+        // In state apps, moving right out of |suggestions_container_| should
+        // give selection to the first tile of all apps.
+        SetSelectedItemByIndex(Index(0, 0));
+      }
     } else if (suggestions_container_->IsValidSelectionIndex(new_index)) {
       suggestions_container_->SetSelectedIndex(new_index);
     }
     return true;
   }
 
-  // Moving down from |suggestions_container_| should give focus to the first
-  // row of all apps.
   if (slot_y_delta == 1) {
-    SetSelectedItemByIndex(Index(0, suggestions_container_->selected_index()));
+    if (contents_view_->GetActiveState() == AppListModel::STATE_START) {
+      // In state start, moving down from |suggestions_container_| should give
+      // selection to the expand arrow.
+      expand_arrow_view_->SetSelected(true);
+    } else {
+      DCHECK(contents_view_->GetActiveState() == AppListModel::STATE_APPS);
+      // In state apps, moving down from |suggestions_container_| should give
+      // selection to the first row of all apps.
+      SetSelectedItemByIndex(
+          Index(0, suggestions_container_->selected_index()));
+    }
     suggestions_container_->ClearSelectedIndex();
     return true;
   }
@@ -1354,6 +1381,29 @@
   return false;
 }
 
+bool AppsGridView::HandleExpandArrowMove(int page_delta,
+                                         int slot_x_delta,
+                                         int slot_y_delta) {
+  DCHECK(suggestions_container_);
+  DCHECK(expand_arrow_view_);
+  DCHECK(contents_view_->GetActiveState() == AppListModel::STATE_START);
+
+  if (page_delta != 0 || slot_x_delta == 1 || slot_y_delta == 1 ||
+      (slot_x_delta == 0 && slot_y_delta == 0)) {
+    return true;
+  }
+
+  if (slot_x_delta == -1 || slot_y_delta == -1) {
+    // Move focus to the last app in ||suggestions_container|.
+    expand_arrow_view_->SetSelected(false);
+    suggestions_container_->SetSelectedIndex(
+        suggestions_container_->num_results() - 1);
+    return true;
+  }
+
+  return false;
+}
+
 const gfx::Vector2d AppsGridView::CalculateTransitionOffset(
     int page_of_view) const {
   gfx::Size grid_size = GetTileGridSize();
diff --git a/ui/app_list/views/apps_grid_view.h b/ui/app_list/views/apps_grid_view.h
index 7002ab4..2310d9d 100644
--- a/ui/app_list/views/apps_grid_view.h
+++ b/ui/app_list/views/apps_grid_view.h
@@ -240,6 +240,10 @@
     page_flip_delay_in_ms_ = page_flip_delay_in_ms;
   }
 
+  ExpandArrowView* expand_arrow_view_for_test() const {
+    return expand_arrow_view_;
+  }
+
  private:
   class FadeoutLayerDelegate;
   friend class test::AppsGridViewTestApi;
@@ -317,6 +321,12 @@
                              int slot_x_delta,
                              int slot_y_delta);
 
+  // Returns true if the given moving operation should be handled by
+  // |expand_arrow_view_|, otherwise false.
+  bool HandleExpandArrowMove(int page_delta,
+                             int slot_x_delta,
+                             int slot_y_delta);
+
   // Calculates the offset for |page_of_view| based on current page and
   // transition target page.
   const gfx::Vector2d CalculateTransitionOffset(int page_of_view) const;
diff --git a/ui/app_list/views/apps_grid_view_unittest.cc b/ui/app_list/views/apps_grid_view_unittest.cc
index 158476b..4e43a088 100644
--- a/ui/app_list/views/apps_grid_view_unittest.cc
+++ b/ui/app_list/views/apps_grid_view_unittest.cc
@@ -34,6 +34,7 @@
 #include "ui/app_list/views/apps_container_view.h"
 #include "ui/app_list/views/apps_grid_view_folder_delegate.h"
 #include "ui/app_list/views/contents_view.h"
+#include "ui/app_list/views/expand_arrow_view.h"
 #include "ui/app_list/views/suggestions_container_view.h"
 #include "ui/app_list/views/test/apps_grid_view_test_api.h"
 #include "ui/aura/window.h"
@@ -125,9 +126,8 @@
     app_list_view_->set_app_list_animation_duration_ms_for_testing(0);
 
     app_list_view_->Initialize(parent, 0, false, false);
-    ContentsView* contents_view =
-        app_list_view_->app_list_main_view()->contents_view();
-    apps_grid_view_ = contents_view->apps_container_view()->apps_grid_view();
+    contents_view_ = app_list_view_->app_list_main_view()->contents_view();
+    apps_grid_view_ = contents_view_->apps_container_view()->apps_grid_view();
     // Initialize around a point that ensures the window is wholly shown. It
     // bails out early with |test_with_fullscreen_|.
     // TODO(warx): remove MaybeSetAnchorPoint setup here when bubble launcher is
@@ -138,6 +138,7 @@
 
     model_ = delegate_->GetTestModel();
     suggestions_container_ = apps_grid_view_->suggestions_container_for_test();
+    expand_arrow_view_ = apps_grid_view_->expand_arrow_view_for_test();
     for (size_t i = 0; i < kNumOfSuggestedApps; ++i)
       model_->results()->Add(base::MakeUnique<TestSuggestedSearchResult>());
     // Needed to update suggestions from |model_|.
@@ -148,8 +149,8 @@
       app_list_view_->Layout();
     } else {
       // Set app list view to show all apps page to test AppsGridView.
-      contents_view->SetActiveState(AppListModel::STATE_APPS);
-      contents_view->Layout();
+      contents_view_->SetActiveState(AppListModel::STATE_APPS);
+      contents_view_->Layout();
     }
 
     test_api_.reset(new AppsGridViewTestApi(apps_grid_view_));
@@ -214,14 +215,44 @@
   }
 
   void SimulateKeyPress(ui::KeyboardCode key_code) {
-    ui::KeyEvent key_event(ui::ET_KEY_PRESSED, key_code, ui::EF_NONE);
+    SimulateKeyPress(key_code, ui::EF_NONE);
+  }
+
+  void SimulateKeyPress(ui::KeyboardCode key_code, int flags) {
+    ui::KeyEvent key_event(ui::ET_KEY_PRESSED, key_code, flags);
     apps_grid_view_->OnKeyPressed(key_event);
   }
 
+  void CheckNoSelection() {
+    EXPECT_FALSE(expand_arrow_view_->selected());
+    EXPECT_EQ(-1, suggestions_container_->selected_index());
+    EXPECT_FALSE(apps_grid_view_->has_selected_view());
+  }
+
+  void CheckSelectionAtSuggestionsContainer(int index) {
+    EXPECT_FALSE(expand_arrow_view_->selected());
+    EXPECT_EQ(index, suggestions_container_->selected_index());
+    EXPECT_FALSE(apps_grid_view_->has_selected_view());
+  }
+
+  void CheckSelectionAtExpandArrow() {
+    EXPECT_TRUE(expand_arrow_view_->selected());
+    EXPECT_EQ(-1, suggestions_container_->selected_index());
+    EXPECT_FALSE(apps_grid_view_->has_selected_view());
+  }
+
+  void CheckSelectionAtAppsGridView(int index) {
+    EXPECT_FALSE(expand_arrow_view_->selected());
+    EXPECT_EQ(-1, suggestions_container_->selected_index());
+    EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(index)));
+  }
+
   AppListView* app_list_view_ = nullptr;    // Owned by native widget.
   AppsGridView* apps_grid_view_ = nullptr;  // Owned by |app_list_view_|.
+  ContentsView* contents_view_ = nullptr;   // Owned by |app_list_view_|.
   SuggestionsContainerView* suggestions_container_ =
       nullptr;  // Owned by |apps_grid_view_|.
+  ExpandArrowView* expand_arrow_view_ = nullptr;  // Owned by |apps_grid_view_|.
   std::unique_ptr<AppListTestViewDelegate> delegate_;
   AppListTestModel* model_ = nullptr;  // Owned by |delegate_|.
   std::unique_ptr<AppsGridViewTestApi> test_api_;
@@ -887,58 +918,162 @@
       apps_grid_view_->IsSelectedView(GetItemViewAt(kAllAppsItems - 1)));
 }
 
-TEST_P(AppsGridViewTest, HandleSuggestionsMove) {
+// Tests the selection movement in state apps.
+TEST_P(AppsGridViewTest, SelectionInStateApps) {
   if (!test_with_fullscreen_)
     return;
 
-  // Tests that a non-up arrow key would move focus to the first tile of
-  // suggestions container, and all apps grid view doesn't have focus.
+  // Simulates that the app list is at state apps.
+  contents_view_->SetActiveState(AppListModel::STATE_APPS);
   const int kPages = 2;
   const int kAllAppsItems = GetTilesPerPage(0) + 1;
   model_->PopulateApps(kAllAppsItems);
-  SimulateKeyPress(ui::VKEY_UP);
-  EXPECT_EQ(-1, suggestions_container_->selected_index());
-  EXPECT_FALSE(apps_grid_view_->has_selected_view());
+
+  // Moves selection to the first app in the suggestions container.
   SimulateKeyPress(ui::VKEY_DOWN);
-  EXPECT_EQ(0, suggestions_container_->selected_index());
-  EXPECT_FALSE(apps_grid_view_->has_selected_view());
 
   // Tests moving to previous page and moving up.
   SimulateKeyPress(ui::VKEY_PRIOR);
-  EXPECT_EQ(0, suggestions_container_->selected_index());
+  CheckSelectionAtSuggestionsContainer(0);
   SimulateKeyPress(ui::VKEY_UP);
-  EXPECT_EQ(0, suggestions_container_->selected_index());
+  CheckSelectionAtSuggestionsContainer(0);
 
   // Tests moving left, moving right and moving right out of suggestions.
   SimulateKeyPress(ui::VKEY_LEFT);
-  EXPECT_EQ(0, suggestions_container_->selected_index());
+  CheckSelectionAtSuggestionsContainer(0);
   SimulateKeyPress(ui::VKEY_RIGHT);
   SimulateKeyPress(ui::VKEY_RIGHT);
-  EXPECT_EQ(kNumOfSuggestedApps - 1, suggestions_container_->selected_index());
+  CheckSelectionAtSuggestionsContainer(kNumOfSuggestedApps - 1);
   SimulateKeyPress(ui::VKEY_RIGHT);
-  EXPECT_EQ(-1, suggestions_container_->selected_index());
-  EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(0)));
+  CheckSelectionAtAppsGridView(0);
 
   // Tests moving down from |suggestions_container_|.
   apps_grid_view_->ClearAnySelectedView();
   SimulateKeyPress(ui::VKEY_RIGHT);
   SimulateKeyPress(ui::VKEY_RIGHT);
-  EXPECT_EQ(1, suggestions_container_->selected_index());
+  CheckSelectionAtSuggestionsContainer(1);
   SimulateKeyPress(ui::VKEY_DOWN);
-  EXPECT_EQ(-1, suggestions_container_->selected_index());
-  EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(1)));
+  CheckSelectionAtAppsGridView(1);
 
   // Tests moving to next page.
   apps_grid_view_->ClearAnySelectedView();
-  SimulateKeyPress(ui::VKEY_LEFT);
-  EXPECT_EQ(0, suggestions_container_->selected_index());
+  SimulateKeyPress(ui::VKEY_DOWN);
   SimulateKeyPress(ui::VKEY_NEXT);
-  EXPECT_EQ(-1, suggestions_container_->selected_index());
-  EXPECT_TRUE(
-      apps_grid_view_->IsSelectedView(GetItemViewAt(kAllAppsItems - 1)));
+  CheckSelectionAtAppsGridView(kAllAppsItems - 1);
   EXPECT_EQ(kPages - 1, GetPaginationModel()->selected_page());
 }
 
+// Tests that in state start there's no selection at the beginning. And hitting
+// down/tab/right arrow key moves the selection to the first app in suggestions
+// container.
+TEST_P(AppsGridViewTest, InitialSelectionInStateStart) {
+  if (!test_with_fullscreen_)
+    return;
+
+  // Simulates that the app list is at state start.
+  contents_view_->SetActiveState(AppListModel::STATE_START);
+  model_->PopulateApps(GetTilesPerPage(0));
+  CheckNoSelection();
+
+  SimulateKeyPress(ui::VKEY_DOWN);
+  CheckSelectionAtSuggestionsContainer(0);
+
+  apps_grid_view_->ClearAnySelectedView();
+  SimulateKeyPress(ui::VKEY_RIGHT);
+  CheckSelectionAtSuggestionsContainer(0);
+
+  apps_grid_view_->ClearAnySelectedView();
+  SimulateKeyPress(ui::VKEY_TAB);
+  CheckSelectionAtSuggestionsContainer(0);
+}
+
+// Tests that in state start when selection exists. Hitting tab key does
+// nothing while hitting shift+tab key clears selection.
+TEST_P(AppsGridViewTest, ClearSelectionInStateStart) {
+  if (!test_with_fullscreen_)
+    return;
+
+  // Simulates that the app list is at state start.
+  contents_view_->SetActiveState(AppListModel::STATE_START);
+  model_->PopulateApps(GetTilesPerPage(0));
+
+  // Moves selection to the first app in the suggestions container.
+  SimulateKeyPress(ui::VKEY_DOWN);
+
+  SimulateKeyPress(ui::VKEY_TAB);
+  CheckSelectionAtSuggestionsContainer(0);
+
+  SimulateKeyPress(ui::VKEY_TAB, ui::EF_SHIFT_DOWN);
+  CheckNoSelection();
+}
+
+// Tests that in state start when selection is on expand arrow, only hitting
+// left/up arrow key moves the selection to the last app in suggestion
+// container.
+TEST_P(AppsGridViewTest, ExpandArrowSelectionInStateStart) {
+  if (!test_with_fullscreen_)
+    return;
+
+  // Simulates that the app list is at state start.
+  contents_view_->SetActiveState(AppListModel::STATE_START);
+  model_->PopulateApps(GetTilesPerPage(0));
+
+  // Moves selection to the expand arrow.
+  expand_arrow_view_->SetSelected(true);
+  CheckSelectionAtExpandArrow();
+
+  SimulateKeyPress(ui::VKEY_DOWN);
+  CheckSelectionAtExpandArrow();
+
+  SimulateKeyPress(ui::VKEY_RIGHT);
+  CheckSelectionAtExpandArrow();
+
+  SimulateKeyPress(ui::VKEY_TAB);
+  CheckSelectionAtExpandArrow();
+
+  SimulateKeyPress(ui::VKEY_UP);
+  CheckSelectionAtSuggestionsContainer(kNumOfSuggestedApps - 1);
+
+  // Resets selection to the expand arrow.
+  apps_grid_view_->ClearAnySelectedView();
+  expand_arrow_view_->SetSelected(true);
+  SimulateKeyPress(ui::VKEY_LEFT);
+  CheckSelectionAtSuggestionsContainer(kNumOfSuggestedApps - 1);
+}
+
+// Tests that in state start when selection is on app in suggestions container,
+// hitting up key does nothing. Hitting left/right key moves the selection to
+// app on the left/right if index is valid. Hitting right key when selection is
+// on the last app in suggestions container or hitting down key move the
+// selection to the expand arrow.
+TEST_P(AppsGridViewTest, SuggestionsContainerSelectionInStateStart) {
+  if (!test_with_fullscreen_)
+    return;
+
+  // Simulates that the app list is at state start.
+  contents_view_->SetActiveState(AppListModel::STATE_START);
+  model_->PopulateApps(GetTilesPerPage(0));
+  SimulateKeyPress(ui::VKEY_DOWN);
+
+  SimulateKeyPress(ui::VKEY_UP);
+  CheckSelectionAtSuggestionsContainer(0);
+
+  SimulateKeyPress(ui::VKEY_RIGHT);
+  CheckSelectionAtSuggestionsContainer(1);
+
+  SimulateKeyPress(ui::VKEY_LEFT);
+  CheckSelectionAtSuggestionsContainer(0);
+
+  SimulateKeyPress(ui::VKEY_DOWN);
+  CheckSelectionAtExpandArrow();
+
+  // Sets selection to the last app in the suggestions container.
+  apps_grid_view_->ClearAnySelectedView();
+  suggestions_container_->SetSelectedIndex(kNumOfSuggestedApps - 1);
+  SimulateKeyPress(ui::VKEY_RIGHT);
+  CheckSelectionAtExpandArrow();
+}
+
 TEST_P(AppsGridViewTest, ItemLabelShortNameOverride) {
   // If the app's full name and short name differ, the title label's tooltip
   // should always be the full name of the app.
diff --git a/ui/app_list/views/expand_arrow_view.h b/ui/app_list/views/expand_arrow_view.h
index f04d0f2a..efab5bf1 100644
--- a/ui/app_list/views/expand_arrow_view.h
+++ b/ui/app_list/views/expand_arrow_view.h
@@ -6,6 +6,7 @@
 #define UI_APP_LIST_VIEWS_EXPAND_ARROW_VIEW_H_
 
 #include "base/memory/weak_ptr.h"
+#include "ui/app_list/app_list_export.h"
 #include "ui/views/controls/button/button.h"
 
 namespace gfx {
@@ -25,7 +26,8 @@
 class ContentsView;
 
 // A tile item for the expand arrow on the start page.
-class ExpandArrowView : public views::Button, public views::ButtonListener {
+class APP_LIST_EXPORT ExpandArrowView : public views::Button,
+                                        public views::ButtonListener {
  public:
   ExpandArrowView(ContentsView* contents_view, AppListView* app_list_view);
   ~ExpandArrowView() override;
diff --git a/ui/app_list/views/search_box_view.cc b/ui/app_list/views/search_box_view.cc
index 784d1ad..e5743b8 100644
--- a/ui/app_list/views/search_box_view.cc
+++ b/ui/app_list/views/search_box_view.cc
@@ -266,6 +266,11 @@
   box_layout_->SetFlexForView(search_box_, 1);
 
   if (is_fullscreen_app_list_enabled_) {
+    // An invisible space view to align |search_box_| to center.
+    search_box_right_space_ = new views::View();
+    search_box_right_space_->SetPreferredSize(gfx::Size(kSearchIconSize, 0));
+    content_container_->AddChildView(search_box_right_space_);
+
     close_button_ = new SearchBoxImageButton(this);
     close_button_->SetImage(
         views::ImageButton::STATE_NORMAL,
@@ -360,7 +365,7 @@
 
   switch (focused_view_) {
     case FOCUS_NONE:
-      focused_view_ = move_up ? FOCUS_CONTENTS_VIEW : FOCUS_SEARCH_BOX;
+      focused_view_ = move_up ? FOCUS_NONE : FOCUS_SEARCH_BOX;
       break;
     case FOCUS_BACK_BUTTON:
     case FOCUS_SEARCH_BOX:
@@ -368,7 +373,7 @@
       focused_view_ = move_up ? FOCUS_NONE : FOCUS_CONTENTS_VIEW;
       break;
     case FOCUS_CONTENTS_VIEW:
-      focused_view_ = move_up ? FOCUS_SEARCH_BOX : FOCUS_NONE;
+      focused_view_ = move_up ? FOCUS_SEARCH_BOX : FOCUS_CONTENTS_VIEW;
       break;
     default:
       NOTREACHED();
@@ -396,7 +401,7 @@
       case FOCUS_NONE:
         focused_view_ =
             move_backwards
-                ? FOCUS_CONTENTS_VIEW
+                ? FOCUS_NONE
                 : (back_button_ && back_button_->visible() ? FOCUS_BACK_BUTTON
                                                            : FOCUS_SEARCH_BOX);
         break;
@@ -424,7 +429,7 @@
                             ? (close_button_ && close_button_->visible()
                                    ? FOCUS_CLOSE_BUTTON
                                    : FOCUS_SEARCH_BOX)
-                            : FOCUS_NONE;
+                            : FOCUS_CONTENTS_VIEW;
         break;
       default:
         NOTREACHED();
@@ -541,6 +546,7 @@
   search_box_->set_placeholder_text_color(active ? kZeroQuerySearchboxColor
                                                  : search_box_color_);
   search_box_->SetCursorEnabled(active);
+  search_box_right_space_->SetVisible(!active);
 
   UpdateKeyboardVisibility();
 
diff --git a/ui/app_list/views/search_box_view.h b/ui/app_list/views/search_box_view.h
index 336c326..19d901f 100644
--- a/ui/app_list/views/search_box_view.h
+++ b/ui/app_list/views/search_box_view.h
@@ -231,6 +231,7 @@
   SearchBoxImageButton* speech_button_ = nullptr;
   SearchBoxImageButton* close_button_ = nullptr;
   views::Textfield* search_box_;
+  views::View* search_box_right_space_ = nullptr;
   views::View* contents_view_ = nullptr;
   app_list::AppListView* app_list_view_;
 
diff --git a/ui/app_list/views/search_result_answer_card_view.cc b/ui/app_list/views/search_result_answer_card_view.cc
index d37e198..735af01 100644
--- a/ui/app_list/views/search_result_answer_card_view.cc
+++ b/ui/app_list/views/search_result_answer_card_view.cc
@@ -163,7 +163,8 @@
 
   set_container_score(have_result ? display_results.front()->relevance() : 0);
   if (title_changed && search_answer_container_view_->selected())
-    NotifyAccessibilityEvent(ui::AX_EVENT_SELECTION, true);
+    search_answer_container_view_->NotifyAccessibilityEvent(
+        ui::AX_EVENT_SELECTION, true);
   return have_result ? 1 : 0;
 }
 
@@ -175,7 +176,8 @@
   const bool is_selected = new_selected == 0;
   search_answer_container_view_->SetSelected(is_selected);
   if (is_selected)
-    NotifyAccessibilityEvent(ui::AX_EVENT_SELECTION, true);
+    search_answer_container_view_->NotifyAccessibilityEvent(
+        ui::AX_EVENT_SELECTION, true);
 }
 
 bool SearchResultAnswerCardView::OnKeyPressed(const ui::KeyEvent& event) {
@@ -187,11 +189,6 @@
   return SearchResultContainerView::OnKeyPressed(event);
 }
 
-void SearchResultAnswerCardView::GetAccessibleNodeData(
-    ui::AXNodeData* node_data) {
-  search_answer_container_view_->GetAccessibleNodeData(node_data);
-}
-
 views::View* SearchResultAnswerCardView::GetSelectedView() const {
   return search_answer_container_view_->selected()
              ? search_answer_container_view_
diff --git a/ui/app_list/views/search_result_answer_card_view.h b/ui/app_list/views/search_result_answer_card_view.h
index fc870a9..e8eb9b8 100644
--- a/ui/app_list/views/search_result_answer_card_view.h
+++ b/ui/app_list/views/search_result_answer_card_view.h
@@ -29,7 +29,6 @@
   int DoUpdate() override;
   void UpdateSelectedIndex(int old_selected, int new_selected) override;
   bool OnKeyPressed(const ui::KeyEvent& event) override;
-  void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
   views::View* GetSelectedView() const override;
 
  private:
diff --git a/ui/app_list/views/search_result_answer_card_view_unittest.cc b/ui/app_list/views/search_result_answer_card_view_unittest.cc
index dbcf5535..3e4d803 100644
--- a/ui/app_list/views/search_result_answer_card_view_unittest.cc
+++ b/ui/app_list/views/search_result_answer_card_view_unittest.cc
@@ -96,7 +96,7 @@
   }
 
   void GetAccessibleNodeData(ui::AXNodeData* node_data) {
-    result_container_view_->GetAccessibleNodeData(node_data);
+    result_container_view_->child_at(0)->GetAccessibleNodeData(node_data);
   }
 
   views::View* result_view() const { return result_view_.get(); }
diff --git a/ui/aura/BUILD.gn b/ui/aura/BUILD.gn
index 8677c428..4bd8c3a 100644
--- a/ui/aura/BUILD.gn
+++ b/ui/aura/BUILD.gn
@@ -74,7 +74,6 @@
     "window_targeter.h",
     "window_tracker.h",
     "window_tree_host.h",
-    "window_tree_host_mac.h",
     "window_tree_host_observer.h",
     "window_tree_host_platform.h",
   ]
diff --git a/ui/aura/mus/window_mus.h b/ui/aura/mus/window_mus.h
index b328d67..4b734983 100644
--- a/ui/aura/mus/window_mus.h
+++ b/ui/aura/mus/window_mus.h
@@ -62,6 +62,9 @@
   virtual ~WindowMus() {}
 
   // Returns the WindowMus associated with |window|.
+  static const WindowMus* Get(const Window* window) {
+    return const_cast<const WindowMus*>(Get(const_cast<Window*>(window)));
+  }
   static WindowMus* Get(Window* window);
 
   Id server_id() const { return server_id_; }
diff --git a/ui/aura/mus/window_tree_host_mus.cc b/ui/aura/mus/window_tree_host_mus.cc
index 8b37862d..9213acff 100644
--- a/ui/aura/mus/window_tree_host_mus.cc
+++ b/ui/aura/mus/window_tree_host_mus.cc
@@ -210,4 +210,18 @@
   Env::GetInstance()->set_last_mouse_location(location_in_pixels);
 }
 
+gfx::Transform WindowTreeHostMus::GetRootTransformForLocalEventCoordinates()
+    const {
+  if (WindowMus::Get(window())->window_mus_type() !=
+      WindowMusType::DISPLAY_MANUALLY_CREATED) {
+    return WindowTreeHost::GetRootTransformForLocalEventCoordinates();
+  }
+  // Local events already have the transform set on the window applied, so
+  // don't apply it again.
+  gfx::Transform transform;
+  const float scale = window()->layer()->device_scale_factor();
+  transform.Scale(scale, scale);
+  return transform;
+}
+
 }  // namespace aura
diff --git a/ui/aura/mus/window_tree_host_mus.h b/ui/aura/mus/window_tree_host_mus.h
index f01e9920..2ab21f4 100644
--- a/ui/aura/mus/window_tree_host_mus.h
+++ b/ui/aura/mus/window_tree_host_mus.h
@@ -101,6 +101,7 @@
   void OnCloseRequest() override;
   void MoveCursorToScreenLocationInPixels(
       const gfx::Point& location_in_pixels) override;
+  gfx::Transform GetRootTransformForLocalEventCoordinates() const override;
 
  private:
   int64_t display_id_;
diff --git a/ui/aura/window_event_dispatcher.cc b/ui/aura/window_event_dispatcher.cc
index 776e00a..9b3aa8fe 100644
--- a/ui/aura/window_event_dispatcher.cc
+++ b/ui/aura/window_event_dispatcher.cc
@@ -257,7 +257,9 @@
 
 void WindowEventDispatcher::TransformEventForDeviceScaleFactor(
     ui::LocatedEvent* event) {
-  event->UpdateForRootTransform(host_->GetInverseRootTransform());
+  event->UpdateForRootTransform(
+      host_->GetInverseRootTransform(),
+      host_->GetInverseRootTransformForLocalEventCoordinates());
 }
 
 void WindowEventDispatcher::DispatchMouseExitToHidingWindow(Window* window) {
@@ -581,7 +583,9 @@
   // the pointer, in dips. OnEventFromSource expects events with co-ordinates
   // in raw pixels, so we convert back to raw pixels here.
   DCHECK(event->type() == ui::ET_TOUCH_CANCELLED);
-  event->UpdateForRootTransform(host_->GetRootTransform());
+  event->UpdateForRootTransform(
+      host_->GetRootTransform(),
+      host_->GetRootTransformForLocalEventCoordinates());
   DispatchDetails details = OnEventFromSource(event);
   if (details.dispatcher_destroyed)
     return;
diff --git a/ui/aura/window_targeter.cc b/ui/aura/window_targeter.cc
index 612a8ed..8be8e14 100644
--- a/ui/aura/window_targeter.cc
+++ b/ui/aura/window_targeter.cc
@@ -104,8 +104,10 @@
       // applying the host's transform.
       ui::LocatedEvent* located_event = static_cast<ui::LocatedEvent*>(event);
       located_event->ConvertLocationToTarget(target, new_root);
+      WindowTreeHost* window_tree_host = new_root->GetHost();
       located_event->UpdateForRootTransform(
-          new_root->GetHost()->GetRootTransform());
+          window_tree_host->GetRootTransform(),
+          window_tree_host->GetRootTransformForLocalEventCoordinates());
     }
     ignore_result(new_root->GetHost()->event_sink()->OnEventFromSource(event));
 
diff --git a/ui/aura/window_tree_host.cc b/ui/aura/window_tree_host.cc
index 4898e64..64d96e7 100644
--- a/ui/aura/window_tree_host.cc
+++ b/ui/aura/window_tree_host.cc
@@ -97,6 +97,20 @@
   return invert;
 }
 
+gfx::Transform WindowTreeHost::GetRootTransformForLocalEventCoordinates()
+    const {
+  return GetRootTransform();
+}
+
+gfx::Transform WindowTreeHost::GetInverseRootTransformForLocalEventCoordinates()
+    const {
+  gfx::Transform invert;
+  gfx::Transform transform = GetRootTransformForLocalEventCoordinates();
+  if (!transform.GetInverse(&invert))
+    return transform;
+  return invert;
+}
+
 void WindowTreeHost::SetOutputSurfacePaddingInPixels(
     const gfx::Insets& padding_in_pixels) {
   if (output_surface_padding_in_pixels_ == padding_in_pixels)
diff --git a/ui/aura/window_tree_host.h b/ui/aura/window_tree_host.h
index 31301558..e30f72c 100644
--- a/ui/aura/window_tree_host.h
+++ b/ui/aura/window_tree_host.h
@@ -83,6 +83,13 @@
   virtual void SetRootTransform(const gfx::Transform& transform);
   virtual gfx::Transform GetInverseRootTransform() const;
 
+  // These functions are used in event translation for translating the local
+  // coordinates of LocatedEvents. Default implementation calls to non-event
+  // ones (e.g. GetRootTransform()).
+  virtual gfx::Transform GetRootTransformForLocalEventCoordinates() const;
+  virtual gfx::Transform GetInverseRootTransformForLocalEventCoordinates()
+      const;
+
   // Sets padding applied to the output surface. The output surface is sized to
   // to the size of the host plus output surface padding. |window()| is offset
   // by |padding_in_pixels|, that is, |window|'s origin is set to
diff --git a/ui/aura/window_tree_host_mac.h b/ui/aura/window_tree_host_mac.h
deleted file mode 100644
index eab989c..0000000
--- a/ui/aura/window_tree_host_mac.h
+++ /dev/null
@@ -1,52 +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 UI_AURA_WINDOW_TREE_HOST_MAC_H_
-#define UI_AURA_WINDOW_TREE_HOST_MAC_H_
-
-#include <vector>
-
-#include "base/mac/scoped_nsobject.h"
-#include "base/macros.h"
-#include "ui/aura/aura_export.h"
-#include "ui/aura/window_tree_host.h"
-#include "ui/gfx/geometry/insets.h"
-#include "ui/gfx/geometry/rect.h"
-
-namespace aura {
-
-class AURA_EXPORT WindowTreeHostMac : public WindowTreeHost {
- public:
-  explicit WindowTreeHostMac(const gfx::Rect& bounds);
-  ~WindowTreeHostMac() override;
-
- private:
-  // WindowTreeHost Overrides.
-  ui::EventSource* GetEventSource() override;
-  gfx::AcceleratedWidget GetAcceleratedWidget() override;
-  void Show() override;
-  void Hide() override;
-  void ToggleFullScreen() override;
-  gfx::Rect GetBounds() const override;
-  void SetBounds(const gfx::Rect& bounds) override;
-  gfx::Insets GetInsets() const override;
-  void SetInsets(const gfx::Insets& insets) override;
-  gfx::Point GetLocationOnNativeScreen() const override;
-  void SetCapture() override;
-  void ReleaseCapture() override;
-  bool ConfineCursorToRootWindow() override;
-  void SetCursorNative(gfx::NativeCursor cursor_type) override;
-  void MoveCursorToNative(const gfx::Point& location) override;
-  void OnCursorVisibilityChangedNative(bool show) override;
-  void OnDeviceScaleFactorChanged(float device_scale_factor) override;
-
- private:
-  base::scoped_nsobject<NSWindow> window_;
-
-  DISALLOW_COPY_AND_ASSIGN(WindowTreeHostMac);
-};
-
-}  // namespace aura
-
-#endif  // UI_AURA_WINDOW_TREE_HOST_MAC_H_
diff --git a/ui/aura/window_tree_host_mac.mm b/ui/aura/window_tree_host_mac.mm
deleted file mode 100644
index 69799d0..0000000
--- a/ui/aura/window_tree_host_mac.mm
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <Cocoa/Cocoa.h>
-
-#include "ui/aura/window_tree_host.h"
-#include "ui/aura/window_tree_host_mac.h"
-
-namespace aura {
-
-WindowTreeHostMac::WindowTreeHostMac(const gfx::Rect& bounds) {
-  window_.reset(
-      [[NSWindow alloc]
-          initWithContentRect:NSRectFromCGRect(bounds.ToCGRect())
-                    styleMask:NSBorderlessWindowMask
-                      backing:NSBackingStoreBuffered
-                        defer:NO]);
-  CreateCompositor();
-  OnAcceleratedWidgetAvailable();
-}
-
-WindowTreeHostMac::~WindowTreeHostMac() {
-  DestroyDispatcher();
-}
-
-EventSource* WindowTreeHostMac::GetEventSource() {
-  NOTIMPLEMENTED();
-  return nil;
-}
-
-gfx::AcceleratedWidget WindowTreeHostMac::GetAcceleratedWidget() {
-  return [window_ contentView];
-}
-void WindowTreeHostMac::ShowImpl() {
-  [window_ makeKeyAndOrderFront:nil];
-}
-
-void WindowTreeHostMac::HideImpl() {
-  [window_ orderOut:nil];
-}
-
-void WindowTreeHostMac::ToggleFullScreen() {
-}
-
-gfx::Rect WindowTreeHostMac::GetBounds() const {
-  return gfx::Rect(NSRectToCGRect([window_ frame]));
-}
-
-void WindowTreeHostMac::SetBounds(const gfx::Rect& bounds) {
-  [window_ setFrame:NSRectFromCGRect(bounds.ToCGRect()) display:YES animate:NO];
-}
-
-gfx::Insets WindowTreeHostMac::GetInsets() const {
-  NOTIMPLEMENTED();
-  return gfx::Insets();
-}
-
-void WindowTreeHostMac::SetInsets(const gfx::Insets& insets) {
-  NOTIMPLEMENTED();
-}
-
-gfx::Point WindowTreeHostMac::GetLocationOnNativeScreen() const {
-  NOTIMPLEMENTED();
-  return gfx::Point(0, 0);
-}
-
-void WindowTreeHostMac::SetCapture() {
-  NOTIMPLEMENTED();
-}
-
-void WindowTreeHostMac::ReleaseCapture() {
-  NOTIMPLEMENTED();
-}
-
-bool WindowTreeHostMac::ConfineCursorToRootWindow() {
-  return false;
-}
-
-void WindowTreeHostMac::SetCursorNative(gfx::NativeCursor cursor_type) {
-  NOTIMPLEMENTED();
-}
-
-void WindowTreeHostMac::MoveCursorToNative(const gfx::Point& location) {
-  NOTIMPLEMENTED();
-}
-
-void WindowTreeHostMac::OnCursorVisibilityChangedNative(bool show) {
-  NOTIMPLEMENTED();
-}
-
-void WindowTreeHostMac::OnDeviceScaleFactorChanged(float device_scale_factor) {
-  NOTIMPLEMENTED();
-}
-
-// static
-WindowTreeHost* WindowTreeHost::Create(const gfx::Rect& bounds) {
-  return new WindowTreeHostMac(bounds);
-}
-
-}  // namespace aura
diff --git a/ui/compositor/layer.cc b/ui/compositor/layer.cc
index 62af63d7..72ccf5e8 100644
--- a/ui/compositor/layer.cc
+++ b/ui/compositor/layer.cc
@@ -921,7 +921,8 @@
 }
 
 void Layer::SetDidScrollCallback(
-    base::Callback<void(const gfx::ScrollOffset&)> callback) {
+    base::Callback<void(const gfx::ScrollOffset&, const cc::ElementId&)>
+        callback) {
   cc_layer_->set_did_scroll_callback(std::move(callback));
 }
 
diff --git a/ui/compositor/layer.h b/ui/compositor/layer.h
index c92d4067..869c00c6 100644
--- a/ui/compositor/layer.h
+++ b/ui/compositor/layer.h
@@ -376,7 +376,10 @@
   // Invoked when scrolling performed by the cc::InputHandler is committed. This
   // will only occur if the Layer has set scroll container bounds.
   void SetDidScrollCallback(
-      base::Callback<void(const gfx::ScrollOffset&)> callback);
+      base::Callback<void(const gfx::ScrollOffset&, const cc::ElementId&)>
+          callback);
+
+  cc::ElementId element_id() const { return cc_layer_->element_id(); }
 
   // Marks this layer as scrollable inside the provided bounds. This size only
   // affects scrolling so if clipping is desired, a separate clipping layer
diff --git a/ui/display/fake_display_snapshot.cc b/ui/display/fake_display_snapshot.cc
index c7294a5..bd595111 100644
--- a/ui/display/fake_display_snapshot.cc
+++ b/ui/display/fake_display_snapshot.cc
@@ -6,7 +6,6 @@
 
 #include <inttypes.h>
 
-#include <sstream>
 #include <utility>
 #include <vector>
 
@@ -29,42 +28,6 @@
   return (1.0f / dpi) * kInchInMm;
 }
 
-std::string ModeListString(
-    const std::vector<std::unique_ptr<const DisplayMode>>& modes) {
-  std::stringstream stream;
-  bool first = true;
-  for (auto& mode : modes) {
-    if (!first)
-      stream << ", ";
-    stream << mode->ToString();
-    first = false;
-  }
-  return stream.str();
-}
-
-std::string DisplayConnectionTypeString(DisplayConnectionType type) {
-  switch (type) {
-    case DISPLAY_CONNECTION_TYPE_NONE:
-      return "none";
-    case DISPLAY_CONNECTION_TYPE_UNKNOWN:
-      return "unknown";
-    case DISPLAY_CONNECTION_TYPE_INTERNAL:
-      return "internal";
-    case DISPLAY_CONNECTION_TYPE_VGA:
-      return "vga";
-    case DISPLAY_CONNECTION_TYPE_HDMI:
-      return "hdmi";
-    case DISPLAY_CONNECTION_TYPE_DVI:
-      return "dvi";
-    case DISPLAY_CONNECTION_TYPE_DISPLAYPORT:
-      return "dp";
-    case DISPLAY_CONNECTION_TYPE_NETWORK:
-      return "network";
-  }
-  NOTREACHED();
-  return "";
-}
-
 // Extracts text after specified delimiter. If the delimiter doesn't appear
 // exactly once the result will be empty and the input string will be
 // unmodified. Otherwise, the input string will contain the text before the
@@ -200,8 +163,8 @@
 
   return base::MakeUnique<FakeDisplaySnapshot>(
       id_, origin_, physical_size, type_, is_aspect_preserving_scaling_,
-      has_overscan_, has_color_correction_matrix_, name_, product_id_,
-      std::move(modes_), current_mode_, native_mode_);
+      has_overscan_, has_color_correction_matrix_, name_, std::move(modes_),
+      current_mode_, native_mode_, product_id_, maximum_cursor_size_);
 }
 
 Builder& Builder::SetId(int64_t id) {
@@ -274,6 +237,11 @@
   return *this;
 }
 
+Builder& Builder::SetMaximumCursorSize(const gfx::Size& maximum_cursor_size) {
+  maximum_cursor_size_ = maximum_cursor_size;
+  return *this;
+}
+
 Builder& Builder::SetDPI(int dpi) {
   dpi_ = static_cast<float>(dpi);
   return *this;
@@ -320,10 +288,11 @@
                                          bool has_overscan,
                                          bool has_color_correction_matrix,
                                          std::string display_name,
-                                         int64_t product_id,
                                          DisplayModeList modes,
                                          const DisplayMode* current_mode,
-                                         const DisplayMode* native_mode)
+                                         const DisplayMode* native_mode,
+                                         int64_t product_id,
+                                         const gfx::Size& maximum_cursor_size)
     : DisplaySnapshot(display_id,
                       origin,
                       physical_size,
@@ -336,9 +305,9 @@
                       std::move(modes),
                       std::vector<uint8_t>(),
                       current_mode,
-                      native_mode) {
-  product_id_ = product_id;
-}
+                      native_mode,
+                      product_id,
+                      maximum_cursor_size) {}
 
 FakeDisplaySnapshot::~FakeDisplaySnapshot() {}
 
@@ -371,17 +340,4 @@
   return builder.Build();
 }
 
-std::string FakeDisplaySnapshot::ToString() const {
-  return base::StringPrintf(
-      "id=%" PRId64
-      " current_mode=%s native_mode=%s origin=%s"
-      " physical_size=%s, type=%s name=\"%s\" modes=(%s)",
-      display_id_,
-      current_mode_ ? current_mode_->ToString().c_str() : "nullptr",
-      native_mode_ ? native_mode_->ToString().c_str() : "nullptr",
-      origin_.ToString().c_str(), physical_size_.ToString().c_str(),
-      DisplayConnectionTypeString(type_).c_str(), display_name_.c_str(),
-      ModeListString(modes_).c_str());
-}
-
 }  // namespace display
diff --git a/ui/display/fake_display_snapshot.h b/ui/display/fake_display_snapshot.h
index 1cb0b46..c6502d9 100644
--- a/ui/display/fake_display_snapshot.h
+++ b/ui/display/fake_display_snapshot.h
@@ -60,6 +60,7 @@
     Builder& SetHasColorCorrectionMatrix(bool val);
     Builder& SetName(const std::string& name);
     Builder& SetProductId(int64_t product_id);
+    Builder& SetMaximumCursorSize(const gfx::Size& maximum_cursor_size);
     // Sets physical_size so that the screen has the specified DPI using the
     // native resolution.
     Builder& SetDPI(int dpi);
@@ -85,6 +86,7 @@
     bool has_color_correction_matrix_ = false;
     std::string name_;
     int64_t product_id_ = DisplaySnapshot::kInvalidProductID;
+    gfx::Size maximum_cursor_size_ = gfx::Size(64, 64);
     DisplayModeList modes_;
     const DisplayMode* current_mode_ = nullptr;
     const DisplayMode* native_mode_ = nullptr;
@@ -100,10 +102,11 @@
                       bool has_overscan,
                       bool has_color_correction_matrix,
                       std::string display_name,
-                      int64_t product_id,
                       DisplayModeList modes,
                       const DisplayMode* current_mode,
-                      const DisplayMode* native_mode);
+                      const DisplayMode* native_mode,
+                      int64_t product_id,
+                      const gfx::Size& maximum_cursor_size);
   ~FakeDisplaySnapshot() override;
 
   // Creates a display snapshot from the provided |spec| string. Returns null if
@@ -113,9 +116,6 @@
       int64_t id,
       const std::string& spec);
 
-  // DisplaySnapshot:
-  std::string ToString() const override;
-
  private:
   DISALLOW_COPY_AND_ASSIGN(FakeDisplaySnapshot);
 };
diff --git a/ui/display/manager/chromeos/display_change_observer_unittest.cc b/ui/display/manager/chromeos/display_change_observer_unittest.cc
index cf74f78..dbf22a33 100644
--- a/ui/display/manager/chromeos/display_change_observer_unittest.cc
+++ b/ui/display/manager/chromeos/display_change_observer_unittest.cc
@@ -93,8 +93,7 @@
 TEST(DisplayChangeObserverTest, GetEmptyExternalManagedDisplayModeList) {
   FakeDisplaySnapshot display_snapshot(
       123, gfx::Point(), gfx::Size(), DISPLAY_CONNECTION_TYPE_UNKNOWN, false,
-      false, false, std::string(), 0,
-      std::vector<std::unique_ptr<const DisplayMode>>(), nullptr, nullptr);
+      false, false, std::string(), {}, nullptr, nullptr, 0, gfx::Size());
 
   ManagedDisplayInfo::ManagedDisplayModeList display_modes =
       DisplayChangeObserver::GetExternalManagedDisplayModeList(
diff --git a/ui/display/manager/forwarding_display_delegate.cc b/ui/display/manager/forwarding_display_delegate.cc
index 5efe1b9f..4443719 100644
--- a/ui/display/manager/forwarding_display_delegate.cc
+++ b/ui/display/manager/forwarding_display_delegate.cc
@@ -8,7 +8,7 @@
 
 #include "base/bind.h"
 #include "mojo/public/cpp/bindings/sync_call_restrictions.h"
-#include "ui/display/types/display_snapshot_mojo.h"
+#include "ui/display/types/display_snapshot.h"
 
 namespace display {
 
@@ -126,7 +126,7 @@
 
 void ForwardingDisplayDelegate::StoreAndForwardDisplays(
     const GetDisplaysCallback& callback,
-    std::vector<std::unique_ptr<DisplaySnapshotMojo>> snapshots) {
+    std::vector<std::unique_ptr<DisplaySnapshot>> snapshots) {
   for (auto& observer : observers_)
     observer.OnDisplaySnapshotsInvalidated();
   snapshots_ = std::move(snapshots);
diff --git a/ui/display/manager/forwarding_display_delegate.h b/ui/display/manager/forwarding_display_delegate.h
index 0f084b1..ebd0ddf 100644
--- a/ui/display/manager/forwarding_display_delegate.h
+++ b/ui/display/manager/forwarding_display_delegate.h
@@ -19,7 +19,7 @@
 
 namespace display {
 
-class DisplaySnapshotMojo;
+class DisplaySnapshot;
 
 // NativeDisplayDelegate implementation that forwards calls to a real
 // NativeDisplayDelegate in another process. Only forwards the methods
@@ -63,7 +63,7 @@
   // Stores display snapshots and forwards pointers to |callback|.
   void StoreAndForwardDisplays(
       const GetDisplaysCallback& callback,
-      std::vector<std::unique_ptr<DisplaySnapshotMojo>> snapshots);
+      std::vector<std::unique_ptr<DisplaySnapshot>> snapshots);
 
   // Forwards display snapshot pointers to |callback|.
   void ForwardDisplays(const GetDisplaysCallback& callback);
@@ -77,7 +77,7 @@
 
   // Display snapshots are owned here but accessed via raw pointers elsewhere.
   // Call OnDisplaySnapshotsInvalidated() on observers before invalidating them.
-  std::vector<std::unique_ptr<DisplaySnapshotMojo>> snapshots_;
+  std::vector<std::unique_ptr<DisplaySnapshot>> snapshots_;
 
   base::ObserverList<display::NativeDisplayObserver> observers_;
 
diff --git a/ui/display/mojo/BUILD.gn b/ui/display/mojo/BUILD.gn
index 7b213c1..92baf0cd 100644
--- a/ui/display/mojo/BUILD.gn
+++ b/ui/display/mojo/BUILD.gn
@@ -10,7 +10,7 @@
     "display_constants.mojom",
     "display_layout.mojom",
     "display_mode.mojom",
-    "display_snapshot_mojo.mojom",
+    "display_snapshot.mojom",
     "gamma_ramp_rgb_entry.mojom",
     "native_display_delegate.mojom",
   ]
diff --git a/ui/display/mojo/display_snapshot_mojo.mojom b/ui/display/mojo/display_snapshot.mojom
similarity index 91%
rename from ui/display/mojo/display_snapshot_mojo.mojom
rename to ui/display/mojo/display_snapshot.mojom
index 8b7ce1c5..839a9c0 100644
--- a/ui/display/mojo/display_snapshot_mojo.mojom
+++ b/ui/display/mojo/display_snapshot.mojom
@@ -9,8 +9,8 @@
 import "ui/display/mojo/display_mode.mojom";
 import "ui/gfx/geometry/mojo/geometry.mojom";
 
-// Corresponds to display::DisplaySnapshotMojo.
-struct DisplaySnapshotMojo {
+// Corresponds to display::DisplaySnapshot.
+struct DisplaySnapshot {
   int64 display_id;
   gfx.mojom.Point origin;
   gfx.mojom.Size physical_size;
diff --git a/ui/display/mojo/display_snapshot.typemap b/ui/display/mojo/display_snapshot.typemap
new file mode 100644
index 0000000..4668070
--- /dev/null
+++ b/ui/display/mojo/display_snapshot.typemap
@@ -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.
+
+mojom = "//ui/display/mojo/display_snapshot.mojom"
+public_headers = [ "//ui/display/types/display_snapshot.h" ]
+traits_headers = [ "//ui/display/mojo/display_snapshot_struct_traits.h" ]
+sources = [
+  "//ui/display/mojo/display_snapshot_struct_traits.cc",
+]
+public_deps = [
+  "//ui/display",
+]
+deps = [
+  "//ui/gfx/geometry",
+]
+type_mappings = [ "display.mojom.DisplaySnapshot=std::unique_ptr<display::DisplaySnapshot>[move_only]" ]
diff --git a/ui/display/mojo/display_snapshot_mojo.typemap b/ui/display/mojo/display_snapshot_mojo.typemap
deleted file mode 100644
index bd06b0fb..0000000
--- a/ui/display/mojo/display_snapshot_mojo.typemap
+++ /dev/null
@@ -1,17 +0,0 @@
-# Copyright 2017 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-mojom = "//ui/display/mojo/display_snapshot_mojo.mojom"
-public_headers = [ "//ui/display/types/display_snapshot_mojo.h" ]
-traits_headers = [ "//ui/display/mojo/display_snapshot_mojo_struct_traits.h" ]
-sources = [
-  "//ui/display/mojo/display_snapshot_mojo_struct_traits.cc",
-]
-public_deps = [
-  "//ui/display",
-]
-deps = [
-  "//ui/gfx/geometry",
-]
-type_mappings = [ "display.mojom.DisplaySnapshotMojo=std::unique_ptr<display::DisplaySnapshotMojo>[move_only]" ]
diff --git a/ui/display/mojo/display_snapshot_mojo_struct_traits.h b/ui/display/mojo/display_snapshot_mojo_struct_traits.h
deleted file mode 100644
index 3c66e17..0000000
--- a/ui/display/mojo/display_snapshot_mojo_struct_traits.h
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_DISPLAY_MOJO_DISPLAY_SNAPSHOT_MOJO_STRUCT_TRAITS_H_
-#define UI_DISPLAY_MOJO_DISPLAY_SNAPSHOT_MOJO_STRUCT_TRAITS_H_
-
-#include "ipc/ipc_message_utils.h"
-#include "ui/display/mojo/display_constants_struct_traits.h"
-#include "ui/display/mojo/display_mode_struct_traits.h"
-#include "ui/display/mojo/display_snapshot_mojo.mojom.h"
-#include "ui/display/types/display_mode.h"
-#include "ui/display/types/display_snapshot_mojo.h"
-#include "ui/gfx/geometry/mojo/geometry_struct_traits.h"
-
-namespace mojo {
-
-template <>
-struct StructTraits<display::mojom::DisplaySnapshotMojoDataView,
-                    std::unique_ptr<display::DisplaySnapshotMojo>> {
-  static int64_t display_id(
-      const std::unique_ptr<display::DisplaySnapshotMojo>& snapshot) {
-    return snapshot->display_id();
-  }
-
-  static const gfx::Point& origin(
-      const std::unique_ptr<display::DisplaySnapshotMojo>& snapshot) {
-    return snapshot->origin();
-  }
-
-  static const gfx::Size& physical_size(
-      const std::unique_ptr<display::DisplaySnapshotMojo>& snapshot) {
-    return snapshot->physical_size();
-  }
-
-  static display::DisplayConnectionType type(
-      const std::unique_ptr<display::DisplaySnapshotMojo>& snapshot) {
-    return snapshot->type();
-  }
-
-  static bool is_aspect_preserving_scaling(
-      const std::unique_ptr<display::DisplaySnapshotMojo>& snapshot) {
-    return snapshot->is_aspect_preserving_scaling();
-  }
-
-  static bool has_overscan(
-      const std::unique_ptr<display::DisplaySnapshotMojo>& snapshot) {
-    return snapshot->has_overscan();
-  }
-
-  static bool has_color_correction_matrix(
-      const std::unique_ptr<display::DisplaySnapshotMojo>& snapshot) {
-    return snapshot->has_color_correction_matrix();
-  }
-
-  static std::string display_name(
-      const std::unique_ptr<display::DisplaySnapshotMojo>& snapshot) {
-    return snapshot->display_name();
-  }
-
-  static const base::FilePath& sys_path(
-      const std::unique_ptr<display::DisplaySnapshotMojo>& snapshot) {
-    return snapshot->sys_path();
-  }
-
-  static std::vector<uint8_t> edid(
-      const std::unique_ptr<display::DisplaySnapshotMojo>& snapshot) {
-    return snapshot->edid();
-  }
-
-  static std::vector<std::unique_ptr<display::DisplayMode>> modes(
-      const std::unique_ptr<display::DisplaySnapshotMojo>& snapshot);
-
-  static uint64_t current_mode_index(
-      const std::unique_ptr<display::DisplaySnapshotMojo>& snapshot);
-
-  static bool has_current_mode(
-      const std::unique_ptr<display::DisplaySnapshotMojo>& snapshot) {
-    return snapshot->current_mode() != nullptr;
-  }
-
-  static uint64_t native_mode_index(
-      const std::unique_ptr<display::DisplaySnapshotMojo>& snapshot);
-
-  static bool has_native_mode(
-      const std::unique_ptr<display::DisplaySnapshotMojo>& snapshot) {
-    return snapshot->native_mode() != nullptr;
-  }
-
-  static int64_t product_id(
-      const std::unique_ptr<display::DisplaySnapshotMojo>& snapshot) {
-    return snapshot->product_id();
-  }
-
-  static const gfx::Size& maximum_cursor_size(
-      const std::unique_ptr<display::DisplaySnapshotMojo>& snapshot) {
-    return snapshot->maximum_cursor_size();
-  }
-
-  static bool Read(display::mojom::DisplaySnapshotMojoDataView data,
-                   std::unique_ptr<display::DisplaySnapshotMojo>* out);
-};
-
-}  // namespace mojo
-
-#endif  // UI_DISPLAY_MOJO_DISPLAY_SNAPSHOT_MOJO_STRUCT_TRAITS_H_
diff --git a/ui/display/mojo/display_snapshot_mojo_struct_traits.cc b/ui/display/mojo/display_snapshot_struct_traits.cc
similarity index 74%
rename from ui/display/mojo/display_snapshot_mojo_struct_traits.cc
rename to ui/display/mojo/display_snapshot_struct_traits.cc
index 1a370dc..325f52b 100644
--- a/ui/display/mojo/display_snapshot_mojo_struct_traits.cc
+++ b/ui/display/mojo/display_snapshot_struct_traits.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 "ui/display/mojo/display_snapshot_mojo_struct_traits.h"
+#include "ui/display/mojo/display_snapshot_struct_traits.h"
 
 #include "ui/display/types/display_constants.h"
 #include "ui/gfx/geometry/size.h"
@@ -32,10 +32,9 @@
 
 // static
 std::vector<std::unique_ptr<display::DisplayMode>>
-StructTraits<display::mojom::DisplaySnapshotMojoDataView,
-             std::unique_ptr<display::DisplaySnapshotMojo>>::
-    modes(
-        const std::unique_ptr<display::DisplaySnapshotMojo>& display_snapshot) {
+StructTraits<display::mojom::DisplaySnapshotDataView,
+             std::unique_ptr<display::DisplaySnapshot>>::
+    modes(const std::unique_ptr<display::DisplaySnapshot>& display_snapshot) {
   std::vector<std::unique_ptr<display::DisplayMode>> display_mode_list;
 
   for (const auto& display_mode : display_snapshot->modes())
@@ -45,28 +44,28 @@
 }
 
 // static
-uint64_t StructTraits<display::mojom::DisplaySnapshotMojoDataView,
-                      std::unique_ptr<display::DisplaySnapshotMojo>>::
+uint64_t StructTraits<display::mojom::DisplaySnapshotDataView,
+                      std::unique_ptr<display::DisplaySnapshot>>::
     current_mode_index(
-        const std::unique_ptr<display::DisplaySnapshotMojo>& display_snapshot) {
+        const std::unique_ptr<display::DisplaySnapshot>& display_snapshot) {
   return GetModeIndex(display_snapshot->modes(),
                       display_snapshot->current_mode());
 }
 
 // static
-uint64_t StructTraits<display::mojom::DisplaySnapshotMojoDataView,
-                      std::unique_ptr<display::DisplaySnapshotMojo>>::
+uint64_t StructTraits<display::mojom::DisplaySnapshotDataView,
+                      std::unique_ptr<display::DisplaySnapshot>>::
     native_mode_index(
-        const std::unique_ptr<display::DisplaySnapshotMojo>& display_snapshot) {
+        const std::unique_ptr<display::DisplaySnapshot>& display_snapshot) {
   return GetModeIndex(display_snapshot->modes(),
                       display_snapshot->native_mode());
 }
 
 // static
-bool StructTraits<display::mojom::DisplaySnapshotMojoDataView,
-                  std::unique_ptr<display::DisplaySnapshotMojo>>::
-    Read(display::mojom::DisplaySnapshotMojoDataView data,
-         std::unique_ptr<display::DisplaySnapshotMojo>* out) {
+bool StructTraits<display::mojom::DisplaySnapshotDataView,
+                  std::unique_ptr<display::DisplaySnapshot>>::
+    Read(display::mojom::DisplaySnapshotDataView data,
+         std::unique_ptr<display::DisplaySnapshot>* out) {
   gfx::Point origin;
   if (!data.ReadOrigin(&origin))
     return false;
@@ -123,12 +122,12 @@
   if (!data.ReadMaximumCursorSize(&maximum_cursor_size))
     return false;
 
-  *out = base::MakeUnique<display::DisplaySnapshotMojo>(
+  *out = base::MakeUnique<display::DisplaySnapshot>(
       data.display_id(), origin, physical_size, type,
       data.is_aspect_preserving_scaling(), data.has_overscan(),
       data.has_color_correction_matrix(), display_name, file_path,
-      data.product_id(), std::move(modes), std::move(edid), current_mode,
-      native_mode, maximum_cursor_size);
+      std::move(modes), std::move(edid), current_mode, native_mode,
+      data.product_id(), maximum_cursor_size);
   return true;
 }
 
diff --git a/ui/display/mojo/display_snapshot_struct_traits.h b/ui/display/mojo/display_snapshot_struct_traits.h
new file mode 100644
index 0000000..cffec53
--- /dev/null
+++ b/ui/display/mojo/display_snapshot_struct_traits.h
@@ -0,0 +1,106 @@
+// 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_DISPLAY_MOJO_DISPLAY_SNAPSHOT_MOJO_STRUCT_TRAITS_H_
+#define UI_DISPLAY_MOJO_DISPLAY_SNAPSHOT_MOJO_STRUCT_TRAITS_H_
+
+#include "ipc/ipc_message_utils.h"
+#include "ui/display/mojo/display_constants_struct_traits.h"
+#include "ui/display/mojo/display_mode_struct_traits.h"
+#include "ui/display/mojo/display_snapshot.mojom.h"
+#include "ui/display/types/display_mode.h"
+#include "ui/display/types/display_snapshot.h"
+#include "ui/gfx/geometry/mojo/geometry_struct_traits.h"
+
+namespace mojo {
+
+template <>
+struct StructTraits<display::mojom::DisplaySnapshotDataView,
+                    std::unique_ptr<display::DisplaySnapshot>> {
+  static int64_t display_id(
+      const std::unique_ptr<display::DisplaySnapshot>& snapshot) {
+    return snapshot->display_id();
+  }
+
+  static const gfx::Point& origin(
+      const std::unique_ptr<display::DisplaySnapshot>& snapshot) {
+    return snapshot->origin();
+  }
+
+  static const gfx::Size& physical_size(
+      const std::unique_ptr<display::DisplaySnapshot>& snapshot) {
+    return snapshot->physical_size();
+  }
+
+  static display::DisplayConnectionType type(
+      const std::unique_ptr<display::DisplaySnapshot>& snapshot) {
+    return snapshot->type();
+  }
+
+  static bool is_aspect_preserving_scaling(
+      const std::unique_ptr<display::DisplaySnapshot>& snapshot) {
+    return snapshot->is_aspect_preserving_scaling();
+  }
+
+  static bool has_overscan(
+      const std::unique_ptr<display::DisplaySnapshot>& snapshot) {
+    return snapshot->has_overscan();
+  }
+
+  static bool has_color_correction_matrix(
+      const std::unique_ptr<display::DisplaySnapshot>& snapshot) {
+    return snapshot->has_color_correction_matrix();
+  }
+
+  static std::string display_name(
+      const std::unique_ptr<display::DisplaySnapshot>& snapshot) {
+    return snapshot->display_name();
+  }
+
+  static const base::FilePath& sys_path(
+      const std::unique_ptr<display::DisplaySnapshot>& snapshot) {
+    return snapshot->sys_path();
+  }
+
+  static std::vector<uint8_t> edid(
+      const std::unique_ptr<display::DisplaySnapshot>& snapshot) {
+    return snapshot->edid();
+  }
+
+  static std::vector<std::unique_ptr<display::DisplayMode>> modes(
+      const std::unique_ptr<display::DisplaySnapshot>& snapshot);
+
+  static uint64_t current_mode_index(
+      const std::unique_ptr<display::DisplaySnapshot>& snapshot);
+
+  static bool has_current_mode(
+      const std::unique_ptr<display::DisplaySnapshot>& snapshot) {
+    return snapshot->current_mode() != nullptr;
+  }
+
+  static uint64_t native_mode_index(
+      const std::unique_ptr<display::DisplaySnapshot>& snapshot);
+
+  static bool has_native_mode(
+      const std::unique_ptr<display::DisplaySnapshot>& snapshot) {
+    return snapshot->native_mode() != nullptr;
+  }
+
+  static int64_t product_id(
+      const std::unique_ptr<display::DisplaySnapshot>& snapshot) {
+    return snapshot->product_id();
+  }
+
+  static const gfx::Size& maximum_cursor_size(
+      const std::unique_ptr<display::DisplaySnapshot>& snapshot) {
+    return snapshot->maximum_cursor_size();
+  }
+
+  static bool Read(display::mojom::DisplaySnapshotDataView data,
+                   std::unique_ptr<display::DisplaySnapshot>* out);
+};
+
+}  // namespace mojo
+
+#endif  // UI_DISPLAY_MOJO_DISPLAY_SNAPSHOT_MOJO_STRUCT_TRAITS_H_
diff --git a/ui/display/mojo/display_struct_traits_unittest.cc b/ui/display/mojo/display_struct_traits_unittest.cc
index 42ae8278..cf740387 100644
--- a/ui/display/mojo/display_struct_traits_unittest.cc
+++ b/ui/display/mojo/display_struct_traits_unittest.cc
@@ -13,12 +13,12 @@
 #include "ui/display/display_layout.h"
 #include "ui/display/mojo/display_layout_struct_traits.h"
 #include "ui/display/mojo/display_mode_struct_traits.h"
-#include "ui/display/mojo/display_snapshot_mojo_struct_traits.h"
+#include "ui/display/mojo/display_snapshot_struct_traits.h"
 #include "ui/display/mojo/display_struct_traits.h"
 #include "ui/display/mojo/gamma_ramp_rgb_entry_struct_traits.h"
 #include "ui/display/types/display_constants.h"
 #include "ui/display/types/display_mode.h"
-#include "ui/display/types/display_snapshot_mojo.h"
+#include "ui/display/types/display_snapshot.h"
 #include "ui/display/types/gamma_ramp_rgb_entry.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
@@ -68,8 +68,8 @@
   EXPECT_EQ(input->refresh_rate(), output->refresh_rate());
 }
 
-void CheckDisplaySnapShotMojoEqual(const DisplaySnapshotMojo& input,
-                                   const DisplaySnapshotMojo& output) {
+void CheckDisplaySnapShotMojoEqual(const DisplaySnapshot& input,
+                                   const DisplaySnapshot& output) {
   // We want to test each component individually to make sure each data member
   // was correctly serialized and deserialized.
   EXPECT_NE(&input, &output);  // Make sure they aren't the same object.
@@ -285,16 +285,14 @@
   const DisplayMode* native_mode = nullptr;
   const std::vector<uint8_t> edid = {1};
 
-  std::unique_ptr<DisplaySnapshotMojo> input =
-      base::MakeUnique<DisplaySnapshotMojo>(
-          display_id, origin, physical_size, type, is_aspect_preserving_scaling,
-          has_overscan, has_color_correction_matrix, display_name, sys_path,
-          product_id, std::move(modes), edid, current_mode, native_mode,
-          maximum_cursor_size);
+  std::unique_ptr<DisplaySnapshot> input = base::MakeUnique<DisplaySnapshot>(
+      display_id, origin, physical_size, type, is_aspect_preserving_scaling,
+      has_overscan, has_color_correction_matrix, display_name, sys_path,
+      std::move(modes), edid, current_mode, native_mode, product_id,
+      maximum_cursor_size);
 
-  std::unique_ptr<DisplaySnapshotMojo> output;
-  SerializeAndDeserialize<mojom::DisplaySnapshotMojo>(
-      DisplaySnapshotMojo::CreateFrom(*input), &output);
+  std::unique_ptr<DisplaySnapshot> output;
+  SerializeAndDeserialize<mojom::DisplaySnapshot>(input->Clone(), &output);
 
   CheckDisplaySnapShotMojoEqual(*input, *output);
 }
@@ -323,16 +321,14 @@
   const DisplayMode* native_mode = modes[0].get();
   const std::vector<uint8_t> edid = {1};
 
-  std::unique_ptr<DisplaySnapshotMojo> input =
-      base::MakeUnique<DisplaySnapshotMojo>(
-          display_id, origin, physical_size, type, is_aspect_preserving_scaling,
-          has_overscan, has_color_correction_matrix, display_name, sys_path,
-          product_id, std::move(modes), edid, current_mode, native_mode,
-          maximum_cursor_size);
+  std::unique_ptr<DisplaySnapshot> input = base::MakeUnique<DisplaySnapshot>(
+      display_id, origin, physical_size, type, is_aspect_preserving_scaling,
+      has_overscan, has_color_correction_matrix, display_name, sys_path,
+      std::move(modes), edid, current_mode, native_mode, product_id,
+      maximum_cursor_size);
 
-  std::unique_ptr<DisplaySnapshotMojo> output;
-  SerializeAndDeserialize<mojom::DisplaySnapshotMojo>(
-      DisplaySnapshotMojo::CreateFrom(*input), &output);
+  std::unique_ptr<DisplaySnapshot> output;
+  SerializeAndDeserialize<mojom::DisplaySnapshot>(input->Clone(), &output);
 
   CheckDisplaySnapShotMojoEqual(*input, *output);
 }
@@ -365,16 +361,14 @@
   const DisplayMode* native_mode = modes[2].get();
   const std::vector<uint8_t> edid = {2, 3, 4, 5};
 
-  std::unique_ptr<DisplaySnapshotMojo> input =
-      base::MakeUnique<DisplaySnapshotMojo>(
-          display_id, origin, physical_size, type, is_aspect_preserving_scaling,
-          has_overscan, has_color_correction_matrix, display_name, sys_path,
-          product_id, std::move(modes), edid, current_mode, native_mode,
-          maximum_cursor_size);
+  std::unique_ptr<DisplaySnapshot> input = base::MakeUnique<DisplaySnapshot>(
+      display_id, origin, physical_size, type, is_aspect_preserving_scaling,
+      has_overscan, has_color_correction_matrix, display_name, sys_path,
+      std::move(modes), edid, current_mode, native_mode, product_id,
+      maximum_cursor_size);
 
-  std::unique_ptr<DisplaySnapshotMojo> output;
-  SerializeAndDeserialize<mojom::DisplaySnapshotMojo>(
-      DisplaySnapshotMojo::CreateFrom(*input), &output);
+  std::unique_ptr<DisplaySnapshot> output;
+  SerializeAndDeserialize<mojom::DisplaySnapshot>(input->Clone(), &output);
 
   CheckDisplaySnapShotMojoEqual(*input, *output);
 }
@@ -402,16 +396,14 @@
   const DisplayMode* native_mode = modes[0].get();
   const std::vector<uint8_t> edid = {2, 3};
 
-  std::unique_ptr<DisplaySnapshotMojo> input =
-      base::MakeUnique<DisplaySnapshotMojo>(
-          display_id, origin, physical_size, type, is_aspect_preserving_scaling,
-          has_overscan, has_color_correction_matrix, display_name, sys_path,
-          product_id, std::move(modes), edid, current_mode, native_mode,
-          maximum_cursor_size);
+  std::unique_ptr<DisplaySnapshot> input = base::MakeUnique<DisplaySnapshot>(
+      display_id, origin, physical_size, type, is_aspect_preserving_scaling,
+      has_overscan, has_color_correction_matrix, display_name, sys_path,
+      std::move(modes), edid, current_mode, native_mode, product_id,
+      maximum_cursor_size);
 
-  std::unique_ptr<DisplaySnapshotMojo> output;
-  SerializeAndDeserialize<mojom::DisplaySnapshotMojo>(
-      DisplaySnapshotMojo::CreateFrom(*input), &output);
+  std::unique_ptr<DisplaySnapshot> output;
+  SerializeAndDeserialize<mojom::DisplaySnapshot>(input->Clone(), &output);
 
   CheckDisplaySnapShotMojoEqual(*input, *output);
 }
diff --git a/ui/display/mojo/native_display_delegate.mojom b/ui/display/mojo/native_display_delegate.mojom
index 9ba1f028..d0d47a8 100644
--- a/ui/display/mojo/native_display_delegate.mojom
+++ b/ui/display/mojo/native_display_delegate.mojom
@@ -6,7 +6,7 @@
 
 import "ui/display/mojo/display_constants.mojom";
 import "ui/display/mojo/display_mode.mojom";
-import "ui/display/mojo/display_snapshot_mojo.mojom";
+import "ui/display/mojo/display_snapshot.mojom";
 import "ui/display/mojo/gamma_ramp_rgb_entry.mojom";
 import "ui/gfx/geometry/mojo/geometry.mojom";
 
@@ -21,7 +21,8 @@
   // Initializes and registers the observer. This is synchronous so that ash
   // initialization has an initial set of displays to use.
   [Sync]
-  Initialize(NativeDisplayObserver observer) => (array<DisplaySnapshotMojo> snapshots);
+  Initialize(NativeDisplayObserver observer) =>
+      (array<DisplaySnapshot> snapshots);
 
   // Take control of the displays from any other controlling process.
   TakeDisplayControl() => (bool result);
@@ -30,7 +31,7 @@
   RelinquishDisplayControl() => (bool result);
 
   // Queries for a list of fresh displays.
-  GetDisplays() => (array<DisplaySnapshotMojo> snapshots);
+  GetDisplays() => (array<DisplaySnapshot> snapshots);
 
   // Configures the display represented by |display_id| to use |mode| and
   // positions the display to |origin| in the framebuffer. |mode| can be null,
diff --git a/ui/display/mojo/typemaps.gni b/ui/display/mojo/typemaps.gni
index 268ff25..e352a044 100644
--- a/ui/display/mojo/typemaps.gni
+++ b/ui/display/mojo/typemaps.gni
@@ -7,6 +7,6 @@
   "//ui/display/mojo/display_constants.typemap",
   "//ui/display/mojo/display_layout.typemap",
   "//ui/display/mojo/display_mode.typemap",
-  "//ui/display/mojo/display_snapshot_mojo.typemap",
+  "//ui/display/mojo/display_snapshot.typemap",
   "//ui/display/mojo/gamma_ramp_rgb_entry.typemap",
 ]
diff --git a/ui/display/types/BUILD.gn b/ui/display/types/BUILD.gn
index 9cef99b..b4053c4 100644
--- a/ui/display/types/BUILD.gn
+++ b/ui/display/types/BUILD.gn
@@ -10,8 +10,6 @@
     "display_mode.h",
     "display_snapshot.cc",
     "display_snapshot.h",
-    "display_snapshot_mojo.cc",
-    "display_snapshot_mojo.h",
     "display_types_export.h",
     "fake_display_controller.h",
     "gamma_ramp_rgb_entry.h",
diff --git a/ui/display/types/display_snapshot.cc b/ui/display/types/display_snapshot.cc
index e3ecd4b..8e59a16 100644
--- a/ui/display/types/display_snapshot.cc
+++ b/ui/display/types/display_snapshot.cc
@@ -4,7 +4,13 @@
 
 #include "ui/display/types/display_snapshot.h"
 
+#include <inttypes.h>
+
 #include <algorithm>
+#include <sstream>
+#include <utility>
+
+#include "base/strings/stringprintf.h"
 
 namespace display {
 
@@ -13,8 +19,44 @@
 // The display serial number beginning byte position and its length in the
 // EDID number as defined in the spec.
 // https://en.wikipedia.org/wiki/Extended_Display_Identification_Data
-const size_t kSerialNumberBeginingByte = 12U;
-const size_t kSerialNumberLengthInBytes = 4U;
+constexpr size_t kSerialNumberBeginingByte = 12U;
+constexpr size_t kSerialNumberLengthInBytes = 4U;
+
+std::string ModeListString(
+    const std::vector<std::unique_ptr<const DisplayMode>>& modes) {
+  std::stringstream stream;
+  bool first = true;
+  for (auto& mode : modes) {
+    if (!first)
+      stream << ", ";
+    stream << mode->ToString();
+    first = false;
+  }
+  return stream.str();
+}
+
+std::string DisplayConnectionTypeString(DisplayConnectionType type) {
+  switch (type) {
+    case DISPLAY_CONNECTION_TYPE_NONE:
+      return "none";
+    case DISPLAY_CONNECTION_TYPE_UNKNOWN:
+      return "unknown";
+    case DISPLAY_CONNECTION_TYPE_INTERNAL:
+      return "internal";
+    case DISPLAY_CONNECTION_TYPE_VGA:
+      return "vga";
+    case DISPLAY_CONNECTION_TYPE_HDMI:
+      return "hdmi";
+    case DISPLAY_CONNECTION_TYPE_DVI:
+      return "dvi";
+    case DISPLAY_CONNECTION_TYPE_DISPLAYPORT:
+      return "dp";
+    case DISPLAY_CONNECTION_TYPE_NETWORK:
+      return "network";
+  }
+  NOTREACHED();
+  return "";
+}
 
 }  // namespace
 
@@ -30,7 +72,9 @@
                                  DisplayModeList modes,
                                  const std::vector<uint8_t>& edid,
                                  const DisplayMode* current_mode,
-                                 const DisplayMode* native_mode)
+                                 const DisplayMode* native_mode,
+                                 int64_t product_id,
+                                 const gfx::Size& maximum_cursor_size)
     : display_id_(display_id),
       origin_(origin),
       physical_size_(physical_size),
@@ -44,7 +88,8 @@
       edid_(edid),
       current_mode_(current_mode),
       native_mode_(native_mode),
-      product_id_(kInvalidProductID) {
+      product_id_(product_id),
+      maximum_cursor_size_(maximum_cursor_size) {
   // We must explicitly clear out the bytes that represent the serial number.
   const size_t end =
       std::min(kSerialNumberBeginingByte + kSerialNumberLengthInBytes,
@@ -55,6 +100,42 @@
 
 DisplaySnapshot::~DisplaySnapshot() {}
 
+std::unique_ptr<DisplaySnapshot> DisplaySnapshot::Clone() {
+  DisplayModeList clone_modes;
+  const DisplayMode* cloned_current_mode = nullptr;
+  const DisplayMode* cloned_native_mode = nullptr;
+
+  // Clone the display modes and find equivalent pointers to the native and
+  // current mode.
+  for (auto& mode : modes_) {
+    clone_modes.push_back(mode->Clone());
+    if (mode.get() == current_mode_)
+      cloned_current_mode = clone_modes.back().get();
+    if (mode.get() == native_mode_)
+      cloned_native_mode = clone_modes.back().get();
+  }
+
+  return std::make_unique<DisplaySnapshot>(
+      display_id_, origin_, physical_size_, type_,
+      is_aspect_preserving_scaling_, has_overscan_,
+      has_color_correction_matrix_, display_name_, sys_path_,
+      std::move(clone_modes), edid_, cloned_current_mode, cloned_native_mode,
+      product_id_, maximum_cursor_size_);
+}
+
+std::string DisplaySnapshot::ToString() const {
+  return base::StringPrintf(
+      "id=%" PRId64
+      " current_mode=%s native_mode=%s origin=%s"
+      " physical_size=%s, type=%s name=\"%s\" modes=(%s)",
+      display_id_,
+      current_mode_ ? current_mode_->ToString().c_str() : "nullptr",
+      native_mode_ ? native_mode_->ToString().c_str() : "nullptr",
+      origin_.ToString().c_str(), physical_size_.ToString().c_str(),
+      DisplayConnectionTypeString(type_).c_str(), display_name_.c_str(),
+      ModeListString(modes_).c_str());
+}
+
 // static
 gfx::BufferFormat DisplaySnapshot::PrimaryFormat() {
   return gfx::BufferFormat::BGRX_8888;
diff --git a/ui/display/types/display_snapshot.h b/ui/display/types/display_snapshot.h
index 3bf0c83..db0ccbbd 100644
--- a/ui/display/types/display_snapshot.h
+++ b/ui/display/types/display_snapshot.h
@@ -7,6 +7,8 @@
 
 #include <stdint.h>
 
+#include <memory>
+#include <string>
 #include <vector>
 
 #include "base/files/file_path.h"
@@ -38,7 +40,9 @@
                   DisplayModeList modes,
                   const std::vector<uint8_t>& edid,
                   const DisplayMode* current_mode,
-                  const DisplayMode* native_mode);
+                  const DisplayMode* native_mode,
+                  int64_t product_id,
+                  const gfx::Size& maximum_cursor_size);
   virtual ~DisplaySnapshot();
 
   const gfx::Point& origin() const { return origin_; }
@@ -72,8 +76,11 @@
     return has_color_correction_matrix_;
   }
 
+  // Clones display state.
+  virtual std::unique_ptr<DisplaySnapshot> Clone();
+
   // Returns a textual representation of this display state.
-  virtual std::string ToString() const = 0;
+  virtual std::string ToString() const;
 
   // Used when no product id known.
   static const int64_t kInvalidProductID = -1;
diff --git a/ui/display/types/display_snapshot_mojo.cc b/ui/display/types/display_snapshot_mojo.cc
deleted file mode 100644
index 09482de3..0000000
--- a/ui/display/types/display_snapshot_mojo.cc
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ui/display/types/display_snapshot_mojo.h"
-
-#include "base/memory/ptr_util.h"
-#include "ui/display/types/display_constants.h"
-
-namespace display {
-
-// static
-std::unique_ptr<DisplaySnapshotMojo> DisplaySnapshotMojo::CreateFrom(
-    const DisplaySnapshot& other) {
-  DisplayModeList clone_modes;
-  const DisplayMode* current_mode = nullptr;
-  const DisplayMode* native_mode = nullptr;
-
-  // Clone the display modes and find equivalent pointers to the native and
-  // current mode.
-  for (auto& mode : other.modes()) {
-    clone_modes.push_back(mode->Clone());
-    if (mode.get() == other.current_mode())
-      current_mode = mode.get();
-    if (mode.get() == other.native_mode())
-      native_mode = mode.get();
-  }
-
-  return base::MakeUnique<DisplaySnapshotMojo>(
-      other.display_id(), other.origin(), other.physical_size(), other.type(),
-      other.is_aspect_preserving_scaling(), other.has_overscan(),
-      other.has_color_correction_matrix(), other.display_name(),
-      other.sys_path(), other.product_id(), std::move(clone_modes),
-      other.edid(), current_mode, native_mode, other.maximum_cursor_size());
-}
-
-DisplaySnapshotMojo::DisplaySnapshotMojo(int64_t display_id,
-                                         const gfx::Point& origin,
-                                         const gfx::Size& physical_size,
-                                         DisplayConnectionType type,
-                                         bool is_aspect_preserving_scaling,
-                                         bool has_overscan,
-                                         bool has_color_correction_matrix,
-                                         std::string display_name,
-                                         const base::FilePath& sys_path,
-                                         int64_t product_id,
-                                         DisplayModeList modes,
-                                         const std::vector<uint8_t>& edid,
-                                         const DisplayMode* current_mode,
-                                         const DisplayMode* native_mode,
-                                         const gfx::Size& maximum_cursor_size)
-    : DisplaySnapshot(display_id,
-                      origin,
-                      physical_size,
-                      type,
-                      is_aspect_preserving_scaling,
-                      has_overscan,
-                      has_color_correction_matrix,
-                      display_name,
-                      sys_path,
-                      std::move(modes),
-                      edid,
-                      current_mode,
-                      native_mode) {
-  product_id_ = product_id;
-  maximum_cursor_size_ = maximum_cursor_size;
-}
-
-DisplaySnapshotMojo::DisplaySnapshotMojo(int64_t display_id,
-                                         const gfx::Point& origin,
-                                         const gfx::Size& physical_size,
-                                         DisplayConnectionType type,
-                                         bool is_aspect_preserving_scaling,
-                                         bool has_overscan,
-                                         bool has_color_correction_matrix,
-                                         std::string display_name,
-                                         const base::FilePath& sys_path,
-                                         DisplayModeList modes,
-                                         const std::vector<uint8_t>& edid,
-                                         const DisplayMode* current_mode,
-                                         const DisplayMode* native_mode,
-                                         std::string string_representation)
-    : DisplaySnapshot(display_id,
-                      origin,
-                      physical_size,
-                      type,
-                      is_aspect_preserving_scaling,
-                      has_overscan,
-                      has_color_correction_matrix,
-                      display_name,
-                      sys_path,
-                      std::move(modes),
-                      edid,
-                      current_mode,
-                      native_mode),
-      string_representation_(string_representation) {}
-
-DisplaySnapshotMojo::~DisplaySnapshotMojo() = default;
-
-std::string DisplaySnapshotMojo::ToString() const {
-  return string_representation_;
-}
-
-}  // namespace display
diff --git a/ui/display/types/display_snapshot_mojo.h b/ui/display/types/display_snapshot_mojo.h
deleted file mode 100644
index d68f93b..0000000
--- a/ui/display/types/display_snapshot_mojo.h
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_DISPLAY_TYPES_DISPLAY_SNAPSHOT_MOJO_H_
-#define UI_DISPLAY_TYPES_DISPLAY_SNAPSHOT_MOJO_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "ui/display/types/display_snapshot.h"
-#include "ui/display/types/display_types_export.h"
-
-namespace display {
-
-// DisplaySnapshot implementation that can be used with Mojo IPC.
-class DISPLAY_TYPES_EXPORT DisplaySnapshotMojo : public DisplaySnapshot {
- public:
-  // Create a new DisplaySnapshotMojo by copying |other|.
-  static std::unique_ptr<DisplaySnapshotMojo> CreateFrom(
-      const DisplaySnapshot& other);
-
-  DisplaySnapshotMojo(int64_t display_id,
-                      const gfx::Point& origin,
-                      const gfx::Size& physical_size,
-                      DisplayConnectionType type,
-                      bool is_aspect_preserving_scaling,
-                      bool has_overscan,
-                      bool has_color_correction_matrix,
-                      std::string display_name,
-                      const base::FilePath& sys_path,
-                      int64_t product_id,
-                      DisplayModeList modes,
-                      const std::vector<uint8_t>& edid,
-                      const DisplayMode* current_mode,
-                      const DisplayMode* native_mode,
-                      const gfx::Size& maximum_cursor_size);
-
-  DisplaySnapshotMojo(int64_t display_id,
-                      const gfx::Point& origin,
-                      const gfx::Size& physical_size,
-                      DisplayConnectionType type,
-                      bool is_aspect_preserving_scaling,
-                      bool has_overscan,
-                      bool has_color_correction_matrix,
-                      std::string display_name,
-                      const base::FilePath& sys_path,
-                      DisplayModeList modes,
-                      const std::vector<uint8_t>& edid,
-                      const DisplayMode* current_mode,
-                      const DisplayMode* native_mode,
-                      std::string string_representation);
-
-  ~DisplaySnapshotMojo() override;
-
-  // display::DisplaySnapshot override:
-  std::string ToString() const override;
-
- private:
-  std::string string_representation_;
-
-  DISALLOW_COPY_AND_ASSIGN(DisplaySnapshotMojo);
-};
-
-}  // namespace display
-
-#endif  // UI_DISPLAY_TYPES_DISPLAY_SNAPSHOT_MOJO_H_
diff --git a/ui/events/event.cc b/ui/events/event.cc
index f3785df0..c487f8f 100644
--- a/ui/events/event.cc
+++ b/ui/events/event.cc
@@ -480,20 +480,21 @@
       root_location_(root_location) {}
 
 void LocatedEvent::UpdateForRootTransform(
-    const gfx::Transform& reversed_root_transform) {
-  // Transform has to be done at root level.
-  gfx::Point3F p(location_);
-  reversed_root_transform.TransformPoint(&p);
-  location_ = p.AsPointF();
+    const gfx::Transform& reversed_root_transform,
+    const gfx::Transform& reversed_local_transform) {
   if (target()) {
-    // If a target is already set, that means that |location_| and
-    // |root_location_| are in different coordinate space. So apply the
-    // transformation separately.
-    p = gfx::Point3F(root_location_);
-    reversed_root_transform.TransformPoint(&p);
-    root_location_ = p.AsPointF();
+    gfx::Point3F transformed_location(location_);
+    reversed_local_transform.TransformPoint(&transformed_location);
+    location_ = transformed_location.AsPointF();
+
+    gfx::Point3F transformed_root_location(root_location_);
+    reversed_root_transform.TransformPoint(&transformed_root_location);
+    root_location_ = transformed_root_location.AsPointF();
   } else {
-    root_location_ = location_;
+    // This mirrors what the code previously did.
+    gfx::Point3F transformed_location(location_);
+    reversed_root_transform.TransformPoint(&transformed_location);
+    root_location_ = location_ = transformed_location.AsPointF();
   }
 }
 
@@ -915,8 +916,10 @@
 }
 
 void TouchEvent::UpdateForRootTransform(
-    const gfx::Transform& inverted_root_transform) {
-  LocatedEvent::UpdateForRootTransform(inverted_root_transform);
+    const gfx::Transform& inverted_root_transform,
+    const gfx::Transform& inverted_local_transform) {
+  LocatedEvent::UpdateForRootTransform(inverted_root_transform,
+                                       inverted_local_transform);
   gfx::DecomposedTransform decomp;
   bool success = gfx::DecomposeTransform(&decomp, inverted_root_transform);
   DCHECK(success);
diff --git a/ui/events/event.h b/ui/events/event.h
index 71baf3b7..6b5ccbd 100644
--- a/ui/events/event.h
+++ b/ui/events/event.h
@@ -365,10 +365,12 @@
     return root_location_;
   }
 
-  // Transform the locations using |inverted_root_transform|.
-  // This is applied to both |location_| and |root_location_|.
+  // Transform the locations using |inverted_root_transform| and
+  // |inverted_local_transform|. |inverted_local_transform| is only used if
+  // the event has a target.
   virtual void UpdateForRootTransform(
-      const gfx::Transform& inverted_root_transform);
+      const gfx::Transform& inverted_root_transform,
+      const gfx::Transform& inverted_local_transform);
 
   template <class T> void ConvertLocationToTarget(T* source, T* target) {
     if (!target || target == source)
@@ -700,7 +702,8 @@
 
   // Overridden from LocatedEvent.
   void UpdateForRootTransform(
-      const gfx::Transform& inverted_root_transform) override;
+      const gfx::Transform& inverted_root_transform,
+      const gfx::Transform& inverted_local_transform) override;
 
   // Marks the event as not participating in synchronous gesture recognition.
   void DisableSynchronousHandling();
diff --git a/ui/events/event_unittest.cc b/ui/events/event_unittest.cc
index 566c8ca..d72bb32 100644
--- a/ui/events/event_unittest.cc
+++ b/ui/events/event_unittest.cc
@@ -1101,7 +1101,7 @@
 }
 
 TEST(EventTest, UpdateForRootTransformation) {
-  gfx::Transform empty_transform;
+  gfx::Transform identity_transform;
   const gfx::Point location(10, 10);
   const gfx::Point root_location(20, 20);
 
@@ -1110,20 +1110,37 @@
   // root_locations, they should be equal afterwards.
   ui::MouseEvent untargeted(ET_MOUSE_PRESSED, location, root_location,
                             EventTimeForNow(), 0, 0);
-  untargeted.UpdateForRootTransform(empty_transform);
-  EXPECT_EQ(untargeted.location(), location);
-  EXPECT_EQ(untargeted.root_location(), location);
+  untargeted.UpdateForRootTransform(identity_transform, identity_transform);
+  EXPECT_EQ(location, untargeted.location());
+  EXPECT_EQ(location, untargeted.root_location());
+
+  ui::test::TestEventTarget target;
 
   // A mouse event that is targeted should not set the root location to the
-  // normal location. They start with different locations and should stay
+  // local location. They start with different locations and should stay
   // unequal after a transform is applied.
-  ui::test::TestEventTarget target;
-  ui::MouseEvent targeted(ET_MOUSE_PRESSED, location, root_location,
-                          EventTimeForNow(), 0, 0);
-  Event::DispatcherApi(&targeted).set_target(&target);
-  targeted.UpdateForRootTransform(empty_transform);
-  EXPECT_EQ(targeted.location(), location);
-  EXPECT_EQ(targeted.root_location(), root_location);
+  {
+    ui::MouseEvent targeted(ET_MOUSE_PRESSED, location, root_location,
+                            EventTimeForNow(), 0, 0);
+    Event::DispatcherApi(&targeted).set_target(&target);
+    targeted.UpdateForRootTransform(identity_transform, identity_transform);
+    EXPECT_EQ(location, targeted.location());
+    EXPECT_EQ(root_location, targeted.root_location());
+  }
+
+  {
+    // Targeted event with 2x and 3x scales.
+    gfx::Transform transform2x;
+    transform2x.Scale(2, 2);
+    gfx::Transform transform3x;
+    transform3x.Scale(3, 3);
+    ui::MouseEvent targeted(ET_MOUSE_PRESSED, location, root_location,
+                            EventTimeForNow(), 0, 0);
+    Event::DispatcherApi(&targeted).set_target(&target);
+    targeted.UpdateForRootTransform(transform2x, transform3x);
+    EXPECT_EQ(gfx::Point(30, 30), targeted.location());
+    EXPECT_EQ(gfx::Point(40, 40), targeted.root_location());
+  }
 }
 
 }  // namespace ui
diff --git a/ui/gfx/color_transform.cc b/ui/gfx/color_transform.cc
index f3c3041..1892574 100644
--- a/ui/gfx/color_transform.cc
+++ b/ui/gfx/color_transform.cc
@@ -816,9 +816,10 @@
         // SMPTE 1886 suggests that we should be using gamma 2.4 for BT709
         // content. However, most displays actually use a gamma of 2.2, and
         // user studies shows that users don't really care. Using the same
-        // gamma as the display will let us optimize a lot more, so lets stick
-        // with using the SRGB transfer function.
-        src.transfer_ = ColorSpace::TransferID::IEC61966_2_1;
+        // gamma as the destination will let us optimize a lot more, so use it
+        // if we can.
+        if (dst.transfer_ == ColorSpace::TransferID::IEC61966_2_1)
+          src.transfer_ = ColorSpace::TransferID::IEC61966_2_1;
         break;
 
       case ColorSpace::TransferID::SMPTEST2084:
diff --git a/ui/gfx/color_transform_unittest.cc b/ui/gfx/color_transform_unittest.cc
index d5217a7..93ce073 100644
--- a/ui/gfx/color_transform_unittest.cc
+++ b/ui/gfx/color_transform_unittest.cc
@@ -133,6 +133,14 @@
       ColorTransform::NewColorTransform(
           bt709, gamma24, ColorTransform::Intent::INTENT_PERCEPTUAL));
   EXPECT_EQ(bt709_to_gamma24->NumberOfStepsForTesting(), 2u);
+
+  // Rec 601 YUV to RGB conversion should have a single step.
+  gfx::ColorSpace rec601 = gfx::ColorSpace::CreateREC601();
+  std::unique_ptr<ColorTransform> rec601_yuv_to_rgb(
+      ColorTransform::NewColorTransform(
+          rec601, rec601.GetAsFullRangeRGB(),
+          ColorTransform::Intent::INTENT_PERCEPTUAL));
+  EXPECT_EQ(rec601_yuv_to_rgb->NumberOfStepsForTesting(), 1u);
 }
 
 TEST(SimpleColorSpace, SRGBFromICCAndNotICC) {
@@ -440,9 +448,9 @@
           ->GetShaderSource();
   std::string expected =
       "float TransferFn1(float v) {\n"
-      "  if (v < 4.04499359e-02)\n"
-      "    return 7.73993805e-02 * v;\n"
-      "  return pow(9.47867334e-01 * v + 5.21326549e-02, 2.40000010e+00);\n"
+      "  if (v < 8.12428594e-02)\n"
+      "    return 2.22222224e-01 * v;\n"
+      "  return pow(9.09672439e-01 * v + 9.03275684e-02, 2.22222233e+00);\n"
       "}\n"
       "float TransferFn3(float v) {\n"
       "  return pow(v, 3.57142866e-01);\n"
diff --git a/ui/gl/gl_context.cc b/ui/gl/gl_context.cc
index fcc9b0c..44cf4133 100644
--- a/ui/gl/gl_context.cc
+++ b/ui/gl/gl_context.cc
@@ -146,7 +146,8 @@
   return std::string(renderer ? renderer : "");
 }
 
-YUVToRGBConverter* GLContext::GetYUVToRGBConverter() {
+YUVToRGBConverter* GLContext::GetYUVToRGBConverter(
+    const gfx::ColorSpace& color_space) {
   return nullptr;
 }
 
diff --git a/ui/gl/gl_context.h b/ui/gl/gl_context.h
index a2ca7ef..13b86df 100644
--- a/ui/gl/gl_context.h
+++ b/ui/gl/gl_context.h
@@ -20,6 +20,10 @@
 #include "ui/gl/gl_workarounds.h"
 #include "ui/gl/gpu_preference.h"
 
+namespace gfx {
+class ColorSpace;
+}  // namespace gfx
+
 namespace gl {
 class YUVToRGBConverter;
 }  // namespace gl
@@ -183,8 +187,10 @@
   // Returns the GL renderer string. The context must be current.
   virtual std::string GetGLRenderer();
 
-  // Returns a helper structure to convert YUV textures to RGB textures.
-  virtual YUVToRGBConverter* GetYUVToRGBConverter();
+  // Returns a helper structure to convert the YUV color space |color_space|
+  // to its associated full-range RGB color space.
+  virtual YUVToRGBConverter* GetYUVToRGBConverter(
+      const gfx::ColorSpace& color_space);
 
   // Get the CurrentGL object for this context containing the driver, version
   // and API.
diff --git a/ui/gl/gl_context_cgl.cc b/ui/gl/gl_context_cgl.cc
index af55dc20..dab9d27 100644
--- a/ui/gl/gl_context_cgl.cc
+++ b/ui/gl/gl_context_cgl.cc
@@ -148,7 +148,7 @@
 }
 
 void GLContextCGL::Destroy() {
-  if (yuv_to_rgb_converter_) {
+  if (!yuv_to_rgb_converters_.empty()) {
     // If this context is not current, bind this context's API so that the YUV
     // converter can safely destruct
     GLContext* current_context = GetRealCurrent();
@@ -157,7 +157,7 @@
     }
 
     ScopedCGLSetCurrentContext(static_cast<CGLContextObj>(context_));
-    yuv_to_rgb_converter_.reset();
+    yuv_to_rgb_converters_.clear();
 
     // Rebind the current context's API if needed.
     if (current_context && current_context != this) {
@@ -223,10 +223,14 @@
   return true;
 }
 
-YUVToRGBConverter* GLContextCGL::GetYUVToRGBConverter() {
-  if (!yuv_to_rgb_converter_)
-    yuv_to_rgb_converter_.reset(new YUVToRGBConverter(*GetVersionInfo()));
-  return yuv_to_rgb_converter_.get();
+YUVToRGBConverter* GLContextCGL::GetYUVToRGBConverter(
+    const gfx::ColorSpace& color_space) {
+  std::unique_ptr<YUVToRGBConverter>& yuv_to_rgb_converter =
+      yuv_to_rgb_converters_[color_space];
+  if (!yuv_to_rgb_converter)
+    yuv_to_rgb_converter.reset(
+        new YUVToRGBConverter(*GetVersionInfo(), color_space));
+  return yuv_to_rgb_converter.get();
 }
 
 bool GLContextCGL::MakeCurrent(GLSurface* surface) {
diff --git a/ui/gl/gl_context_cgl.h b/ui/gl/gl_context_cgl.h
index 72fbb4d..737f326 100644
--- a/ui/gl/gl_context_cgl.h
+++ b/ui/gl/gl_context_cgl.h
@@ -7,9 +7,11 @@
 
 #include <OpenGL/CGLTypes.h>
 
+#include <map>
 #include <memory>
 
 #include "base/macros.h"
+#include "ui/gfx/color_space.h"
 #include "ui/gl/gl_context.h"
 #include "ui/gl/gl_export.h"
 
@@ -32,7 +34,8 @@
   void OnSetSwapInterval(int interval) override;
   void SetSafeToForceGpuSwitch() override;
   bool ForceGpuSwitchIfNeeded() override;
-  YUVToRGBConverter* GetYUVToRGBConverter() override;
+  YUVToRGBConverter* GetYUVToRGBConverter(
+      const gfx::ColorSpace& color_space) override;
 
  protected:
   ~GLContextCGL() override;
@@ -43,7 +46,8 @@
 
   void* context_;
   GpuPreference gpu_preference_;
-  std::unique_ptr<YUVToRGBConverter> yuv_to_rgb_converter_;
+  std::map<gfx::ColorSpace, std::unique_ptr<YUVToRGBConverter>>
+      yuv_to_rgb_converters_;
 
   CGLPixelFormatObj discrete_pixelformat_;
 
diff --git a/ui/gl/gl_image_io_surface.h b/ui/gl/gl_image_io_surface.h
index 9cffcec..bf5f069 100644
--- a/ui/gl/gl_image_io_surface.h
+++ b/ui/gl/gl_image_io_surface.h
@@ -71,6 +71,11 @@
   // signal about whether the Window Server is still using the IOSurface.
   bool CanCheckIOSurfaceIsInUse() const;
 
+  // For IOSurfaces that need manual conversion to a GL texture before being
+  // sampled from, specify the color space in which to do the required YUV to
+  // RGB transformation.
+  void SetColorSpaceForYUVToRGBConversion(const gfx::ColorSpace& color_space);
+
   static unsigned GetInternalFormatForTesting(gfx::BufferFormat format);
 
   // Downcasts from |image|. Returns |nullptr| on failure.
@@ -97,6 +102,8 @@
   base::ScopedCFTypeRef<CVPixelBufferRef> cv_pixel_buffer_;
   gfx::GenericSharedMemoryId io_surface_id_;
   base::ThreadChecker thread_checker_;
+  // The default value of Rec. 601 is based on historical shader code.
+  gfx::ColorSpace color_space_for_yuv_to_rgb_ = gfx::ColorSpace::CreateREC601();
 
   DISALLOW_COPY_AND_ASSIGN(GLImageIOSurface);
 };
diff --git a/ui/gl/gl_image_io_surface.mm b/ui/gl/gl_image_io_surface.mm
index c9fabee..1dc5a6d 100644
--- a/ui/gl/gl_image_io_surface.mm
+++ b/ui/gl/gl_image_io_surface.mm
@@ -14,6 +14,7 @@
 #include "base/trace_event/memory_dump_manager.h"
 #include "base/trace_event/process_memory_dump.h"
 #include "base/trace_event/trace_event.h"
+#include "ui/gfx/color_space.h"
 #include "ui/gl/gl_bindings.h"
 #include "ui/gl/gl_context.h"
 #include "ui/gl/scoped_binders.h"
@@ -298,7 +299,8 @@
   GLContext* gl_context = GLContext::GetCurrent();
   DCHECK(gl_context);
 
-  YUVToRGBConverter* yuv_to_rgb_converter = gl_context->GetYUVToRGBConverter();
+  YUVToRGBConverter* yuv_to_rgb_converter =
+      gl_context->GetYUVToRGBConverter(color_space_for_yuv_to_rgb_);
   DCHECK(yuv_to_rgb_converter);
 
   // Note that state restoration is done explicitly instead of scoped binders to
@@ -405,6 +407,13 @@
   return !cv_pixel_buffer_;
 }
 
+void GLImageIOSurface::SetColorSpaceForYUVToRGBConversion(
+    const gfx::ColorSpace& color_space) {
+  DCHECK(color_space.IsValid());
+  DCHECK_NE(color_space, color_space.GetAsFullRangeRGB());
+  color_space_for_yuv_to_rgb_ = color_space;
+}
+
 base::ScopedCFTypeRef<IOSurfaceRef> GLImageIOSurface::io_surface() {
   return io_surface_;
 }
diff --git a/ui/gl/gl_image_io_surface_unittest.cc b/ui/gl/gl_image_io_surface_unittest.cc
index 31dd78b..a55e63f1 100644
--- a/ui/gl/gl_image_io_surface_unittest.cc
+++ b/ui/gl/gl_image_io_surface_unittest.cc
@@ -14,10 +14,10 @@
 namespace gl {
 namespace {
 
-// These values are picked so that RGB -> YUV on the CPU converted
-// back to RGB on the GPU produces the original RGB values without
-// any error.
-const uint8_t kYuvImageColor[] = {0x10, 0x20, 0, 0xFF};
+// These values are picked so that RGB -> YUV on the CPU converted back to RGB
+// on the GPU produces the original RGB values without any error. Note that
+// some values will be off-by-one.
+const uint8_t kYuvImageColor[] = {0x30, 0x40, 0x10, 0xFF};
 
 template <gfx::BufferFormat format>
 class GLImageIOSurfaceTestDelegate {
diff --git a/ui/gl/yuv_to_rgb_converter.cc b/ui/gl/yuv_to_rgb_converter.cc
index daef399..73410cb 100644
--- a/ui/gl/yuv_to_rgb_converter.cc
+++ b/ui/gl/yuv_to_rgb_converter.cc
@@ -6,6 +6,7 @@
 
 #include "base/strings/stringize_macros.h"
 #include "base/strings/stringprintf.h"
+#include "ui/gfx/color_transform.h"
 #include "ui/gl/gl_helper.h"
 #include "ui/gl/gl_version_info.h"
 #include "ui/gl/scoped_binders.h"
@@ -55,21 +56,25 @@
   uniform sampler2DRect a_uv_texture;
   VARYING vec2 v_texCoord;
   void main() {
-    vec3 yuv_adj = vec3(-0.0625, -0.5, -0.5);
-    mat3 yuv_matrix = mat3(vec3(1.164, 1.164, 1.164),
-                           vec3(0.0, -.391, 2.018),
-                           vec3(1.596, -.813, 0.0));
     vec3 yuv = vec3(
         TEX(a_y_texture, v_texCoord).r,
         TEX(a_uv_texture, v_texCoord * 0.5).rg);
-    FRAGCOLOR = vec4(yuv_matrix * (yuv + yuv_adj), 1.0);
+    FRAGCOLOR = vec4(DoColorConversion(yuv), 1.0);
   }
 );
 // clang-format on
 
 }  // namespace
 
-YUVToRGBConverter::YUVToRGBConverter(const GLVersionInfo& gl_version_info) {
+YUVToRGBConverter::YUVToRGBConverter(const GLVersionInfo& gl_version_info,
+                                     const gfx::ColorSpace color_space) {
+  std::unique_ptr<gfx::ColorTransform> color_transform =
+      gfx::ColorTransform::NewColorTransform(
+          color_space, color_space.GetAsFullRangeRGB(),
+          gfx::ColorTransform::Intent::INTENT_PERCEPTUAL);
+  DCHECK(color_transform->CanGetShaderSource());
+  std::string do_color_conversion = color_transform->GetShaderSource();
+
   bool use_core_profile = gl_version_info.is_desktop_core_profile;
   glGenFramebuffersEXT(1, &framebuffer_);
   vertex_buffer_ = GLHelper::SetupQuadVertexBuffer();
@@ -82,10 +87,10 @@
           .c_str());
   fragment_shader_ = GLHelper::LoadShader(
       GL_FRAGMENT_SHADER,
-      base::StringPrintf("%s\n%s",
+      base::StringPrintf("%s\n%s\n%s",
                          use_core_profile ? kFragmentHeaderCoreProfile
                                           : kFragmentHeaderCompatiblityProfile,
-                         kFragmentShader)
+                         do_color_conversion.c_str(), kFragmentShader)
           .c_str());
   program_ = GLHelper::SetupProgram(vertex_shader_, fragment_shader_);
 
diff --git a/ui/gl/yuv_to_rgb_converter.h b/ui/gl/yuv_to_rgb_converter.h
index 7d9f8222..94fbf6a 100644
--- a/ui/gl/yuv_to_rgb_converter.h
+++ b/ui/gl/yuv_to_rgb_converter.h
@@ -7,13 +7,18 @@
 
 #include "ui/gfx/geometry/size.h"
 
+namespace gfx {
+class ColorSpace;
+}  // namespace gfx
+
 namespace gl {
 
 struct GLVersionInfo;
 
 class YUVToRGBConverter {
  public:
-  explicit YUVToRGBConverter(const GLVersionInfo& gl_version_info);
+  explicit YUVToRGBConverter(const GLVersionInfo& gl_version_info,
+                             const gfx::ColorSpace color_space);
   ~YUVToRGBConverter();
 
   // The input Y and UV textures should be bound to these texture objects
diff --git a/ui/ozone/common/display_snapshot_proxy.cc b/ui/ozone/common/display_snapshot_proxy.cc
index 88ef6cc..985495e 100644
--- a/ui/ozone/common/display_snapshot_proxy.cc
+++ b/ui/ozone/common/display_snapshot_proxy.cc
@@ -22,7 +22,7 @@
 }  // namespace
 
 DisplaySnapshotProxy::DisplaySnapshotProxy(const DisplaySnapshot_Params& params)
-    : DisplaySnapshotMojo(
+    : DisplaySnapshot(
           params.display_id,
           params.origin,
           params.physical_size,
@@ -36,7 +36,8 @@
           params.edid,
           nullptr,
           nullptr,
-          params.string_representation) {
+          params.product_id,
+          params.maximum_cursor_size) {
   for (size_t i = 0; i < params.modes.size(); ++i) {
     modes_.push_back(base::MakeUnique<display::DisplayMode>(
         params.modes[i].size, params.modes[i].is_interlaced,
@@ -50,9 +51,6 @@
         SameModes(params.modes[i], params.native_mode))
       native_mode_ = modes_.back().get();
   }
-
-  product_id_ = params.product_id;
-  maximum_cursor_size_ = params.maximum_cursor_size;
 }
 
 DisplaySnapshotProxy::~DisplaySnapshotProxy() {
diff --git a/ui/ozone/common/display_snapshot_proxy.h b/ui/ozone/common/display_snapshot_proxy.h
index a53e2121..ccf4664 100644
--- a/ui/ozone/common/display_snapshot_proxy.h
+++ b/ui/ozone/common/display_snapshot_proxy.h
@@ -6,13 +6,13 @@
 #define UI_OZONE_COMMON_DISPLAY_SNAPSHOT_PROXY_H_
 
 #include "base/macros.h"
-#include "ui/display/types/display_snapshot_mojo.h"
+#include "ui/display/types/display_snapshot.h"
 
 namespace ui {
 
 struct DisplaySnapshot_Params;
 
-class DisplaySnapshotProxy : public display::DisplaySnapshotMojo {
+class DisplaySnapshotProxy : public display::DisplaySnapshot {
  public:
   DisplaySnapshotProxy(const DisplaySnapshot_Params& params);
   ~DisplaySnapshotProxy() override;
diff --git a/ui/ozone/common/gpu/ozone_gpu_message_params.h b/ui/ozone/common/gpu/ozone_gpu_message_params.h
index 9c1affac..20405185 100644
--- a/ui/ozone/common/gpu/ozone_gpu_message_params.h
+++ b/ui/ozone/common/gpu/ozone_gpu_message_params.h
@@ -52,7 +52,6 @@
   bool has_native_mode = false;
   DisplayMode_Params native_mode;
   int64_t product_id = 0;
-  std::string string_representation;
   gfx::Size maximum_cursor_size;
 };
 
diff --git a/ui/ozone/common/gpu/ozone_gpu_messages.h b/ui/ozone/common/gpu/ozone_gpu_messages.h
index c20cc08..b97616c 100644
--- a/ui/ozone/common/gpu/ozone_gpu_messages.h
+++ b/ui/ozone/common/gpu/ozone_gpu_messages.h
@@ -60,7 +60,6 @@
   IPC_STRUCT_TRAITS_MEMBER(has_native_mode)
   IPC_STRUCT_TRAITS_MEMBER(native_mode)
   IPC_STRUCT_TRAITS_MEMBER(product_id)
-  IPC_STRUCT_TRAITS_MEMBER(string_representation)
   IPC_STRUCT_TRAITS_MEMBER(maximum_cursor_size)
 IPC_STRUCT_TRAITS_END()
 
diff --git a/ui/ozone/platform/drm/common/display_types.h b/ui/ozone/platform/drm/common/display_types.h
index 5d0b1ac..da8b5c0 100644
--- a/ui/ozone/platform/drm/common/display_types.h
+++ b/ui/ozone/platform/drm/common/display_types.h
@@ -6,13 +6,13 @@
 #define UI_OZONE_PLATFORM_DRM_COMMON_DISPLAY_TYPES_H_
 
 namespace display {
-class DisplaySnapshotMojo;
+class DisplaySnapshot;
 }  // namespace display
 
 namespace ui {
 
 using MovableDisplaySnapshots =
-    std::vector<std::unique_ptr<display::DisplaySnapshotMojo>>;
+    std::vector<std::unique_ptr<display::DisplaySnapshot>>;
 
 }  // namespace ui
 
diff --git a/ui/ozone/platform/drm/common/drm_util.cc b/ui/ozone/platform/drm/common/drm_util.cc
index 6e9ed609..1701f59 100644
--- a/ui/ozone/platform/drm/common/drm_util.cc
+++ b/ui/ozone/platform/drm/common/drm_util.cc
@@ -16,7 +16,7 @@
 #include "base/containers/flat_map.h"
 #include "base/memory/ptr_util.h"
 #include "ui/display/types/display_mode.h"
-#include "ui/display/types/display_snapshot_mojo.h"
+#include "ui/display/types/display_snapshot.h"
 #include "ui/display/util/edid_parser.h"
 #include "ui/ozone/common/display_snapshot_proxy.h"
 
@@ -427,7 +427,6 @@
     }
 
     p.product_id = d->product_id();
-    p.string_representation = d->ToString();
     p.maximum_cursor_size = d->maximum_cursor_size();
 
     params.push_back(p);
diff --git a/ui/ozone/platform/drm/common/drm_util_unittest.cc b/ui/ozone/platform/drm/common/drm_util_unittest.cc
index 61bbce4..c86453d 100644
--- a/ui/ozone/platform/drm/common/drm_util_unittest.cc
+++ b/ui/ozone/platform/drm/common/drm_util_unittest.cc
@@ -7,7 +7,7 @@
 #include <map>
 
 #include "testing/gtest/include/gtest/gtest.h"
-#include "ui/display/types/display_snapshot_mojo.h"
+#include "ui/display/types/display_snapshot.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/ozone/common/gpu/ozone_gpu_message_params.h"
 
@@ -33,7 +33,6 @@
          a.current_mode == b.current_mode &&
          a.has_native_mode == b.has_native_mode &&
          a.native_mode == b.native_mode && a.product_id == b.product_id &&
-         a.string_representation == b.string_representation &&
          a.maximum_cursor_size == b.maximum_cursor_size;
 }
 
@@ -65,7 +64,6 @@
   EXPECT_EQ(a.has_native_mode, b.has_native_mode);
   EXPECT_EQ(a.native_mode, b.native_mode);
   EXPECT_EQ(a.product_id, b.product_id);
-  EXPECT_EQ(a.string_representation, b.string_representation);
   EXPECT_EQ(a.maximum_cursor_size, b.maximum_cursor_size);
 }
 
@@ -106,7 +104,6 @@
   fp.has_native_mode = true;
   fp.native_mode = MakeDisplay(1.1);
   fp.product_id = 7;
-  fp.string_representation = "bending glass display";
   fp.maximum_cursor_size = gfx::Size(103, 44);
 
   sp.display_id = 1002;
@@ -125,7 +122,6 @@
   sp.has_native_mode = true;
   sp.native_mode = MakeDisplay(500.2);
   sp.product_id = 8;
-  sp.string_representation = "rigid glass display";
   sp.maximum_cursor_size = gfx::Size(500, 44);
 
   ep.display_id = 2002;
@@ -144,7 +140,6 @@
   ep.current_mode = MakeDisplay(1000.2);
   ep.has_native_mode = false;
   ep.product_id = 9;
-  ep.string_representation = "fluted glass display";
   ep.maximum_cursor_size = gfx::Size(1000, 44);
 
   orig_params.push_back(fp);
diff --git a/ui/ozone/platform/drm/gpu/drm_thread.cc b/ui/ozone/platform/drm/gpu/drm_thread.cc
index 7176583..a8f8a211 100644
--- a/ui/ozone/platform/drm/gpu/drm_thread.cc
+++ b/ui/ozone/platform/drm/gpu/drm_thread.cc
@@ -14,7 +14,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/trace_event/trace_event.h"
 #include "ui/display/types/display_mode.h"
-#include "ui/display/types/display_snapshot_mojo.h"
+#include "ui/display/types/display_snapshot.h"
 #include "ui/ozone/common/display_snapshot_proxy.h"
 #include "ui/ozone/platform/drm/common/drm_util.h"
 #include "ui/ozone/platform/drm/gpu/drm_buffer.h"
diff --git a/ui/ozone/platform/drm/gpu/drm_thread_message_proxy.cc b/ui/ozone/platform/drm/gpu/drm_thread_message_proxy.cc
index e1f60514f..fb46660 100644
--- a/ui/ozone/platform/drm/gpu/drm_thread_message_proxy.cc
+++ b/ui/ozone/platform/drm/gpu/drm_thread_message_proxy.cc
@@ -8,7 +8,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "ipc/ipc_message.h"
 #include "ipc/ipc_sender.h"
-#include "ui/display/types/display_snapshot_mojo.h"
+#include "ui/display/types/display_snapshot.h"
 #include "ui/ozone/common/gpu/ozone_gpu_messages.h"
 #include "ui/ozone/platform/drm/common/drm_util.h"
 #include "ui/ozone/platform/drm/gpu/drm_thread_proxy.h"
diff --git a/ui/ozone/platform/drm/mus_thread_proxy.cc b/ui/ozone/platform/drm/mus_thread_proxy.cc
index 8389ac2d..95fb8621 100644
--- a/ui/ozone/platform/drm/mus_thread_proxy.cc
+++ b/ui/ozone/platform/drm/mus_thread_proxy.cc
@@ -10,7 +10,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "services/ui/public/interfaces/constants.mojom.h"
-#include "ui/display/types/display_snapshot_mojo.h"
+#include "ui/display/types/display_snapshot.h"
 #include "ui/ozone/platform/drm/common/drm_util.h"
 #include "ui/ozone/platform/drm/cursor_proxy_mojo.h"
 #include "ui/ozone/platform/drm/gpu/drm_thread.h"
@@ -308,7 +308,7 @@
 
 // TODO(rjkroege): Remove the unnecessary conversion back into params.
 void MusThreadProxy::GpuRefreshNativeDisplaysCallback(
-    std::vector<std::unique_ptr<display::DisplaySnapshotMojo>> displays) const {
+    MovableDisplaySnapshots displays) const {
   DCHECK(on_window_server_thread_.CalledOnValidThread());
   display_manager_->GpuHasUpdatedNativeDisplays(
       CreateParamsFromSnapshot(displays));
diff --git a/ui/ozone/platform/drm/mus_thread_proxy.h b/ui/ozone/platform/drm/mus_thread_proxy.h
index b967a68..680eb99 100644
--- a/ui/ozone/platform/drm/mus_thread_proxy.h
+++ b/ui/ozone/platform/drm/mus_thread_proxy.h
@@ -11,6 +11,7 @@
 #include "base/observer_list.h"
 #include "base/synchronization/lock.h"
 #include "ui/gfx/native_widget_types.h"
+#include "ui/ozone/platform/drm/common/display_types.h"
 #include "ui/ozone/platform/drm/gpu/inter_thread_messaging_proxy.h"
 #include "ui/ozone/platform/drm/host/drm_cursor.h"
 #include "ui/ozone/platform/drm/host/gpu_thread_adapter.h"
@@ -21,10 +22,6 @@
 class SingleThreadTaskRunner;
 }
 
-namespace display {
-class DisplaySnapshotMojo;
-}
-
 namespace service_manager {
 class Connector;
 }
@@ -115,9 +112,7 @@
   void GpuConfigureNativeDisplayCallback(int64_t display_id,
                                          bool success) const;
 
-  void GpuRefreshNativeDisplaysCallback(
-      std::vector<std::unique_ptr<display::DisplaySnapshotMojo>> displays)
-      const;
+  void GpuRefreshNativeDisplaysCallback(MovableDisplaySnapshots displays) const;
   void GpuDisableNativeDisplayCallback(int64_t display_id, bool success) const;
   void GpuTakeDisplayControlCallback(bool success) const;
   void GpuRelinquishDisplayControlCallback(bool success) const;
diff --git a/ui/ozone/public/interfaces/gpu_adapter.mojom b/ui/ozone/public/interfaces/gpu_adapter.mojom
index 9d65a2b..21f0871e 100644
--- a/ui/ozone/public/interfaces/gpu_adapter.mojom
+++ b/ui/ozone/public/interfaces/gpu_adapter.mojom
@@ -8,7 +8,7 @@
 import "mojo/common/file_path.mojom";
 import "ui/display/mojo/display_constants.mojom";
 import "ui/display/mojo/display_mode.mojom";
-import "ui/display/mojo/display_snapshot_mojo.mojom";
+import "ui/display/mojo/display_snapshot.mojom";
 import "ui/display/mojo/gamma_ramp_rgb_entry.mojom";
 import "ui/gfx/geometry/mojo/geometry.mojom";
 import "ui/gfx/mojo/accelerated_widget.mojom";
@@ -41,7 +41,7 @@
 
   // Requests a callback providing a list of the available displays.
   RefreshNativeDisplays() =>
-      (array<display.mojom.DisplaySnapshotMojo> display_snapshots);
+      (array<display.mojom.DisplaySnapshot> display_snapshots);
 
   // Transfers ownership of a DRM device to the GPU process.
   AddGraphicsDevice(mojo.common.mojom.FilePath path,
diff --git a/ui/views/controls/scroll_view.cc b/ui/views/controls/scroll_view.cc
index 61f853fe..95f4acc 100644
--- a/ui/views/controls/scroll_view.cc
+++ b/ui/views/controls/scroll_view.cc
@@ -763,7 +763,7 @@
     // but will only be invoked (asynchronously) when a Compositor is present
     // and commits a frame, which isn't true in some tests.
     // See http://crbug.com/637521.
-    OnLayerScrolled(offset);
+    OnLayerScrolled(offset, contents_->layer()->element_id());
   } else {
     contents_->SetPosition(gfx::Point(-offset.x(), -offset.y()));
     ScrollHeader();
@@ -787,7 +787,8 @@
   UpdateBackground();
 }
 
-void ScrollView::OnLayerScrolled(const gfx::ScrollOffset&) {
+void ScrollView::OnLayerScrolled(const gfx::ScrollOffset&,
+                                 const cc::ElementId&) {
   UpdateScrollBarPositions();
   ScrollHeader();
 }
diff --git a/ui/views/controls/scroll_view.h b/ui/views/controls/scroll_view.h
index a39fbbd..dfb8f1e 100644
--- a/ui/views/controls/scroll_view.h
+++ b/ui/views/controls/scroll_view.h
@@ -172,7 +172,7 @@
   bool ScrollsWithLayers() const;
 
   // Callback entrypoint when hosted Layers are scrolled by the Compositor.
-  void OnLayerScrolled(const gfx::ScrollOffset& offset);
+  void OnLayerScrolled(const gfx::ScrollOffset&, const cc::ElementId&);
 
   // Horizontally scrolls the header (if any) to match the contents.
   void ScrollHeader();