diff --git a/DEPS b/DEPS
index 28f794e..f4fda8c 100644
--- a/DEPS
+++ b/DEPS
@@ -44,7 +44,7 @@
   # 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': '90043159789d2ea05da752676ce67b8c490d67e5',
+  'v8_revision': '05d62e61b5fe5462bb00832bff94a257f94c4506',
   # 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': '12c74ac6ee2da69cd6def0580b920ab4a529f713',
+  'pdfium_revision': '445831ebcec3426f5642d32336d654fb391ca07a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling openmax_dl
   # and whatever else without interference from each other.
@@ -96,7 +96,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '6e6baa037e67c164d489a5c6c97e07efa0d9e66f',
+  'catapult_revision': 'f57871dac2cd1736f1fba0d8370a623bd51b4039',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
diff --git a/android_webview/java/src/org/chromium/android_webview/AwAutofillClient.java b/android_webview/java/src/org/chromium/android_webview/AwAutofillClient.java
index 9cb710c..31482648 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwAutofillClient.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwAutofillClient.java
@@ -67,6 +67,9 @@
                 }
                 @Override
                 public void deleteSuggestion(int listIndex) {}
+
+                @Override
+                public void accessibilityFocusCleared() {}
             });
         }
         mAutofillPopup.filterAndShow(suggestions, isRtl, Color.TRANSPARENT /* backgroundColor */,
diff --git a/ash/accelerators/accelerator_controller_unittest.cc b/ash/accelerators/accelerator_controller_unittest.cc
index 8a7cb189..32d2df9d 100644
--- a/ash/accelerators/accelerator_controller_unittest.cc
+++ b/ash/accelerators/accelerator_controller_unittest.cc
@@ -9,6 +9,7 @@
 #include "ash/accessibility_types.h"
 #include "ash/ash_switches.h"
 #include "ash/ime_control_delegate.h"
+#include "ash/public/cpp/config.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/session/session_controller.h"
 #include "ash/shell.h"
@@ -523,7 +524,7 @@
 
 TEST_F(AcceleratorControllerTest, RotateScreen) {
   // TODO: needs GetDisplayInfo http://crbug.com/622480.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   display::Display display = display::Screen::GetScreen()->GetPrimaryDisplay();
@@ -663,7 +664,7 @@
 
 TEST_F(AcceleratorControllerTest, GlobalAccelerators) {
   // TODO: TestScreenshotDelegate is null in mash http://crbug.com/632111.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   // CycleBackward
@@ -1041,7 +1042,7 @@
 
 TEST_F(PreferredReservedAcceleratorsTest, AcceleratorsWithFullscreen) {
   // TODO: needs LockStateController ported: http://crbug.com/632189.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   aura::Window* w1 = CreateTestWindowInShellWithId(0);
@@ -1092,7 +1093,7 @@
 
 TEST_F(PreferredReservedAcceleratorsTest, AcceleratorsWithPinned) {
   // TODO: needs LockStateController ported: http://crbug.com/632189.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
   aura::Window* w1 = CreateTestWindowInShellWithId(0);
   aura::Window* w2 = CreateTestWindowInShellWithId(1);
@@ -1124,7 +1125,7 @@
 
 TEST_F(AcceleratorControllerTest, DisallowedAtModalWindow) {
   // TODO: TestScreenshotDelegate is null in mash http://crbug.com/632111.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   std::set<AcceleratorAction> all_actions;
@@ -1315,7 +1316,7 @@
 TEST_F(DeprecatedAcceleratorTester, TestDeprecatedAcceleratorsBehavior) {
   // TODO: disabled because of UnblockUserSession() not working:
   // http://crbug.com/632201.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
   for (size_t i = 0; i < kDeprecatedAcceleratorsLength; ++i) {
     const AcceleratorData& entry = kDeprecatedAccelerators[i];
diff --git a/ash/accelerators/accelerator_filter_unittest.cc b/ash/accelerators/accelerator_filter_unittest.cc
index c808576..fe8b2d6324 100644
--- a/ash/accelerators/accelerator_filter_unittest.cc
+++ b/ash/accelerators/accelerator_filter_unittest.cc
@@ -8,10 +8,10 @@
 
 #include "ash/accelerators/accelerator_controller.h"
 #include "ash/accelerators/accelerator_delegate.h"
+#include "ash/public/cpp/config.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/session/session_controller.h"
 #include "ash/shell.h"
-#include "ash/shell_port.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/test/ash_test_helper.h"
 #include "ash/test/test_screenshot_delegate.h"
@@ -36,7 +36,7 @@
 // Tests if AcceleratorFilter works without a focused window.
 TEST_F(AcceleratorFilterTest, TestFilterWithoutFocus) {
   // TODO: mash doesn't support ScreenshotDelgate yet. http://crbug.com/632111.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   const TestScreenshotDelegate* delegate = GetScreenshotDelegate();
@@ -54,7 +54,7 @@
 // Tests if AcceleratorFilter works as expected with a focused window.
 TEST_F(AcceleratorFilterTest, TestFilterWithFocus) {
   // TODO: mash doesn't support ScreenshotDelgate yet. http://crbug.com/632111.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   aura::test::TestWindowDelegate test_delegate;
@@ -80,7 +80,7 @@
 // Tests if AcceleratorFilter ignores the flag for Caps Lock.
 TEST_F(AcceleratorFilterTest, TestCapsLockMask) {
   // TODO: mash doesn't support ScreenshotDelgate yet. http://crbug.com/632111.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   const TestScreenshotDelegate* delegate = GetScreenshotDelegate();
diff --git a/ash/aura/shell_port_classic.cc b/ash/aura/shell_port_classic.cc
index 5da492d..9d87e86 100644
--- a/ash/aura/shell_port_classic.cc
+++ b/ash/aura/shell_port_classic.cc
@@ -54,7 +54,7 @@
 
 // static
 ShellPortClassic* ShellPortClassic::Get() {
-  CHECK(!ShellPort::Get()->IsRunningInMash());
+  CHECK(Shell::GetAshConfig() == Config::CLASSIC);
   return static_cast<ShellPortClassic*>(ShellPort::Get());
 }
 
@@ -69,10 +69,6 @@
   Shell::Get()->window_tree_host_manager()->Shutdown();
 }
 
-bool ShellPortClassic::IsRunningInMash() const {
-  return false;
-}
-
 Config ShellPortClassic::GetAshConfig() const {
   return Config::CLASSIC;
 }
diff --git a/ash/aura/shell_port_classic.h b/ash/aura/shell_port_classic.h
index a45ca52..b5c7704 100644
--- a/ash/aura/shell_port_classic.h
+++ b/ash/aura/shell_port_classic.h
@@ -34,7 +34,6 @@
 
   // ShellPort:
   void Shutdown() override;
-  bool IsRunningInMash() const override;
   Config GetAshConfig() const override;
   WmWindow* GetPrimaryRootWindow() override;
   WmWindow* GetRootWindowForDisplayId(int64_t display_id) override;
diff --git a/ash/autoclick/autoclick_unittest.cc b/ash/autoclick/autoclick_unittest.cc
index 6c975fb..2bd15b10 100644
--- a/ash/autoclick/autoclick_unittest.cc
+++ b/ash/autoclick/autoclick_unittest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "ash/autoclick/autoclick_controller.h"
+#include "ash/public/cpp/config.h"
 #include "ash/shell.h"
 #include "ash/shell_port.h"
 #include "ash/test/ash_test_base.h"
@@ -71,7 +72,7 @@
 
     // Make sure the display is initialized so we don't fail the test due to any
     // input events caused from creating the display.
-    if (!ShellPort::Get()->IsRunningInMash())
+    if (Shell::GetAshConfig() != Config::MASH)
       Shell::Get()->display_manager()->UpdateDisplays();
     RunAllPendingInMessageLoop();
   }
diff --git a/ash/display/display_configuration_controller_unittest.cc b/ash/display/display_configuration_controller_unittest.cc
index 13f9f84c3..d1658af 100644
--- a/ash/display/display_configuration_controller_unittest.cc
+++ b/ash/display/display_configuration_controller_unittest.cc
@@ -2,9 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "ash/public/cpp/config.h"
 #include "ash/rotator/screen_rotation_animator.h"
 #include "ash/shell.h"
-#include "ash/shell_port.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/test/display_configuration_controller_test_api.h"
 #include "base/macros.h"
@@ -17,7 +17,7 @@
 TEST_F(DisplayConfigurationControllerTest, ErasesAnimatorOnAnimationEnded) {
   // TODO(wutao): needs display_configuration_controller
   // http://crbug.com/686839.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   display::Display display = display::Screen::GetScreen()->GetPrimaryDisplay();
diff --git a/ash/laser/laser_pointer_controller_unittest.cc b/ash/laser/laser_pointer_controller_unittest.cc
index f7b3ef6..b35b759 100644
--- a/ash/laser/laser_pointer_controller_unittest.cc
+++ b/ash/laser/laser_pointer_controller_unittest.cc
@@ -6,8 +6,8 @@
 
 #include "ash/laser/laser_pointer_controller_test_api.h"
 #include "ash/laser/laser_pointer_view.h"
+#include "ash/public/cpp/config.h"
 #include "ash/shell.h"
-#include "ash/shell_port.h"
 #include "ash/test/ash_test_base.h"
 #include "ui/events/test/event_generator.h"
 
@@ -44,7 +44,7 @@
 // points from stylus movements as expected.
 TEST_F(LaserPointerControllerTest, LaserPointerRenderer) {
   // Crashes in mash mode: crbug.com/702657
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
   LaserPointerControllerTestApi controller_test_api_(controller_.get());
 
@@ -121,7 +121,7 @@
 // prediction as expected when it receives points from stylus movements.
 TEST_F(LaserPointerControllerTest, LaserPointerPrediction) {
   // Crashes in mash mode: crbug.com/702657
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
   LaserPointerControllerTestApi controller_test_api_(controller_.get());
 
diff --git a/ash/metrics/desktop_task_switch_metric_recorder_unittest.cc b/ash/metrics/desktop_task_switch_metric_recorder_unittest.cc
index 8cf6336..40731cd 100644
--- a/ash/metrics/desktop_task_switch_metric_recorder_unittest.cc
+++ b/ash/metrics/desktop_task_switch_metric_recorder_unittest.cc
@@ -6,8 +6,8 @@
 
 #include <memory>
 
+#include "ash/public/cpp/config.h"
 #include "ash/shell.h"
-#include "ash/shell_port.h"
 #include "ash/test/ash_test_base.h"
 #include "base/test/user_action_tester.h"
 #include "ui/aura/test/test_window_delegate.h"
@@ -121,7 +121,7 @@
 TEST_F(DesktopTaskSwitchMetricRecorderTest,
        ActivatePositionableWindowWhenNullWindowWasActivatedLast) {
   // TODO: investigate failure in mash, http://crbug.com/695628.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   std::unique_ptr<aura::Window> null_window;
@@ -141,7 +141,7 @@
     DesktopTaskSwitchMetricRecorderTest,
     ActivatePositionableWindowWhenADifferentPositionableWindowWasActivatedLast) {
   // TODO: investigate failure in mash, http://crbug.com/695628.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   std::unique_ptr<aura::Window> positionable_window_1 =
@@ -176,7 +176,7 @@
 TEST_F(DesktopTaskSwitchMetricRecorderTest,
        ActivatePositionableWindowWhenANonPositionableWindowWasActivatedLast) {
   // TODO: investigate failure in mash, http://crbug.com/695628.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   std::unique_ptr<aura::Window> non_positionable_window =
@@ -321,7 +321,7 @@
 TEST_F(DesktopTaskSwitchMetricRecorderWithShellIntegrationTest,
        ActivatePositionableWindowWithInputEvent) {
   // TODO: investigate failure in mash, http://crbug.com/695628.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
   aura::Window* positionable_window =
       CreatePositionableWindowInShellWithBounds(gfx::Rect(0, 0, 10, 10));
diff --git a/ash/metrics/user_metrics_recorder_unittest.cc b/ash/metrics/user_metrics_recorder_unittest.cc
index 1a9731cf..84993be 100644
--- a/ash/metrics/user_metrics_recorder_unittest.cc
+++ b/ash/metrics/user_metrics_recorder_unittest.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "ash/login_status.h"
+#include "ash/public/cpp/config.h"
 #include "ash/shelf/shelf_model.h"
 #include "ash/shell.h"
 #include "ash/shell_port.h"
@@ -189,7 +190,7 @@
 // UserMetricsRecorder::RecordPeriodicMetrics() method.
 TEST_F(UserMetricsRecorderTest, ValuesRecordedByRecordShelfItemCounts) {
   // TODO: investigate failure in mash, http://crbug.com/695629.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   test::TestShelfDelegate* test_shelf_delegate =
diff --git a/ash/mus/bridge/shell_port_mash.cc b/ash/mus/bridge/shell_port_mash.cc
index 7285ebd..1a78a0a5 100644
--- a/ash/mus/bridge/shell_port_mash.cc
+++ b/ash/mus/bridge/shell_port_mash.cc
@@ -154,10 +154,6 @@
   window_manager_->DeleteAllRootWindowControllers();
 }
 
-bool ShellPortMash::IsRunningInMash() const {
-  return GetAshConfig() == Config::MASH;
-}
-
 Config ShellPortMash::GetAshConfig() const {
   return window_manager_->config();
 }
diff --git a/ash/mus/bridge/shell_port_mash.h b/ash/mus/bridge/shell_port_mash.h
index c30336d..ac839c8 100644
--- a/ash/mus/bridge/shell_port_mash.h
+++ b/ash/mus/bridge/shell_port_mash.h
@@ -60,7 +60,6 @@
 
   // ShellPort:
   void Shutdown() override;
-  bool IsRunningInMash() const override;
   Config GetAshConfig() const override;
   WmWindow* GetPrimaryRootWindow() override;
   WmWindow* GetRootWindowForDisplayId(int64_t display_id) override;
diff --git a/ash/root_window_controller.cc b/ash/root_window_controller.cc
index c7dbc443..38906af6 100644
--- a/ash/root_window_controller.cc
+++ b/ash/root_window_controller.cc
@@ -269,7 +269,7 @@
   if (!WmWindow::GetAuraWindow(window)->owned_by_parent())
     return false;
 
-  if (!ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() != Config::MASH)
     return true;
 
   aura::WindowMus* window_mus =
@@ -626,7 +626,7 @@
 }
 
 void RootWindowController::InitTouchHuds() {
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
@@ -771,7 +771,7 @@
 
   root_window_layout_manager_->OnWindowResized();
   if (root_window_type == RootWindowType::PRIMARY) {
-    if (!shell_port->IsRunningInMash())
+    if (Shell::GetAshConfig() != Config::MASH)
       shell->InitKeyboard();
   } else {
     window_tree_host_->Show();
@@ -788,7 +788,7 @@
   // http://crbug.com/679782
   if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kAshDisableTouchExplorationMode) &&
-      !shell_port->IsRunningInMash()) {
+      Shell::GetAshConfig() != Config::MASH) {
     touch_exploration_manager_.reset(new AshTouchExplorationManager(this));
   }
 }
diff --git a/ash/root_window_controller_unittest.cc b/ash/root_window_controller_unittest.cc
index ce3611c..3671f30 100644
--- a/ash/root_window_controller_unittest.cc
+++ b/ash/root_window_controller_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 
+#include "ash/public/cpp/config.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/session/session_controller.h"
 #include "ash/shell.h"
@@ -186,7 +187,7 @@
   EXPECT_EQ(root_windows[1], panel->GetRootWindow());
   EXPECT_EQ(kShellWindowId_PanelContainer, panel->parent()->id());
 
-  if (ShellPort::Get()->IsRunningInMash()) {
+  if (Shell::GetAshConfig() == Config::MASH) {
     // TODO(erg): Ignore this one part of the test when running mash. We would
     // crash because the aura::Window |d2| created in the other block doesn't
     // get deleted, and thus continues to contain a reference to its delegate,
@@ -302,7 +303,7 @@
 // Make sure lock related windows moves.
 TEST_F(RootWindowControllerTest, MoveWindows_LockWindowsInUnified) {
   // TODO: requires unified desktop mode. http://crbug.com/581462.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   display_manager()->SetUnifiedDesktopEnabled(true);
@@ -763,7 +764,7 @@
 TEST_F(VirtualKeyboardRootWindowControllerTest,
        VirtualKeyboardOnTouchableDisplayOnly) {
   // TODO: investigate failure in mash. http://crbug.com/695640.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   UpdateDisplay("500x500,500x500");
@@ -815,7 +816,7 @@
 // virtual keyboard follows the input focus.
 TEST_F(VirtualKeyboardRootWindowControllerTest, FollowInputFocus) {
   // TODO: investigate failure in mash. http://crbug.com/695640.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   UpdateDisplay("500x500,500x500");
@@ -885,7 +886,7 @@
 TEST_F(VirtualKeyboardRootWindowControllerTest,
        VirtualKeyboardShowOnSpecifiedDisplay) {
   // TODO: fails in mash. http://crbug.com/695640.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   UpdateDisplay("500x500,500x500");
@@ -1082,7 +1083,7 @@
 TEST_F(VirtualKeyboardRootWindowControllerTest,
        EnsureCaretInWorkAreaWithMultipleDisplays) {
   // TODO: fails in mash. http://crbug.com/695640.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   UpdateDisplay("500x500,600x600");
diff --git a/ash/rotator/screen_rotation_animator_unittest.cc b/ash/rotator/screen_rotation_animator_unittest.cc
index daefa2e..09e64d3 100644
--- a/ash/rotator/screen_rotation_animator_unittest.cc
+++ b/ash/rotator/screen_rotation_animator_unittest.cc
@@ -5,6 +5,7 @@
 #include "ash/rotator/screen_rotation_animator.h"
 
 #include "ash/ash_switches.h"
+#include "ash/public/cpp/config.h"
 #include "ash/rotator/screen_rotation_animator_observer.h"
 #include "ash/rotator/test/screen_rotation_animator_test_api.h"
 #include "ash/shell.h"
@@ -165,7 +166,7 @@
 
 TEST_F(ScreenRotationAnimatorTest, ShouldNotifyObserver) {
   // TODO(wutao): needs GetDisplayInfo http://crbug.com/622480.
-  if (ShellPort::Get()->IsRunningInMash()) {
+  if (Shell::GetAshConfig() == Config::MASH) {
     ASSERT_TRUE(ShellPort::Get()->GetDisplayInfo(display_id()).id() !=
                 display_id());
     return;
@@ -188,7 +189,7 @@
 
 TEST_F(ScreenRotationAnimatorTest, ShouldNotifyObserverOnce) {
   // TODO(wutao): needs GetDisplayInfo http://crbug.com/622480.
-  if (ShellPort::Get()->IsRunningInMash()) {
+  if (Shell::GetAshConfig() == Config::MASH) {
     ASSERT_TRUE(ShellPort::Get()->GetDisplayInfo(display_id()).id() !=
                 display_id());
     return;
@@ -215,7 +216,7 @@
 
 TEST_F(ScreenRotationAnimatorTest, RotatesToDifferentRotation) {
   // TODO(wutao): needs GetDisplayInfo http://crbug.com/622480.
-  if (ShellPort::Get()->IsRunningInMash()) {
+  if (Shell::GetAshConfig() == Config::MASH) {
     ASSERT_TRUE(ShellPort::Get()->GetDisplayInfo(display_id()).id() !=
                 display_id());
     return;
@@ -232,7 +233,7 @@
 
 TEST_F(ScreenRotationAnimatorTest, ShouldNotRotateTheSameRotation) {
   // TODO(wutao): needs GetDisplayInfo http://crbug.com/622480.
-  if (ShellPort::Get()->IsRunningInMash()) {
+  if (Shell::GetAshConfig() == Config::MASH) {
     ASSERT_TRUE(ShellPort::Get()->GetDisplayInfo(display_id()).id() !=
                 display_id());
     return;
@@ -249,7 +250,7 @@
 // request to the |last_pending_request_|.
 TEST_F(ScreenRotationAnimatorTest, RotatesDuringRotation) {
   // TODO(wutao): needs GetDisplayInfo http://crbug.com/622480.
-  if (ShellPort::Get()->IsRunningInMash()) {
+  if (Shell::GetAshConfig() == Config::MASH) {
     ASSERT_TRUE(ShellPort::Get()->GetDisplayInfo(display_id()).id() !=
                 display_id());
     return;
@@ -271,7 +272,7 @@
 // last request and finish the rotation animation.
 TEST_F(ScreenRotationAnimatorTest, ShouldCompleteAnimations) {
   // TODO(wutao): needs GetDisplayInfo http://crbug.com/622480.
-  if (ShellPort::Get()->IsRunningInMash()) {
+  if (Shell::GetAshConfig() == Config::MASH) {
     ASSERT_TRUE(ShellPort::Get()->GetDisplayInfo(display_id()).id() !=
                 display_id());
     return;
@@ -298,7 +299,7 @@
 // Test enable smooth screen rotation code path.
 TEST_F(ScreenRotationAnimatorTest, RotatesToDifferentRotationWithCopyCallback) {
   // TODO(wutao): needs GetDisplayInfo http://crbug.com/622480.
-  if (ShellPort::Get()->IsRunningInMash()) {
+  if (Shell::GetAshConfig() == Config::MASH) {
     ASSERT_TRUE(ShellPort::Get()->GetDisplayInfo(display_id()).id() !=
                 display_id());
     return;
@@ -319,7 +320,7 @@
 // If the external display is removed, it should not crash.
 TEST_F(ScreenRotationAnimatorTest, RemoveSecondaryDisplayAfterCopyCallback) {
   // TODO(wutao): needs GetDisplayInfo http://crbug.com/622480.
-  if (ShellPort::Get()->IsRunningInMash()) {
+  if (Shell::GetAshConfig() == Config::MASH) {
     ASSERT_TRUE(ShellPort::Get()->GetDisplayInfo(display_id()).id() !=
                 display_id());
     return;
diff --git a/ash/screen_util_unittest.cc b/ash/screen_util_unittest.cc
index bf07727..c72c16a 100644
--- a/ash/screen_util_unittest.cc
+++ b/ash/screen_util_unittest.cc
@@ -4,8 +4,8 @@
 
 #include "ash/screen_util.h"
 
+#include "ash/public/cpp/config.h"
 #include "ash/shell.h"
-#include "ash/shell_port.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/wm/window_util.h"
 #include "ash/wm/wm_screen_util.h"
@@ -106,7 +106,7 @@
 
 TEST_F(ScreenUtilTest, ShelfDisplayBoundsInUnifiedDesktop) {
   // TODO: requires unified desktop mode. http://crbug.com/581462.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   display_manager()->SetUnifiedDesktopEnabled(true);
diff --git a/ash/shelf/shelf_button_pressed_metric_tracker_unittest.cc b/ash/shelf/shelf_button_pressed_metric_tracker_unittest.cc
index 40a1f9d9e..b85266b 100644
--- a/ash/shelf/shelf_button_pressed_metric_tracker_unittest.cc
+++ b/ash/shelf/shelf_button_pressed_metric_tracker_unittest.cc
@@ -6,8 +6,9 @@
 
 #include <utility>
 
+#include "ash/public/cpp/config.h"
 #include "ash/shelf/wm_shelf.h"
-#include "ash/shell_port.h"
+#include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/test/shelf_button_pressed_metric_tracker_test_api.h"
 #include "ash/test/shelf_view_test_api.h"
@@ -155,7 +156,7 @@
 TEST_F(ShelfButtonPressedMetricTrackerTest,
        Launcher_ButtonPressed_MouseIsRecordedWhenIconActivatedByMouse) {
   // TODO: investigate failure in mash. http://crbug.com/695565.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   const ui::MouseEvent mouse_event(ui::ET_MOUSE_PRESSED, gfx::Point(),
@@ -172,7 +173,7 @@
 TEST_F(ShelfButtonPressedMetricTrackerTest,
        Launcher_ButtonPressed_MouseIsRecordedWhenIconActivatedByTouch) {
   // TODO: investigate failure in mash. http://crbug.com/695565.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   const ui::TouchEvent touch_event(
@@ -190,7 +191,7 @@
 TEST_F(ShelfButtonPressedMetricTrackerTest,
        Launcher_LaunchTaskIsRecordedWhenNewWindowIsCreated) {
   // TODO: investigate failure in mash. http://crbug.com/695565.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   base::UserActionTester user_action_tester;
@@ -203,7 +204,7 @@
 TEST_F(ShelfButtonPressedMetricTrackerTest,
        Launcher_MinimizeTaskIsRecordedWhenWindowIsMinimized) {
   // TODO: investigate failure in mash. http://crbug.com/695565.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   base::UserActionTester user_action_tester;
@@ -216,7 +217,7 @@
 TEST_F(ShelfButtonPressedMetricTrackerTest,
        Launcher_SwitchTaskIsRecordedWhenExistingWindowIsActivated) {
   // TODO: investigate failure in mash. http://crbug.com/695565.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   base::UserActionTester user_action_tester;
diff --git a/ash/shelf/shelf_layout_manager_unittest.cc b/ash/shelf/shelf_layout_manager_unittest.cc
index 7a7dfdb..cc45d218 100644
--- a/ash/shelf/shelf_layout_manager_unittest.cc
+++ b/ash/shelf/shelf_layout_manager_unittest.cc
@@ -7,6 +7,7 @@
 #include "ash/accelerators/accelerator_controller.h"
 #include "ash/accelerators/accelerator_table.h"
 #include "ash/focus_cycler.h"
+#include "ash/public/cpp/config.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/root_window_controller.h"
 #include "ash/session/session_controller.h"
@@ -685,7 +686,7 @@
 // Various assertions around auto-hide.
 TEST_F(ShelfLayoutManagerTest, AutoHide) {
   // TODO: investigate failure in mash, http://crbug.com/695686.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   ui::test::EventGenerator& generator(GetEventGenerator());
@@ -748,7 +749,7 @@
 // boundary between the primary and the secondary display.
 TEST_F(ShelfLayoutManagerTest, AutoHideShelfOnScreenBoundary) {
   // TODO: investigate failure in mash, http://crbug.com/695686.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   UpdateDisplay("800x600,800x600");
@@ -959,7 +960,7 @@
   EXPECT_EQ(SHELF_VISIBLE, shelf->GetVisibilityState());
 
   // TODO: fails in mash because of AppListPresenter. http://crbug.com/696028.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   // Show the app list and the shelf stays visible.
@@ -991,7 +992,7 @@
   EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->GetAutoHideState());
 
   // TODO: fails in mash because of AppListPresenter. http://crbug.com/696028.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   // Show the app list and the shelf should be temporarily visible.
@@ -1059,7 +1060,7 @@
   EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf_2->GetAutoHideState());
 
   // TODO: fails in mash because of AppListPresenter. http://crbug.com/696028.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   // Show the app list; only the shelf on the same display should be shown.
@@ -1099,7 +1100,7 @@
   EXPECT_EQ(SHELF_HIDDEN, shelf->GetVisibilityState());
 
   // TODO: fails in mash because of AppListPresenter. http://crbug.com/696028.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   // Show the app list and the shelf should be temporarily visible.
@@ -1244,7 +1245,7 @@
 // Test for Pinned mode.
 TEST_F(ShelfLayoutManagerTest, PinnedWindowHidesShelf) {
   // TODO: investigate failure in mash, http://crbug.com/695686.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   WmShelf* shelf = GetPrimaryShelf();
@@ -1334,7 +1335,7 @@
 
 TEST_F(ShelfLayoutManagerTest, GestureDrag) {
   // TODO: investigate failure in mash, http://crbug.com/695686.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   // Slop is an implementation detail of gesture recognition, and complicates
@@ -1522,7 +1523,7 @@
 
 TEST_F(ShelfLayoutManagerTest, ShelfFlickerOnTrayActivation) {
   // TODO: investigate failure in mash, http://crbug.com/695686.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   WmShelf* shelf = GetPrimaryShelf();
@@ -1720,7 +1721,7 @@
 
 TEST_F(ShelfLayoutManagerTest, ShelfLayoutInUnifiedDesktop) {
   // TODO: requires unified desktop mode. http://crbug.com/581462.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   Shell::Get()->display_manager()->SetUnifiedDesktopEnabled(true);
diff --git a/ash/shelf/shelf_tooltip_manager_unittest.cc b/ash/shelf/shelf_tooltip_manager_unittest.cc
index 18464f3..8dd94623 100644
--- a/ash/shelf/shelf_tooltip_manager_unittest.cc
+++ b/ash/shelf/shelf_tooltip_manager_unittest.cc
@@ -4,11 +4,12 @@
 
 #include "ash/shelf/shelf_tooltip_manager.h"
 
+#include "ash/public/cpp/config.h"
 #include "ash/shelf/app_list_button.h"
 #include "ash/shelf/shelf_model.h"
 #include "ash/shelf/shelf_view.h"
 #include "ash/shelf/wm_shelf.h"
-#include "ash/shell_port.h"
+#include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/test/shelf_view_test_api.h"
 #include "ash/test/test_shelf_item_delegate.h"
@@ -167,7 +168,7 @@
 
 TEST_F(ShelfTooltipManagerTest, HideForEvents) {
   // TODO: investigate failure in mash. http://crbug.com/695563.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   ui::test::EventGenerator& generator = GetEventGenerator();
@@ -203,7 +204,7 @@
 
 TEST_F(ShelfTooltipManagerTest, HideForExternalEvents) {
   // TODO: investigate failure in mash. http://crbug.com/695563.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   ui::test::EventGenerator& generator = GetEventGenerator();
diff --git a/ash/shelf/shelf_view_unittest.cc b/ash/shelf/shelf_view_unittest.cc
index c2388ad..c9cb8fb8 100644
--- a/ash/shelf/shelf_view_unittest.cc
+++ b/ash/shelf/shelf_view_unittest.cc
@@ -9,6 +9,7 @@
 #include <utility>
 #include <vector>
 
+#include "ash/public/cpp/config.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/root_window_controller.h"
 #include "ash/shelf/app_list_button.h"
@@ -200,7 +201,7 @@
 // shelf on external display as well as one on primary.
 TEST_F(WmShelfObserverIconTest, AddRemoveWithMultipleDisplays) {
   // TODO: investigate failure in mash, http://crbug.com/695751.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   UpdateDisplay("400x400,400x400");
@@ -1785,7 +1786,7 @@
 TEST_F(ShelfViewTest,
        Launcher_ButtonPressedUserActionsRecordedWhenItemSelected) {
   // TODO: investigate failure in mash, http://crbug.com/695751.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   base::UserActionTester user_action_tester;
@@ -1805,7 +1806,7 @@
 // selected.
 TEST_F(ShelfViewTest, Launcher_TaskUserActionsRecordedWhenItemSelected) {
   // TODO: investigate failure in mash, http://crbug.com/695751.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   base::UserActionTester user_action_tester;
@@ -2097,7 +2098,7 @@
 // ink drop states correctly.
 TEST_F(ShelfViewInkDropTest, AppListButtonWhenVisibilityChanges) {
   // TODO: investigate failure in mash, http://crbug.com/695751.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   InitAppListButtonInkDrop();
@@ -2154,7 +2155,7 @@
 // tests that mouse drag and mouse release does not affect the ink drop state.
 TEST_F(ShelfViewInkDropTest, AppListButtonMouseEventsWhenVisible) {
   // TODO: investigate failure in mash, http://crbug.com/695751.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   InitAppListButtonInkDrop();
@@ -2221,7 +2222,7 @@
 // transitions ink drop states correctly.
 TEST_F(ShelfViewInkDropTest, AppListButtonGestureTapWhenVisible) {
   // TODO: investigate failure in mash, http://crbug.com/695751.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   InitAppListButtonInkDrop();
@@ -2291,7 +2292,7 @@
 // and dragging the touch point transitions ink drop states correctly.
 TEST_F(ShelfViewInkDropTest, AppListButtonGestureTapDragWhenVisible) {
   // TODO: investigate failure in mash, http://crbug.com/695751.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   InitAppListButtonInkDrop();
diff --git a/ash/shelf/shelf_window_watcher.cc b/ash/shelf/shelf_window_watcher.cc
index 43edc68..6964bb12 100644
--- a/ash/shelf/shelf_window_watcher.cc
+++ b/ash/shelf/shelf_window_watcher.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <utility>
 
+#include "ash/public/cpp/config.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/public/cpp/window_properties.h"
 #include "ash/shelf/shelf_constants.h"
@@ -33,7 +34,7 @@
 // Returns the shelf item type, with special temporary behavior for Mash:
 // Mash provides a default shelf item type (TYPE_APP) for non-ignored windows.
 ShelfItemType GetShelfItemType(aura::Window* window) {
-  if (!ShellPort::Get()->IsRunningInMash() ||
+  if (Shell::GetAshConfig() != Config::MASH ||
       window->GetProperty(kShelfItemTypeKey) != TYPE_UNDEFINED) {
     return static_cast<ShelfItemType>(window->GetProperty(kShelfItemTypeKey));
   }
diff --git a/ash/shelf/shelf_window_watcher_unittest.cc b/ash/shelf/shelf_window_watcher_unittest.cc
index 1d5ad61..1319f4b 100644
--- a/ash/shelf/shelf_window_watcher_unittest.cc
+++ b/ash/shelf/shelf_window_watcher_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "ash/shelf/shelf_window_watcher.h"
 
+#include "ash/public/cpp/config.h"
 #include "ash/public/cpp/shelf_item.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/public/cpp/window_properties.h"
@@ -75,7 +76,7 @@
 
 TEST_F(ShelfWindowWatcherTest, CreateAndRemoveShelfItemProperties) {
   // TODO: investigate failure in mash. http://crbug.com/695562.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   // ShelfModel only has an APP_LIST item.
@@ -119,7 +120,7 @@
 
 TEST_F(ShelfWindowWatcherTest, ActivateWindow) {
   // TODO: investigate failure in mash. http://crbug.com/695562.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   // ShelfModel only have APP_LIST item.
@@ -157,7 +158,7 @@
 
 TEST_F(ShelfWindowWatcherTest, UpdateWindowProperty) {
   // TODO: investigate failure in mash. http://crbug.com/695562.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   // ShelfModel only has an APP_LIST item.
@@ -186,7 +187,7 @@
 
 TEST_F(ShelfWindowWatcherTest, MaximizeAndRestoreWindow) {
   // TODO: investigate failure in mash. http://crbug.com/695562.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   // ShelfModel only has an APP_LIST item.
@@ -228,7 +229,7 @@
 // TODO(simonhong): Add a test for removing a Window during the dragging.
 TEST_F(ShelfWindowWatcherTest, DragWindow) {
   // TODO: investigate failure in mash. http://crbug.com/695562.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   // ShelfModel only has an APP_LIST item.
@@ -260,7 +261,7 @@
 // Ensure shelf items are added and removed as panels are opened and closed.
 TEST_F(ShelfWindowWatcherTest, PanelWindow) {
   // TODO: investigate failure in mash. http://crbug.com/695562.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   // ShelfModel only has an APP_LIST item.
diff --git a/ash/shelf/wm_shelf.cc b/ash/shelf/wm_shelf.cc
index 3f90f11..e7373bbd 100644
--- a/ash/shelf/wm_shelf.cc
+++ b/ash/shelf/wm_shelf.cc
@@ -4,6 +4,7 @@
 
 #include "ash/shelf/wm_shelf.h"
 
+#include "ash/public/cpp/config.h"
 #include "ash/public/cpp/shelf_item_delegate.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/root_window_controller.h"
@@ -16,7 +17,6 @@
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shelf/wm_shelf_observer.h"
 #include "ash/shell.h"
-#include "ash/shell_port.h"
 #include "ash/system/tray/system_tray_delegate.h"
 #include "ash/wm_window.h"
 #include "base/logging.h"
@@ -121,7 +121,7 @@
 
   // TODO: ShelfBezelEventHandler needs to work with mus too.
   // http://crbug.com/636647
-  if (!ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() != Config::MASH)
     bezel_event_handler_ = base::MakeUnique<ShelfBezelEventHandler>(this);
 }
 
@@ -342,7 +342,7 @@
 }
 
 void WmShelf::WillDeleteShelfLayoutManager() {
-  if (ShellPort::Get()->IsRunningInMash()) {
+  if (Shell::GetAshConfig() == Config::MASH) {
     // TODO(sky): this should be removed once Shell is used everywhere.
     ShutdownShelfWidget();
   }
@@ -362,7 +362,7 @@
   if (new_state != SHELF_AUTO_HIDE) {
     auto_hide_event_handler_.reset();
   } else if (!auto_hide_event_handler_ &&
-             !ShellPort::Get()->IsRunningInMash()) {
+             Shell::GetAshConfig() != Config::MASH) {
     auto_hide_event_handler_ =
         base::MakeUnique<AutoHideEventHandler>(shelf_layout_manager());
   }
diff --git a/ash/shell.cc b/ash/shell.cc
index 36297cd..48ed353 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -363,7 +363,7 @@
 }
 
 bool Shell::ShouldSaveDisplaySettings() {
-  DCHECK(!shell_port_->IsRunningInMash());
+  DCHECK(GetAshConfig() != Config::MASH);
   return !(
       screen_orientation_controller_->ignore_display_configuration_updates() ||
       resolution_notification_controller_->DoesNotificationTimeout());
@@ -571,7 +571,8 @@
   // TODO(sky): better refactor cash/mash dependencies. Perhaps put all cash
   // state on ShellPortClassic. http://crbug.com/671246.
 
-  if (!shell_port_->IsRunningInMash()) {
+  // Don't use Shell::GetAshConfig() as |instance_| has not yet been set.
+  if (shell_port_->GetAshConfig() != Config::MASH) {
     gpu_support_.reset(shell_delegate_->CreateGPUSupport());
     display_manager_.reset(ScreenAsh::CreateDisplayManager());
     window_tree_host_manager_.reset(new WindowTreeHostManager);
@@ -769,7 +770,6 @@
 }
 
 void Shell::Init(const ShellInitParams& init_params) {
-  const bool is_mash = shell_port_->IsRunningInMash();
   const Config config = shell_port_->GetAshConfig();
 
   blocking_pool_ = init_params.blocking_pool;
@@ -777,7 +777,7 @@
   wallpaper_delegate_ = shell_delegate_->CreateWallpaperDelegate();
 
   // Can be null in tests.
-  if (shell_port_->IsRunningInMash() && shell_delegate_->GetShellConnector()) {
+  if (config == Config::MASH && shell_delegate_->GetShellConnector()) {
     prefs::ConnectToPrefService(
         shell_delegate_->GetShellConnector(),
         make_scoped_refptr(new PrefRegistrySimple()),
@@ -811,11 +811,11 @@
     devtools_server_->AttachClient(std::move(devtools_client));
   }
 
-  if (is_mash)
+  if (config == Config::MASH)
     app_list_delegate_impl_ = base::MakeUnique<AppListDelegateImpl>();
 
   // TODO(sky): move creation to ShellPort.
-  if (!is_mash)
+  if (config != Config::MASH)
     immersive_handler_factory_ = base::MakeUnique<ImmersiveHandlerFactoryAsh>();
 
   scoped_overview_animation_settings_factory_.reset(
@@ -911,7 +911,7 @@
   shell_port_->CreatePrimaryHost();
   root_window_for_new_windows_ = WmWindow::Get(GetPrimaryRootWindow());
 
-  if (!is_mash) {
+  if (config != Config::MASH) {
     resolution_notification_controller_.reset(
         new ResolutionNotificationController);
   }
@@ -957,7 +957,7 @@
   toplevel_window_event_handler_ =
       base::MakeUnique<ToplevelWindowEventHandler>();
 
-  if (!is_mash) {
+  if (config != Config::MASH) {
     system_gesture_filter_.reset(new SystemGestureEventFilter);
     AddPreTargetHandler(system_gesture_filter_.get());
   }
@@ -976,7 +976,7 @@
   AddShellObserver(lock_state_controller_.get());
 
   // The connector is unavailable in some tests.
-  if (is_mash && shell_delegate_->GetShellConnector()) {
+  if (config == Config::MASH && shell_delegate_->GetShellConnector()) {
     ui::mojom::UserActivityMonitorPtr user_activity_monitor;
     shell_delegate_->GetShellConnector()->BindInterface(ui::mojom::kServiceName,
                                                         &user_activity_monitor);
@@ -1040,7 +1040,7 @@
   // WindowTreeHostManager::InitDisplays()
   // since AshTouchTransformController listens on
   // WindowTreeHostManager::Observer::OnDisplaysInitialized().
-  if (!is_mash) {
+  if (config != Config::MASH) {
     touch_transformer_controller_.reset(new AshTouchTransformController(
         display_configurator_.get(), display_manager_.get()));
   }
@@ -1051,7 +1051,7 @@
 
   // Needs to be created after InitDisplays() since it may cause the virtual
   // keyboard to be deployed.
-  if (!is_mash)
+  if (config != Config::MASH)
     virtual_keyboard_controller_.reset(new VirtualKeyboardController);
 
   audio_a11y_controller_.reset(new chromeos::AudioA11yController);
@@ -1073,7 +1073,7 @@
   video_activity_notifier_.reset(
       new VideoActivityNotifier(video_detector_.get()));
   bluetooth_notification_controller_.reset(new BluetoothNotificationController);
-  if (!is_mash) {
+  if (config != Config::MASH) {
     screen_orientation_controller_.reset(new ScreenOrientationController());
     screen_layout_observer_.reset(new ScreenLayoutObserver());
   }
@@ -1082,13 +1082,13 @@
   // The compositor thread and main message loop have to be running in
   // order to create mirror window. Run it after the main message loop
   // is started.
-  if (!is_mash)
+  if (config != Config::MASH)
     display_manager_->CreateMirrorWindowAsyncIfAny();
 
   for (auto& observer : shell_observers_)
     observer.OnShellInitialized();
 
-  if (!is_mash)
+  if (config != Config::MASH)
     user_metrics_recorder_->OnShellInitialized();
 }
 
@@ -1220,7 +1220,7 @@
   if (state == session_manager::SessionState::ACTIVE) {
     CreateShelfView();
 
-    if (!shell_port_->IsRunningInMash()) {
+    if (GetAshConfig() != Config::MASH) {
       // Recreate the keyboard after initial login and after multiprofile login.
       CreateKeyboard();
     }
diff --git a/ash/shell_port.h b/ash/shell_port.h
index 52ae107..9585d47 100644
--- a/ash/shell_port.h
+++ b/ash/shell_port.h
@@ -70,9 +70,6 @@
 
   virtual void Shutdown();
 
-  // Returns true when ash is running as a service_manager::Service.
-  // TODO(sky): remove and convert to GetAshConfig().
-  virtual bool IsRunningInMash() const = 0;
   virtual Config GetAshConfig() const = 0;
 
   // Convenience for GetPrimaryRootWindow()->GetRootWindowController().
diff --git a/ash/shell_unittest.cc b/ash/shell_unittest.cc
index b8e1c2c..1a981617 100644
--- a/ash/shell_unittest.cc
+++ b/ash/shell_unittest.cc
@@ -9,6 +9,7 @@
 
 #include "ash/display/mouse_cursor_event_filter.h"
 #include "ash/drag_drop/drag_drop_controller.h"
+#include "ash/public/cpp/config.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/root_window_controller.h"
 #include "ash/session/session_controller.h"
@@ -463,7 +464,7 @@
 // pre-target list.
 TEST_F(ShellTest, TestPreTargetHandlerOrder) {
   // TODO: investigate failure in mash, http://crbug.com/695758.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   Shell* shell = Shell::Get();
@@ -514,7 +515,7 @@
   // applicable to mash as all windows must be destroyed before ash, that isn't
   // the case with classic-ash where embedders can separately create
   // aura::Windows.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   window_.reset(new aura::Window(NULL));
diff --git a/ash/sticky_keys/sticky_keys_overlay_unittest.cc b/ash/sticky_keys/sticky_keys_overlay_unittest.cc
index 6d379ec..576db82 100644
--- a/ash/sticky_keys/sticky_keys_overlay_unittest.cc
+++ b/ash/sticky_keys/sticky_keys_overlay_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "ash/sticky_keys/sticky_keys_overlay.h"
 
+#include "ash/public/cpp/config.h"
 #include "ash/shell.h"
 #include "ash/shell_port.h"
 #include "ash/sticky_keys/sticky_keys_controller.h"
@@ -47,7 +48,7 @@
 // caused by using sticky keys with multiple displays.
 TEST_F(StickyKeysOverlayTest, OverlayNotDestroyedAfterDisplayRemoved) {
   // TODO: investigate failure in mash, http://crbug.com/696006.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   // Add a secondary display to the left of the primary one.
@@ -58,7 +59,7 @@
   int64_t secondary_display_id = display_ids[1];
   // TODO: disabled as ScreenRotationAnimator does not work in mash,
   // http://crbug.com/696754.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
   display_manager()->SetLayoutForCurrentDisplays(
       display::test::CreateDisplayLayout(display_manager(),
diff --git a/ash/system/ime/tray_ime_chromeos_unittest.cc b/ash/system/ime/tray_ime_chromeos_unittest.cc
index 9f4431e..a7b2fe47 100644
--- a/ash/system/ime/tray_ime_chromeos_unittest.cc
+++ b/ash/system/ime/tray_ime_chromeos_unittest.cc
@@ -6,8 +6,8 @@
 
 #include "ash/accessibility_delegate.h"
 #include "ash/accessibility_types.h"
+#include "ash/public/cpp/config.h"
 #include "ash/shell.h"
-#include "ash/shell_port.h"
 #include "ash/system/ime_menu/ime_list_view.h"
 #include "ash/system/tray/system_tray_notifier.h"
 #include "ash/test/ash_test_base.h"
@@ -161,7 +161,7 @@
 // enabled.
 TEST_F(TrayIMETest, HidesOnA11yEnabled) {
   // TODO: investigate failure in mash. http://crbug.com/695561.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   SetIMELength(0);
@@ -179,7 +179,7 @@
 // to toggle between enabled and disabled.
 TEST_F(TrayIMETest, PerformActionOnDetailedView) {
   // TODO: investigate failure in mash. http://crbug.com/695561.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   SetIMELength(0);
diff --git a/ash/system/overview/overview_button_tray_unittest.cc b/ash/system/overview/overview_button_tray_unittest.cc
index 45cd98a..33a7ae7 100644
--- a/ash/system/overview/overview_button_tray_unittest.cc
+++ b/ash/system/overview/overview_button_tray_unittest.cc
@@ -5,6 +5,7 @@
 #include "ash/system/overview/overview_button_tray.h"
 
 #include "ash/login_status.h"
+#include "ash/public/cpp/config.h"
 #include "ash/public/cpp/shelf_types.h"
 #include "ash/root_window_controller.h"
 #include "ash/rotator/screen_rotation_animator.h"
@@ -112,7 +113,7 @@
 // Tests that tapping on the control will record the user action Tray_Overview.
 TEST_F(OverviewButtonTrayTest, TrayOverviewUserAction) {
   // TODO: investigate failure in mash, http://crbug.com/698129.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   ASSERT_FALSE(Shell::Get()->window_selector_controller()->IsSelecting());
@@ -213,7 +214,7 @@
 TEST_F(OverviewButtonTrayTest, HideAnimationAlwaysCompletes) {
   // TODO: disabled as ScreenRotationAnimator does not work in mash,
   // http://crbug.com/696754.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   Shell::Get()->maximize_mode_controller()->EnableMaximizeModeWindowManager(
diff --git a/ash/system/power/power_event_observer.cc b/ash/system/power/power_event_observer.cc
index adce7d3a0..9a773a6 100644
--- a/ash/system/power/power_event_observer.cc
+++ b/ash/system/power/power_event_observer.cc
@@ -4,6 +4,7 @@
 
 #include "ash/system/power/power_event_observer.h"
 
+#include "ash/public/cpp/config.h"
 #include "ash/session/session_controller.h"
 #include "ash/shell.h"
 #include "ash/shell_port.h"
@@ -121,7 +122,7 @@
 
   // TODO(derat): After mus exposes a method for suspending displays, call it
   // here: http://crbug.com/692193
-  if (!ShellPort::Get()->IsRunningInMash()) {
+  if (Shell::GetAshConfig() != Config::MASH) {
     Shell::Get()->display_configurator()->SuspendDisplays(base::Bind(
         &OnSuspendDisplaysCompleted, chromeos::DBusThreadManager::Get()
                                          ->GetPowerManagerClient()
@@ -132,7 +133,7 @@
 void PowerEventObserver::SuspendDone(const base::TimeDelta& sleep_duration) {
   // TODO(derat): After mus exposes a method for resuming displays, call it
   // here: http://crbug.com/692193
-  if (!ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() != Config::MASH)
     Shell::Get()->display_configurator()->ResumeDisplays();
   Shell::Get()->system_tray_notifier()->NotifyRefreshClock();
 
diff --git a/ash/system/toast/toast_manager_unittest.cc b/ash/system/toast/toast_manager_unittest.cc
index df76847..cdb94cd4 100644
--- a/ash/system/toast/toast_manager_unittest.cc
+++ b/ash/system/toast/toast_manager_unittest.cc
@@ -3,10 +3,11 @@
 // found in the LICENSE file.
 
 #include "ash/system/toast/toast_manager.h"
+
+#include "ash/public/cpp/config.h"
 #include "ash/shelf/shelf_constants.h"
 #include "ash/shelf/wm_shelf.h"
 #include "ash/shell.h"
-#include "ash/shell_port.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/wm/wm_screen_util.h"
 #include "base/run_loop.h"
@@ -129,7 +130,7 @@
 
 TEST_F(ToastManagerTest, ShowAndCloseManuallyDuringAnimation) {
   // TODO: gets wedged running animator. http://crbug.com/698016.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   ui::ScopedAnimationDurationScaleMode slow_animation_duration(
@@ -260,7 +261,7 @@
 
 TEST_F(ToastManagerTest, PositionWithUnifiedDesktop) {
   // TODO: needs unified mode. http://crbug.com/698024.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   display_manager()->SetUnifiedDesktopEnabled(true);
diff --git a/ash/system/web_notification/ash_popup_alignment_delegate_unittest.cc b/ash/system/web_notification/ash_popup_alignment_delegate_unittest.cc
index 1a30d35..f907f6f 100644
--- a/ash/system/web_notification/ash_popup_alignment_delegate_unittest.cc
+++ b/ash/system/web_notification/ash_popup_alignment_delegate_unittest.cc
@@ -7,12 +7,12 @@
 #include <utility>
 #include <vector>
 
+#include "ash/public/cpp/config.h"
 #include "ash/public/cpp/shelf_types.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/root_window_controller.h"
 #include "ash/shelf/wm_shelf.h"
 #include "ash/shell.h"
-#include "ash/shell_port.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/wm_window.h"
 #include "base/command_line.h"
@@ -168,7 +168,7 @@
 
 TEST_F(AshPopupAlignmentDelegateTest, DockedMode) {
   // TODO: needs unified mode. http://crbug.com/698024.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   const gfx::Rect toast_size(0, 0, 10, 10);
@@ -223,7 +223,7 @@
 
 TEST_F(AshPopupAlignmentDelegateTest, Unified) {
   // TODO: needs unified mode. http://crbug.com/698024.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   display_manager()->SetUnifiedDesktopEnabled(true);
diff --git a/ash/system/web_notification/web_notification_tray_unittest.cc b/ash/system/web_notification/web_notification_tray_unittest.cc
index 61baab5..7848c39 100644
--- a/ash/system/web_notification/web_notification_tray_unittest.cc
+++ b/ash/system/web_notification/web_notification_tray_unittest.cc
@@ -7,11 +7,11 @@
 #include <utility>
 #include <vector>
 
+#include "ash/public/cpp/config.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/shelf/shelf_layout_manager.h"
 #include "ash/shelf/wm_shelf.h"
 #include "ash/shell.h"
-#include "ash/shell_port.h"
 #include "ash/system/screen_layout_observer.h"
 #include "ash/system/status_area_widget.h"
 #include "ash/system/tray/system_tray.h"
@@ -254,7 +254,7 @@
 // Verifies if the notification appears on both displays when extended mode.
 TEST_F(WebNotificationTrayTest, PopupShownOnBothDisplays) {
   // TODO: needs ScreenLayoutObserver, http://crbug.com/696752.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   Shell::Get()->screen_layout_observer()->set_show_notifications_for_testing(
diff --git a/ash/test/ash_test.cc b/ash/test/ash_test.cc
index 0d7a8bd..125dfc1 100644
--- a/ash/test/ash_test.cc
+++ b/ash/test/ash_test.cc
@@ -4,6 +4,7 @@
 
 #include "ash/test/ash_test.h"
 
+#include "ash/public/cpp/config.h"
 #include "ash/root_window_controller.h"
 #include "ash/shelf/wm_shelf.h"
 #include "ash/shell.h"
@@ -108,7 +109,7 @@
 bool AshTest::SetSecondaryDisplayPlacement(
     display::DisplayPlacement::Position position,
     int offset) {
-  if (ShellPort::Get()->IsRunningInMash()) {
+  if (Shell::GetAshConfig() == Config::MASH) {
     NOTIMPLEMENTED();
     return false;
   }
diff --git a/ash/test/ash_test_base.cc b/ash/test/ash_test_base.cc
index 22a33c47..ec5972b 100644
--- a/ash/test/ash_test_base.cc
+++ b/ash/test/ash_test_base.cc
@@ -362,7 +362,7 @@
 void AshTestBase::DisableIME() {
   // WindowTreeHostManager isn't applicable to mash and IME is routed
   // differently in mash.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   Shell::Get()->RemovePreTargetHandler(
diff --git a/ash/tooltips/tooltip_controller_unittest.cc b/ash/tooltips/tooltip_controller_unittest.cc
index 2784cb3..bd2f864 100644
--- a/ash/tooltips/tooltip_controller_unittest.cc
+++ b/ash/tooltips/tooltip_controller_unittest.cc
@@ -4,8 +4,8 @@
 
 #include "ui/views/corewm/tooltip_controller.h"
 
+#include "ash/public/cpp/config.h"
 #include "ash/shell.h"
-#include "ash/shell_port.h"
 #include "ash/test/ash_test_base.h"
 #include "base/strings/utf_string_conversions.h"
 #include "ui/aura/env.h"
@@ -115,7 +115,7 @@
   EXPECT_TRUE(helper_->IsTooltipVisible());
 
   // TODO: CursorManager not created in mash. http://crbug.com/631103.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   // Disable mouse event which hides the cursor and check again.
diff --git a/ash/wallpaper/wallpaper_controller_unittest.cc b/ash/wallpaper/wallpaper_controller_unittest.cc
index 8d395f6..90e7a6da 100644
--- a/ash/wallpaper/wallpaper_controller_unittest.cc
+++ b/ash/wallpaper/wallpaper_controller_unittest.cc
@@ -8,6 +8,7 @@
 #include <cstdlib>
 
 #include "ash/ash_switches.h"
+#include "ash/public/cpp/config.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/root_window_controller.h"
 #include "ash/session/session_controller.h"
@@ -404,7 +405,7 @@
             WallpaperController::GetMaxDisplaySizeInNative().ToString());
 
   // TODO: mash doesn't support rotation yet, http://crbug.com/695556.
-  if (!ShellPort::Get()->IsRunningInMash()) {
+  if (Shell::GetAshConfig() != Config::MASH) {
     // Rotated display should return the rotated size.
     UpdateDisplay("1000x300*2/r");
     EXPECT_EQ("300x1000",
diff --git a/ash/wm/immersive_fullscreen_controller_unittest.cc b/ash/wm/immersive_fullscreen_controller_unittest.cc
index 270a204..e56ec8ef 100644
--- a/ash/wm/immersive_fullscreen_controller_unittest.cc
+++ b/ash/wm/immersive_fullscreen_controller_unittest.cc
@@ -4,12 +4,12 @@
 
 #include "ash/shared/immersive_fullscreen_controller.h"
 
+#include "ash/public/cpp/config.h"
 #include "ash/public/cpp/shelf_types.h"
 #include "ash/root_window_controller.h"
 #include "ash/shared/immersive_fullscreen_controller_delegate.h"
 #include "ash/shelf/wm_shelf.h"
 #include "ash/shell.h"
-#include "ash/shell_port.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/test/immersive_fullscreen_controller_test_api.h"
 #include "ash/wm/window_state.h"
@@ -261,7 +261,7 @@
 // top-of-window views getting hidden and revealed.
 TEST_F(ImmersiveFullscreenControllerTest, Delegate) {
   // TODO: investigate failure. http://crbug.com/698085.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   // Initial state.
@@ -345,7 +345,7 @@
 // Test mouse event processing for top-of-screen reveal triggering.
 TEST_F(ImmersiveFullscreenControllerTest, OnMouseEvent) {
   // TODO: investigate failure. http://crbug.com/698085.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   // Set up initial state.
@@ -449,7 +449,7 @@
 // top container's widget is inactive.
 TEST_F(ImmersiveFullscreenControllerTest, Inactive) {
   // TODO: investigate failure. http://crbug.com/698085.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   // Set up initial state.
@@ -510,7 +510,7 @@
 TEST_F(ImmersiveFullscreenControllerTest, MouseEventsVerticalDisplayLayout) {
   // TODO: SetLayoutForCurrentDisplays() needs to ported to mash.
   // http://crbug.com/698043.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   // Set up initial state.
@@ -596,7 +596,7 @@
 // Test behavior when the mouse becomes hovered without moving.
 TEST_F(ImmersiveFullscreenControllerTest, MouseHoveredWithoutMoving) {
   // TODO: investigate failure. http://crbug.com/698085.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   SetEnabled(true);
@@ -649,7 +649,7 @@
 // the mouse off of the top-of-window views.
 TEST_F(ImmersiveFullscreenControllerTest, DifferentModalityEnterExit) {
   // TODO: investigate failure. http://crbug.com/698085.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   SetEnabled(true);
@@ -686,7 +686,7 @@
 // Test when the SWIPE_CLOSE edge gesture closes the top-of-window views.
 TEST_F(ImmersiveFullscreenControllerTest, EndRevealViaGesture) {
   // TODO: investigate failure. http://crbug.com/698085.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   SetEnabled(true);
@@ -722,7 +722,7 @@
 // the child window consumes all events.
 TEST_F(ImmersiveFullscreenControllerTest, RevealViaGestureChildConsumesEvents) {
   // TODO: investigate failure. http://crbug.com/698085.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   // Enabling initially hides the top views.
@@ -805,7 +805,7 @@
 // revealed.
 TEST_F(ImmersiveFullscreenControllerTest, Focus) {
   // TODO: investigate failure. http://crbug.com/698085.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   // Add views to the view hierarchy which we will focus and unfocus during the
@@ -874,7 +874,7 @@
 // revealed.
 TEST_F(ImmersiveFullscreenControllerTest, Transient) {
   // TODO: investigate failure. http://crbug.com/698085.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   views::Widget* top_container_widget = top_container()->GetWidget();
@@ -923,7 +923,7 @@
 // Test how bubbles affect whether the top-of-window views are revealed.
 TEST_F(ImmersiveFullscreenControllerTest, Bubbles) {
   // TODO: investigate failure. http://crbug.com/698085.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   std::unique_ptr<ImmersiveRevealedLock> revealed_lock;
diff --git a/ash/wm/lock_state_controller_unittest.cc b/ash/wm/lock_state_controller_unittest.cc
index 9e16491..cff7f3399 100644
--- a/ash/wm/lock_state_controller_unittest.cc
+++ b/ash/wm/lock_state_controller_unittest.cc
@@ -7,9 +7,9 @@
 #include <memory>
 #include <utility>
 
+#include "ash/public/cpp/config.h"
 #include "ash/session/session_controller.h"
 #include "ash/shell.h"
-#include "ash/shell_port.h"
 #include "ash/shutdown_controller.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/test/lock_state_controller_test_api.h"
@@ -93,7 +93,7 @@
   }
 
   void TearDown() override {
-    const bool is_mash = ShellPort::Get()->IsRunningInMash();
+    const bool is_mash = Shell::GetAshConfig() == Config::MASH;
     AshTestBase::TearDown();
     // Mash shuts down DBus during normal destruction.
     if (!is_mash)
@@ -401,7 +401,7 @@
   // Make sure a mouse move event won't show the cursor.
   GenerateMouseMoveEvent();
   // TODO: CursorManager not created in mash. http://crbug.com/631103.
-  if (!ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() != Config::MASH)
     EXPECT_FALSE(cursor_visible());
 
   EXPECT_TRUE(test_api_->real_shutdown_timer_is_running());
@@ -812,7 +812,7 @@
       SessionStateAnimator::ANIMATION_HIDE_IMMEDIATELY));
   GenerateMouseMoveEvent();
   // TODO: CursorManager not created in mash. http://crbug.com/631103.
-  if (!ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() != Config::MASH)
     EXPECT_FALSE(cursor_visible());
 }
 
@@ -828,7 +828,7 @@
 
   GenerateMouseMoveEvent();
   // TODO: CursorManager not created in mash. http://crbug.com/631103.
-  if (!ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() != Config::MASH)
     EXPECT_FALSE(cursor_visible());
 
   EXPECT_EQ(0, NumShutdownRequests());
@@ -852,7 +852,7 @@
 
   GenerateMouseMoveEvent();
   // TODO: CursorManager not created in mash. http://crbug.com/631103.
-  if (!ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() != Config::MASH)
     EXPECT_FALSE(cursor_visible());
 
   EXPECT_EQ(0, NumShutdownRequests());
@@ -1031,7 +1031,7 @@
 
 TEST_F(LockStateControllerTest, Screenshot) {
   // TODO: fails because of no screenshot in mash. http://crbug.com/698033.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   test::TestScreenshotDelegate* delegate = GetScreenshotDelegate();
diff --git a/ash/wm/maximize_mode/maximize_mode_window_manager_unittest.cc b/ash/wm/maximize_mode/maximize_mode_window_manager_unittest.cc
index e8f095c..b4a1b87 100644
--- a/ash/wm/maximize_mode/maximize_mode_window_manager_unittest.cc
+++ b/ash/wm/maximize_mode/maximize_mode_window_manager_unittest.cc
@@ -7,6 +7,7 @@
 #include <string>
 
 #include "ash/ash_switches.h"
+#include "ash/public/cpp/config.h"
 #include "ash/root_window_controller.h"
 #include "ash/screen_util.h"
 #include "ash/shelf/wm_shelf.h"
@@ -1293,7 +1294,7 @@
 // Test that an edge swipe from the top will end full screen mode.
 TEST_F(MaximizeModeWindowManagerTest, ExitFullScreenWithEdgeSwipeFromTop) {
   // TODO: investigate failure. http://crbug.com/698093.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   gfx::Rect rect(10, 10, 200, 50);
@@ -1337,7 +1338,7 @@
 // Test that an edge swipe from the bottom will end full screen mode.
 TEST_F(MaximizeModeWindowManagerTest, ExitFullScreenWithEdgeSwipeFromBottom) {
   // TODO: investigate failure. http://crbug.com/698093.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   gfx::Rect rect(10, 10, 200, 50);
@@ -1375,7 +1376,7 @@
 // Test that an edge touch press at the top will end full screen mode.
 TEST_F(MaximizeModeWindowManagerTest, ExitFullScreenWithEdgeTouchAtTop) {
   // TODO: investigate failure. http://crbug.com/698093.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   gfx::Rect rect(10, 10, 200, 50);
@@ -1415,7 +1416,7 @@
 // Test that an edge touch press at the bottom will end full screen mode.
 TEST_F(MaximizeModeWindowManagerTest, ExitFullScreenWithEdgeTouchAtBottom) {
   // TODO: investigate failure. http://crbug.com/698093.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   gfx::Rect rect(10, 10, 200, 50);
diff --git a/ash/wm/overview/window_selector_unittest.cc b/ash/wm/overview/window_selector_unittest.cc
index 5e37cac..6cdd5e83 100644
--- a/ash/wm/overview/window_selector_unittest.cc
+++ b/ash/wm/overview/window_selector_unittest.cc
@@ -8,9 +8,9 @@
 #include "ash/accessibility_delegate.h"
 #include "ash/accessibility_types.h"
 #include "ash/drag_drop/drag_drop_controller.h"
+#include "ash/public/cpp/config.h"
 #include "ash/shelf/wm_shelf.h"
 #include "ash/shell.h"
-#include "ash/shell_port.h"
 #include "ash/system/tray/system_tray.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/test/shelf_view_test_api.h"
@@ -341,7 +341,7 @@
 TEST_F(WindowSelectorTest, OverviewScreenRotation) {
   // TODO: fails in mash because rotation is not supported.
   // http://crbug.com/695556
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   gfx::Rect bounds(0, 0, 400, 300);
@@ -419,7 +419,7 @@
   EXPECT_TRUE(wm::IsActiveWindow(window2.get()));
   EXPECT_EQ(window2.get(), wm::GetFocusedWindow());
   // TODO: mash doesn't support CursorClient. http://crbug.com/637853.
-  if (!ShellPort::Get()->IsRunningInMash()) {
+  if (Shell::GetAshConfig() != Config::MASH) {
     // Hide the cursor before entering overview to test that it will be shown.
     aura::client::GetCursorClient(root_window)->HideCursor();
   }
@@ -439,7 +439,7 @@
   EXPECT_EQ(window1.get(), wm::GetFocusedWindow());
 
   // TODO: mash doesn't support CursorClient. http://crbug.com/637853.
-  if (!ShellPort::Get()->IsRunningInMash()) {
+  if (Shell::GetAshConfig() != Config::MASH) {
     // Cursor should have been unlocked.
     EXPECT_FALSE(aura::client::GetCursorClient(root_window)->IsCursorLocked());
   }
@@ -555,7 +555,7 @@
 // in overview mode which is different from the previously-active window.
 TEST_F(WindowSelectorTest, ActiveWindowChangedUserActionRecorded) {
   // TODO: fails because of metrics. http://crbug.com/698129.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   base::UserActionTester user_action_tester;
@@ -930,7 +930,7 @@
 // Tests that beginning window selection hides the app list.
 TEST_F(WindowSelectorTest, SelectingHidesAppList) {
   // TODO: fails in mash because of AppListPresenter. http://crbug.com/696028.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   gfx::Rect bounds(0, 0, 400, 400);
@@ -1223,7 +1223,7 @@
 TEST_F(WindowSelectorTest, RemoveDisplay) {
   // TODO: hits CHECK in stl as order of |ShelfModel::items_| is wrong.
   // http://crbug.com/698878.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   UpdateDisplay("400x400,400x400");
@@ -1309,7 +1309,7 @@
 TEST_F(WindowSelectorTest, DisplayOrientationChanged) {
   // TODO: fails in mash because rotation is not supported.
   // http://crbug.com/695556
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   aura::Window* root_window = Shell::Get()->GetPrimaryRootWindow();
@@ -1444,7 +1444,7 @@
 TEST_F(WindowSelectorTest, MultiMonitorReversedOrder) {
   // TODO: SetLayoutForCurrentDisplays() needs to ported to mash.
   // http://crbug.com/698043.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   UpdateDisplay("400x400,400x400");
diff --git a/ash/wm/panels/panel_layout_manager_unittest.cc b/ash/wm/panels/panel_layout_manager_unittest.cc
index 449ca6f..52c0804 100644
--- a/ash/wm/panels/panel_layout_manager_unittest.cc
+++ b/ash/wm/panels/panel_layout_manager_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "ash/wm/panels/panel_layout_manager.h"
 
+#include "ash/public/cpp/config.h"
 #include "ash/public/cpp/shelf_types.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/root_window_controller.h"
@@ -14,7 +15,6 @@
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shelf/wm_shelf.h"
 #include "ash/shell.h"
-#include "ash/shell_port.h"
 #include "ash/system/web_notification/web_notification_tray.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/test/shelf_view_test_api.h"
@@ -305,7 +305,7 @@
 TEST_F(PanelLayoutManagerTest, UndockTest) {
   // TODO: mash doesn't support SetFirstDisplayAsInternalDisplay().
   // http://crbug.com/698091.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   std::vector<display::ManagedDisplayInfo> info_list;
@@ -341,7 +341,7 @@
 TEST_F(PanelLayoutManagerTest, DockUndockTest) {
   // TODO: mash doesn't support SetFirstDisplayAsInternalDisplay().
   // http://crbug.com/698091.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   std::vector<display::ManagedDisplayInfo> info_list;
@@ -507,7 +507,7 @@
   EXPECT_TRUE(IsPanelCalloutVisible(w3.get()));
 
   // TODO: investigate failure. http://crbug.com/698887.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   wm::ActivateWindow(w1.get());
@@ -542,7 +542,7 @@
 
 TEST_F(PanelLayoutManagerTest, RemoveMiddlePanel) {
   // TODO: fails because of ShelfModel. http://crbug.com/698878.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   gfx::Rect bounds(0, 0, 201, 201);
@@ -561,7 +561,7 @@
 
 TEST_F(PanelLayoutManagerTest, RemoveRightPanel) {
   // TODO: fails because of ShelfModel. http://crbug.com/698878.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   gfx::Rect bounds(0, 0, 201, 201);
@@ -580,7 +580,7 @@
 
 TEST_F(PanelLayoutManagerTest, RemoveNonActivePanel) {
   // TODO: fails because of ShelfModel. http://crbug.com/698878.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   gfx::Rect bounds(0, 0, 201, 201);
@@ -675,7 +675,7 @@
 
 TEST_F(PanelLayoutManagerTest, PanelMoveBetweenMultipleDisplays) {
   // TODO: fails because of ShelfModel. http://crbug.com/698878.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   // Keep the displays wide so that shelves have enough space for launcher
diff --git a/ash/wm/panels/panel_window_resizer_unittest.cc b/ash/wm/panels/panel_window_resizer_unittest.cc
index 5b03cec..03636f7 100644
--- a/ash/wm/panels/panel_window_resizer_unittest.cc
+++ b/ash/wm/panels/panel_window_resizer_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "ash/wm/panels/panel_window_resizer.h"
 
+#include "ash/public/cpp/config.h"
 #include "ash/public/cpp/shelf_types.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/public/cpp/window_properties.h"
@@ -225,7 +226,7 @@
 // reattached.
 TEST_F(PanelWindowResizerTest, PanelDetachReattachBottom) {
   // TODO: investigate failure. http://crbug.com/698888.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   std::unique_ptr<aura::Window> window(CreatePanelWindow(gfx::Point(0, 0)));
@@ -234,7 +235,7 @@
 
 TEST_F(PanelWindowResizerTest, PanelDetachReattachLeft) {
   // TODO: investigate failure. http://crbug.com/698888.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   GetPrimaryShelf()->SetAlignment(SHELF_ALIGNMENT_LEFT);
@@ -244,7 +245,7 @@
 
 TEST_F(PanelWindowResizerTest, PanelDetachReattachRight) {
   // TODO: investigate failure. http://crbug.com/698888.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   GetPrimaryShelf()->SetAlignment(SHELF_ALIGNMENT_RIGHT);
@@ -282,7 +283,7 @@
 
 TEST_F(PanelWindowResizerTest, PanelDetachReattachMultipleDisplays) {
   // TODO: investigate failure. http://crbug.com/698888.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   UpdateDisplay("600x400,600x400");
@@ -294,7 +295,7 @@
 
 TEST_F(PanelWindowResizerTest, DetachThenDragAcrossDisplays) {
   // TODO: investigate failure. http://crbug.com/698888.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   UpdateDisplay("600x400,600x400");
@@ -323,7 +324,7 @@
 
 TEST_F(PanelWindowResizerTest, DetachAcrossDisplays) {
   // TODO: investigate failure. http://crbug.com/698888.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   UpdateDisplay("600x400,600x400");
@@ -343,7 +344,7 @@
 
 TEST_F(PanelWindowResizerTest, DetachThenAttachToSecondDisplay) {
   // TODO: investigate failure. http://crbug.com/698888.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   UpdateDisplay("600x400,600x600");
@@ -376,7 +377,7 @@
 
 TEST_F(PanelWindowResizerTest, AttachToSecondDisplay) {
   // TODO: investigate failure. http://crbug.com/698888.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   UpdateDisplay("600x400,600x600");
@@ -402,7 +403,7 @@
 
 TEST_F(PanelWindowResizerTest, AttachToSecondFullscreenDisplay) {
   // TODO: investigate failure. http://crbug.com/698888.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   UpdateDisplay("600x400,600x600");
@@ -492,7 +493,7 @@
 // The transient children should be reparented in sync with the panel.
 TEST_P(PanelWindowResizerTransientTest, PanelWithTransientChild) {
   // TODO: investigate failure. http://crbug.com/698888.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   std::unique_ptr<aura::Window> window(CreatePanelWindow(gfx::Point(0, 0)));
diff --git a/ash/wm/system_modal_container_layout_manager.cc b/ash/wm/system_modal_container_layout_manager.cc
index 7f473b3..d76a045b 100644
--- a/ash/wm/system_modal_container_layout_manager.cc
+++ b/ash/wm/system_modal_container_layout_manager.cc
@@ -6,6 +6,7 @@
 
 #include <cmath>
 
+#include "ash/public/cpp/config.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/session/session_controller.h"
 #include "ash/shell.h"
@@ -79,7 +80,7 @@
          child->GetType() == ui::wm::WINDOW_TYPE_POPUP);
   // TODO(mash): IsUserSessionBlocked() depends on knowing the login state. We
   // need a non-stub version of SessionStateDelegate. crbug.com/648964
-  if (!ShellPort::Get()->IsRunningInMash()) {
+  if (Shell::GetAshConfig() != Config::MASH) {
     DCHECK(container_->GetShellWindowId() !=
                kShellWindowId_LockSystemModalContainer ||
            Shell::Get()->session_controller()->IsUserSessionBlocked());
diff --git a/ash/wm/system_modal_container_layout_manager_unittest.cc b/ash/wm/system_modal_container_layout_manager_unittest.cc
index bc3a0ac..70146ee6 100644
--- a/ash/wm/system_modal_container_layout_manager_unittest.cc
+++ b/ash/wm/system_modal_container_layout_manager_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 
+#include "ash/public/cpp/config.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/root_window_controller.h"
 #include "ash/session/session_state_delegate.h"
@@ -153,7 +154,7 @@
         keyboard::switches::kEnableVirtualKeyboard);
     AshTestBase::SetUp();
     // TODO: mash doesn't support virtual keyboard. http://crbug.com/698892.
-    if (!ShellPort::Get()->IsRunningInMash()) {
+    if (Shell::GetAshConfig() != Config::MASH) {
       Shell::GetPrimaryRootWindowController()->ActivateKeyboard(
           keyboard::KeyboardController::GetInstance());
     }
@@ -161,7 +162,7 @@
 
   void TearDown() override {
     // TODO: mash doesn't support virtual keyboard. http://crbug.com/698892.
-    if (!ShellPort::Get()->IsRunningInMash()) {
+    if (Shell::GetAshConfig() != Config::MASH) {
       Shell::GetPrimaryRootWindowController()->DeactivateKeyboard(
           keyboard::KeyboardController::GetInstance());
     }
@@ -662,7 +663,7 @@
 TEST_F(SystemModalContainerLayoutManagerTest,
        SystemModalDialogGetPushedFromKeyboard) {
   // TODO: mash doesn't support virtual keyboard. http://crbug.com/698892.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   const gfx::Rect& container_bounds = GetModalContainer()->bounds();
@@ -702,7 +703,7 @@
 TEST_F(SystemModalContainerLayoutManagerTest,
        SystemModalDialogGetPushedButNotCroppedFromKeyboard) {
   // TODO: mash doesn't support virtual keyboard. http://crbug.com/698892.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   const gfx::Rect& container_bounds = GetModalContainer()->bounds();
@@ -739,7 +740,7 @@
 TEST_F(SystemModalContainerLayoutManagerTest,
        SystemModalDialogGetPushedButNotCroppedFromKeyboardIfNotCentered) {
   // TODO: mash doesn't support virtual keyboard. http://crbug.com/698892.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   const gfx::Size screen_size = Shell::GetPrimaryRootWindow()->bounds().size();
diff --git a/ash/wm/toplevel_window_event_handler_unittest.cc b/ash/wm/toplevel_window_event_handler_unittest.cc
index cc5fcf55..f5b5d0e 100644
--- a/ash/wm/toplevel_window_event_handler_unittest.cc
+++ b/ash/wm/toplevel_window_event_handler_unittest.cc
@@ -4,10 +4,10 @@
 
 #include "ash/wm/toplevel_window_event_handler.h"
 
+#include "ash/public/cpp/config.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/root_window_controller.h"
 #include "ash/shell.h"
-#include "ash/shell_port.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/wm/resize_shadow.h"
 #include "ash/wm/resize_shadow_controller.h"
@@ -786,7 +786,7 @@
 TEST_F(ToplevelWindowEventHandlerTest, DragSnappedWindowToExternalDisplay) {
   // TODO: SetLayoutForCurrentDisplays() needs to ported to mash.
   // http://crbug.com/698043.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   UpdateDisplay("940x550,940x550");
diff --git a/ash/wm/window_cycle_controller_unittest.cc b/ash/wm/window_cycle_controller_unittest.cc
index ca1a965d..1e62240 100644
--- a/ash/wm/window_cycle_controller_unittest.cc
+++ b/ash/wm/window_cycle_controller_unittest.cc
@@ -8,13 +8,13 @@
 #include <memory>
 
 #include "ash/focus_cycler.h"
+#include "ash/public/cpp/config.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/scoped_root_window_for_new_windows.h"
 #include "ash/session/session_controller.h"
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shelf/wm_shelf.h"
 #include "ash/shell.h"
-#include "ash/shell_port.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/test/shelf_view_test_api.h"
 #include "ash/test/test_app_list_view_presenter_impl.h"
@@ -482,7 +482,7 @@
 // Tests that beginning window selection hides the app list.
 TEST_F(WindowCycleControllerTest, SelectingHidesAppList) {
   // TODO: fails in mash because of AppListPresenter. http://crbug.com/696028.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   // The tested behavior relies on the app list presenter implementation.
@@ -614,7 +614,7 @@
 // Tests that the tab key events are not sent to the window.
 TEST_F(WindowCycleControllerTest, TabKeyNotLeaked) {
   // TODO: investigate failure in mash. http://crbug.com/698894.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   std::unique_ptr<Window> w0(CreateTestWindowInShellWithId(0));
@@ -638,7 +638,7 @@
 // While the UI is active, mouse events are captured.
 TEST_F(WindowCycleControllerTest, MouseEventsCaptured) {
   // TODO: investigate failure in mash. http://crbug.com/698894.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   // This delegate allows the window to receive mouse events.
@@ -716,7 +716,7 @@
 // display it's on. See crbug.com/675718
 TEST_F(WindowCycleControllerTest, MultiDisplayPositioning) {
   // TODO: investigate failure in mash. http://crbug.com/698894.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   int64_t primary_id = display::Screen::GetScreen()->GetPrimaryDisplay().id();
diff --git a/ash/wm/window_manager_unittest.cc b/ash/wm/window_manager_unittest.cc
index 52c06a18..69647c3 100644
--- a/ash/wm/window_manager_unittest.cc
+++ b/ash/wm/window_manager_unittest.cc
@@ -2,9 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "ash/public/cpp/config.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/shell.h"
-#include "ash/shell_port.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/test/test_activation_delegate.h"
 #include "ash/wm/window_util.h"
@@ -469,7 +469,7 @@
 
 TEST_F(WindowManagerTest, MouseEventCursors) {
   // TODO: investigate failure in mash. http://crbug.com/698895.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   aura::Window* root_window = Shell::GetPrimaryRootWindow();
@@ -708,7 +708,7 @@
 // Touch visually hides the cursor.
 TEST_F(WindowManagerTest, UpdateCursorVisibility) {
   // TODO: mash doesn't support CursorManager. http://crbug.com/631103.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   ui::test::EventGenerator& generator = GetEventGenerator();
@@ -731,7 +731,7 @@
 // Cursor is hidden on keypress.
 TEST_F(WindowManagerTest, UpdateCursorVisibilityOnKeyEvent) {
   // TODO: mash doesn't support CursorManager. http://crbug.com/631103.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   ui::test::EventGenerator& generator = GetEventGenerator();
@@ -755,7 +755,7 @@
 // Test that pressing an accelerator does not hide the cursor.
 TEST_F(WindowManagerTest, UpdateCursorVisibilityAccelerator) {
   // TODO: mash doesn't support CursorManager. http://crbug.com/631103.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   ui::test::EventGenerator& generator = GetEventGenerator();
@@ -780,7 +780,7 @@
 
 TEST_F(WindowManagerTest, TestCursorClientObserver) {
   // TODO: mash doesn't support CursorManager. http://crbug.com/631103.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   ui::test::EventGenerator& generator = GetEventGenerator();
diff --git a/ash/wm/workspace/workspace_event_handler_unittest.cc b/ash/wm/workspace/workspace_event_handler_unittest.cc
index 88e0f31..0e108ea 100644
--- a/ash/wm/workspace/workspace_event_handler_unittest.cc
+++ b/ash/wm/workspace/workspace_event_handler_unittest.cc
@@ -4,9 +4,9 @@
 
 #include "ash/wm/workspace/workspace_event_handler.h"
 
+#include "ash/public/cpp/config.h"
 #include "ash/screen_util.h"
 #include "ash/shell.h"
-#include "ash/shell_port.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/window_state_aura.h"
@@ -97,7 +97,7 @@
 
 TEST_F(WorkspaceEventHandlerTest, DoubleClickSingleAxisResizeEdge) {
   // TODO: investigate failure. http://crbug.com/699175.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   // Double clicking the vertical resize edge of a window should maximize it
@@ -207,7 +207,7 @@
 // Tests the behavior when double clicking the border of a side snapped window.
 TEST_F(WorkspaceEventHandlerTest, DoubleClickSingleAxisWhenSideSnapped) {
   // TODO: investigate failure. http://crbug.com/699175.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   gfx::Rect restored_bounds(10, 10, 50, 50);
@@ -326,7 +326,7 @@
 // Test the behavior as a result of double clicking the window header.
 TEST_F(WorkspaceEventHandlerTest, DoubleClickCaptionTogglesMaximize) {
   // TODO: investigate failure. http://crbug.com/699175.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   aura::test::TestWindowDelegate delegate;
@@ -409,7 +409,7 @@
 
 TEST_F(WorkspaceEventHandlerTest, DoubleTapCaptionTogglesMaximize) {
   // TODO: investigate failure. http://crbug.com/699175.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   aura::test::TestWindowDelegate delegate;
diff --git a/ash/wm/workspace/workspace_layout_manager_unittest.cc b/ash/wm/workspace/workspace_layout_manager_unittest.cc
index 802e7d6..70c89b51 100644
--- a/ash/wm/workspace/workspace_layout_manager_unittest.cc
+++ b/ash/wm/workspace/workspace_layout_manager_unittest.cc
@@ -7,6 +7,7 @@
 #include <string>
 #include <utility>
 
+#include "ash/public/cpp/config.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/root_window_controller.h"
 #include "ash/session/session_controller.h"
@@ -397,7 +398,7 @@
   // TODO: fix. This test verifies that when a window is added the bounds are
   // adjusted. CreateTestWindow() for mus adds, then sets the bounds (this comes
   // from NativeWidgetAura), which means this test now fails for aura-mus.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   // Normal window bounds shouldn't be changed.
@@ -482,7 +483,7 @@
   // TODO: fix. This test verifies that when a window is added the bounds are
   // adjusted. CreateTestWindow() for mus adds, then sets the bounds (this comes
   // from NativeWidgetAura), which means this test now fails for aura-mus.
-  if (!ShellPort::Get()->IsRunningInMash()) {
+  if (Shell::GetAshConfig() != Config::MASH) {
     EXPECT_EQ(gfx::Rect(gfx::Point(100, 101), work_area).ToString(),
               window->GetBounds().ToString());
   }
diff --git a/ash/wm/workspace/workspace_window_resizer_unittest.cc b/ash/wm/workspace/workspace_window_resizer_unittest.cc
index 2e07f220..3fd046e 100644
--- a/ash/wm/workspace/workspace_window_resizer_unittest.cc
+++ b/ash/wm/workspace/workspace_window_resizer_unittest.cc
@@ -4,12 +4,12 @@
 
 #include "ash/wm/workspace/workspace_window_resizer.h"
 
+#include "ash/public/cpp/config.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/screen_util.h"
 #include "ash/shelf/shelf_constants.h"
 #include "ash/shelf/wm_shelf.h"
 #include "ash/shell.h"
-#include "ash/shell_port.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/wm/window_positioning_utils.h"
 #include "ash/wm/window_state.h"
@@ -498,7 +498,7 @@
 TEST_F(WorkspaceWindowResizerTest, MouseMoveWithTouchDrag) {
   // TODO: fails because mash doesn't support CursorManager.
   // http://crbug.com/631103.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   window_->SetBounds(gfx::Rect(0, 300, 400, 300));
@@ -802,7 +802,7 @@
 TEST_F(WorkspaceWindowResizerTest, DontDragOffBottomWithMultiDisplay) {
   // TODO: SetLayoutForCurrentDisplays() needs to ported to mash.
   // http://crbug.com/698043.
-  if (ShellPort::Get()->IsRunningInMash())
+  if (Shell::GetAshConfig() == Config::MASH)
     return;
 
   UpdateDisplay("800x600,800x600");
diff --git a/ash/wm_window.cc b/ash/wm_window.cc
index f009c10..aa950b0 100644
--- a/ash/wm_window.cc
+++ b/ash/wm_window.cc
@@ -6,11 +6,11 @@
 
 #include "ash/ash_constants.h"
 #include "ash/aura/aura_layout_manager_adapter.h"
+#include "ash/public/cpp/config.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/public/cpp/window_properties.h"
 #include "ash/root_window_controller.h"
 #include "ash/shell.h"
-#include "ash/shell_port.h"
 #include "ash/wm/resize_handle_window_targeter.h"
 #include "ash/wm/resize_shadow_controller.h"
 #include "ash/wm/widget_finder.h"
@@ -576,7 +576,7 @@
 }
 
 void WmWindow::CloseWidget() {
-  if (ShellPort::Get()->IsRunningInMash() &&
+  if (Shell::GetAshConfig() == Config::MASH &&
       aura_window()->GetProperty(kWidgetCreationTypeKey) ==
           WidgetCreationType::FOR_CLIENT) {
     // NOTE: in the FOR_CLIENT case there is not necessarily a widget associated
@@ -639,7 +639,7 @@
 }
 
 void WmWindow::ShowResizeShadow(int component) {
-  if (ShellPort::Get()->IsRunningInMash()) {
+  if (Shell::GetAshConfig() == Config::MASH) {
     // TODO: http://crbug.com/640773.
     return;
   }
@@ -650,7 +650,7 @@
 }
 
 void WmWindow::HideResizeShadow() {
-  if (ShellPort::Get()->IsRunningInMash()) {
+  if (Shell::GetAshConfig() == Config::MASH) {
     // TODO: http://crbug.com/640773.
     return;
   }
@@ -721,7 +721,7 @@
 
 void WmWindow::AddLimitedPreTargetHandler(ui::EventHandler* handler) {
   // In mus AddPreTargetHandler() only works for windows created by this client.
-  DCHECK(!ShellPort::Get()->IsRunningInMash() ||
+  DCHECK(Shell::GetAshConfig() != Config::MASH ||
          Shell::window_tree_client()->WasCreatedByThisClient(
              aura::WindowMus::Get(window_)));
   window_->AddPreTargetHandler(handler);
diff --git a/base/trace_event/memory_dump_manager.cc b/base/trace_event/memory_dump_manager.cc
index 795f9cbe..5f217ea 100644
--- a/base/trace_event/memory_dump_manager.cc
+++ b/base/trace_event/memory_dump_manager.cc
@@ -20,6 +20,7 @@
 #include "base/debug/stack_trace.h"
 #include "base/debug/thread_heap_usage_tracker.h"
 #include "base/memory/ptr_util.h"
+#include "base/optional.h"
 #include "base/strings/pattern.h"
 #include "base/strings/string_piece.h"
 #include "base/threading/thread.h"
@@ -83,7 +84,7 @@
 
 // Callback wrapper to hook upon the completion of RequestGlobalDump() and
 // inject trace markers.
-void OnGlobalDumpDone(MemoryDumpCallback wrapped_callback,
+void OnGlobalDumpDone(GlobalMemoryDumpCallback wrapped_callback,
                       uint64_t dump_guid,
                       bool success) {
   char guid_str[20];
@@ -439,7 +440,7 @@
 void MemoryDumpManager::RequestGlobalDump(
     MemoryDumpType dump_type,
     MemoryDumpLevelOfDetail level_of_detail,
-    const MemoryDumpCallback& callback) {
+    const GlobalMemoryDumpCallback& callback) {
   // Bail out immediately if tracing is not enabled at all or if the dump mode
   // is not allowed.
   if (!UNLIKELY(subtle::NoBarrier_Load(&memory_tracing_enabled_)) ||
@@ -462,7 +463,7 @@
       kTraceCategory, "GlobalMemoryDump", TRACE_ID_LOCAL(guid), "dump_type",
       MemoryDumpTypeToString(dump_type), "level_of_detail",
       MemoryDumpLevelOfDetailToString(level_of_detail));
-  MemoryDumpCallback wrapped_callback = Bind(&OnGlobalDumpDone, callback);
+  GlobalMemoryDumpCallback wrapped_callback = Bind(&OnGlobalDumpDone, callback);
 
   // The delegate will coordinate the IPC broadcast and at some point invoke
   // CreateProcessDump() to get a dump for the current process.
@@ -483,7 +484,7 @@
 void MemoryDumpManager::RequestGlobalDump(
     MemoryDumpType dump_type,
     MemoryDumpLevelOfDetail level_of_detail) {
-  RequestGlobalDump(dump_type, level_of_detail, MemoryDumpCallback());
+  RequestGlobalDump(dump_type, level_of_detail, GlobalMemoryDumpCallback());
 }
 
 bool MemoryDumpManager::IsDumpProviderRegisteredForTesting(
@@ -497,8 +498,9 @@
   return false;
 }
 
-void MemoryDumpManager::CreateProcessDump(const MemoryDumpRequestArgs& args,
-                                          const MemoryDumpCallback& callback) {
+void MemoryDumpManager::CreateProcessDump(
+    const MemoryDumpRequestArgs& args,
+    const ProcessMemoryDumpCallback& callback) {
   char guid_str[20];
   sprintf(guid_str, "0x%" PRIx64, args.dump_guid);
   TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(kTraceCategory, "ProcessMemoryDump",
@@ -740,7 +742,7 @@
 
   // The results struct to fill.
   // TODO(hjd): Transitional until we send the full PMD. See crbug.com/704203
-  MemoryDumpCallbackResult result;
+  base::Optional<MemoryDumpCallbackResult> result_opt;
 
   for (const auto& kv : pmd_async_state->process_dumps) {
     ProcessId pid = kv.first;  // kNullProcessId for the current process.
@@ -769,7 +771,7 @@
     if (pmd_async_state->req_args.level_of_detail ==
         MemoryDumpLevelOfDetail::DETAILED)
       continue;
-
+    MemoryDumpCallbackResult result;
     // TODO(hjd): Transitional until we send the full PMD. See crbug.com/704203
     if (pid == kNullProcessId) {
       result.chrome_dump.malloc_total_kb =
@@ -790,6 +792,7 @@
       auto& os_dump = result.extra_processes_dump[pid];
       FillOsDumpFromProcessMemoryDump(process_memory_dump, &os_dump);
     }
+    result_opt = result;
   }
 
   bool tracing_still_enabled;
@@ -801,7 +804,8 @@
   }
 
   if (!pmd_async_state->callback.is_null()) {
-    pmd_async_state->callback.Run(dump_guid, pmd_async_state->dump_successful);
+    pmd_async_state->callback.Run(dump_guid, pmd_async_state->dump_successful,
+                                  result_opt);
     pmd_async_state->callback.Reset();
   }
 
@@ -948,7 +952,7 @@
     MemoryDumpRequestArgs req_args,
     const MemoryDumpProviderInfo::OrderedSet& dump_providers,
     scoped_refptr<MemoryDumpSessionState> session_state,
-    MemoryDumpCallback callback,
+    ProcessMemoryDumpCallback callback,
     scoped_refptr<SingleThreadTaskRunner> dump_thread_task_runner)
     : req_args(req_args),
       session_state(std::move(session_state)),
diff --git a/base/trace_event/memory_dump_manager.h b/base/trace_event/memory_dump_manager.h
index 4e5f8a2..d359a8c3 100644
--- a/base/trace_event/memory_dump_manager.h
+++ b/base/trace_event/memory_dump_manager.h
@@ -113,7 +113,7 @@
   // successful).
   void RequestGlobalDump(MemoryDumpType dump_type,
                          MemoryDumpLevelOfDetail level_of_detail,
-                         const MemoryDumpCallback& callback);
+                         const GlobalMemoryDumpCallback& callback);
 
   // Same as above (still asynchronous), but without callback.
   void RequestGlobalDump(MemoryDumpType dump_type,
@@ -178,7 +178,7 @@
         MemoryDumpRequestArgs req_args,
         const MemoryDumpProviderInfo::OrderedSet& dump_providers,
         scoped_refptr<MemoryDumpSessionState> session_state,
-        MemoryDumpCallback callback,
+        ProcessMemoryDumpCallback callback,
         scoped_refptr<SingleThreadTaskRunner> dump_thread_task_runner);
     ~ProcessMemoryDumpAsyncState();
 
@@ -205,7 +205,7 @@
     scoped_refptr<MemoryDumpSessionState> session_state;
 
     // Callback passed to the initial call to CreateProcessDump().
-    MemoryDumpCallback callback;
+    ProcessMemoryDumpCallback callback;
 
     // The |success| field that will be passed as argument to the |callback|.
     bool dump_successful;
@@ -242,7 +242,7 @@
   // |callback| will be invoked asynchronously upon completion on the same
   // thread on which CreateProcessDump() was called.
   void CreateProcessDump(const MemoryDumpRequestArgs& args,
-                         const MemoryDumpCallback& callback);
+                         const ProcessMemoryDumpCallback& callback);
 
   // Calls InvokeOnMemoryDump() for the next MDP on the task runner specified by
   // the MDP while registration. On failure to do so, skips and continues to
@@ -317,14 +317,15 @@
   MemoryDumpManagerDelegate() {}
   virtual ~MemoryDumpManagerDelegate() {}
 
-  virtual void RequestGlobalMemoryDump(const MemoryDumpRequestArgs& args,
-                                       const MemoryDumpCallback& callback) = 0;
+  virtual void RequestGlobalMemoryDump(
+      const MemoryDumpRequestArgs& args,
+      const GlobalMemoryDumpCallback& callback) = 0;
 
   virtual bool IsCoordinator() const = 0;
 
  protected:
   void CreateProcessDump(const MemoryDumpRequestArgs& args,
-                         const MemoryDumpCallback& callback) {
+                         const ProcessMemoryDumpCallback& callback) {
     MemoryDumpManager::GetInstance()->CreateProcessDump(args, callback);
   }
 
diff --git a/base/trace_event/memory_dump_manager_unittest.cc b/base/trace_event/memory_dump_manager_unittest.cc
index 48f747d..ba76b9a 100644
--- a/base/trace_event/memory_dump_manager_unittest.cc
+++ b/base/trace_event/memory_dump_manager_unittest.cc
@@ -116,6 +116,18 @@
   event.Wait();
 }
 
+// Adapts a ProcessMemoryDumpCallback into a GlobalMemoryDumpCallback by
+// trimming off the result argument and calling the global callback.
+// TODO (fmeawad): we should keep the results for verification, but currently
+// all results are empty.
+void ProcessDumpCallbackAdapter(
+    GlobalMemoryDumpCallback callback,
+    uint64_t dump_guid,
+    bool success,
+    const base::Optional<base::trace_event::MemoryDumpCallbackResult>&) {
+  callback.Run(dump_guid, success);
+}
+
 // Testing MemoryDumpManagerDelegate which, by default, short-circuits dump
 // requests locally to the MemoryDumpManager instead of performing IPC dances.
 class MemoryDumpManagerDelegateForTesting : public MemoryDumpManagerDelegate {
@@ -123,13 +135,17 @@
   MemoryDumpManagerDelegateForTesting(bool is_coordinator)
       : is_coordinator_(is_coordinator) {
     ON_CALL(*this, RequestGlobalMemoryDump(_, _))
-        .WillByDefault(Invoke(
-            this, &MemoryDumpManagerDelegateForTesting::CreateProcessDump));
+        .WillByDefault(Invoke([this](const MemoryDumpRequestArgs& args,
+                                     const GlobalMemoryDumpCallback& callback) {
+          ProcessMemoryDumpCallback process_callback =
+              Bind(&ProcessDumpCallbackAdapter, callback);
+          CreateProcessDump(args, process_callback);
+        }));
   }
 
   MOCK_METHOD2(RequestGlobalMemoryDump,
                void(const MemoryDumpRequestArgs& args,
-                    const MemoryDumpCallback& callback));
+                    const GlobalMemoryDumpCallback& callback));
 
   bool IsCoordinator() const override { return is_coordinator_; }
 
@@ -234,12 +250,14 @@
     TraceLog::DeleteForTesting();
   }
 
-  // Turns a Closure into a MemoryDumpCallback, keeping track of the callback
-  // result and taking care of posting the closure on the correct task runner.
-  void DumpCallbackAdapter(scoped_refptr<SingleThreadTaskRunner> task_runner,
-                           Closure closure,
-                           uint64_t dump_guid,
-                           bool success) {
+  // Turns a Closure into a GlobalMemoryDumpCallback, keeping track of the
+  // callback result and taking care of posting the closure on the correct task
+  // runner.
+  void GlobalDumpCallbackAdapter(
+      scoped_refptr<SingleThreadTaskRunner> task_runner,
+      Closure closure,
+      uint64_t dump_guid,
+      bool success) {
     last_callback_success_ = success;
     task_runner->PostTask(FROM_HERE, closure);
   }
@@ -254,9 +272,9 @@
   void RequestGlobalDumpAndWait(MemoryDumpType dump_type,
                                 MemoryDumpLevelOfDetail level_of_detail) {
     RunLoop run_loop;
-    MemoryDumpCallback callback =
-        Bind(&MemoryDumpManagerTest::DumpCallbackAdapter, Unretained(this),
-             ThreadTaskRunnerHandle::Get(), run_loop.QuitClosure());
+    GlobalMemoryDumpCallback callback = Bind(
+        &MemoryDumpManagerTest::GlobalDumpCallbackAdapter, Unretained(this),
+        ThreadTaskRunnerHandle::Get(), run_loop.QuitClosure());
     mdm_->RequestGlobalDump(dump_type, level_of_detail, callback);
     run_loop.Run();
   }
@@ -313,9 +331,10 @@
   EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory);
   EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(3);
   EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(3).WillRepeatedly(Return(true));
-  for (int i = 0; i < 3; ++i)
+  for (int i = 0; i < 3; ++i) {
     RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
                              MemoryDumpLevelOfDetail::DETAILED);
+  }
   DisableTracing();
 
   mdm_->UnregisterDumpProvider(&mdp);
@@ -980,11 +999,11 @@
   EXPECT_CALL(delegate, RequestGlobalMemoryDump(IsLightDump(), _))
       .Times(kHeavyDumpRate - 2);
   EXPECT_CALL(delegate, RequestGlobalMemoryDump(IsLightDump(), _))
-      .WillOnce(Invoke(
-          [test_task_runner, quit_closure](const MemoryDumpRequestArgs& args,
-                                           const MemoryDumpCallback& callback) {
-            test_task_runner->PostTask(FROM_HERE, quit_closure);
-          }));
+      .WillOnce(Invoke([test_task_runner, quit_closure](
+                           const MemoryDumpRequestArgs& args,
+                           const GlobalMemoryDumpCallback& callback) {
+        test_task_runner->PostTask(FROM_HERE, quit_closure);
+      }));
 
   // Swallow all the final spurious calls until tracing gets disabled.
   EXPECT_CALL(delegate, RequestGlobalMemoryDump(_, _)).Times(AnyNumber());
@@ -1042,8 +1061,8 @@
 
   last_callback_success_ = true;
   RunLoop run_loop;
-  MemoryDumpCallback callback =
-      Bind(&MemoryDumpManagerTest::DumpCallbackAdapter, Unretained(this),
+  GlobalMemoryDumpCallback callback =
+      Bind(&MemoryDumpManagerTest::GlobalDumpCallbackAdapter, Unretained(this),
            ThreadTaskRunnerHandle::Get(), run_loop.QuitClosure());
   mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED,
                           MemoryDumpLevelOfDetail::DETAILED, callback);
@@ -1074,9 +1093,11 @@
 
   EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _))
       .WillOnce(Invoke([this](const MemoryDumpRequestArgs& args,
-                              const MemoryDumpCallback& callback) {
+                              const GlobalMemoryDumpCallback& callback) {
         DisableTracing();
-        delegate_->CreateProcessDump(args, callback);
+        ProcessMemoryDumpCallback process_callback =
+            Bind(&ProcessDumpCallbackAdapter, callback);
+        delegate_->CreateProcessDump(args, process_callback);
       }));
 
   // If tracing is disabled for current session CreateProcessDump() should NOT
@@ -1244,11 +1265,11 @@
   EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(IsBackgroundDump(), _))
       .Times(5);
   EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(IsBackgroundDump(), _))
-      .WillOnce(Invoke(
-          [test_task_runner, quit_closure](const MemoryDumpRequestArgs& args,
-                                           const MemoryDumpCallback& callback) {
-            test_task_runner->PostTask(FROM_HERE, quit_closure);
-          }));
+      .WillOnce(Invoke([test_task_runner, quit_closure](
+                           const MemoryDumpRequestArgs& args,
+                           const GlobalMemoryDumpCallback& callback) {
+        test_task_runner->PostTask(FROM_HERE, quit_closure);
+      }));
   EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(AnyNumber());
 
   EnableTracingWithTraceConfig(
diff --git a/base/trace_event/memory_dump_request_args.cc b/base/trace_event/memory_dump_request_args.cc
index f2744007..0b525d4 100644
--- a/base/trace_event/memory_dump_request_args.cc
+++ b/base/trace_event/memory_dump_request_args.cc
@@ -62,6 +62,9 @@
 
 MemoryDumpCallbackResult::MemoryDumpCallbackResult() {}
 
+MemoryDumpCallbackResult::MemoryDumpCallbackResult(
+    const MemoryDumpCallbackResult&) = default;
+
 MemoryDumpCallbackResult::~MemoryDumpCallbackResult() {}
 
 }  // namespace trace_event
diff --git a/base/trace_event/memory_dump_request_args.h b/base/trace_event/memory_dump_request_args.h
index fd42608..0f42a1853 100644
--- a/base/trace_event/memory_dump_request_args.h
+++ b/base/trace_event/memory_dump_request_args.h
@@ -14,6 +14,7 @@
 
 #include "base/base_export.h"
 #include "base/callback.h"
+#include "base/optional.h"
 #include "base/process/process_handle.h"
 
 namespace base {
@@ -78,7 +79,7 @@
 // Summarises information about memory use as seen by a single process.
 // This information will eventually be passed to a service to be colated
 // and reported.
-struct MemoryDumpCallbackResult {
+struct BASE_EXPORT MemoryDumpCallbackResult {
   struct OSMemDump {
     uint32_t resident_set_kb = 0;
   };
@@ -98,10 +99,17 @@
   std::map<ProcessId, OSMemDump> extra_processes_dump;
 
   MemoryDumpCallbackResult();
+  MemoryDumpCallbackResult(const MemoryDumpCallbackResult&);
   ~MemoryDumpCallbackResult();
 };
 
-using MemoryDumpCallback = Callback<void(uint64_t dump_guid, bool success)>;
+using GlobalMemoryDumpCallback =
+    Callback<void(uint64_t dump_guid, bool success)>;
+
+using ProcessMemoryDumpCallback =
+    Callback<void(uint64_t dump_guid,
+                  bool success,
+                  const Optional<MemoryDumpCallbackResult>& result)>;
 
 BASE_EXPORT const char* MemoryDumpTypeToString(const MemoryDumpType& dump_type);
 
diff --git a/build/secondary/third_party/crashpad/crashpad/handler/BUILD.gn b/build/secondary/third_party/crashpad/crashpad/handler/BUILD.gn
index d2ce8b9..c4ca09c 100644
--- a/build/secondary/third_party/crashpad/crashpad/handler/BUILD.gn
+++ b/build/secondary/third_party/crashpad/crashpad/handler/BUILD.gn
@@ -14,6 +14,8 @@
     "mac/exception_handler_server.h",
     "prune_crash_reports_thread.cc",
     "prune_crash_reports_thread.h",
+    "user_stream_data_source.cc",
+    "user_stream_data_source.h",
     "win/crash_report_exception_handler.cc",
     "win/crash_report_exception_handler.h",
   ]
diff --git a/build/secondary/third_party/crashpad/crashpad/util/BUILD.gn b/build/secondary/third_party/crashpad/crashpad/util/BUILD.gn
index 398a115..7695e18a 100644
--- a/build/secondary/third_party/crashpad/crashpad/util/BUILD.gn
+++ b/build/secondary/third_party/crashpad/crashpad/util/BUILD.gn
@@ -125,6 +125,8 @@
     "posix/drop_privileges.h",
     "posix/process_info.h",
     "posix/process_info_mac.cc",
+    "posix/scoped_dir.cc",
+    "posix/scoped_dir.h",
     "posix/scoped_mmap.cc",
     "posix/scoped_mmap.h",
     "posix/signals.cc",
diff --git a/cc/input/main_thread_scrolling_reason.h b/cc/input/main_thread_scrolling_reason.h
index 9746d942..a8c586e 100644
--- a/cc/input/main_thread_scrolling_reason.h
+++ b/cc/input/main_thread_scrolling_reason.h
@@ -37,12 +37,14 @@
     // These *AndLCDText reasons are due to subpixel text rendering which can
     // only be applied by blending glyphs with the background at a specific
     // screen position; transparency and transforms break this.
+    kNonCompositedReasonsFirst = 16,
     kHasOpacityAndLCDText = 1 << 16,
     kHasTransformAndLCDText = 1 << 17,
     kBackgroundNotOpaqueInRectAndLCDText = 1 << 18,
     kHasBorderRadius = 1 << 19,
     kHasClipRelatedProperty = 1 << 20,
     kHasBoxShadowFromNonRootLayer = 1 << 21,
+    kNonCompositedReasonsLast = 21,
 
     // Transient scrolling reasons. These are computed for each scroll begin.
     kNonFastScrollableRegion = 1 << 5,
@@ -60,6 +62,11 @@
     kMainThreadScrollingReasonCount = 22,
   };
 
+  static const uint32_t kNonCompositedReasons =
+      kHasOpacityAndLCDText | kHasTransformAndLCDText |
+      kBackgroundNotOpaqueInRectAndLCDText | kHasBorderRadius |
+      kHasClipRelatedProperty | kHasBoxShadowFromNonRootLayer;
+
   // Returns true if the given MainThreadScrollingReason can be set by the main
   // thread.
   static bool MainThreadCanSetScrollReasons(uint32_t reasons) {
@@ -67,10 +74,7 @@
         kNotScrollingOnMain | kHasBackgroundAttachmentFixedObjects |
         kHasNonLayerViewportConstrainedObjects | kThreadedScrollingDisabled |
         kScrollbarScrolling | kPageOverlay | kHandlingScrollFromMainThread |
-        kCustomScrollbarScrolling | kHasOpacityAndLCDText |
-        kHasTransformAndLCDText | kBackgroundNotOpaqueInRectAndLCDText |
-        kHasBorderRadius | kHasClipRelatedProperty |
-        kHasBoxShadowFromNonRootLayer;
+        kCustomScrollbarScrolling;
     return (reasons & reasons_set_by_main_thread) == reasons;
   }
 
@@ -84,6 +88,12 @@
     return (reasons & reasons_set_by_compositor) == reasons;
   }
 
+  // Returns true if there are any reasons that prevented the scroller
+  // from being composited.
+  static bool HasNonCompositedScrollReasons(uint32_t reasons) {
+    return (reasons & kNonCompositedReasons) != 0;
+  }
+
   static std::string mainThreadScrollingReasonsAsText(uint32_t reasons) {
     base::trace_event::TracedValue tracedValue;
     mainThreadScrollingReasonsAsTracedValue(reasons, &tracedValue);
@@ -148,20 +158,6 @@
       tracedValue->AppendString("Page-based scrolling");
     tracedValue->EndArray();
   }
-
-  // For a given reason, return its index in enum
-  static int getReasonIndex(uint32_t reason) {
-    // Multiple reasons provided
-    if (reason & (reason - 1))
-      return -1;
-
-    int index = -1;
-    while (reason > 0) {
-      reason = reason >> 1;
-      ++index;
-    }
-    return index;
-  }
 };
 
 }  // namespace cc
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillKeyboardAccessoryBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillKeyboardAccessoryBridge.java
index a2109ba..b339c1a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillKeyboardAccessoryBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillKeyboardAccessoryBridge.java
@@ -57,6 +57,9 @@
     }
 
     @Override
+    public void accessibilityFocusCleared() {}
+
+    @Override
     public void onClick(DialogInterface dialog, int which) {
         assert which == DialogInterface.BUTTON_POSITIVE;
         if (mNativeAutofillKeyboardAccessory == 0) return;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillPopupBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillPopupBridge.java
index 2aa55b5..856c757 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillPopupBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillPopupBridge.java
@@ -81,6 +81,13 @@
     }
 
     @Override
+    public void accessibilityFocusCleared() {
+        if (mBrowserAccessibilityManager != null) {
+            mBrowserAccessibilityManager.onAutofillPopupAccessibilityFocusCleared();
+        }
+    }
+
+    @Override
     public void onClick(DialogInterface dialog, int which) {
         assert which == DialogInterface.BUTTON_POSITIVE;
         nativeDeletionConfirmed(mNativeAutofillPopup);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/PersonalDataManager.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/PersonalDataManager.java
index 3be63e69..2428e99 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/PersonalDataManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/PersonalDataManager.java
@@ -10,7 +10,6 @@
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
-import org.chromium.base.annotations.SuppressFBWarnings;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ResourceId;
 import org.chromium.chrome.browser.preferences.autofill.AutofillAndPaymentsPreferences;
@@ -19,7 +18,6 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
-import java.util.concurrent.TimeUnit;
 
 /**
  * Android wrapper of the PersonalDataManager which provides access from the Java
@@ -64,20 +62,6 @@
     }
 
     /**
-     * Callback for subKeys request.
-     */
-    public interface GetSubKeysRequestDelegate {
-        /**
-         * Called when the sub-keys are received sucessfully.
-         * Here the sub-keys are admin areas.
-         *
-         * @param subKeys The subKeys.
-         */
-        @CalledByNative("GetSubKeysRequestDelegate")
-        void onSubKeysReceived(String[] subKeys);
-    }
-
-    /**
      * Callback for normalized addresses.
      */
     public interface NormalizedAddressRequestDelegate {
@@ -528,8 +512,6 @@
 
     private static PersonalDataManager sManager;
 
-    // Suppress FindBugs warning, since |sManager| is only used on the UI thread.
-    @SuppressFBWarnings("LI_LAZY_INIT_STATIC")
     public static PersonalDataManager getInstance() {
         ThreadUtils.assertOnUiThread();
         if (sManager == null) {
@@ -823,42 +805,16 @@
      *
      * @param regionCode The code of the region for which to load the rules.
      */
-    public void loadRulesForAddressNormalization(String regionCode) {
+    public void loadRulesForRegion(String regionCode) {
         ThreadUtils.assertOnUiThread();
-        nativeLoadRulesForAddressNormalization(mPersonalDataManagerAndroid, regionCode);
+        nativeLoadRulesForRegion(mPersonalDataManagerAndroid, regionCode);
     }
 
     /**
-     * Starts loading the sub-key request rules for the specified {@code regionCode}.
-     *
-     * @param regionCode The code of the region for which to load the rules.
-     */
-    public void loadRulesForSubKeys(String regionCode) {
-        ThreadUtils.assertOnUiThread();
-        nativeLoadRulesForSubKeys(mPersonalDataManagerAndroid, regionCode);
-    }
-
-    /**
-     * Starts loading the sub keys for the specified {@code regionCode}.
-     *
-     * @param regionCode The code of the region for which to load the sub keys.
-     */
-    public void getRegionSubKeys(String regionCode, GetSubKeysRequestDelegate delegate) {
-        ThreadUtils.assertOnUiThread();
-        nativeStartRegionSubKeysRequest(mPersonalDataManagerAndroid, regionCode, delegate);
-    }
-
-    /** Cancels the pending sub keys request. */
-    public void cancelPendingGetSubKeys() {
-        ThreadUtils.assertOnUiThread();
-        nativeCancelPendingGetSubKeys(mPersonalDataManagerAndroid);
-    }
-
-    /**
-     * Normalizes the address of the profile associated with the {@code guid} if the rules
-     * associated with the {@code regionCode} are done loading. Otherwise sets up the callback to
-     * start normalizing the address when the rules are loaded. The normalized profile will be sent
-     * to the {@code delegate}. If the profile is not normalized in the specified
+     * Normalizes the address of the {@code profile} if the rules associated with the
+     * {@code regionCode} are done loading. Otherwise sets up the callback to start normalizing the
+     * address when the rules are loaded. The normalized profile will be sent to the
+     * {@code delegate}. If the profile is not normalized in the specified
      * {@code sNormalizationTimeoutSeconds}, the {@code delegate} will be notified.
      *
      * @param profile The profile to normalize.
@@ -932,10 +888,6 @@
         sNormalizationTimeoutSeconds = timeout;
     }
 
-    public static long getRequestTimeoutMS() {
-        return TimeUnit.SECONDS.toMillis(sNormalizationTimeoutSeconds);
-    }
-
     private native long nativeInit();
     private native boolean nativeIsDataLoaded(long nativePersonalDataManagerAndroid);
     private native String[] nativeGetProfileGUIDsForSettings(long nativePersonalDataManagerAndroid);
@@ -995,15 +947,11 @@
             long nativePersonalDataManagerAndroid, String guid);
     private native void nativeGetFullCardForPaymentRequest(long nativePersonalDataManagerAndroid,
             WebContents webContents, CreditCard card, FullCardRequestDelegate delegate);
-    private native void nativeLoadRulesForAddressNormalization(
-            long nativePersonalDataManagerAndroid, String regionCode);
-    private native void nativeLoadRulesForSubKeys(
+    private native void nativeLoadRulesForRegion(
             long nativePersonalDataManagerAndroid, String regionCode);
     private native void nativeStartAddressNormalization(long nativePersonalDataManagerAndroid,
             AutofillProfile profile, String regionCode, int timeoutSeconds,
             NormalizedAddressRequestDelegate delegate);
-    private native void nativeStartRegionSubKeysRequest(long nativePersonalDataManagerAndroid,
-            String regionCode, GetSubKeysRequestDelegate delegate);
     private static native boolean nativeHasProfiles(long nativePersonalDataManagerAndroid);
     private static native boolean nativeHasCreditCards(long nativePersonalDataManagerAndroid);
     private static native boolean nativeIsAutofillEnabled();
@@ -1012,5 +960,4 @@
     private static native boolean nativeIsPaymentsIntegrationEnabled();
     private static native void nativeSetPaymentsIntegrationEnabled(boolean enable);
     private static native String nativeToCountryCode(String countryName);
-    private static native void nativeCancelPendingGetSubKeys(long nativePersonalDataManagerAndroid);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java
index 471fdd5..a26b021 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java
@@ -368,7 +368,7 @@
         ChromeNotificationBuilder builder =
                 NotificationBuilderFactory
                         .createChromeNotificationBuilder(
-                                true /* preferCompat */, ChannelsInitializer.CHANNEL_ID_BROWSER)
+                                true /* preferCompat */, ChannelsInitializer.CHANNEL_ID_DOWNLOADS)
                         .setContentTitle(
                                 context.getString(R.string.download_notification_summary_title))
                         .setSubText(context.getString(R.string.menu_downloads))
@@ -1079,7 +1079,7 @@
         ChromeNotificationBuilder builder =
                 NotificationBuilderFactory
                         .createChromeNotificationBuilder(
-                                true /* preferCompat */, ChannelsInitializer.CHANNEL_ID_BROWSER)
+                                true /* preferCompat */, ChannelsInitializer.CHANNEL_ID_DOWNLOADS)
                         .setContentTitle(
                                 DownloadUtils.getAbbreviatedFileName(title, MAX_FILE_NAME_LENGTH))
                         .setSmallIcon(iconId)
@@ -1217,6 +1217,8 @@
                     ContentId id = getContentIdFromIntent(intent);
                     if (LegacyHelpers.isLegacyOfflinePage(id)) {
                         OfflinePageDownloadBridge.openDownloadedPage(id);
+                    } else if (id != null) {
+                        OfflineContentAggregatorNotificationBridgeUiFactory.instance().openItem(id);
                     }
                 } else {
                         Log.e(TAG, "Unrecognized intent action.", intent);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadSnackbarController.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadSnackbarController.java
index 17bd109..5acb1ba 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadSnackbarController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadSnackbarController.java
@@ -13,9 +13,11 @@
 import org.chromium.base.BuildInfo;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.customtabs.CustomTabActivity;
+import org.chromium.chrome.browser.download.items.OfflineContentAggregatorNotificationBridgeUiFactory;
 import org.chromium.chrome.browser.offlinepages.downloads.OfflinePageDownloadBridge;
 import org.chromium.chrome.browser.snackbar.Snackbar;
 import org.chromium.chrome.browser.snackbar.SnackbarManager;
+import org.chromium.components.offline_items_collection.LegacyHelpers;
 
 /**
  * Class for displaying a snackbar when a download completes.
@@ -50,18 +52,23 @@
             DownloadManagerService.openDownloadsPage(mContext);
             return;
         }
-        final ActionDataInfo download = (ActionDataInfo) actionData;
-        if (download.downloadInfo.isOfflinePage()) {
-            OfflinePageDownloadBridge.openDownloadedPage(download.downloadInfo.getContentId());
-            return;
-        }
-        if (download.usesAndroidDownloadManager) {
-            mContext.startActivity(new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS).addFlags(
-                    Intent.FLAG_ACTIVITY_NEW_TASK));
-            return;
-        }
+
         DownloadManagerService manager = DownloadManagerService.getDownloadManagerService();
-        manager.openDownloadedContent(download.downloadInfo, download.systemDownloadId);
+        final ActionDataInfo download = (ActionDataInfo) actionData;
+        if (LegacyHelpers.isLegacyOfflinePage(download.downloadInfo.getContentId())) {
+            OfflinePageDownloadBridge.openDownloadedPage(download.downloadInfo.getContentId());
+        } else if (LegacyHelpers.isLegacyDownload(download.downloadInfo.getContentId())) {
+            if (download.usesAndroidDownloadManager) {
+                mContext.startActivity(new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS)
+                                               .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+            } else {
+                manager.openDownloadedContent(download.downloadInfo, download.systemDownloadId);
+            }
+        } else {
+            OfflineContentAggregatorNotificationBridgeUiFactory.instance().openItem(
+                    download.downloadInfo.getContentId());
+        }
+
         if (download.notificationId != INVALID_NOTIFICATION_ID) {
             manager.getDownloadNotifier().removeDownloadNotification(
                     download.notificationId, download.downloadInfo);
@@ -99,7 +106,8 @@
         // TODO(qinmin): Coalesce snackbars if multiple downloads finish at the same time.
         snackbar.setDuration(SNACKBAR_DURATION_IN_MILLISECONDS).setSingleLine(false);
         ActionDataInfo info = null;
-        if (canBeResolved || downloadInfo.isOfflinePage() || usesAndroidDownloadManager) {
+        if (canBeResolved || !LegacyHelpers.isLegacyDownload(downloadInfo.getContentId())
+                || usesAndroidDownloadManager) {
             info = new ActionDataInfo(downloadInfo, notificationId, downloadId,
                     usesAndroidDownloadManager);
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/items/OfflineContentAggregatorNotificationBridgeUi.java b/chrome/android/java/src/org/chromium/chrome/browser/download/items/OfflineContentAggregatorNotificationBridgeUi.java
index d60f9bf..23c7124 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/items/OfflineContentAggregatorNotificationBridgeUi.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/items/OfflineContentAggregatorNotificationBridgeUi.java
@@ -41,6 +41,11 @@
         destroyServiceDelegate();
     }
 
+    /** @see OfflineContentProvider#openItem(ContentId) */
+    public void openItem(ContentId id) {
+        mProvider.openItem(id);
+    }
+
     // OfflineContentProvider.Observer implementation.
     @Override
     public void onItemsAvailable() {}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoNotificationManager.java b/chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoNotificationManager.java
index 0b1cd04..046acf53 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoNotificationManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoNotificationManager.java
@@ -35,7 +35,7 @@
         ChromeNotificationBuilder builder =
                 NotificationBuilderFactory
                         .createChromeNotificationBuilder(
-                                true /* preferCompat */, ChannelsInitializer.CHANNEL_ID_BROWSER)
+                                true /* preferCompat */, ChannelsInitializer.CHANNEL_ID_INCOGNITO)
                         .setContentTitle(title)
                         .setContentIntent(
                                 IncognitoNotificationService.getRemoveAllIncognitoTabsIntent(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/MediaCaptureNotificationService.java b/chrome/android/java/src/org/chromium/chrome/browser/media/MediaCaptureNotificationService.java
index f6aac38a..9f9d302 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/MediaCaptureNotificationService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/MediaCaptureNotificationService.java
@@ -165,7 +165,7 @@
         ChromeNotificationBuilder builder =
                 NotificationBuilderFactory
                         .createChromeNotificationBuilder(
-                                true /* preferCompat */, ChannelsInitializer.CHANNEL_ID_BROWSER)
+                                true /* preferCompat */, ChannelsInitializer.CHANNEL_ID_MEDIA)
                         .setAutoCancel(false)
                         .setOngoing(true)
                         .setContentTitle(mContext.getString(R.string.app_name))
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastSessionImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastSessionImpl.java
index 3541d437..460045ae 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastSessionImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastSessionImpl.java
@@ -295,39 +295,48 @@
     public CastSessionInfo getSessionInfo() {
         if (isApiClientInvalid()) return null;
 
-        CastSessionInfo.VolumeInfo.Builder volumeBuilder =
-                new CastSessionInfo.VolumeInfo.Builder()
-                        .setLevel(Cast.CastApi.getVolume(mApiClient))
-                        .setMuted(Cast.CastApi.isMute(mApiClient));
+        // Due to a Google Play Services issue, the api client might still get disconnected so that
+        // calls like {@link Cast.CastApi#getVolume()} will throw an {@link IllegalStateException}.
+        // Until the issue is fixed, catch the exception and return null if it was thrown instead of
+        // crashing. See https://crbug.com/708964 for details.
+        try {
+            CastSessionInfo.VolumeInfo.Builder volumeBuilder =
+                    new CastSessionInfo.VolumeInfo.Builder()
+                            .setLevel(Cast.CastApi.getVolume(mApiClient))
+                            .setMuted(Cast.CastApi.isMute(mApiClient));
 
-        CastSessionInfo.ReceiverInfo.Builder receiverBuilder =
-                new CastSessionInfo.ReceiverInfo.Builder()
-                        .setLabel(mCastDevice.getDeviceId())
-                        .setFriendlyName(mCastDevice.getFriendlyName())
-                        .setVolume(volumeBuilder.build())
-                        .setIsActiveInput(Cast.CastApi.getActiveInputState(mApiClient))
-                        .setDisplayStatus(null)
-                        .setReceiverType("cast")
-                        .addCapabilities(getCapabilities(mCastDevice));
+            CastSessionInfo.ReceiverInfo.Builder receiverBuilder =
+                    new CastSessionInfo.ReceiverInfo.Builder()
+                            .setLabel(mCastDevice.getDeviceId())
+                            .setFriendlyName(mCastDevice.getFriendlyName())
+                            .setVolume(volumeBuilder.build())
+                            .setIsActiveInput(Cast.CastApi.getActiveInputState(mApiClient))
+                            .setDisplayStatus(null)
+                            .setReceiverType("cast")
+                            .addCapabilities(getCapabilities(mCastDevice));
 
-        CastSessionInfo.Builder sessionInfoBuilder =
-                new CastSessionInfo.Builder()
-                        .setSessionId(mSessionId)
-                        .setStatusText(mApplicationStatus)
-                        .setReceiver(receiverBuilder.build())
-                        .setStatus("connected")
-                        .setTransportId("web-4")
-                        .addNamespaces(mNamespaces);
+            CastSessionInfo.Builder sessionInfoBuilder =
+                    new CastSessionInfo.Builder()
+                            .setSessionId(mSessionId)
+                            .setStatusText(mApplicationStatus)
+                            .setReceiver(receiverBuilder.build())
+                            .setStatus("connected")
+                            .setTransportId("web-4")
+                            .addNamespaces(mNamespaces);
 
-        if (mApplicationMetadata != null) {
-            sessionInfoBuilder.setAppId(mApplicationMetadata.getApplicationId())
-                    .setDisplayName(mApplicationMetadata.getName());
-        } else {
-            sessionInfoBuilder.setAppId(mSource.getApplicationId())
-                    .setDisplayName(mCastDevice.getFriendlyName());
+            if (mApplicationMetadata != null) {
+                sessionInfoBuilder.setAppId(mApplicationMetadata.getApplicationId())
+                        .setDisplayName(mApplicationMetadata.getName());
+            } else {
+                sessionInfoBuilder.setAppId(mSource.getApplicationId())
+                        .setDisplayName(mCastDevice.getFriendlyName());
+            }
+
+            return sessionInfoBuilder.build();
+        } catch (IllegalStateException e) {
+            Log.e(TAG, "Couldn't get session info", e);
+            return null;
         }
-
-        return sessionInfoBuilder.build();
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java b/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java
index 659d14a0..17ba5e0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java
@@ -771,7 +771,7 @@
 
     private void updateNotificationBuilder() {
         mNotificationBuilder = NotificationBuilderFactory.createChromeNotificationBuilder(
-                true /* preferCompat */, ChannelsInitializer.CHANNEL_ID_BROWSER);
+                true /* preferCompat */, ChannelsInitializer.CHANNEL_ID_MEDIA);
         setMediaStyleLayoutForNotificationBuilder(mNotificationBuilder);
 
         mNotificationBuilder.setSmallIcon(mMediaNotificationInfo.notificationSmallIcon);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/ChannelsInitializer.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/ChannelsInitializer.java
index f3f21c9..afd4d32 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/ChannelsInitializer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/ChannelsInitializer.java
@@ -28,10 +28,14 @@
 
     // To define a new channel, add the channel ID to this StringDef and add a new entry to
     // PredefinedChannels.MAP below with the appropriate channel parameters.
-    @StringDef({CHANNEL_ID_BROWSER, CHANNEL_ID_SITES})
+    @StringDef({CHANNEL_ID_BROWSER, CHANNEL_ID_DOWNLOADS, CHANNEL_ID_INCOGNITO, CHANNEL_ID_MEDIA,
+            CHANNEL_ID_SITES})
     @Retention(RetentionPolicy.SOURCE)
     public @interface ChannelId {}
     public static final String CHANNEL_ID_BROWSER = "browser";
+    public static final String CHANNEL_ID_DOWNLOADS = "downloads";
+    public static final String CHANNEL_ID_INCOGNITO = "incognito";
+    public static final String CHANNEL_ID_MEDIA = "media";
     public static final String CHANNEL_ID_SITES = "sites";
 
     @StringDef({CHANNEL_GROUP_ID_GENERAL})
@@ -53,6 +57,18 @@
                     new Channel(CHANNEL_ID_BROWSER,
                             org.chromium.chrome.R.string.notification_category_browser,
                             NotificationManager.IMPORTANCE_LOW, CHANNEL_GROUP_ID_GENERAL));
+            map.put(CHANNEL_ID_DOWNLOADS,
+                    new Channel(CHANNEL_ID_DOWNLOADS,
+                            org.chromium.chrome.R.string.notification_category_downloads,
+                            NotificationManager.IMPORTANCE_LOW, CHANNEL_GROUP_ID_GENERAL));
+            map.put(CHANNEL_ID_INCOGNITO,
+                    new Channel(CHANNEL_ID_INCOGNITO,
+                            org.chromium.chrome.R.string.notification_category_incognito,
+                            NotificationManager.IMPORTANCE_LOW, CHANNEL_GROUP_ID_GENERAL));
+            map.put(CHANNEL_ID_MEDIA,
+                    new Channel(CHANNEL_ID_MEDIA,
+                            org.chromium.chrome.R.string.notification_category_media,
+                            NotificationManager.IMPORTANCE_LOW, CHANNEL_GROUP_ID_GENERAL));
             map.put(CHANNEL_ID_SITES,
                     new Channel(CHANNEL_ID_SITES,
                             org.chromium.chrome.R.string.notification_category_sites,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/AddressEditor.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/AddressEditor.java
index 9ec6bb4..4fc0ae9c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/AddressEditor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/AddressEditor.java
@@ -4,7 +4,6 @@
 
 package org.chromium.chrome.browser.payments;
 
-import android.app.ProgressDialog;
 import android.os.Handler;
 import android.util.Pair;
 
@@ -12,7 +11,6 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.autofill.PersonalDataManager;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
-import org.chromium.chrome.browser.autofill.PersonalDataManager.GetSubKeysRequestDelegate;
 import org.chromium.chrome.browser.autofill.PhoneNumberUtil;
 import org.chromium.chrome.browser.payments.ui.EditorFieldModel;
 import org.chromium.chrome.browser.payments.ui.EditorFieldModel.EditorFieldValidator;
@@ -33,27 +31,15 @@
 /**
  * An address editor. Can be used for either shipping or billing address editing.
  */
-public class AddressEditor
-        extends EditorBase<AutofillAddress> implements GetSubKeysRequestDelegate {
+public class AddressEditor extends EditorBase<AutofillAddress> {
     private final Handler mHandler = new Handler();
     private final Map<Integer, EditorFieldModel> mAddressFields = new HashMap<>();
     private final Set<CharSequence> mPhoneNumbers = new HashSet<>();
-    @Nullable
-    private AutofillProfileBridge mAutofillProfileBridge;
-    @Nullable
-    private EditorFieldModel mCountryField;
-    @Nullable
-    private EditorFieldModel mPhoneField;
-    @Nullable
-    private EditorFieldValidator mPhoneValidator;
-    @Nullable
-    private List<AddressUiComponent> mAddressUiComponents;
-    private boolean mAdminAreasLoaded;
-    private String mRecentlySelectedCountry;
-    private Runnable mCountryChangeCallback;
-    private AutofillProfile mProfile;
-    private EditorModel mEditor;
-    private ProgressDialog mProgressDialog;
+    @Nullable private AutofillProfileBridge mAutofillProfileBridge;
+    @Nullable private EditorFieldModel mCountryField;
+    @Nullable private EditorFieldModel mPhoneField;
+    @Nullable private EditorFieldValidator mPhoneValidator;
+    @Nullable private List<AddressUiComponent> mAddressUiComponents;
 
     /**
      * Adds the given phone number to the autocomplete set, if it's valid.
@@ -86,20 +72,17 @@
         // default locale on this device.
         boolean isNewAddress = toEdit == null;
 
-        // Ensure that |address| and |mProfile| are always not null.
+        // Ensure that |address| and |profile| are always not null.
         final AutofillAddress address =
                 isNewAddress ? new AutofillAddress(mContext, new AutofillProfile()) : toEdit;
-        mProfile = address.getProfile();
+        final AutofillProfile profile = address.getProfile();
 
         // The title of the editor depends on whether we're adding a new address or editing an
         // existing address.
-        mEditor =
-                new EditorModel(isNewAddress ? mContext.getString(R.string.autofill_create_profile)
-                                             : toEdit.getEditTitle());
-
-        // When edit is called, a new form is started, so the country on the
-        // dropdown list is not changed. => mRecentlySelectedCountry should be null.
-        mRecentlySelectedCountry = null;
+        final EditorModel editor =
+                new EditorModel(isNewAddress
+                        ? mContext.getString(R.string.autofill_create_profile)
+                        : toEdit.getEditTitle());
 
         // The country dropdown is always present on the editor.
         if (mCountryField == null) {
@@ -113,24 +96,23 @@
         // discarded, so their contents are preserved.
         mCountryField.setDropdownCallback(new Callback<Pair<String, Runnable>>() {
             @Override
-            /**
-             * If the selected country on the country dropdown list is changed,
-             * the first element of eventData is the recently selected dropdown key,
-             * the second element is the callback to invoke for when the dropdown
-             * change has been processed.
-             */
             public void onResult(Pair<String, Runnable> eventData) {
-                mEditor.removeAllFields();
-                showProgressDialog();
-                mRecentlySelectedCountry = eventData.first;
-                mCountryChangeCallback = eventData.second;
-                loadAdminAreasForCountry(mRecentlySelectedCountry);
+                editor.removeAllFields();
+                editor.addField(mCountryField);
+                addAddressTextFieldsToEditor(editor, eventData.first,
+                        Locale.getDefault().getLanguage());
+                editor.addField(mPhoneField);
+
+                // Notify EditorView that the fields in the model have changed. EditorView should
+                // re-read the model and update the UI accordingly.
+                mHandler.post(eventData.second);
             }
         });
 
         // Country dropdown is cached, so the selected item needs to be updated for the new profile
         // that's being edited. This will not fire the dropdown callback.
-        mCountryField.setValue(AutofillAddress.getCountryCode(mProfile));
+        mCountryField.setValue(AutofillAddress.getCountryCode(profile));
+        editor.addField(mCountryField);
 
         // There's a finite number of fields for address editing. Changing the country will re-order
         // and relabel the fields. The meaning of each field remains the same.
@@ -140,6 +122,10 @@
             mAddressFields.put(AddressField.DEPENDENT_LOCALITY, EditorFieldModel.createTextInput());
             mAddressFields.put(AddressField.ORGANIZATION, EditorFieldModel.createTextInput());
 
+            // State should be formatted in all capitals.
+            mAddressFields.put(AddressField.ADMIN_AREA, EditorFieldModel.createTextInput(
+                        EditorFieldModel.INPUT_TYPE_HINT_REGION));
+
             // Sorting code and postal code (a.k.a. ZIP code) should show both letters and digits on
             // the keyboard, if possible.
             mAddressFields.put(AddressField.SORTING_CODE, EditorFieldModel.createTextInput(
@@ -156,6 +142,18 @@
                     EditorFieldModel.INPUT_TYPE_HINT_PERSON_NAME));
         }
 
+        // Address fields are cached, so their values need to be updated for every new profile
+        // that's being edited.
+        for (Map.Entry<Integer, EditorFieldModel> entry : mAddressFields.entrySet()) {
+            entry.getValue().setValue(AutofillAddress.getProfileField(profile, entry.getKey()));
+        }
+
+        // Both country code and language code dictate which fields should be added to the editor.
+        // For example, "US" will not add dependent locality to the editor. A "JP" address will
+        // start with a person's full name or a with a prefecture name, depending on whether the
+        // language code is "ja-Latn" or "ja".
+        addAddressTextFieldsToEditor(
+                editor, mCountryField.getValue().toString(), profile.getLanguageCode());
 
         // Phone number is present and required for all countries.
         if (mPhoneField == null) {
@@ -168,48 +166,30 @@
 
         // Phone number field is cached, so its value needs to be updated for every new profile
         // that's being edited.
-        mPhoneField.setValue(mProfile.getPhoneNumber());
+        mPhoneField.setValue(profile.getPhoneNumber());
+        editor.addField(mPhoneField);
 
         // If the user clicks [Cancel], send |toEdit| address back to the caller, which was the
         // original state (could be null, a complete address, a partial address).
-        mEditor.setCancelCallback(new Runnable() {
+        editor.setCancelCallback(new Runnable() {
             @Override
             public void run() {
-                // This makes sure that onSubKeysReceived returns early if it's
-                // ever called when Cancel has already occurred.
-                mAdminAreasLoaded = true;
-                PersonalDataManager.getInstance().cancelPendingGetSubKeys();
                 callback.onResult(toEdit);
             }
         });
 
         // If the user clicks [Done], save changes on disk, mark the address "complete," and send it
         // back to the caller.
-        mEditor.setDoneCallback(new Runnable() {
+        editor.setDoneCallback(new Runnable() {
             @Override
             public void run() {
-                mAdminAreasLoaded = true;
-                PersonalDataManager.getInstance().cancelPendingGetSubKeys();
-                commitChanges(mProfile);
-                address.completeAddress(mProfile);
+                commitChanges(profile);
+                address.completeAddress(profile);
                 callback.onResult(address);
             }
         });
 
-        loadAdminAreasForCountry(mProfile.getCountryCode());
-    }
-
-    private void showProgressDialog() {
-        mProgressDialog = new ProgressDialog(mContext);
-        mProgressDialog.setMessage(mContext.getText(R.string.payments_loading_message));
-        mProgressDialog.show();
-    }
-
-    private void dismissProgressDialog() {
-        if (mProgressDialog != null && mProgressDialog.isShowing()) {
-            mProgressDialog.dismiss();
-        }
-        mProgressDialog = null;
+        mEditorView.show(editor);
     }
 
     /** Saves the edited profile on disk. */
@@ -241,7 +221,7 @@
         }
 
         // Save the edited autofill profile locally.
-        profile.setGUID(PersonalDataManager.getInstance().setProfileToLocal(mProfile));
+        profile.setGUID(PersonalDataManager.getInstance().setProfileToLocal(profile));
         profile.setIsLocal(true);
     }
 
@@ -286,109 +266,27 @@
         return value == null ? "" : value.toString();
     }
 
-    private void setAddressFieldValuesFromCache() {
-        // Address fields are cached, so their values need to be updated for every new profile
-        // that's being edited.
-        for (Map.Entry<Integer, EditorFieldModel> entry : mAddressFields.entrySet()) {
-            entry.getValue().setValue(AutofillAddress.getProfileField(mProfile, entry.getKey()));
-        }
-    }
-
-    @Override
-    public void onSubKeysReceived(String[] adminAreas) {
-        if (mAdminAreasLoaded) return;
-        mAdminAreasLoaded = true;
-
-        // If Chrome can't get admin areas from the server or there is no admin area on the server,
-        // then use the text field.
-        // Otherwise, use the dropdown list.
-        if (adminAreas == null || adminAreas.length == 0) {
-            mAddressFields.put(AddressField.ADMIN_AREA, EditorFieldModel.createTextInput());
-        } else {
-            mAddressFields.put(AddressField.ADMIN_AREA, EditorFieldModel.createDropdown());
-        }
-
-        // Admin areas need to be fetched in two cases:
-        // 1. Initial loading of the form.
-        // 2. When the selected country is changed in the form.
-        // mRecentlySelectedCountry is not null if and only if it's the second case
-        if (mRecentlySelectedCountry != null) {
-            dismissProgressDialog();
-            // Both country code and language code dictate which fields should be added to the
-            // editor.
-            // For example, "US" will not add dependent locality to the editor. A "JP" address will
-            // start with a person's full name or a with a prefecture name, depending on whether the
-            // language code is "ja-Latn" or "ja".
-            addAddressFieldsToEditor(
-                    mRecentlySelectedCountry, Locale.getDefault().getLanguage(), adminAreas);
-            // Notify EditorView that the fields in the model have changed. EditorView should
-            // re-read the model and update the UI accordingly.
-            mHandler.post(mCountryChangeCallback);
-        } else {
-            // This should be called when all required fields are put in mAddressField.
-            setAddressFieldValuesFromCache();
-            addAddressFieldsToEditor(
-                    mProfile.getCountryCode(), mProfile.getLanguageCode(), adminAreas);
-            mEditorView.show(mEditor);
-        }
-    }
-
-    /** Requests the list of admin areas. */
-    private void loadAdminAreasForCountry(String countryCode) {
-        // Used to check if the callback is called (for time-out).
-        mAdminAreasLoaded = false;
-
-        onAdminAreasLoading();
-        // In each rule, admin area keys are saved under sub-keys of country.
-        PersonalDataManager.getInstance().loadRulesForSubKeys(countryCode);
-        PersonalDataManager.getInstance().getRegionSubKeys(countryCode, this);
-    }
-
-    /** Cancels the request for the list of admin areas. */
-    private void cancelAdminAreasRequest() {
-        if (mAdminAreasLoaded) return;
-        onSubKeysReceived(null);
-        PersonalDataManager.getInstance().cancelPendingGetSubKeys();
-    }
-
     /**
-     * In case the the admin areas are not loaded yet, or the time out is set to 0 for testing,
-     * starts a timer to cancel the request.
+     * Adds text fields to the editor model based on the country and language code of the profile
+     * that's being edited.
      */
-    public void onAdminAreasLoading() {
-        if (mAdminAreasLoaded) return;
-        new Handler().postDelayed(new Runnable() {
-            @Override
-            public void run() {
-                cancelAdminAreasRequest();
-            }
-        }, PersonalDataManager.getInstance().getRequestTimeoutMS());
-    }
+    private void addAddressTextFieldsToEditor(
+            EditorModel container, String countryCode, String languageCode) {
+        mAddressUiComponents = mAutofillProfileBridge.getAddressUiComponents(countryCode,
+                languageCode);
 
-    /**
-     * Adds fields to the editor model based on the country and language code of
-     * the profile that's being edited.
-     */
-    private void addAddressFieldsToEditor(
-            String countryCode, String languageCode, String[] adminAreas) {
-        mAddressUiComponents =
-                mAutofillProfileBridge.getAddressUiComponents(countryCode, languageCode);
-        // In terms of order, country must be the first field.
-        mEditor.addField(mCountryField);
         for (int i = 0; i < mAddressUiComponents.size(); i++) {
             AddressUiComponent component = mAddressUiComponents.get(i);
 
+            // The country field is a dropdown, so there's no need to add a text field for it.
+            if (component.id == AddressField.COUNTRY) continue;
+
             EditorFieldModel field = mAddressFields.get(component.id);
             // Labels depend on country, e.g., state is called province in some countries. These are
             // already localized.
             field.setLabel(component.label);
             field.setIsFullLine(component.isFullLine);
 
-            if (component.id == AddressField.ADMIN_AREA && field.isDropdownField()) {
-                field.setDropdownKeyValues(
-                        mAutofillProfileBridge.getAdminAreaDropdownList(adminAreas));
-            }
-
             // Libaddressinput formats do not always require the full name (RECIPIENT), but
             // PaymentRequest does.
             if (component.isRequired || component.id == AddressField.RECIPIENT) {
@@ -397,10 +295,9 @@
             } else {
                 field.setRequiredErrorMessage(null);
             }
-            mEditor.addField(field);
+
+            container.addField(field);
         }
-        // Phone number must be the last field.
-        mEditor.addField(mPhoneField);
     }
 
     private EditorFieldValidator getPhoneValidator() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
index 33f8944..843622c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
@@ -528,7 +528,7 @@
             String countryCode = AutofillAddress.getCountryCode(addresses.get(i).getProfile());
             if (!uniqueCountryCodes.contains(countryCode)) {
                 uniqueCountryCodes.add(countryCode);
-                PersonalDataManager.getInstance().loadRulesForAddressNormalization(countryCode);
+                PersonalDataManager.getInstance().loadRulesForRegion(countryCode);
             }
         }
 
@@ -1388,7 +1388,7 @@
             String countryCode = AutofillAddress.getCountryCode(creditCard.getBillingAddress());
             if (!uniqueCountryCodes.contains(countryCode)) {
                 uniqueCountryCodes.add(countryCode);
-                PersonalDataManager.getInstance().loadRulesForAddressNormalization(countryCode);
+                PersonalDataManager.getInstance().loadRulesForRegion(countryCode);
             }
 
             // If there's a card on file with a valid number and a name, then
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/EditorDropdownField.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/EditorDropdownField.java
index e1568b4..e7f30183 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/EditorDropdownField.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/EditorDropdownField.java
@@ -18,15 +18,12 @@
 import android.widget.TextView;
 
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.payments.ui.PaymentRequestUI.PaymentRequestObserverForTest;
 import org.chromium.chrome.browser.preferences.autofill.AutofillProfileBridge.DropdownKeyValue;
 import org.chromium.ui.UiUtils;
 
 import java.util.ArrayList;
 import java.util.List;
 
-import javax.annotation.Nullable;
-
 /**
  * Helper class for creating a dropdown view with a label.
  */
@@ -36,8 +33,6 @@
     private final TextView mLabel;
     private final Spinner mDropdown;
     private int mSelectedIndex;
-    @Nullable
-    private PaymentRequestObserverForTest mObserverForTest;
 
     /**
      * Builds a dropdown view.
@@ -49,10 +44,9 @@
      *                        processed.
      */
     public EditorDropdownField(Context context, ViewGroup root, final EditorFieldModel fieldModel,
-            final Runnable changedCallback, @Nullable PaymentRequestObserverForTest observer) {
+            final Runnable changedCallback) {
         assert fieldModel.getInputTypeHint() == EditorFieldModel.INPUT_TYPE_HINT_DROPDOWN;
         mFieldModel = fieldModel;
-        mObserverForTest = observer;
 
         mLayout = LayoutInflater.from(context).inflate(
                 R.layout.payment_request_editor_dropdown, root, false);
@@ -103,9 +97,6 @@
                             mFieldModel.getDropdownKeyValues().get(position).getKey(),
                             changedCallback);
                 }
-                if (mObserverForTest != null) {
-                    mObserverForTest.onPaymentRequestEditorTextUpdate();
-                }
             }
 
             @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/EditorFieldModel.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/EditorFieldModel.java
index 08037a6..690834b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/EditorFieldModel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/EditorFieldModel.java
@@ -198,11 +198,6 @@
         return result;
     }
 
-    /** Constructs a dropdown field model. */
-    public static EditorFieldModel createDropdown() {
-        return new EditorFieldModel(INPUT_TYPE_HINT_DROPDOWN);
-    }
-
     /**
      * Constructs a dropdown field model.
      *
@@ -337,17 +332,11 @@
         return mValueIconGenerator;
     }
 
-    /** @return Whether the input is a text field. */
-    public boolean isTextField() {
+    private boolean isTextField() {
         return mInputTypeHint >= INPUT_TYPE_HINT_MIN_INCLUSIVE
                 && mInputTypeHint < INPUT_TYPE_HINT_MAX_TEXT_INPUT_EXCLUSIVE;
     }
 
-    /** @return Whether the input is a dropdown field. */
-    public boolean isDropdownField() {
-        return mInputTypeHint == INPUT_TYPE_HINT_DROPDOWN;
-    }
-
     /** @return The type of input, for example, INPUT_TYPE_HINT_PHONE. */
     public int getInputTypeHint() {
         return mInputTypeHint;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/EditorView.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/EditorView.java
index 45b3f20..31756ca 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/EditorView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/EditorView.java
@@ -333,7 +333,7 @@
                                    == EditorFieldModel.INPUT_TYPE_HINT_DROPDOWN);
             }
 
-            if (useFullLine || isLastField) {
+            if (useFullLine) {
                 addFieldViewToEditor(mDataView, fieldModel);
             } else {
                 // Create a LinearLayout to put it and the next view side by side.
@@ -390,8 +390,8 @@
                     if (mObserverForTest != null) mObserverForTest.onPaymentRequestReadyToEdit();
                 }
             };
-            EditorDropdownField dropdownView = new EditorDropdownField(
-                    mContext, parent, fieldModel, prepareEditorRunnable, mObserverForTest);
+            EditorDropdownField dropdownView =
+                    new EditorDropdownField(mContext, parent, fieldModel, prepareEditorRunnable);
             mFieldViews.add(dropdownView);
             mDropdownFields.add(dropdownView.getDropdown());
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/autofill/AutofillProfileBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/autofill/AutofillProfileBridge.java
index a5bc8eb5..7f0f63d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/autofill/AutofillProfileBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/autofill/AutofillProfileBridge.java
@@ -97,31 +97,10 @@
                 return result;
             }
         });
+
         return countries;
     }
 
-    /** @return The list of admin areas sorted by their localized display names. */
-    public static List<DropdownKeyValue> getAdminAreaDropdownList(String[] keys) {
-        List<DropdownKeyValue> adminAreas = new ArrayList<>();
-
-        for (int i = 0; i < keys.length; ++i) {
-            // TODO (parastoog): show names, save keys. @crbug.com/691643
-            adminAreas.add(new DropdownKeyValue(keys[i], keys[i]));
-        }
-
-        final Collator collator = Collator.getInstance(Locale.getDefault());
-        collator.setStrength(Collator.PRIMARY);
-        Collections.sort(adminAreas, new Comparator<DropdownKeyValue>() {
-            @Override
-            public int compare(DropdownKeyValue lhs, DropdownKeyValue rhs) {
-                // Sorted according to the admin area values, such as Quebec,
-                // rather than the admin area keys, such as QC.
-                return collator.compare(lhs.getValue(), rhs.getValue());
-            }
-        });
-        return adminAreas;
-    }
-
     /** @return The list of required fields. COUNTRY is always included. RECIPIENT often omitted. */
     public static List<Integer> getRequiredAddressFields(String countryCode) {
         List<Integer> requiredFields = new ArrayList<>();
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index d47c3ee9..b1ab03b 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -250,6 +250,15 @@
       <message name="IDS_NOTIFICATION_CATEGORY_GROUP_GENERAL" desc='Subheading for "General" section of a list of notification categories. [CHAR-LIMIT=32]'>
         General
       </message>
+      <message name="IDS_NOTIFICATION_CATEGORY_INCOGNITO" desc="Label for notification that indicates incognito mode is active, within a list of notification categories. [CHAR-LIMIT=32]">
+        Incognito
+      </message>
+      <message name="IDS_NOTIFICATION_CATEGORY_MEDIA" desc="Label for notifications shown when media is playing or recording, within a list of notification categories. [CHAR-LIMIT=32]">
+        Media
+      </message>
+      <message name="IDS_NOTIFICATION_CATEGORY_DOWNLOADS" desc="Label for notifications shown when something is downloading, within a list of notification categories. [CHAR-LIMIT=32]">
+        Downloads
+      </message>
       <message name="IDS_NOTIFICATION_CATEGORY_BROWSER" desc="Label for browser-related notifications, within a list of notification categories. [CHAR-LIMIT=32]">
         Browser
       </message>
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillTest.java
index ce63c66f..ab3aa5f 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillTest.java
@@ -106,6 +106,10 @@
         @Override
         public void dismissed() {
         }
+
+        @Override
+        public void accessibilityFocusCleared() {
+        }
     }
 
     private AutofillSuggestion[] createTwoAutofillSuggestionArray() {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillTestHelper.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillTestHelper.java
index f792814c..5892cb4 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillTestHelper.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillTestHelper.java
@@ -31,7 +31,7 @@
         ThreadUtils.runOnUiThreadBlocking(new Runnable() {
             @Override
             public void run() {
-                PersonalDataManager.getInstance().setNormalizationTimeoutForTesting(0);
+                PersonalDataManager.getInstance().setNormalizationTimeoutForTesting(1);
             }
         });
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/history/HistoryActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/history/HistoryActivityTest.java
index 93f8ac9..fd1084e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/history/HistoryActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/history/HistoryActivityTest.java
@@ -561,6 +561,5 @@
                 SigninManager.get(getActivity()).removeSignInStateObserver(mTestObserver);
             }
         });
-        SigninTestUtil.tearDownAuthForTest();
     }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/PasswordViewingTypeTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/PasswordViewingTypeTest.java
index c6f4514..462a089 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/PasswordViewingTypeTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/PasswordViewingTypeTest.java
@@ -12,7 +12,6 @@
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 
-import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
@@ -77,11 +76,6 @@
         mAccountManager.addAccountHolderExplicitly(accountHolder.build());
     }
 
-    @After
-    public void tearDown() {
-        AccountManagerHelper.resetAccountManagerHelperForTests();
-    }
-
     /**
      * Launches the main preferences.
      */
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/privacy/ClearBrowsingDataPreferencesTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/privacy/ClearBrowsingDataPreferencesTest.java
index b3f2aab..1bd21923a 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/privacy/ClearBrowsingDataPreferencesTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/privacy/ClearBrowsingDataPreferencesTest.java
@@ -59,7 +59,6 @@
     @Override
     protected void tearDown() throws Exception {
         mTestServer.stopAndDestroyServer();
-        SigninTestUtil.tearDownAuthForTest();
         super.tearDown();
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/OAuth2TokenServiceIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/OAuth2TokenServiceIntegrationTest.java
index 2d97c009..8500c46 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/OAuth2TokenServiceIntegrationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/OAuth2TokenServiceIntegrationTest.java
@@ -65,17 +65,15 @@
         mapAccountNamesToIds();
         ApplicationData.clearAppData(
                 InstrumentationRegistry.getInstrumentation().getTargetContext());
+        mActivityTestRule.loadNativeLibraryAndInitBrowserProcess();
 
-        // loadNativeLibraryAndInitBrowserProcess will access AccountManagerHelper, so it should
-        // be initialized beforehand.
+        // Set up AccountManager.
         mContext = new AdvancedMockContext(
                 InstrumentationRegistry.getInstrumentation().getTargetContext());
         mAccountManager = new MockAccountManager(
                 mContext, InstrumentationRegistry.getInstrumentation().getContext());
         AccountManagerHelper.overrideAccountManagerHelperForTests(mContext, mAccountManager);
 
-        mActivityTestRule.loadNativeLibraryAndInitBrowserProcess();
-
         // Make sure there is no account signed in yet.
         mChromeSigninController = ChromeSigninController.get();
         mChromeSigninController.setSignedInAccountName(null);
@@ -100,7 +98,6 @@
                 mOAuth2TokenService.validateAccounts(false);
             }
         });
-        AccountManagerHelper.resetAccountManagerHelperForTests();
     }
 
     private void mapAccountNamesToIds() {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/OAuth2TokenServiceTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/OAuth2TokenServiceTest.java
index be6527b..e1500a7 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/OAuth2TokenServiceTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/OAuth2TokenServiceTest.java
@@ -8,7 +8,6 @@
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 
-import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
@@ -41,11 +40,6 @@
         AccountManagerHelper.overrideAccountManagerHelperForTests(mContext, mAccountManager);
     }
 
-    @After
-    public void tearDown() {
-        AccountManagerHelper.resetAccountManagerHelperForTests();
-    }
-
     /*
      *  @SmallTest
      *  @Feature({"Sync"})
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninHelperTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninHelperTest.java
index 7f7f724..1c183d7 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninHelperTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninHelperTest.java
@@ -8,7 +8,6 @@
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 
-import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
@@ -45,11 +44,6 @@
         AccountManagerHelper.overrideAccountManagerHelperForTests(mContext, mAccountManager);
     }
 
-    @After
-    public void tearDown() {
-        AccountManagerHelper.resetAccountManagerHelperForTests();
-    }
-
     @Test
     @SmallTest
     @RetryOnFailure
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninTest.java
index 97c8eb93..3edc918ba 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninTest.java
@@ -260,9 +260,6 @@
                 mBookmarks.destroy();
             }
         });
-
-        SigninTestUtil.tearDownAuthForTest();
-
         super.tearDown();
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/superviseduser/SupervisedUserContentProviderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/superviseduser/SupervisedUserContentProviderTest.java
index b69e51ce5..fdbf6b0 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/superviseduser/SupervisedUserContentProviderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/superviseduser/SupervisedUserContentProviderTest.java
@@ -56,7 +56,6 @@
     @Override
     public void tearDown() throws Exception {
         SigninTestUtil.resetSigninState();
-        SigninTestUtil.tearDownAuthForTest();
         super.tearDown();
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ui/PassphraseActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ui/PassphraseActivityTest.java
index 3348686..85072bf 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ui/PassphraseActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ui/PassphraseActivityTest.java
@@ -53,7 +53,6 @@
         // Clear ProfileSyncService in case it was mocked.
         ProfileSyncService.overrideForTests(null);
         SigninTestUtil.resetSigninState();
-        SigninTestUtil.tearDownAuthForTest();
     }
 
     /**
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/util/FeatureUtilitiesTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/util/FeatureUtilitiesTest.java
index 0a358100..42f616c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/util/FeatureUtilitiesTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/util/FeatureUtilitiesTest.java
@@ -15,7 +15,6 @@
 import android.test.mock.MockContext;
 import android.test.mock.MockPackageManager;
 
-import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
@@ -61,11 +60,6 @@
                 InstrumentationRegistry.getInstrumentation().getTargetContext());
     }
 
-    @After
-    public void tearDown() {
-        AccountManagerHelper.resetAccountManagerHelperForTests();
-    }
-
     private static class IntentTestPackageManager extends MockPackageManager {
 
         private final String mAction;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/invalidation/InvalidationControllerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/invalidation/InvalidationControllerTest.java
index ba2cc24..88534f0 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/invalidation/InvalidationControllerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/invalidation/InvalidationControllerTest.java
@@ -12,7 +12,6 @@
 import android.content.pm.PackageInfo;
 import android.os.Bundle;
 
-import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
@@ -31,9 +30,7 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.browser.sync.ProfileSyncService;
-import org.chromium.components.signin.AccountManagerHelper;
 import org.chromium.components.signin.ChromeSigninController;
-import org.chromium.components.signin.SystemAccountManagerDelegate;
 import org.chromium.components.sync.AndroidSyncSettings;
 import org.chromium.components.sync.ModelType;
 import org.chromium.components.sync.ModelTypeHelper;
@@ -114,9 +111,6 @@
 
         ContextUtils.initApplicationContextForTests(mContext.getApplicationContext());
 
-        AccountManagerHelper.overrideAccountManagerHelperForTests(
-                mContext, new SystemAccountManagerDelegate());
-
         ModelTypeHelper.setTestDelegate(new ModelTypeHelper.TestDelegate() {
             @Override
             public String toNotificationType(int modelType) {
@@ -145,11 +139,6 @@
         AndroidSyncSettings.enableChromeSync(mContext);
     }
 
-    @After
-    public void tearDown() {
-        AccountManagerHelper.resetAccountManagerHelperForTests();
-    }
-
     /**
      * Verify the intent sent by InvalidationController#stop().
      */
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/notifications/ChannelsInitializerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/notifications/ChannelsInitializerTest.java
index 7a161b01..a658c5a 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/notifications/ChannelsInitializerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/notifications/ChannelsInitializerTest.java
@@ -4,7 +4,7 @@
 
 package org.chromium.chrome.browser.notifications;
 
-import static org.hamcrest.Matchers.greaterThan;
+import static org.hamcrest.Matchers.containsInAnyOrder;
 import static org.hamcrest.Matchers.is;
 import static org.junit.Assert.assertThat;
 
@@ -34,7 +34,12 @@
     @Test
     public void testInitializeStartupChannels() throws Exception {
         mChannelsInitializer.initializeStartupChannels();
-        assertThat(mMockNotificationManager.getChannels().size(), is(greaterThan(0)));
+        assertThat(mMockNotificationManager.getNotificationChannelIds(),
+                containsInAnyOrder(ChannelsInitializer.CHANNEL_ID_BROWSER,
+                        ChannelsInitializer.CHANNEL_ID_DOWNLOADS,
+                        ChannelsInitializer.CHANNEL_ID_INCOGNITO,
+                        ChannelsInitializer.CHANNEL_ID_SITES,
+                        ChannelsInitializer.CHANNEL_ID_MEDIA));
     }
 
     @Test
@@ -58,6 +63,66 @@
     }
 
     @Test
+    public void testEnsureInitialized_downloadsChannel() throws Exception {
+        mChannelsInitializer.ensureInitialized(ChannelsInitializer.CHANNEL_ID_DOWNLOADS);
+
+        assertThat(mMockNotificationManager.getChannels().size(), is(1));
+        ChannelsInitializer.Channel channel = mMockNotificationManager.getChannels().get(0);
+        assertThat(channel.mId, is(ChannelsInitializer.CHANNEL_ID_DOWNLOADS));
+        assertThat(channel.mNameResId,
+                is(org.chromium.chrome.R.string.notification_category_downloads));
+        assertThat(channel.mImportance, is(NotificationManager.IMPORTANCE_LOW));
+        assertThat(channel.mGroupId, is(ChannelsInitializer.CHANNEL_GROUP_ID_GENERAL));
+
+        assertThat(mMockNotificationManager.getNotificationChannelGroups().size(), is(1));
+        ChannelsInitializer.ChannelGroup group =
+                mMockNotificationManager.getNotificationChannelGroups().get(0);
+        assertThat(group.mId, is(ChannelsInitializer.CHANNEL_GROUP_ID_GENERAL));
+        assertThat(group.mNameResId,
+                is(org.chromium.chrome.R.string.notification_category_group_general));
+    }
+
+    @Test
+    public void testEnsureInitialized_incognitoChannel() throws Exception {
+        mChannelsInitializer.ensureInitialized(ChannelsInitializer.CHANNEL_ID_INCOGNITO);
+
+        assertThat(mMockNotificationManager.getChannels().size(), is(1));
+        ChannelsInitializer.Channel channel = mMockNotificationManager.getChannels().get(0);
+        assertThat(channel.mId, is(ChannelsInitializer.CHANNEL_ID_INCOGNITO));
+        assertThat(channel.mNameResId,
+                is(org.chromium.chrome.R.string.notification_category_incognito));
+        assertThat(channel.mImportance, is(NotificationManager.IMPORTANCE_LOW));
+        assertThat(channel.mGroupId, is(ChannelsInitializer.CHANNEL_GROUP_ID_GENERAL));
+
+        assertThat(mMockNotificationManager.getNotificationChannelGroups().size(), is(1));
+        ChannelsInitializer.ChannelGroup group =
+                mMockNotificationManager.getNotificationChannelGroups().get(0);
+        assertThat(group.mId, is(ChannelsInitializer.CHANNEL_GROUP_ID_GENERAL));
+        assertThat(group.mNameResId,
+                is(org.chromium.chrome.R.string.notification_category_group_general));
+    }
+
+    @Test
+    public void testEnsureInitialized_mediaChannel() throws Exception {
+        mChannelsInitializer.ensureInitialized(ChannelsInitializer.CHANNEL_ID_MEDIA);
+
+        assertThat(mMockNotificationManager.getChannels().size(), is(1));
+        ChannelsInitializer.Channel channel = mMockNotificationManager.getChannels().get(0);
+        assertThat(channel.mId, is(ChannelsInitializer.CHANNEL_ID_MEDIA));
+        assertThat(
+                channel.mNameResId, is(org.chromium.chrome.R.string.notification_category_media));
+        assertThat(channel.mImportance, is(NotificationManager.IMPORTANCE_LOW));
+        assertThat(channel.mGroupId, is(ChannelsInitializer.CHANNEL_GROUP_ID_GENERAL));
+
+        assertThat(mMockNotificationManager.getNotificationChannelGroups().size(), is(1));
+        ChannelsInitializer.ChannelGroup group =
+                mMockNotificationManager.getNotificationChannelGroups().get(0);
+        assertThat(group.mId, is(ChannelsInitializer.CHANNEL_GROUP_ID_GENERAL));
+        assertThat(group.mNameResId,
+                is(org.chromium.chrome.R.string.notification_category_group_general));
+    }
+
+    @Test
     public void testEnsureInitialized_sitesChannel() throws Exception {
         mChannelsInitializer.ensureInitialized(ChannelsInitializer.CHANNEL_ID_SITES);
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/notifications/ChannelsUpdaterTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/notifications/ChannelsUpdaterTest.java
index fbc3ac7..e3ef2f4 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/notifications/ChannelsUpdaterTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/notifications/ChannelsUpdaterTest.java
@@ -85,7 +85,10 @@
         assertThat(mMockNotificationManager.getChannels().size(), is(greaterThan(0)));
         assertThat(mMockNotificationManager.getNotificationChannelIds(),
                 containsInAnyOrder(ChannelsInitializer.CHANNEL_ID_BROWSER,
-                        ChannelsInitializer.CHANNEL_ID_SITES));
+                        ChannelsInitializer.CHANNEL_ID_DOWNLOADS,
+                        ChannelsInitializer.CHANNEL_ID_INCOGNITO,
+                        ChannelsInitializer.CHANNEL_ID_SITES,
+                        ChannelsInitializer.CHANNEL_ID_MEDIA));
         assertThat(mMockSharedPreferences.getInt(ChannelsUpdater.CHANNELS_VERSION_KEY, -1), is(21));
     }
 
@@ -108,6 +111,9 @@
 
         assertThat(mMockNotificationManager.getNotificationChannelIds(),
                 containsInAnyOrder(ChannelsInitializer.CHANNEL_ID_BROWSER,
-                        ChannelsInitializer.CHANNEL_ID_SITES));
+                        ChannelsInitializer.CHANNEL_ID_DOWNLOADS,
+                        ChannelsInitializer.CHANNEL_ID_INCOGNITO,
+                        ChannelsInitializer.CHANNEL_ID_SITES,
+                        ChannelsInitializer.CHANNEL_ID_MEDIA));
     }
 }
\ No newline at end of file
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/superviseduser/SupervisedUserContentProviderUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/superviseduser/SupervisedUserContentProviderUnitTest.java
index 3698d006..037097f 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/superviseduser/SupervisedUserContentProviderUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/superviseduser/SupervisedUserContentProviderUnitTest.java
@@ -237,8 +237,6 @@
                 .nativeShouldProceed(eq(5678L),
                         any(SupervisedUserContentProvider.SupervisedUserQueryReply.class),
                         eq("url"));
-
-        AccountManagerHelper.resetAccountManagerHelperForTests();
     }
 
     @Test
@@ -268,8 +266,6 @@
 
         assertThat(result.shouldProceed(), is(false));
         assertThat(result.getErrorInt(0), is(5));
-
-        AccountManagerHelper.resetAccountManagerHelperForTests();
     }
 
     @Test
diff --git a/chrome/android/sync_shell/javatests/src/org/chromium/chrome/browser/sync/SyncTestBase.java b/chrome/android/sync_shell/javatests/src/org/chromium/chrome/browser/sync/SyncTestBase.java
index 7eeeab0f..917fe94 100644
--- a/chrome/android/sync_shell/javatests/src/org/chromium/chrome/browser/sync/SyncTestBase.java
+++ b/chrome/android/sync_shell/javatests/src/org/chromium/chrome/browser/sync/SyncTestBase.java
@@ -128,7 +128,6 @@
             }
         });
         SigninTestUtil.resetSigninState();
-        SigninTestUtil.tearDownAuthForTest();
 
         super.tearDown();
     }
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 663cca3..2ee6b3d 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -12070,6 +12070,15 @@
         <message name="IDS_INTENT_PICKER_BUBBLE_VIEW_OPEN_WITH" desc="Message contained in a label used as a header for a list of applications from which the user will pick one.">
           Open with
         </message>
+        <message name="IDS_INTENT_PICKER_BUBBLE_VIEW_REMEMBER_SELECTION" desc="Message contained in a checkbox that must be marked in order for the intent picker to not be shown again for a similar URL.">
+          Remember my choice
+        </message>
+        <message name="IDS_INTENT_PICKER_BUBBLE_VIEW_USE_APP" desc="Message contained in a button to continue the navigation in an ARC app selected by the user.">
+          Use app
+        </message>
+        <message name="IDS_INTENT_PICKER_BUBBLE_VIEW_STAY_IN_CHROME" desc="Message contained in a button to continue the navigation within Chrome.">
+          Stay in Chrome
+        </message>
       </if>
 
       <message name="IDS_SAVE_PASSWORD_DIFFERENT_DOMAINS_TITLE" desc="The title of the save password bubble when the submitted form origin isn't equal to the page origin.">
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 59164e3..259dc24 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -327,6 +327,8 @@
     "download/all_download_item_notifier.h",
     "download/chrome_download_manager_delegate.cc",
     "download/chrome_download_manager_delegate.h",
+    "download/download_confirmation_reason.h",
+    "download/download_confirmation_result.h",
     "download/download_crx_util_android.cc",
     "download/download_file_picker.cc",
     "download/download_file_picker.h",
@@ -360,6 +362,7 @@
     "download/download_target_determiner.cc",
     "download/download_target_determiner.h",
     "download/download_target_determiner_delegate.h",
+    "download/download_target_info.cc",
     "download/download_target_info.h",
     "download/download_ui_controller.cc",
     "download/download_ui_controller.h",
diff --git a/chrome/browser/android/download/chrome_duplicate_download_infobar_delegate.cc b/chrome/browser/android/download/chrome_duplicate_download_infobar_delegate.cc
index 4c468d90..9378ea2 100644
--- a/chrome/browser/android/download/chrome_duplicate_download_infobar_delegate.cc
+++ b/chrome/browser/android/download/chrome_duplicate_download_infobar_delegate.cc
@@ -19,13 +19,14 @@
 namespace {
 
 void CreateNewFileDone(
-    const DownloadTargetDeterminerDelegate::FileSelectedCallback& callback,
-    const base::FilePath& target_path, bool verified) {
+    const DownloadTargetDeterminerDelegate::ConfirmationCallback& callback,
+    PathValidationResult result,
+    const base::FilePath& target_path) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  if (verified)
-    callback.Run(target_path);
+  if (result == PathValidationResult::SUCCESS)
+    callback.Run(DownloadConfirmationResult::CONFIRMED, target_path);
   else
-    callback.Run(base::FilePath());
+    callback.Run(DownloadConfirmationResult::FAILED, base::FilePath());
 }
 
 }  // namespace
@@ -44,7 +45,7 @@
     InfoBarService* infobar_service,
     content::DownloadItem* download_item,
     const base::FilePath& file_path,
-    const DownloadTargetDeterminerDelegate::FileSelectedCallback& callback) {
+    const DownloadTargetDeterminerDelegate::ConfirmationCallback& callback) {
   infobar_service->AddInfoBar(DuplicateDownloadInfoBar::CreateInfoBar(
       base::WrapUnique(new ChromeDuplicateDownloadInfoBarDelegate(
           download_item, file_path, callback))));
@@ -59,7 +60,7 @@
 ChromeDuplicateDownloadInfoBarDelegate::ChromeDuplicateDownloadInfoBarDelegate(
     content::DownloadItem* download_item,
     const base::FilePath& file_path,
-    const DownloadTargetDeterminerDelegate::FileSelectedCallback&
+    const DownloadTargetDeterminerDelegate::ConfirmationCallback&
         file_selected_callback)
     : download_item_(download_item),
       file_path_(file_path),
@@ -95,7 +96,8 @@
   if (!download_item_)
     return true;
 
-  file_selected_callback_.Run(base::FilePath());
+  file_selected_callback_.Run(DownloadConfirmationResult::CANCELED,
+                              base::FilePath());
   // TODO(qinmin): rename this histogram enum.
   DownloadController::RecordDownloadCancelReason(
       DownloadController::CANCEL_REASON_OVERWRITE_INFOBAR_DISMISSED);
diff --git a/chrome/browser/android/download/chrome_duplicate_download_infobar_delegate.h b/chrome/browser/android/download/chrome_duplicate_download_infobar_delegate.h
index 0962cbe9..6de397a 100644
--- a/chrome/browser/android/download/chrome_duplicate_download_infobar_delegate.h
+++ b/chrome/browser/android/download/chrome_duplicate_download_infobar_delegate.h
@@ -29,7 +29,7 @@
       InfoBarService* infobar_service,
       content::DownloadItem* download_item,
       const base::FilePath& file_path,
-      const DownloadTargetDeterminerDelegate::FileSelectedCallback&
+      const DownloadTargetDeterminerDelegate::ConfirmationCallback&
           file_selected_callback);
 
   // content::DownloadItem::Observer
@@ -39,7 +39,7 @@
   ChromeDuplicateDownloadInfoBarDelegate(
       content::DownloadItem* download_item,
       const base::FilePath& file_path,
-      const DownloadTargetDeterminerDelegate::FileSelectedCallback& callback);
+      const DownloadTargetDeterminerDelegate::ConfirmationCallback& callback);
 
   // DownloadOverwriteInfoBarDelegate:
   infobars::InfoBarDelegate::InfoBarIdentifier GetIdentifier() const override;
@@ -63,7 +63,7 @@
 
   // A callback to download target determiner to notify that file selection
   // is made (or cancelled).
-  DownloadTargetDeterminerDelegate::FileSelectedCallback
+  DownloadTargetDeterminerDelegate::ConfirmationCallback
       file_selected_callback_;
 
   DISALLOW_COPY_AND_ASSIGN(ChromeDuplicateDownloadInfoBarDelegate);
diff --git a/chrome/browser/android/offline_pages/offline_page_mhtml_archiver.cc b/chrome/browser/android/offline_pages/offline_page_mhtml_archiver.cc
index d8560f1..9b6cb1e 100644
--- a/chrome/browser/android/offline_pages/offline_page_mhtml_archiver.cc
+++ b/chrome/browser/android/offline_pages/offline_page_mhtml_archiver.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/ssl/security_state_tab_helper.h"
 #include "components/security_state/core/security_state.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/mhtml_generation_params.h"
 #include "net/base/filename_util.h"
@@ -58,11 +59,26 @@
   DCHECK(!callback.is_null());
   callback_ = callback;
 
+  // TODO(chili): crbug/710248 These checks should probably be done inside
+  // the offliner.
   if (HasConnectionSecurityError()) {
     ReportFailure(ArchiverResult::ERROR_SECURITY_CERTIFICATE);
     return;
   }
 
+  // Don't save chrome error pages.
+  if (GetPageType() == content::PageType::PAGE_TYPE_ERROR) {
+    ReportFailure(ArchiverResult::ERROR_ERROR_PAGE);
+    return;
+  }
+
+  // Don't save chrome-injected interstitial info pages
+  // i.e. "This site may be dangerous. Are you sure you want to continue?"
+  if (GetPageType() == content::PageType::PAGE_TYPE_INTERSTITIAL) {
+    ReportFailure(ArchiverResult::ERROR_INTERSTITIAL_PAGE);
+    return;
+  }
+
   GenerateMHTML(archives_dir, create_archive_params);
 }
 
@@ -130,6 +146,10 @@
          security_info.security_level;
 }
 
+content::PageType OfflinePageMHTMLArchiver::GetPageType() {
+  return web_contents_->GetController().GetVisibleEntry()->GetPageType();
+}
+
 void OfflinePageMHTMLArchiver::ReportFailure(ArchiverResult result) {
   DCHECK(result != ArchiverResult::SUCCESSFULLY_CREATED);
   base::ThreadTaskRunnerHandle::Get()->PostTask(
diff --git a/chrome/browser/android/offline_pages/offline_page_mhtml_archiver.h b/chrome/browser/android/offline_pages/offline_page_mhtml_archiver.h
index 59272ad..5a316ed 100644
--- a/chrome/browser/android/offline_pages/offline_page_mhtml_archiver.h
+++ b/chrome/browser/android/offline_pages/offline_page_mhtml_archiver.h
@@ -15,6 +15,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "components/offline_pages/core/offline_page_archiver.h"
+#include "content/public/common/page_type.h"
 
 namespace base {
 class FilePath;
@@ -71,6 +72,9 @@
   // not affected.
   virtual bool HasConnectionSecurityError();
 
+  // Returns the page type of the page being saved.
+  virtual content::PageType GetPageType();
+
   // Reports failure to create archive a page to the client that requested it.
   void ReportFailure(ArchiverResult result);
 
diff --git a/chrome/browser/android/offline_pages/offline_page_mhtml_archiver_unittest.cc b/chrome/browser/android/offline_pages/offline_page_mhtml_archiver_unittest.cc
index 8499989..52197d0 100644
--- a/chrome/browser/android/offline_pages/offline_page_mhtml_archiver_unittest.cc
+++ b/chrome/browser/android/offline_pages/offline_page_mhtml_archiver_unittest.cc
@@ -37,6 +37,8 @@
     NOT_ABLE_TO_ARCHIVE,
     WEB_CONTENTS_MISSING,
     CONNECTION_SECURITY_ERROR,
+    ERROR_PAGE,
+    INTERSTITIAL_PAGE,
   };
 
   TestMHTMLArchiver(const GURL& url, const TestScenario test_scenario);
@@ -46,6 +48,7 @@
   void GenerateMHTML(const base::FilePath& archives_dir,
                      const CreateArchiveParams& create_archive_params) override;
   bool HasConnectionSecurityError() override;
+  content::PageType GetPageType() override;
 
   const GURL url_;
   const TestScenario test_scenario_;
@@ -86,6 +89,14 @@
   return test_scenario_ == TestScenario::CONNECTION_SECURITY_ERROR;
 }
 
+content::PageType TestMHTMLArchiver::GetPageType() {
+  if (test_scenario_ == TestScenario::ERROR_PAGE)
+    return content::PageType::PAGE_TYPE_ERROR;
+  if (test_scenario_ == TestScenario::INTERSTITIAL_PAGE)
+    return content::PageType::PAGE_TYPE_INTERSTITIAL;
+  return content::PageType::PAGE_TYPE_NORMAL;
+}
+
 }  // namespace
 
 class OfflinePageMHTMLArchiverTest : public testing::Test {
@@ -217,6 +228,31 @@
   EXPECT_EQ(0LL, last_file_size());
 }
 
+// Tests for archiver handling of an error page.
+TEST_F(OfflinePageMHTMLArchiverTest, PageError) {
+  GURL page_url = GURL(kTestURL);
+  std::unique_ptr<TestMHTMLArchiver> archiver(
+      CreateArchive(page_url, TestMHTMLArchiver::TestScenario::ERROR_PAGE));
+
+  EXPECT_EQ(archiver.get(), last_archiver());
+  EXPECT_EQ(OfflinePageArchiver::ArchiverResult::ERROR_ERROR_PAGE,
+            last_result());
+  EXPECT_EQ(base::FilePath(), last_file_path());
+  EXPECT_EQ(0LL, last_file_size());
+}
+
+// Tests for archiver handling of an interstitial page.
+TEST_F(OfflinePageMHTMLArchiverTest, InterstitialPage) {
+  GURL page_url = GURL(kTestURL);
+  std::unique_ptr<TestMHTMLArchiver> archiver(CreateArchive(
+      page_url, TestMHTMLArchiver::TestScenario::INTERSTITIAL_PAGE));
+  EXPECT_EQ(archiver.get(), last_archiver());
+  EXPECT_EQ(OfflinePageArchiver::ArchiverResult::ERROR_INTERSTITIAL_PAGE,
+            last_result());
+  EXPECT_EQ(base::FilePath(), last_file_path());
+  EXPECT_EQ(0LL, last_file_size());
+}
+
 // Tests for successful creation of the offline page archive.
 TEST_F(OfflinePageMHTMLArchiverTest, SuccessfullyCreateOfflineArchive) {
   GURL page_url = GURL(kTestURL);
diff --git a/chrome/browser/android/vr_shell/BUILD.gn b/chrome/browser/android/vr_shell/BUILD.gn
index 3887951..4902ba6 100644
--- a/chrome/browser/android/vr_shell/BUILD.gn
+++ b/chrome/browser/android/vr_shell/BUILD.gn
@@ -36,6 +36,8 @@
       "ui_interface.h",
       "ui_scene.cc",
       "ui_scene.h",
+      "ui_scene_manager.cc",
+      "ui_scene_manager.h",
       "vr_compositor.cc",
       "vr_compositor.h",
       "vr_controller.cc",
diff --git a/chrome/browser/android/vr_shell/ui_scene_manager.cc b/chrome/browser/android/vr_shell/ui_scene_manager.cc
new file mode 100644
index 0000000..543aa48
--- /dev/null
+++ b/chrome/browser/android/vr_shell/ui_scene_manager.cc
@@ -0,0 +1,53 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/android/vr_shell/ui_scene_manager.h"
+
+#include "base/memory/ptr_util.h"
+#include "chrome/browser/android/vr_shell/ui_element.h"
+#include "chrome/browser/android/vr_shell/ui_scene.h"
+
+namespace vr_shell {
+
+namespace {
+
+enum Elements {
+  // For now, use fixed element IDs, and ensure they do not collide with HTML UI
+  // dynamically-chosen ID numbers.
+  SecureOriginWarning = 1000,
+};
+
+}  // namespace
+
+UiSceneManager::UiSceneManager(UiScene* scene)
+    : scene_(scene), weak_ptr_factory_(this) {
+  // Create an invisible dummy warning quad. Actual security warnings will come
+  // in a follow-on chance.
+  auto warning = base::MakeUnique<UiElement>();
+  warning->id = Elements::SecureOriginWarning;
+  warning->name = "test quad";
+  warning->translation = {0, 0, -1};
+  warning->fill = vr_shell::Fill::NONE;
+  scene_->AddUiElement(std::move(warning));
+}
+
+UiSceneManager::~UiSceneManager() {}
+
+base::WeakPtr<UiSceneManager> UiSceneManager::GetWeakPtr() {
+  return weak_ptr_factory_.GetWeakPtr();
+}
+
+void UiSceneManager::UpdateScene(std::unique_ptr<base::ListValue> commands) {
+  scene_->HandleCommands(std::move(commands), base::TimeTicks::Now());
+}
+
+void UiSceneManager::SetWebVRMode(bool web_vr) {
+  web_vr_mode_ = web_vr;
+}
+
+void UiSceneManager::SetWebVRSecureOrigin(bool secure) {
+  secure_origin_ = secure;
+}
+
+}  // namespace vr_shell
diff --git a/chrome/browser/android/vr_shell/ui_scene_manager.h b/chrome/browser/android/vr_shell/ui_scene_manager.h
new file mode 100644
index 0000000..b066bd2
--- /dev/null
+++ b/chrome/browser/android/vr_shell/ui_scene_manager.h
@@ -0,0 +1,41 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ANDROID_VR_SHELL_UI_SCENE_MANAGER_H_
+#define CHROME_BROWSER_ANDROID_VR_SHELL_UI_SCENE_MANAGER_H_
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/values.h"
+
+namespace vr_shell {
+
+class UiScene;
+
+class UiSceneManager {
+ public:
+  explicit UiSceneManager(UiScene* scene);
+  ~UiSceneManager();
+
+  base::WeakPtr<UiSceneManager> GetWeakPtr();
+
+  void UpdateScene(std::unique_ptr<base::ListValue> commands);
+
+  void SetWebVRSecureOrigin(bool secure);
+  void SetWebVRMode(bool web_vr);
+
+ private:
+  UiScene* scene_;
+
+  bool web_vr_mode_ = false;
+  bool secure_origin_ = false;
+
+  base::WeakPtrFactory<UiSceneManager> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(UiSceneManager);
+};
+
+}  // namespace vr_shell
+
+#endif  // CHROME_BROWSER_ANDROID_VR_SHELL_UI_SCENE_MANAGER_H_
diff --git a/chrome/browser/android/vr_shell/vr_gl_thread.cc b/chrome/browser/android/vr_shell/vr_gl_thread.cc
index 1093187d..e5555f6 100644
--- a/chrome/browser/android/vr_shell/vr_gl_thread.cc
+++ b/chrome/browser/android/vr_shell/vr_gl_thread.cc
@@ -6,6 +6,8 @@
 
 #include <utility>
 
+#include "chrome/browser/android/vr_shell/ui_scene.h"
+#include "chrome/browser/android/vr_shell/ui_scene_manager.h"
 #include "chrome/browser/android/vr_shell/vr_input_manager.h"
 #include "chrome/browser/android/vr_shell/vr_shell.h"
 #include "chrome/browser/android/vr_shell/vr_shell_gl.h"
@@ -30,10 +32,13 @@
 }
 
 void VrGLThread::Init() {
-  vr_shell_gl_.reset(new VrShellGl(
+  scene_ = base::MakeUnique<UiScene>();
+  scene_manager_ = base::MakeUnique<UiSceneManager>(scene_.get());
+  vr_shell_gl_ = base::MakeUnique<VrShellGl>(
       std::move(weak_vr_shell_), std::move(main_thread_task_runner_), gvr_api_,
-      initially_web_vr_, reprojected_rendering_));
+      initially_web_vr_, reprojected_rendering_, scene_.get());
   weak_vr_shell_gl_ = vr_shell_gl_->GetWeakPtr();
+  weak_scene_manager_ = scene_manager_->GetWeakPtr();
   vr_shell_gl_->Initialize();
 }
 
diff --git a/chrome/browser/android/vr_shell/vr_gl_thread.h b/chrome/browser/android/vr_shell/vr_gl_thread.h
index 4eb27d0..9e35938 100644
--- a/chrome/browser/android/vr_shell/vr_gl_thread.h
+++ b/chrome/browser/android/vr_shell/vr_gl_thread.h
@@ -15,6 +15,8 @@
 
 namespace vr_shell {
 
+class UiScene;
+class UiSceneManager;
 class VrShell;
 class VrShellGl;
 
@@ -29,6 +31,9 @@
 
   ~VrGLThread() override;
   base::WeakPtr<VrShellGl> GetVrShellGl() { return weak_vr_shell_gl_; }
+  base::WeakPtr<UiSceneManager> GetSceneManager() {
+    return weak_scene_manager_;
+  }
 
  protected:
   void Init() override;
@@ -36,6 +41,9 @@
 
  private:
   // Created on GL thread.
+  std::unique_ptr<UiScene> scene_;
+  std::unique_ptr<UiSceneManager> scene_manager_;
+  base::WeakPtr<UiSceneManager> weak_scene_manager_;
   std::unique_ptr<VrShellGl> vr_shell_gl_;
   base::WeakPtr<VrShellGl> weak_vr_shell_gl_;
 
diff --git a/chrome/browser/android/vr_shell/vr_shell.cc b/chrome/browser/android/vr_shell/vr_shell.cc
index eaedca5..76a7142 100644
--- a/chrome/browser/android/vr_shell/vr_shell.cc
+++ b/chrome/browser/android/vr_shell/vr_shell.cc
@@ -21,6 +21,7 @@
 #include "chrome/browser/android/tab_android.h"
 #include "chrome/browser/android/vr_shell/android_ui_gesture_target.h"
 #include "chrome/browser/android/vr_shell/ui_interface.h"
+#include "chrome/browser/android/vr_shell/ui_scene_manager.h"
 #include "chrome/browser/android/vr_shell/vr_compositor.h"
 #include "chrome/browser/android/vr_shell/vr_controller_model.h"
 #include "chrome/browser/android/vr_shell/vr_gl_thread.h"
@@ -303,6 +304,8 @@
 
   html_interface_->SetMode(enabled ? UiInterface::Mode::WEB_VR
                                    : UiInterface::Mode::STANDARD);
+  PostToGlThreadWhenReady(base::Bind(&UiSceneManager::SetWebVRMode,
+                                     gl_thread_->GetSceneManager(), enabled));
 }
 
 void VrShell::OnLoadProgressChanged(JNIEnv* env,
@@ -352,6 +355,9 @@
 void VrShell::SetWebVRSecureOrigin(bool secure_origin) {
   // TODO(cjgrant): Align this state with the logic that drives the omnibox.
   html_interface_->SetWebVRSecureOrigin(secure_origin);
+  PostToGlThreadWhenReady(base::Bind(&UiSceneManager::SetWebVRSecureOrigin,
+                                     gl_thread_->GetSceneManager(),
+                                     secure_origin));
 }
 
 void VrShell::SubmitWebVRFrame(int16_t frame_index,
@@ -468,8 +474,8 @@
 }
 
 void VrShell::UpdateScene(const base::ListValue* args) {
-  PostToGlThreadWhenReady(base::Bind(&VrShellGl::UpdateScene,
-                                     gl_thread_->GetVrShellGl(),
+  PostToGlThreadWhenReady(base::Bind(&UiSceneManager::UpdateScene,
+                                     gl_thread_->GetSceneManager(),
                                      base::Passed(args->CreateDeepCopy())));
 }
 
diff --git a/chrome/browser/android/vr_shell/vr_shell_gl.cc b/chrome/browser/android/vr_shell/vr_shell_gl.cc
index fcf9b02e..4badf1d 100644
--- a/chrome/browser/android/vr_shell/vr_shell_gl.cc
+++ b/chrome/browser/android/vr_shell/vr_shell_gl.cc
@@ -157,8 +157,10 @@
     scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner,
     gvr_context* gvr_api,
     bool initially_web_vr,
-    bool reprojected_rendering)
-    : web_vr_mode_(initially_web_vr),
+    bool reprojected_rendering,
+    UiScene* scene)
+    : scene_(scene),
+      web_vr_mode_(initially_web_vr),
       surfaceless_rendering_(reprojected_rendering),
       task_runner_(base::ThreadTaskRunnerHandle::Get()),
       binding_(this),
@@ -191,8 +193,6 @@
 }
 
 void VrShellGl::Initialize() {
-  scene_ = base::MakeUnique<UiScene>();
-
   if (surfaceless_rendering_) {
     // If we're rendering surfaceless, we'll never get a java surface to render
     // into, so we can initialize GL right away.
@@ -1315,10 +1315,6 @@
       FROM_HERE, base::Bind(&VrShell::ForceExitVr, weak_vr_shell_));
 }
 
-void VrShellGl::UpdateScene(std::unique_ptr<base::ListValue> commands) {
-  scene_->HandleCommands(std::move(commands), base::TimeTicks::Now());
-}
-
 void VrShellGl::SendVSync(base::TimeDelta time,
                           const GetVSyncCallback& callback) {
   uint8_t frame_index = frame_index_++;
diff --git a/chrome/browser/android/vr_shell/vr_shell_gl.h b/chrome/browser/android/vr_shell/vr_shell_gl.h
index 03dfcf1..15606b999 100644
--- a/chrome/browser/android/vr_shell/vr_shell_gl.h
+++ b/chrome/browser/android/vr_shell/vr_shell_gl.h
@@ -23,10 +23,6 @@
 #include "third_party/gvr-android-sdk/src/libraries/headers/vr/gvr/capi/include/gvr_types.h"
 #include "ui/gfx/native_widget_types.h"
 
-namespace base {
-class ListValue;
-}
-
 namespace blink {
 class WebInputEvent;
 }
@@ -76,7 +72,8 @@
             scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner,
             gvr_context* gvr_api,
             bool initially_web_vr,
-            bool reprojected_rendering);
+            bool reprojected_rendering,
+            UiScene* scene);
   ~VrShellGl() override;
 
   void Initialize();
@@ -102,8 +99,6 @@
                                 const gfx::RectF& right_bounds,
                                 const gfx::Size& source_size);
 
-  void UpdateScene(std::unique_ptr<base::ListValue> commands);
-
   void UpdateVSyncInterval(int64_t timebase_nanos, double interval_seconds);
 
   void OnRequest(device::mojom::VRVSyncProviderRequest request);
@@ -165,7 +160,7 @@
   // samplerExternalOES texture data for WebVR content image.
   int webvr_texture_id_ = 0;
 
-  std::unique_ptr<UiScene> scene_;
+  UiScene* scene_;
 
   scoped_refptr<gl::GLSurface> surface_;
   scoped_refptr<gl::GLContext> context_;
diff --git a/chrome/browser/autofill/android/personal_data_manager_android.cc b/chrome/browser/autofill/android/personal_data_manager_android.cc
index cc60fce..f270be2 100644
--- a/chrome/browser/autofill/android/personal_data_manager_android.cc
+++ b/chrome/browser/autofill/android/personal_data_manager_android.cc
@@ -13,7 +13,6 @@
 #include "base/android/jni_string.h"
 #include "base/command_line.h"
 #include "base/format_macros.h"
-#include "base/memory/ptr_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
 #include "chrome/browser/android/resource_mapper.h"
@@ -306,40 +305,6 @@
   DISALLOW_COPY_AND_ASSIGN(AndroidAddressNormalizerDelegate);
 };
 
-class AndroidSubKeyRequesterDelegate
-    : public PersonalDataManagerAndroid::SubKeyRequestDelegate,
-      public base::SupportsWeakPtr<AndroidSubKeyRequesterDelegate> {
- public:
-  AndroidSubKeyRequesterDelegate(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& jdelegate,
-      const std::string& region_code,
-      base::WeakPtr<PersonalDataManagerAndroid> personal_data_manager_android) {
-    jdelegate_.Reset(env, jdelegate);
-    region_code_ = region_code;
-    personal_data_manager_android_ = personal_data_manager_android;
-  }
-
-  ~AndroidSubKeyRequesterDelegate() override {}
-
- private:
-  // PersonalDataManagerAndroid::SubKeyRequestDelegate:
-  void OnRulesSuccessfullyLoaded() override {
-    if (personal_data_manager_android_) {
-      JNIEnv* env = base::android::AttachCurrentThread();
-      Java_GetSubKeysRequestDelegate_onSubKeysReceived(
-          env, jdelegate_,
-          personal_data_manager_android_->GetSubKeys(env, region_code_));
-    }
-  }
-
-  ScopedJavaGlobalRef<jobject> jdelegate_;
-  std::string region_code_;
-  base::WeakPtr<PersonalDataManagerAndroid> personal_data_manager_android_;
-
-  DISALLOW_COPY_AND_ASSIGN(AndroidSubKeyRequesterDelegate);
-};
-
 }  // namespace
 
 PersonalDataManagerAndroid::PersonalDataManagerAndroid(JNIEnv* env, jobject obj)
@@ -351,13 +316,7 @@
               new autofill::ChromeMetadataSource(
                   I18N_ADDRESS_VALIDATION_DATA_URL,
                   personal_data_manager_->GetURLRequestContextGetter())),
-          ValidationRulesStorageFactory::CreateStorage()),
-      address_validator_(
-          base::MakeUnique<autofill::ChromeMetadataSource>(
-              I18N_ADDRESS_VALIDATION_DATA_URL,
-              personal_data_manager_->GetURLRequestContextGetter()),
-          ValidationRulesStorageFactory::CreateStorage(),
-          this) {
+          ValidationRulesStorageFactory::CreateStorage()) {
   personal_data_manager_->AddObserver(this);
 }
 
@@ -738,7 +697,7 @@
   return base::Time::Now().ToTimeT();
 }
 
-void PersonalDataManagerAndroid::LoadRulesForAddressNormalization(
+void PersonalDataManagerAndroid::LoadRulesForRegion(
     JNIEnv* env,
     const base::android::JavaParamRef<jobject>& unused_obj,
     const base::android::JavaParamRef<jstring>& jregion_code) {
@@ -746,13 +705,6 @@
       ConvertJavaStringToUTF8(env, jregion_code));
 }
 
-void PersonalDataManagerAndroid::LoadRulesForSubKeys(
-    JNIEnv* env,
-    const base::android::JavaParamRef<jobject>& unused_obj,
-    const base::android::JavaParamRef<jstring>& jregion_code) {
-  address_validator_.LoadRules(ConvertJavaStringToUTF8(env, jregion_code));
-}
-
 void PersonalDataManagerAndroid::StartAddressNormalization(
     JNIEnv* env,
     const JavaParamRef<jobject>& unused_obj,
@@ -786,53 +738,6 @@
   return !personal_data_manager_->GetCreditCards().empty();
 }
 
-base::android::ScopedJavaLocalRef<jobjectArray>
-PersonalDataManagerAndroid::GetSubKeys(JNIEnv* env,
-                                       const std::string& region_code) {
-  std::vector<std::string> sub_keys =
-      address_validator_.GetRegionSubKeys(region_code);
-  return base::android::ToJavaArrayOfStrings(env, sub_keys);
-}
-
-void PersonalDataManagerAndroid::OnAddressRulesLoaded(
-    const std::string& region_code,
-    bool success) {
-  // if |success| == false, AddressValidator::GetRegionSubKeys will return an
-  // empty list of sub-keys. => No need to check for |success|.
-  // Check if there is any sub-key request for that region code.
-  if (!pending_subkey_region_code_.compare(region_code))
-    pending_subkey_request_->OnRulesSuccessfullyLoaded();
-  pending_subkey_region_code_.clear();
-  pending_subkey_request_.reset();
-}
-
-void PersonalDataManagerAndroid::StartRegionSubKeysRequest(
-    JNIEnv* env,
-    const JavaParamRef<jobject>& unused_obj,
-    const JavaParamRef<jstring>& jregion_code,
-    const JavaParamRef<jobject>& jdelegate) {
-  const std::string region_code = ConvertJavaStringToUTF8(env, jregion_code);
-  std::unique_ptr<SubKeyRequestDelegate> requester =
-      base::MakeUnique<AndroidSubKeyRequesterDelegate>(
-          env, jdelegate, region_code, AsWeakPtr());
-
-  if (AreRulesLoadedForRegion(region_code)) {
-    requester->OnRulesSuccessfullyLoaded();
-  } else {
-    // Setup the variables so that the sub-keys request is sent, when the rules
-    // are loaded.
-    pending_subkey_region_code_ = region_code;
-    pending_subkey_request_ = std::move(requester);
-  }
-}
-
-void PersonalDataManagerAndroid::CancelPendingGetSubKeys(
-    JNIEnv* env,
-    const base::android::JavaParamRef<jobject>& unused_obj) {
-  pending_subkey_region_code_.clear();
-  pending_subkey_request_.reset();
-}
-
 ScopedJavaLocalRef<jobjectArray> PersonalDataManagerAndroid::GetProfileGUIDs(
     JNIEnv* env,
     const std::vector<AutofillProfile*>& profiles) {
@@ -855,7 +760,7 @@
 
 bool PersonalDataManagerAndroid::AreRulesLoadedForRegion(
     const std::string& region_code) {
-  return address_validator_.AreRulesLoadedForRegion(region_code);
+  return address_normalizer_.AreRulesLoadedForRegion(region_code);
 }
 
 ScopedJavaLocalRef<jobjectArray> PersonalDataManagerAndroid::GetProfileLabels(
diff --git a/chrome/browser/autofill/android/personal_data_manager_android.h b/chrome/browser/autofill/android/personal_data_manager_android.h
index 3dd7936..801e8b6c 100644
--- a/chrome/browser/autofill/android/personal_data_manager_android.h
+++ b/chrome/browser/autofill/android/personal_data_manager_android.h
@@ -12,25 +12,14 @@
 #include "components/autofill/core/browser/personal_data_manager.h"
 #include "components/autofill/core/browser/personal_data_manager_observer.h"
 #include "components/payments/core/address_normalizer.h"
-#include "third_party/libaddressinput/chromium/chrome_address_validator.h"
 
 namespace autofill {
 
 // Android wrapper of the PersonalDataManager which provides access from the
 // Java layer. Note that on Android, there's only a single profile, and
 // therefore a single instance of this wrapper.
-class PersonalDataManagerAndroid
-    : public PersonalDataManagerObserver,
-      public LoadRulesListener,
-      public base::SupportsWeakPtr<PersonalDataManagerAndroid> {
+class PersonalDataManagerAndroid : public PersonalDataManagerObserver {
  public:
-  // The interface for the sub-key request.
-  class SubKeyRequestDelegate {
-   public:
-    virtual void OnRulesSuccessfullyLoaded() = 0;
-    virtual ~SubKeyRequestDelegate() {}
-  };
-
   // Registers the JNI bindings for this class.
   static bool Register(JNIEnv* env);
 
@@ -295,14 +284,7 @@
 
   // Starts loading the address validation rules for the specified
   // |region_code|.
-  void LoadRulesForAddressNormalization(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& unused_obj,
-      const base::android::JavaParamRef<jstring>& region_code);
-
-  // Starts loading the rules for the specified |region_code| for the further
-  // sub-key request.
-  void LoadRulesForSubKeys(
+  void LoadRulesForRegion(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& unused_obj,
       const base::android::JavaParamRef<jstring>& region_code);
@@ -330,31 +312,6 @@
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& unused_obj);
 
-  // Gets the sub-keys for the region with |jregion_code| code, if the
-  // |jregion_code| rules have finished loading. Otherwise, sets up a task to
-  // get the sub-keys, when the rules are loaded.
-  void StartRegionSubKeysRequest(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& unused_obj,
-      const base::android::JavaParamRef<jstring>& jregion_code,
-      const base::android::JavaParamRef<jobject>& jdelegate);
-
-  // Gets the sub-keys of the rule associated with |jregion_code|. Should only
-  // be called when the rules are loaded.
-  base::android::ScopedJavaLocalRef<jobjectArray> GetSubKeys(
-      JNIEnv* env,
-      const std::string& jregion_code);
-
-  // Callback of the sub-keys request.
-  // This is called when the sub-keys are loaded.
-  void OnAddressRulesLoaded(const std::string& region_code,
-                            bool success) override;
-
-  // Cancels the pending sub-key request task.
-  void CancelPendingGetSubKeys(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& unused_obj);
-
  private:
   ~PersonalDataManagerAndroid() override;
 
@@ -405,13 +362,6 @@
   // The address validator used to normalize addresses.
   payments::AddressNormalizer address_normalizer_;
 
-  // The address validator used for sub-key request.
-  AddressValidator address_validator_;
-
-  // The region code and the request for the pending sub-key request.
-  std::unique_ptr<SubKeyRequestDelegate> pending_subkey_request_;
-  std::string pending_subkey_region_code_;
-
   DISALLOW_COPY_AND_ASSIGN(PersonalDataManagerAndroid);
 };
 
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 f86630e..5666ce3b 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
@@ -66,6 +66,10 @@
 
   ash::Shell::Get()->accelerator_controller()->Register(
       {ui::Accelerator(ui::VKEY_A, ui::EF_COMMAND_DOWN)}, this);
+  // Temporary shortcut added to enable the metalayer experiment.
+  ash::Shell::Get()->accelerator_controller()->Register(
+      {ui::Accelerator(ui::VKEY_A, ui::EF_COMMAND_DOWN | ui::EF_SHIFT_DOWN)},
+      this);
 }
 
 void ArcVoiceInteractionFrameworkService::OnInstanceClosed() {
@@ -76,12 +80,23 @@
 bool ArcVoiceInteractionFrameworkService::AcceleratorPressed(
     const ui::Accelerator& accelerator) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  mojom::VoiceInteractionFrameworkInstance* framework_instance =
-      ARC_GET_INSTANCE_FOR_METHOD(
-          arc_bridge_service()->voice_interaction_framework(),
-          StartVoiceInteractionSession);
-  DCHECK(framework_instance);
-  framework_instance->StartVoiceInteractionSession();
+
+  if (accelerator.IsShiftDown()) {
+    mojom::VoiceInteractionFrameworkInstance* framework_instance =
+        ARC_GET_INSTANCE_FOR_METHOD(
+            arc_bridge_service()->voice_interaction_framework(),
+            ToggleMetalayer);
+    DCHECK(framework_instance);
+    framework_instance->ToggleMetalayer();
+  } else {
+    mojom::VoiceInteractionFrameworkInstance* framework_instance =
+        ARC_GET_INSTANCE_FOR_METHOD(
+            arc_bridge_service()->voice_interaction_framework(),
+            StartVoiceInteractionSession);
+    DCHECK(framework_instance);
+    framework_instance->StartVoiceInteractionSession();
+  }
+
   return true;
 }
 
@@ -107,4 +122,18 @@
                                  base::Bind(&ScreenshotCallback, callback));
 }
 
+void ArcVoiceInteractionFrameworkService::CaptureFullscreen(
+    const CaptureFullscreenCallback& callback) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  // Since ARC currently only runs in primary display, we restrict
+  // the screenshot to it.
+  aura::Window* window = ash::Shell::GetPrimaryRootWindow();
+  DCHECK(window);
+  ui::GrabWindowSnapshotAsyncPNG(window, gfx::Rect(window->bounds().size()),
+                                 base::CreateTaskRunnerWithTraits(
+                                     base::TaskTraits().MayBlock().WithPriority(
+                                         base::TaskPriority::USER_BLOCKING)),
+                                 base::Bind(&ScreenshotCallback, callback));
+}
+
 }  // namespace arc
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 bcc283e..015acf8 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
@@ -41,6 +41,7 @@
   // mojom::VoiceInteractionFrameworkHost overrides.
   void CaptureFocusedWindow(
       const CaptureFocusedWindowCallback& callback) override;
+  void CaptureFullscreen(const CaptureFullscreenCallback& callback) override;
 
   // Whether enable-voice-interaction switch is present.
   static bool IsVoiceInteractionEnabled();
diff --git a/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.cc b/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.cc
index 576de47..dcf21cd 100644
--- a/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.cc
+++ b/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.cc
@@ -425,8 +425,7 @@
 }
 
 void UserCloudPolicyManagerChromeOS::OnBlockingFetchTimeout() {
-  if (!wait_for_policy_fetch_)
-    return;
+  DCHECK(wait_for_policy_fetch_);
   LOG(WARNING) << "Timed out while waiting for the policy fetch. "
                << "The session will start with the cached policy.";
   CancelWaitForPolicyFetch(false);
diff --git a/chrome/browser/download/chrome_download_manager_delegate.cc b/chrome/browser/download/chrome_download_manager_delegate.cc
index f698c12..f9b4e53 100644
--- a/chrome/browser/download/chrome_download_manager_delegate.cc
+++ b/chrome/browser/download/chrome_download_manager_delegate.cc
@@ -59,6 +59,8 @@
 
 #if defined(OS_ANDROID)
 #include "chrome/browser/android/download/chrome_duplicate_download_infobar_delegate.h"
+#include "chrome/browser/android/download/download_controller.h"
+#include "chrome/browser/android/download/download_manager_service.h"
 #include "chrome/browser/infobars/infobar_service.h"
 #endif
 
@@ -193,6 +195,16 @@
   DANGEROUS_FILE_REASON_MAX
 };
 
+// On Android, Chrome wants to warn the user of file overwrites rather than
+// uniquify.
+#if defined(OS_ANDROID)
+const DownloadPathReservationTracker::FilenameConflictAction
+    kDefaultPlatformConflictAction = DownloadPathReservationTracker::PROMPT;
+#else
+const DownloadPathReservationTracker::FilenameConflictAction
+    kDefaultPlatformConflictAction = DownloadPathReservationTracker::UNIQUIFY;
+#endif
+
 }  // namespace
 
 ChromeDownloadManagerDelegate::ChromeDownloadManagerDelegate(Profile* profile)
@@ -278,8 +290,7 @@
   DownloadTargetDeterminer::Start(
       download,
       GetPlatformDownloadPath(profile_, download, PLATFORM_TARGET_PATH),
-      download_prefs_.get(),
-      this,
+      kDefaultPlatformConflictAction, download_prefs_.get(), this,
       target_determined_callback);
   return true;
 }
@@ -596,10 +607,8 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!virtual_path.empty());
 #if defined(OS_CHROMEOS)
-  // TODO(asanka): Handle path reservations for virtual paths as well.
-  //               http://crbug.com/151618
   if (drive::util::IsUnderDriveMountPoint(virtual_path)) {
-    callback.Run(virtual_path, true);
+    callback.Run(PathValidationResult::SUCCESS, virtual_path);
     return;
   }
 #endif
@@ -612,23 +621,65 @@
       callback);
 }
 
-void ChromeDownloadManagerDelegate::PromptUserForDownloadPath(
+void ChromeDownloadManagerDelegate::RequestConfirmation(
     DownloadItem* download,
     const base::FilePath& suggested_path,
-    const DownloadTargetDeterminerDelegate::FileSelectedCallback& callback) {
+    DownloadConfirmationReason reason,
+    const DownloadTargetDeterminerDelegate::ConfirmationCallback& callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 #if defined(OS_ANDROID)
-  content::WebContents* web_contents = download->GetWebContents();
-  if (!web_contents) {
-    callback.Run(base::FilePath());
-    return;
+  switch (reason) {
+    case DownloadConfirmationReason::NONE:
+      NOTREACHED();
+      return;
+
+    case DownloadConfirmationReason::TARGET_PATH_NOT_WRITEABLE:
+      DownloadManagerService::OnDownloadCanceled(
+          download, DownloadController::CANCEL_REASON_NO_EXTERNAL_STORAGE);
+      callback.Run(DownloadConfirmationResult::CANCELED, base::FilePath());
+      return;
+
+    case DownloadConfirmationReason::NAME_TOO_LONG:
+    case DownloadConfirmationReason::TARGET_NO_SPACE:
+    // These are errors. But rather than cancel the download we are going to
+    // continue with the current path so that the download will get
+    // interrupted again.
+    //
+    // Ideally we'd allow the user to try another location, but on Android,
+    // the user doesn't have much of a choice (currently). So we skip the
+    // prompt and try the same location.
+
+    case DownloadConfirmationReason::SAVE_AS:
+    case DownloadConfirmationReason::PREFERENCE:
+      callback.Run(DownloadConfirmationResult::CONTINUE_WITHOUT_CONFIRMATION,
+                   suggested_path);
+      return;
+
+    case DownloadConfirmationReason::TARGET_CONFLICT:
+      if (download->GetWebContents()) {
+        chrome::android::ChromeDuplicateDownloadInfoBarDelegate::Create(
+            InfoBarService::FromWebContents(download->GetWebContents()),
+            download, suggested_path, callback);
+        return;
+      }
+    // Fallthrough
+
+    // If we cannot reserve the path and the WebContent is already gone, there
+    // is no way to prompt user for an infobar. This could happen after chrome
+    // gets killed, and user tries to resume a download while another app has
+    // created the target file (not the temporary .crdownload file).
+    case DownloadConfirmationReason::UNEXPECTED:
+      DownloadManagerService::OnDownloadCanceled(
+          download,
+          DownloadController::CANCEL_REASON_CANNOT_DETERMINE_DOWNLOAD_TARGET);
+      callback.Run(DownloadConfirmationResult::CANCELED, base::FilePath());
+      return;
   }
-  chrome::android::ChromeDuplicateDownloadInfoBarDelegate::Create(
-      InfoBarService::FromWebContents(web_contents), download,
-      suggested_path, callback);
-#else
+#else   // !OS_ANDROID
+  // Desktop Chrome displays a file picker for all confirmation needs. We can do
+  // better.
   DownloadFilePicker::ShowFilePicker(download, suggested_path, callback);
-#endif
+#endif  // !OS_ANDROID
 }
 
 void ChromeDownloadManagerDelegate::DetermineLocalPath(
@@ -786,9 +837,7 @@
   }
   callback.Run(target_info->target_path, target_info->target_disposition,
                target_info->danger_type, target_info->intermediate_path,
-               target_info->target_path.empty()
-                   ? content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED
-                   : content::DOWNLOAD_INTERRUPT_REASON_NONE);
+               target_info->result);
 }
 
 bool ChromeDownloadManagerDelegate::IsOpenInBrowserPreferreredForFile(
diff --git a/chrome/browser/download/chrome_download_manager_delegate.h b/chrome/browser/download/chrome_download_manager_delegate.h
index 1091d3ed..a03a227c 100644
--- a/chrome/browser/download/chrome_download_manager_delegate.h
+++ b/chrome/browser/download/chrome_download_manager_delegate.h
@@ -8,9 +8,12 @@
 #include <stdint.h>
 
 #include <memory>
+#include <string>
+#include <vector>
 
 #include "base/compiler_specific.h"
 #include "base/containers/hash_tables.h"
+#include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/download/download_path_reservation_tracker.h"
@@ -93,10 +96,6 @@
   DownloadPrefs* download_prefs() { return download_prefs_.get(); }
 
  protected:
-  // So that test classes that inherit from this for override purposes
-  // can call back into the DownloadManager.
-  content::DownloadManager* download_manager_;
-
   virtual safe_browsing::DownloadProtectionService*
       GetDownloadProtectionService();
 
@@ -110,9 +109,10 @@
       bool create_directory,
       DownloadPathReservationTracker::FilenameConflictAction conflict_action,
       const ReservedPathCallback& callback) override;
-  void PromptUserForDownloadPath(content::DownloadItem* download,
-                                 const base::FilePath& suggested_virtual_path,
-                                 const FileSelectedCallback& callback) override;
+  void RequestConfirmation(content::DownloadItem* download,
+                           const base::FilePath& suggested_virtual_path,
+                           DownloadConfirmationReason reason,
+                           const ConfirmationCallback& callback) override;
   void DetermineLocalPath(content::DownloadItem* download,
                           const base::FilePath& virtual_path,
                           const LocalPathCallback& callback) override;
@@ -122,8 +122,14 @@
   void GetFileMimeType(const base::FilePath& path,
                        const GetFileMimeTypeCallback& callback) override;
 
+  // So that test classes that inherit from this for override purposes
+  // can call back into the DownloadManager.
+  content::DownloadManager* download_manager_;
+
  private:
   friend class base::RefCountedThreadSafe<ChromeDownloadManagerDelegate>;
+  FRIEND_TEST_ALL_PREFIXES(ChromeDownloadManagerDelegateTest,
+                           RequestConfirmation_Android);
 
   typedef std::vector<content::DownloadIdCallback> IdCallbackVector;
 
diff --git a/chrome/browser/download/chrome_download_manager_delegate_unittest.cc b/chrome/browser/download/chrome_download_manager_delegate_unittest.cc
index 65176e3..29e2d37 100644
--- a/chrome/browser/download/chrome_download_manager_delegate_unittest.cc
+++ b/chrome/browser/download/chrome_download_manager_delegate_unittest.cc
@@ -5,10 +5,13 @@
 #include <stddef.h>
 #include <stdint.h>
 
+#include <string>
+
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/location.h"
+#include "base/memory/ptr_util.h"
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -17,6 +20,7 @@
 #include "chrome/browser/download/download_item_model.h"
 #include "chrome/browser/download/download_prefs.h"
 #include "chrome/browser/download/download_target_info.h"
+#include "chrome/common/features.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "chrome/test/base/testing_profile.h"
@@ -36,14 +40,24 @@
 #include "chrome/browser/safe_browsing/download_protection_service.h"
 #endif
 
-#if !defined(OS_ANDROID)
+#if BUILDFLAG(ENABLE_PLUGINS)
 #include "content/public/browser/plugin_service.h"
 #endif
 
+#if defined(OS_ANDROID)
+#include "chrome/browser/infobars/infobar_service.h"
+#include "components/infobars/core/infobar.h"
+#include "components/infobars/core/infobar_delegate.h"
+#include "components/infobars/core/infobar_manager.h"
+#endif
+
+using ::testing::AnyNumber;
 using ::testing::AtMost;
+using ::testing::DoAll;
 using ::testing::Invoke;
 using ::testing::Ref;
 using ::testing::Return;
+using ::testing::ReturnArg;
 using ::testing::ReturnPointee;
 using ::testing::ReturnRef;
 using ::testing::ReturnRefOfCopy;
@@ -60,8 +74,43 @@
   ~MockWebContentsDelegate() override {}
 };
 
-// Subclass of the ChromeDownloadManagerDelegate that uses a mock
-// DownloadProtectionService.
+// Google Mock action that posts a task to the current message loop that invokes
+// the first argument of the mocked method as a callback. Said argument must be
+// a base::Callback<void(ParamType0, ParamType1)>. |result0| and |result1| must
+// be of ParamType0 and ParamType1 respectively and will be bound as such.
+//
+// Example:
+//    class FooClass {
+//     public:
+//      virtual void Foo(base::Callback<void(bool, std::string)> callback);
+//    };
+//    ...
+//    EXPECT_CALL(mock_fooclass_instance, Foo(callback))
+//      .WillOnce(ScheduleCallback2(false, "hello"));
+//
+ACTION_P2(ScheduleCallback2, result0, result1) {
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::Bind(arg0, result0, result1));
+}
+
+// Struct for holding the result of calling DetermineDownloadTarget.
+struct DetermineDownloadTargetResult {
+  DetermineDownloadTargetResult();
+
+  base::FilePath target_path;
+  content::DownloadItem::TargetDisposition disposition;
+  content::DownloadDangerType danger_type;
+  base::FilePath intermediate_path;
+  content::DownloadInterruptReason interrupt_reason;
+};
+
+DetermineDownloadTargetResult::DetermineDownloadTargetResult()
+    : disposition(content::DownloadItem::TARGET_DISPOSITION_OVERWRITE),
+      danger_type(content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS),
+      interrupt_reason(content::DOWNLOAD_INTERRUPT_REASON_NONE) {}
+
+// Subclass of the ChromeDownloadManagerDelegate that replaces a few interaction
+// points for ease of testing.
 class TestChromeDownloadManagerDelegate : public ChromeDownloadManagerDelegate {
  public:
   explicit TestChromeDownloadManagerDelegate(Profile* profile)
@@ -70,10 +119,18 @@
         .WillByDefault(Return(content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS));
     ON_CALL(*this, GetDownloadProtectionService())
         .WillByDefault(Return(nullptr));
+    ON_CALL(*this, MockReserveVirtualPath(_, _, _, _, _))
+        .WillByDefault(DoAll(SetArgPointee<4>(PathValidationResult::SUCCESS),
+                             ReturnArg<1>()));
   }
 
   ~TestChromeDownloadManagerDelegate() override {}
 
+  // The concrete implementation talks to the ExtensionDownloadsEventRouter to
+  // dispatch a OnDeterminingFilename event. While we would like to test this as
+  // well in this unit test, we are currently going to rely on the extension
+  // browser test to provide test coverage here. Instead we are going to mock it
+  // out for unit tests.
   void NotifyExtensions(content::DownloadItem* download,
                         const base::FilePath& suggested_virtual_path,
                         const NotifyExtensionsCallback& callback) override {
@@ -81,6 +138,10 @@
                  DownloadPathReservationTracker::UNIQUIFY);
   }
 
+  // DownloadPathReservationTracker talks to the underlying file system. For
+  // tests we are going to mock it out so that we can test how
+  // ChromeDownloadManagerDelegate reponds to various DownloadTargetDeterminer
+  // results.
   void ReserveVirtualPath(
       content::DownloadItem* download,
       const base::FilePath& virtual_path,
@@ -88,47 +149,62 @@
       DownloadPathReservationTracker::FilenameConflictAction conflict_action,
       const DownloadPathReservationTracker::ReservedPathCallback& callback)
       override {
-    // Pretend the path reservation succeeded without any change to
-    // |target_path|.
+    PathValidationResult result = PathValidationResult::SUCCESS;
+    base::FilePath path_to_return = MockReserveVirtualPath(
+        download, virtual_path, create_directory, conflict_action, &result);
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::Bind(callback, virtual_path, true));
+        FROM_HERE, base::Bind(callback, result, path_to_return));
   }
 
-  void PromptUserForDownloadPath(
-      DownloadItem* download,
-      const base::FilePath& suggested_path,
-      const DownloadTargetDeterminerDelegate::FileSelectedCallback& callback)
-      override {
-    base::FilePath return_path = MockPromptUserForDownloadPath(download,
-                                                               suggested_path,
-                                                               callback);
-    callback.Run(return_path);
-  }
+  MOCK_METHOD5(
+      MockReserveVirtualPath,
+      base::FilePath(content::DownloadItem*,
+                     const base::FilePath&,
+                     bool,
+                     DownloadPathReservationTracker::FilenameConflictAction,
+                     PathValidationResult*));
 
+  // The concrete implementation invokes SafeBrowsing's
+  // DownloadProtectionService. Now that SafeBrowsingService is testable, we
+  // should migrate to using TestSafeBrowsingService instead.
   void CheckDownloadUrl(DownloadItem* download,
                         const base::FilePath& virtual_path,
                         const CheckDownloadUrlCallback& callback) override {
     callback.Run(MockCheckDownloadUrl(download, virtual_path));
   }
 
-  MOCK_METHOD0(GetDownloadProtectionService,
-               safe_browsing::DownloadProtectionService*());
-
-  MOCK_METHOD3(
-      MockPromptUserForDownloadPath,
-      base::FilePath(
-          DownloadItem*,
-          const base::FilePath&,
-          const DownloadTargetDeterminerDelegate::FileSelectedCallback&));
-
   MOCK_METHOD2(MockCheckDownloadUrl,
                content::DownloadDangerType(DownloadItem*,
                                            const base::FilePath&));
+
+  MOCK_METHOD0(GetDownloadProtectionService,
+               safe_browsing::DownloadProtectionService*());
+
+  // The concrete implementation on desktop just invokes a file picker. Android
+  // has a non-trivial implementation. The former is tested via browser tests,
+  // and the latter is exercised in this unit test.
+  MOCK_METHOD4(
+      RequestConfirmation,
+      void(DownloadItem*,
+           const base::FilePath&,
+           DownloadConfirmationReason,
+           const DownloadTargetDeterminerDelegate::ConfirmationCallback&));
+
+  // For testing the concrete implementation.
+  void RequestConfirmationConcrete(
+      DownloadItem* download_item,
+      const base::FilePath& path,
+      DownloadConfirmationReason reason,
+      const DownloadTargetDeterminerDelegate::ConfirmationCallback& callback) {
+    ChromeDownloadManagerDelegate::RequestConfirmation(download_item, path,
+                                                       reason, callback);
+  }
 };
 
 class ChromeDownloadManagerDelegateTest
     : public ChromeRenderViewHostTestHarness {
  public:
+  // Result of calling DetermineDownloadTarget.
   ChromeDownloadManagerDelegateTest();
 
   // ::testing::Test
@@ -151,7 +227,7 @@
   void SetDefaultDownloadPath(const base::FilePath& path);
 
   void DetermineDownloadTarget(DownloadItem* download,
-                               DownloadTargetInfo* result);
+                               DetermineDownloadTargetResult* result);
 
   // Invokes ChromeDownloadManagerDelegate::CheckForFileExistence and waits for
   // the asynchronous callback. The result passed into
@@ -180,7 +256,9 @@
   ChromeRenderViewHostTestHarness::SetUp();
 
   CHECK(profile());
-  delegate_.reset(new TestChromeDownloadManagerDelegate(profile()));
+  delegate_ =
+      base::MakeUnique<::testing::NiceMock<TestChromeDownloadManagerDelegate>>(
+          profile());
   delegate_->SetDownloadManager(download_manager_.get());
   pref_service_ = profile()->GetTestingPrefService();
   web_contents()->SetDelegate(&web_contents_delegate_);
@@ -253,22 +331,23 @@
 
 void StoreDownloadTargetInfo(
     const base::Closure& closure,
-    DownloadTargetInfo* target_info,
+    DetermineDownloadTargetResult* result,
     const base::FilePath& target_path,
     DownloadItem::TargetDisposition target_disposition,
     content::DownloadDangerType danger_type,
     const base::FilePath& intermediate_path,
     content::DownloadInterruptReason interrupt_reason) {
-  target_info->target_path = target_path;
-  target_info->target_disposition = target_disposition;
-  target_info->danger_type = danger_type;
-  target_info->intermediate_path = intermediate_path;
+  result->target_path = target_path;
+  result->disposition = target_disposition;
+  result->danger_type = danger_type;
+  result->intermediate_path = intermediate_path;
+  result->interrupt_reason = interrupt_reason;
   closure.Run();
 }
 
 void ChromeDownloadManagerDelegateTest::DetermineDownloadTarget(
     DownloadItem* download_item,
-    DownloadTargetInfo* result) {
+    DetermineDownloadTargetResult* result) {
   base::RunLoop loop_runner;
   delegate()->DetermineDownloadTarget(
       download_item,
@@ -315,53 +394,51 @@
 
 }  // namespace
 
-// There is no "save as" context menu option on Android.
-#if !defined(OS_ANDROID)
-TEST_F(ChromeDownloadManagerDelegateTest, StartDownload_LastSavePath) {
+TEST_F(ChromeDownloadManagerDelegateTest, LastSavePath) {
   GURL download_url("http://example.com/foo.txt");
 
   std::unique_ptr<content::MockDownloadItem> save_as_download =
       CreateActiveDownloadItem(0);
   EXPECT_CALL(*save_as_download, GetURL())
-      .Times(::testing::AnyNumber())
+      .Times(AnyNumber())
       .WillRepeatedly(ReturnRef(download_url));
   EXPECT_CALL(*save_as_download, GetTargetDisposition())
-      .Times(::testing::AnyNumber())
+      .Times(AnyNumber())
       .WillRepeatedly(Return(DownloadItem::TARGET_DISPOSITION_PROMPT));
 
   std::unique_ptr<content::MockDownloadItem> automatic_download =
       CreateActiveDownloadItem(1);
   EXPECT_CALL(*automatic_download, GetURL())
-      .Times(::testing::AnyNumber())
+      .Times(AnyNumber())
       .WillRepeatedly(ReturnRef(download_url));
   EXPECT_CALL(*automatic_download, GetTargetDisposition())
-      .Times(::testing::AnyNumber())
+      .Times(AnyNumber())
       .WillRepeatedly(Return(DownloadItem::TARGET_DISPOSITION_OVERWRITE));
 
   {
     // When the prompt is displayed for the first download, the user selects a
     // path in a different directory.
-    DownloadTargetInfo result;
+    DetermineDownloadTargetResult result;
     base::FilePath expected_prompt_path(GetPathInDownloadDir("foo.txt"));
     base::FilePath user_selected_path(GetPathInDownloadDir("bar/baz.txt"));
-    EXPECT_CALL(*delegate(),
-                MockPromptUserForDownloadPath(save_as_download.get(),
-                                              expected_prompt_path, _))
-        .WillOnce(Return(user_selected_path));
+    EXPECT_CALL(*delegate(), RequestConfirmation(save_as_download.get(),
+                                                 expected_prompt_path, _, _))
+        .WillOnce(WithArg<3>(ScheduleCallback2(
+            DownloadConfirmationResult::CONFIRMED, user_selected_path)));
     DetermineDownloadTarget(save_as_download.get(), &result);
     EXPECT_EQ(user_selected_path, result.target_path);
     VerifyAndClearExpectations();
   }
 
   {
-    // The prompt path for the second download is the user selected directroy
+    // The prompt path for the second download is the user selected directory
     // from the previous download.
-    DownloadTargetInfo result;
+    DetermineDownloadTargetResult result;
     base::FilePath expected_prompt_path(GetPathInDownloadDir("bar/foo.txt"));
-    EXPECT_CALL(*delegate(),
-                MockPromptUserForDownloadPath(save_as_download.get(),
-                                              expected_prompt_path, _))
-        .WillOnce(Return(base::FilePath()));
+    EXPECT_CALL(*delegate(), RequestConfirmation(save_as_download.get(),
+                                                 expected_prompt_path, _, _))
+        .WillOnce(WithArg<3>(ScheduleCallback2(
+            DownloadConfirmationResult::CANCELED, base::FilePath())));
     DetermineDownloadTarget(save_as_download.get(), &result);
     VerifyAndClearExpectations();
   }
@@ -369,7 +446,7 @@
   {
     // Start an automatic download. This one should get the default download
     // path since the last download path only affects Save As downloads.
-    DownloadTargetInfo result;
+    DetermineDownloadTargetResult result;
     base::FilePath expected_path(GetPathInDownloadDir("foo.txt"));
     DetermineDownloadTarget(automatic_download.get(), &result);
     EXPECT_EQ(expected_path, result.target_path);
@@ -379,20 +456,49 @@
   {
     // The prompt path for the next download should be the default.
     download_prefs()->SetSaveFilePath(download_prefs()->DownloadPath());
-    DownloadTargetInfo result;
+    DetermineDownloadTargetResult result;
     base::FilePath expected_prompt_path(GetPathInDownloadDir("foo.txt"));
-    EXPECT_CALL(*delegate(),
-                MockPromptUserForDownloadPath(save_as_download.get(),
-                                              expected_prompt_path, _))
-        .WillOnce(Return(base::FilePath()));
+    EXPECT_CALL(*delegate(), RequestConfirmation(save_as_download.get(),
+                                                 expected_prompt_path, _, _))
+        .WillOnce(WithArg<3>(ScheduleCallback2(
+            DownloadConfirmationResult::CANCELED, base::FilePath())));
     DetermineDownloadTarget(save_as_download.get(), &result);
     VerifyAndClearExpectations();
   }
 }
-#endif  // !defined(OS_ANDROID)
+
+TEST_F(ChromeDownloadManagerDelegateTest, ConflictAction) {
+  const GURL kUrl("http://example.com/foo");
+  const std::string kTargetDisposition("attachment; filename=\"foo.txt\"");
+
+  std::unique_ptr<content::MockDownloadItem> download_item =
+      CreateActiveDownloadItem(0);
+  EXPECT_CALL(*download_item, GetURL()).WillRepeatedly(ReturnRef(kUrl));
+  EXPECT_CALL(*download_item, GetContentDisposition())
+      .WillRepeatedly(Return(kTargetDisposition));
+
+  base::FilePath kExpectedPath = GetPathInDownloadDir("bar.txt");
+
+  DetermineDownloadTargetResult result;
+
+  EXPECT_CALL(*delegate(), MockReserveVirtualPath(_, _, _, _, _))
+      .WillOnce(DoAll(SetArgPointee<4>(PathValidationResult::CONFLICT),
+                      ReturnArg<1>()));
+  EXPECT_CALL(
+      *delegate(),
+      RequestConfirmation(_, _, DownloadConfirmationReason::TARGET_CONFLICT, _))
+      .WillOnce(WithArg<3>(ScheduleCallback2(
+          DownloadConfirmationResult::CONFIRMED, kExpectedPath)));
+  DetermineDownloadTarget(download_item.get(), &result);
+  EXPECT_EQ(content::DownloadItem::TARGET_DISPOSITION_PROMPT,
+            result.disposition);
+  EXPECT_EQ(kExpectedPath, result.target_path);
+
+  VerifyAndClearExpectations();
+}
 
 TEST_F(ChromeDownloadManagerDelegateTest, MaybeDangerousContent) {
-#if !defined(OS_ANDROID)
+#if BUILDFLAG(ENABLE_PLUGINS)
   content::PluginService::GetInstance()->Init();
 #endif
 
@@ -412,13 +518,13 @@
         "attachment; filename=\"foo.swf\"");
     EXPECT_CALL(*download_item, GetContentDisposition())
         .WillRepeatedly(Return(kDangerousContentDisposition));
-    DownloadTargetInfo target_info;
-    DetermineDownloadTarget(download_item.get(), &target_info);
+    DetermineDownloadTargetResult result;
+    DetermineDownloadTarget(download_item.get(), &result);
 
     EXPECT_EQ(DownloadFileType::DANGEROUS,
               DownloadItemModel(download_item.get()).GetDangerLevel());
     EXPECT_EQ(content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT,
-              target_info.danger_type);
+              result.danger_type);
   }
 
   {
@@ -426,12 +532,12 @@
         "attachment; filename=\"foo.txt\"");
     EXPECT_CALL(*download_item, GetContentDisposition())
         .WillRepeatedly(Return(kSafeContentDisposition));
-    DownloadTargetInfo target_info;
-    DetermineDownloadTarget(download_item.get(), &target_info);
+    DetermineDownloadTargetResult result;
+    DetermineDownloadTarget(download_item.get(), &result);
     EXPECT_EQ(DownloadFileType::NOT_DANGEROUS,
               DownloadItemModel(download_item.get()).GetDangerLevel());
     EXPECT_EQ(content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT,
-              target_info.danger_type);
+              result.danger_type);
   }
 
   {
@@ -439,12 +545,12 @@
         "attachment; filename=\"foo.crx\"");
     EXPECT_CALL(*download_item, GetContentDisposition())
         .WillRepeatedly(Return(kModerateContentDisposition));
-    DownloadTargetInfo target_info;
-    DetermineDownloadTarget(download_item.get(), &target_info);
+    DetermineDownloadTargetResult result;
+    DetermineDownloadTarget(download_item.get(), &result);
     EXPECT_EQ(DownloadFileType::ALLOW_ON_USER_GESTURE,
               DownloadItemModel(download_item.get()).GetDangerLevel());
     EXPECT_EQ(content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT,
-              target_info.danger_type);
+              result.danger_type);
   }
 }
 
@@ -664,3 +770,131 @@
 }
 
 #endif  // FULL_SAFE_BROWSING
+
+#if defined(OS_ANDROID)
+
+namespace {
+
+class AndroidDownloadInfobarCounter
+    : public infobars::InfoBarManager::Observer {
+ public:
+  explicit AndroidDownloadInfobarCounter(content::WebContents* web_contents)
+      : infobar_service_(InfoBarService::FromWebContents(web_contents)) {
+    infobar_service_->AddObserver(this);
+  }
+
+  ~AndroidDownloadInfobarCounter() override {
+    infobar_service_->RemoveObserver(this);
+  }
+
+  int CheckAndResetInfobarCount() {
+    int count = infobar_count_;
+    infobar_count_ = 0;
+    return count;
+  }
+
+ private:
+  void OnInfoBarAdded(infobars::InfoBar* infobar) override {
+    if (infobar->delegate()->GetIdentifier() ==
+        infobars::InfoBarDelegate::CHROME_DUPLICATE_DOWNLOAD_INFOBAR_DELEGATE) {
+      ++infobar_count_;
+    }
+    infobar->delegate()->InfoBarDismissed();
+    infobar->RemoveSelf();
+  }
+
+  InfoBarService* infobar_service_;
+  int infobar_count_ = 0;
+};
+
+}  // namespace
+
+TEST_F(ChromeDownloadManagerDelegateTest, RequestConfirmation_Android) {
+  enum class WebContents { AVAILABLE, NONE };
+  enum class ExpectPath { FULL, EMPTY };
+  enum class ExpectInfoBar { YES, NO };
+  struct {
+    DownloadConfirmationReason confirmation_reason;
+    DownloadConfirmationResult expected_result;
+    WebContents web_contents;
+    ExpectInfoBar info_bar;
+    ExpectPath path;
+  } kTestCases[] = {
+      {DownloadConfirmationReason::TARGET_PATH_NOT_WRITEABLE,
+       DownloadConfirmationResult::CANCELED, WebContents::AVAILABLE,
+       ExpectInfoBar::NO, ExpectPath::EMPTY},
+
+      {DownloadConfirmationReason::NAME_TOO_LONG,
+       DownloadConfirmationResult::CONTINUE_WITHOUT_CONFIRMATION,
+       WebContents::AVAILABLE, ExpectInfoBar::NO, ExpectPath::FULL},
+
+      {DownloadConfirmationReason::TARGET_NO_SPACE,
+       DownloadConfirmationResult::CONTINUE_WITHOUT_CONFIRMATION,
+       WebContents::AVAILABLE, ExpectInfoBar::NO, ExpectPath::FULL},
+
+      {DownloadConfirmationReason::SAVE_AS,
+       DownloadConfirmationResult::CONTINUE_WITHOUT_CONFIRMATION,
+       WebContents::AVAILABLE, ExpectInfoBar::NO, ExpectPath::FULL},
+
+      {DownloadConfirmationReason::PREFERENCE,
+       DownloadConfirmationResult::CONTINUE_WITHOUT_CONFIRMATION,
+       WebContents::AVAILABLE, ExpectInfoBar::NO, ExpectPath::FULL},
+
+      // This case results in an infobar. The logic above dismisses the infobar
+      // and counts it for testing. The functionality of the infobar is not
+      // tested here other than that dimssing the infobar is treated as a user
+      // initiated cancellation.
+      {DownloadConfirmationReason::TARGET_CONFLICT,
+       DownloadConfirmationResult::CANCELED, WebContents::AVAILABLE,
+       ExpectInfoBar::YES, ExpectPath::EMPTY},
+
+      {DownloadConfirmationReason::TARGET_CONFLICT,
+       DownloadConfirmationResult::CANCELED, WebContents::NONE,
+       ExpectInfoBar::NO, ExpectPath::EMPTY},
+
+      {DownloadConfirmationReason::UNEXPECTED,
+       DownloadConfirmationResult::CANCELED, WebContents::AVAILABLE,
+       ExpectInfoBar::NO, ExpectPath::EMPTY},
+  };
+
+  EXPECT_CALL(*delegate(), RequestConfirmation(_, _, _, _))
+      .WillRepeatedly(Invoke(
+          delegate(),
+          &TestChromeDownloadManagerDelegate::RequestConfirmationConcrete));
+  InfoBarService::CreateForWebContents(web_contents());
+  base::FilePath fake_path = GetPathInDownloadDir(FILE_PATH_LITERAL("foo.txt"));
+  GURL url("http://example.com");
+  AndroidDownloadInfobarCounter infobar_counter(web_contents());
+
+  for (const auto& test_case : kTestCases) {
+    std::unique_ptr<content::MockDownloadItem> download_item =
+        CreateActiveDownloadItem(1);
+    EXPECT_CALL(*download_item, GetWebContents())
+        .WillRepeatedly(Return(test_case.web_contents == WebContents::AVAILABLE
+                                   ? web_contents()
+                                   : nullptr));
+    EXPECT_CALL(*download_item, GetURL()).WillRepeatedly(ReturnRef(url));
+    infobar_counter.CheckAndResetInfobarCount();
+
+    base::RunLoop loop;
+    const auto callback = base::Bind(
+        [](const base::Closure& quit_closure,
+           DownloadConfirmationResult expected_result,
+           const base::FilePath& expected_path,
+           DownloadConfirmationResult actual_result,
+           const base::FilePath& actual_path) {
+          EXPECT_EQ(expected_result, actual_result);
+          EXPECT_EQ(expected_path, actual_path);
+          quit_closure.Run();
+        },
+        loop.QuitClosure(), test_case.expected_result,
+        test_case.path == ExpectPath::FULL ? fake_path : base::FilePath());
+    delegate()->RequestConfirmation(download_item.get(), fake_path,
+                                    test_case.confirmation_reason, callback);
+    loop.Run();
+
+    EXPECT_EQ(test_case.info_bar == ExpectInfoBar::YES ? 1 : 0,
+              infobar_counter.CheckAndResetInfobarCount());
+  }
+}
+#endif  // OS_ANDROID
diff --git a/chrome/browser/download/download_browsertest.cc b/chrome/browser/download/download_browsertest.cc
index 1dd85d0..0696283 100644
--- a/chrome/browser/download/download_browsertest.cc
+++ b/chrome/browser/download/download_browsertest.cc
@@ -5,8 +5,13 @@
 #include "chrome/browser/download/download_browsertest.h"
 
 #include <stdint.h>
+
+#include <memory>
+#include <set>
 #include <sstream>
+#include <string>
 #include <utility>
+#include <vector>
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
@@ -481,12 +486,12 @@
   }
 
   // Location of the file source (the place from which it is downloaded).
-  base::FilePath OriginFile(base::FilePath file) {
+  base::FilePath OriginFile(const base::FilePath& file) {
     return test_dir_.Append(file);
   }
 
   // Location of the file destination (place to which it is downloaded).
-  base::FilePath DestinationFile(Browser* browser, base::FilePath file) {
+  base::FilePath DestinationFile(Browser* browser, const base::FilePath& file) {
     return GetDownloadDirectory(browser).Append(file.BaseName());
   }
 
@@ -1112,6 +1117,7 @@
     return nullptr;
   }
 
+ private:
   DISALLOW_COPY_AND_ASSIGN(FakeSafeBrowsingService);
 };
 
@@ -2950,22 +2956,24 @@
   };
 
   std::vector<DownloadItem*> download_items;
-  base::FilePath origin(FILE_PATH_LITERAL("origin"));
-  ASSERT_TRUE(base::CreateDirectory(DestinationFile(browser(), origin)));
+  base::FilePath origin_directory =
+      GetDownloadDirectory(browser()).Append(FILE_PATH_LITERAL("origin"));
+  ASSERT_TRUE(base::CreateDirectory(origin_directory));
 
   for (size_t index = 0; index < arraysize(kCrazyFilenames); ++index) {
+    SCOPED_TRACE(testing::Message() << "Index " << index);
     base::string16 crazy16;
     std::string crazy8;
     const wchar_t* crazy_w = kCrazyFilenames[index];
     ASSERT_TRUE(base::WideToUTF8(crazy_w, wcslen(crazy_w), &crazy8));
     ASSERT_TRUE(base::WideToUTF16(crazy_w, wcslen(crazy_w), &crazy16));
-    base::FilePath file_path(DestinationFile(browser(), origin.Append(
+    base::FilePath file_path(origin_directory.Append(
 #if defined(OS_WIN)
-            crazy16
+        crazy16
 #elif defined(OS_POSIX)
-            crazy8
+        crazy8
 #endif
-        )));
+        ));
 
     // Create the file.
     EXPECT_EQ(static_cast<int>(crazy8.size()),
@@ -3042,8 +3050,10 @@
 #endif
 IN_PROC_BROWSER_TEST_F(DownloadTest, MAYBE_DownloadTest_PercentComplete) {
   // Write a huge file.
-  base::FilePath file_path(DestinationFile(
-      browser(), base::FilePath(FILE_PATH_LITERAL("DownloadTest_BigZip.zip"))));
+  base::FilePath file_path(
+      GetDownloadDirectory(browser()).AppendASCII("source").AppendASCII(
+          "DownloadTest_BigZip.zip"));
+  ASSERT_TRUE(CreateDirectory(file_path.DirName()));
   base::File file(file_path, base::File::FLAG_CREATE | base::File::FLAG_WRITE);
   ASSERT_TRUE(file.IsValid());
   int64_t size = 1 << 25;
diff --git a/chrome/browser/download/download_confirmation_reason.h b/chrome/browser/download/download_confirmation_reason.h
new file mode 100644
index 0000000..b3cafd5
--- /dev/null
+++ b/chrome/browser/download/download_confirmation_reason.h
@@ -0,0 +1,36 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_DOWNLOAD_DOWNLOAD_CONFIRMATION_REASON_H_
+#define CHROME_BROWSER_DOWNLOAD_DOWNLOAD_CONFIRMATION_REASON_H_
+
+// Reason why DownloadTargetDeterminer requested additional confirmation for the
+// target path via RequestConfirmation delegate method.
+enum class DownloadConfirmationReason {
+  NONE,
+
+  // Unexpected error.
+  UNEXPECTED,
+
+  // "Save as" or "Save link as".
+  SAVE_AS,
+
+  // The user has set a preference requiring prompts for all downloads.
+  PREFERENCE,
+
+  // The target name was too long and couldn't be truncated.
+  NAME_TOO_LONG,
+
+  // There were unresolved conflicts with the target path.
+  TARGET_CONFLICT,
+
+  // The target path isn't writeable. Also may indicate that a previous attempt
+  // to write to the path failed.
+  TARGET_PATH_NOT_WRITEABLE,
+
+  // The target path cannot accommodate a file of this size.
+  TARGET_NO_SPACE,
+};
+
+#endif  // CHROME_BROWSER_DOWNLOAD_DOWNLOAD_CONFIRMATION_REASON_H_
diff --git a/chrome/browser/download/download_confirmation_result.h b/chrome/browser/download/download_confirmation_result.h
new file mode 100644
index 0000000..b09a0a38
--- /dev/null
+++ b/chrome/browser/download/download_confirmation_result.h
@@ -0,0 +1,28 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_DOWNLOAD_DOWNLOAD_CONFIRMATION_RESULT_H_
+#define CHROME_BROWSER_DOWNLOAD_DOWNLOAD_CONFIRMATION_RESULT_H_
+
+// Result of RequestConfirmation delegate method for
+// DownloadTargetDeterminerDelegate.
+enum class DownloadConfirmationResult {
+  // The user confirmed the path. Only use this value if the user was explicitly
+  // shown the path or at least the filename being downloaded.
+  CONFIRMED,
+
+  // The operation failed due to a reason other than a user cancellation.
+  FAILED,
+
+  // The user cancelled.
+  CANCELED,
+
+  // User was not explicitly prompted, but continue with current path. The
+  // delegate should use this value instead of CONFIRMED if the user was not
+  // presented with some UI that explicitly called out the filename being
+  // downloaded.
+  CONTINUE_WITHOUT_CONFIRMATION
+};
+
+#endif  // CHROME_BROWSER_DOWNLOAD_DOWNLOAD_CONFIRMATION_RESULT_H_
diff --git a/chrome/browser/download/download_file_picker.cc b/chrome/browser/download/download_file_picker.cc
index 07d1ca3..aa4675c 100644
--- a/chrome/browser/download/download_file_picker.cc
+++ b/chrome/browser/download/download_file_picker.cc
@@ -49,10 +49,9 @@
 
 }  // namespace
 
-DownloadFilePicker::DownloadFilePicker(
-    DownloadItem* item,
-    const base::FilePath& suggested_path,
-    const FileSelectedCallback& callback)
+DownloadFilePicker::DownloadFilePicker(DownloadItem* item,
+                                       const base::FilePath& suggested_path,
+                                       const ConfirmationCallback& callback)
     : suggested_path_(suggested_path),
       file_selected_callback_(callback),
       should_record_file_picker_result_(false) {
@@ -104,7 +103,10 @@
 void DownloadFilePicker::OnFileSelected(const base::FilePath& path) {
   if (should_record_file_picker_result_)
     RecordFilePickerResult(suggested_path_, path);
-  file_selected_callback_.Run(path);
+  file_selected_callback_.Run(path.empty()
+                                  ? DownloadConfirmationResult::CANCELED
+                                  : DownloadConfirmationResult::CONFIRMED,
+                              path);
   delete this;
 }
 
@@ -123,7 +125,7 @@
 // static
 void DownloadFilePicker::ShowFilePicker(DownloadItem* item,
                                         const base::FilePath& suggested_path,
-                                        const FileSelectedCallback& callback) {
+                                        const ConfirmationCallback& callback) {
   new DownloadFilePicker(item, suggested_path, callback);
   // DownloadFilePicker deletes itself.
 }
diff --git a/chrome/browser/download/download_file_picker.h b/chrome/browser/download/download_file_picker.h
index 0763cf09..80fc2c4 100644
--- a/chrome/browser/download/download_file_picker.h
+++ b/chrome/browser/download/download_file_picker.h
@@ -7,6 +7,7 @@
 
 #include "base/callback.h"
 #include "base/macros.h"
+#include "chrome/browser/download/download_confirmation_result.h"
 #include "ui/shell_dialogs/select_file_dialog.h"
 
 namespace base {
@@ -26,20 +27,21 @@
   //    selection, then this parameter will be the empty path. On Chrome OS,
   //    this path may contain virtual mount points if the user chose a virtual
   //    path (e.g. Google Drive).
-  typedef base::Callback<void(const base::FilePath& virtual_path)>
-      FileSelectedCallback;
+  typedef base::Callback<void(DownloadConfirmationResult,
+                              const base::FilePath& virtual_path)>
+      ConfirmationCallback;
 
   // Display a file picker dialog for |item|. The |suggested_path| will be used
   // as the initial path displayed to the user. |callback| will always be
   // invoked even if |item| is destroyed prior to the file picker completing.
   static void ShowFilePicker(content::DownloadItem* item,
                              const base::FilePath& suggested_path,
-                             const FileSelectedCallback& callback);
+                             const ConfirmationCallback& callback);
 
  private:
   DownloadFilePicker(content::DownloadItem* item,
                      const base::FilePath& suggested_path,
-                     const FileSelectedCallback& callback);
+                     const ConfirmationCallback& callback);
   ~DownloadFilePicker() override;
 
   // Runs |file_selected_callback_| with |virtual_path| and then deletes this
@@ -56,7 +58,7 @@
   base::FilePath suggested_path_;
 
   // Callback invoked when a file selection is complete.
-  FileSelectedCallback file_selected_callback_;
+  ConfirmationCallback file_selected_callback_;
 
   // For managing select file dialogs.
   scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
diff --git a/chrome/browser/download/download_path_reservation_tracker.cc b/chrome/browser/download/download_path_reservation_tracker.cc
index cb813a5b..60f0a22 100644
--- a/chrome/browser/download/download_path_reservation_tracker.cc
+++ b/chrome/browser/download/download_path_reservation_tracker.cc
@@ -7,6 +7,7 @@
 #include <stddef.h>
 
 #include <map>
+#include <string>
 
 #include "base/bind.h"
 #include "base/callback.h"
@@ -24,6 +25,8 @@
 #include "chrome/common/features.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/download_item.h"
+#include "net/base/filename_util.h"
+#include "url/gurl.h"
 
 using content::BrowserThread;
 using content::DownloadItem;
@@ -35,13 +38,13 @@
 
 // The lower bound for file name truncation. If the truncation results in a name
 // shorter than this limit, we give up automatic truncation and prompt the user.
-static const size_t kTruncatedNameLengthLowerbound = 5;
+const size_t kTruncatedNameLengthLowerbound = 5;
 
 // The length of the suffix string we append for an intermediate file name.
 // In the file name truncation, we keep the margin to append the suffix.
 // TODO(kinaba): remove the margin. The user should be able to set maximum
 // possible filename.
-static const size_t kIntermediateNameSuffixLength = sizeof(".crdownload") - 1;
+const size_t kIntermediateNameSuffixLength = sizeof(".crdownload") - 1;
 
 // Map of download path reservations. Each reserved path is associated with a
 // ReservationKey=DownloadItem*. This object is destroyed in |Revoke()| when
@@ -150,22 +153,104 @@
   return true;
 }
 
+// Create a unique filename by appending a uniquifier. Modifies |path| in place
+// if successful and returns true. Otherwise |path| is left unmodified and
+// returns false.
+bool CreateUniqueFilename(int max_path_component_length, base::FilePath* path) {
+  for (int uniquifier = 1;
+       uniquifier <= DownloadPathReservationTracker::kMaxUniqueFiles;
+       ++uniquifier) {
+    // Append uniquifier.
+    std::string suffix(base::StringPrintf(" (%d)", uniquifier));
+    base::FilePath path_to_check(*path);
+    // If the name length limit is available (max_length != -1), and the
+    // the current name exceeds the limit, truncate.
+    if (max_path_component_length != -1) {
+      int limit = max_path_component_length - kIntermediateNameSuffixLength -
+                  suffix.size();
+      // If truncation failed, give up uniquification.
+      if (limit <= 0 || !TruncateFileName(&path_to_check, limit))
+        break;
+    }
+    path_to_check = path_to_check.InsertBeforeExtensionASCII(suffix);
+
+    if (!IsPathInUse(path_to_check)) {
+      *path = path_to_check;
+      return true;
+    }
+  }
+  return false;
+}
+
+struct CreateReservationInfo {
+  ReservationKey key;
+  base::FilePath source_path;
+  base::FilePath suggested_path;
+  base::FilePath default_download_path;
+  bool create_target_directory;
+  DownloadPathReservationTracker::FilenameConflictAction conflict_action;
+  DownloadPathReservationTracker::ReservedPathCallback completion_callback;
+};
+
+// Verify that |target_path| can be written to and also resolve any conflicts if
+// necessary by uniquifying the filename.
+PathValidationResult ValidatePathAndResolveConflicts(
+    const CreateReservationInfo& info,
+    base::FilePath* target_path) {
+  // Check writability of the suggested path. If we can't write to it, default
+  // to the user's Documents directory. We'll prompt them in this case. No
+  // further amendments are made to the filename since the user is going to be
+  // prompted.
+  if (!base::PathIsWritable(target_path->DirName())) {
+    DVLOG(1) << "Unable to write to path \"" << target_path->value() << "\"";
+    base::FilePath target_dir;
+    PathService::Get(chrome::DIR_USER_DOCUMENTS, &target_dir);
+    *target_path = target_dir.Append(target_path->BaseName());
+    return PathValidationResult::PATH_NOT_WRITABLE;
+  }
+
+  int max_path_component_length =
+      base::GetMaximumPathComponentLength(target_path->DirName());
+  // Check the limit of file name length if it could be obtained. When the
+  // suggested name exceeds the limit, truncate or prompt the user.
+  if (max_path_component_length != -1) {
+    int limit = max_path_component_length - kIntermediateNameSuffixLength;
+    if (limit <= 0 || !TruncateFileName(target_path, limit))
+      return PathValidationResult::NAME_TOO_LONG;
+  }
+
+  if (!IsPathInUse(*target_path))
+    return PathValidationResult::SUCCESS;
+
+  switch (info.conflict_action) {
+    case DownloadPathReservationTracker::UNIQUIFY:
+      return CreateUniqueFilename(max_path_component_length, target_path)
+                 ? PathValidationResult::SUCCESS
+                 : PathValidationResult::CONFLICT;
+
+    case DownloadPathReservationTracker::OVERWRITE:
+      return PathValidationResult::SUCCESS;
+
+    case DownloadPathReservationTracker::PROMPT:
+      return PathValidationResult::CONFLICT;
+  }
+  NOTREACHED();
+  return PathValidationResult::SUCCESS;
+}
+
 // Called on the FILE thread to reserve a download path. This method:
 // - Creates directory |default_download_path| if it doesn't exist.
 // - Verifies that the parent directory of |suggested_path| exists and is
 //   writeable.
 // - Truncates the suggested name if it exceeds the filesystem's limit.
 // - Uniquifies |suggested_path| if |should_uniquify_path| is true.
-// - Returns true if |reserved_path| has been successfully verified.
-bool CreateReservation(
-    ReservationKey key,
-    const base::FilePath& suggested_path,
-    const base::FilePath& default_download_path,
-    bool create_directory,
-    DownloadPathReservationTracker::FilenameConflictAction conflict_action,
-    base::FilePath* reserved_path) {
+// - Schedules |callback| on the UI thread with the reserved path and a flag
+//   indicating whether the returned path has been successfully verified.
+// - Returns the result of creating the path reservation.
+PathValidationResult CreateReservation(const CreateReservationInfo& info,
+                                       base::FilePath* reserved_path) {
   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
-  DCHECK(suggested_path.IsAbsolute());
+  DCHECK(info.suggested_path.IsAbsolute());
 
   // Create a reservation map if one doesn't exist. It will be automatically
   // deleted when all the reservations are revoked.
@@ -178,14 +263,11 @@
   //
   // Revoking and re-acquiring the reservation forces us to re-verify the claims
   // we are making about the path.
-  g_reservation_map->erase(key);
+  g_reservation_map->erase(info.key);
 
-  base::FilePath target_path(suggested_path.NormalizePathSeparators());
+  base::FilePath target_path(info.suggested_path.NormalizePathSeparators());
   base::FilePath target_dir = target_path.DirName();
   base::FilePath filename = target_path.BaseName();
-  bool is_path_writeable = true;
-  bool has_conflicts = false;
-  bool name_too_long = false;
 
   // Create target_dir if necessary and appropriate. target_dir may be the last
   // directory that the user selected in a FilePicker; if that directory has
@@ -193,79 +275,17 @@
   // create the directory if it is the default Downloads directory or if the
   // caller explicitly requested automatic directory creation.
   if (!base::DirectoryExists(target_dir) &&
-      (create_directory ||
-       (!default_download_path.empty() &&
-        (default_download_path == target_dir)))) {
+      (info.create_target_directory ||
+       (!info.default_download_path.empty() &&
+        (info.default_download_path == target_dir)))) {
     base::CreateDirectory(target_dir);
   }
 
-  // Check writability of the suggested path. If we can't write to it, default
-  // to the user's "My Documents" directory. We'll prompt them in this case.
-  if (!base::PathIsWritable(target_dir)) {
-    DVLOG(1) << "Unable to write to directory \"" << target_dir.value() << "\"";
-#if defined(OS_ANDROID)
-    // On Android, DIR_USER_DOCUMENTS is in reality a subdirectory
-    // of DIR_ANDROID_APP_DATA which isn't accessible by other apps.
-    reserved_path->clear();
-    (*g_reservation_map)[key] = *reserved_path;
-    return false;
-#else
-    is_path_writeable = false;
-    PathService::Get(chrome::DIR_USER_DOCUMENTS, &target_dir);
-    target_path = target_dir.Append(filename);
-#endif  // defined(OS_ANDROID)
-  }
-
-  if (is_path_writeable) {
-    // Check the limit of file name length if it could be obtained. When the
-    // suggested name exceeds the limit, truncate or prompt the user.
-    int max_length = base::GetMaximumPathComponentLength(target_dir);
-    if (max_length != -1) {
-      int limit = max_length - kIntermediateNameSuffixLength;
-      if (limit <= 0 || !TruncateFileName(&target_path, limit))
-        name_too_long = true;
-    }
-
-    // Uniquify the name, if it already exists.
-    if (!name_too_long && IsPathInUse(target_path)) {
-      has_conflicts = true;
-      if (conflict_action == DownloadPathReservationTracker::OVERWRITE) {
-        has_conflicts = false;
-      }
-      // If ...PROMPT, then |has_conflicts| will remain true, |verified| will be
-      // false, and CDMD will prompt.
-      if (conflict_action == DownloadPathReservationTracker::UNIQUIFY) {
-        for (int uniquifier = 1;
-            uniquifier <= DownloadPathReservationTracker::kMaxUniqueFiles;
-            ++uniquifier) {
-          // Append uniquifier.
-          std::string suffix(base::StringPrintf(" (%d)", uniquifier));
-          base::FilePath path_to_check(target_path);
-          // If the name length limit is available (max_length != -1), and the
-          // the current name exceeds the limit, truncate.
-          if (max_length != -1) {
-            int limit =
-                max_length - kIntermediateNameSuffixLength - suffix.size();
-            // If truncation failed, give up uniquification.
-            if (limit <= 0 || !TruncateFileName(&path_to_check, limit))
-              break;
-          }
-          path_to_check = path_to_check.InsertBeforeExtensionASCII(suffix);
-
-          if (!IsPathInUse(path_to_check)) {
-            target_path = path_to_check;
-            has_conflicts = false;
-            break;
-          }
-        }
-      }
-    }
-  }
-
-  (*g_reservation_map)[key] = target_path;
-  bool verified = (is_path_writeable && !has_conflicts && !name_too_long);
+  PathValidationResult result =
+      ValidatePathAndResolveConflicts(info, &target_path);
+  (*g_reservation_map)[info.key] = target_path;
   *reserved_path = target_path;
-  return verified;
+  return result;
 }
 
 // Called on the FILE thread to update the path of the reservation associated
@@ -301,9 +321,9 @@
 void RunGetReservedPathCallback(
     const DownloadPathReservationTracker::ReservedPathCallback& callback,
     const base::FilePath* reserved_path,
-    bool verified) {
+    PathValidationResult result) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  callback.Run(*reserved_path, verified);
+  callback.Run(result, *reserved_path);
 }
 
 DownloadItemObserver::DownloadItemObserver(DownloadItem* download_item)
@@ -385,18 +405,21 @@
   // DownloadItemObserver deletes itself.
 
   base::FilePath* reserved_path = new base::FilePath;
+  base::FilePath source_path;
+  if (download_item->GetURL().SchemeIsFile())
+    net::FileURLToFilePath(download_item->GetURL(), &source_path);
+  CreateReservationInfo info = {static_cast<ReservationKey>(download_item),
+                                source_path,
+                                target_path,
+                                default_path,
+                                create_directory,
+                                conflict_action,
+                                callback};
+
   BrowserThread::PostTaskAndReplyWithResult(
-      BrowserThread::FILE,
-      FROM_HERE,
-      base::Bind(&CreateReservation,
-                 download_item,
-                 target_path,
-                 default_path,
-                 create_directory,
-                 conflict_action,
-                 reserved_path),
-      base::Bind(&RunGetReservedPathCallback,
-                 callback,
+      BrowserThread::FILE, FROM_HERE,
+      base::Bind(&CreateReservation, info, reserved_path),
+      base::Bind(&RunGetReservedPathCallback, callback,
                  base::Owned(reserved_path)));
 }
 
diff --git a/chrome/browser/download/download_path_reservation_tracker.h b/chrome/browser/download/download_path_reservation_tracker.h
index 494253d..673e629 100644
--- a/chrome/browser/download/download_path_reservation_tracker.h
+++ b/chrome/browser/download/download_path_reservation_tracker.h
@@ -15,6 +15,13 @@
 class DownloadItem;
 }
 
+enum class PathValidationResult {
+  SUCCESS,
+  PATH_NOT_WRITABLE,
+  NAME_TOO_LONG,
+  CONFLICT
+};
+
 // Chrome attempts to uniquify filenames that are assigned to downloads in order
 // to avoid overwriting files that already exist on the file system. Downloads
 // that are considered potentially dangerous use random intermediate filenames.
@@ -24,16 +31,16 @@
 class DownloadPathReservationTracker {
  public:
   // Callback used with |GetReservedPath|. |target_path| specifies the target
-  // path for the download. |target_path_verified| is true if all of the
-  // following is true:
+  // path for the download. If |result| is SUCCESS then:
   // - |requested_target_path| (passed into GetReservedPath()) was writeable.
   // - |target_path| was verified as being unique if uniqueness was
   //   required.
   //
   // If |requested_target_path| was not writeable, then the parent directory of
   // |target_path| may be different from that of |requested_target_path|.
-  typedef base::Callback<void(const base::FilePath& target_path,
-                              bool target_path_verified)> ReservedPathCallback;
+  using ReservedPathCallback =
+      base::Callback<void(PathValidationResult result,
+                          const base::FilePath& target_path)>;
 
   // The largest index for the uniquification suffix that we will try while
   // attempting to come up with a unique path.
diff --git a/chrome/browser/download/download_path_reservation_tracker_unittest.cc b/chrome/browser/download/download_path_reservation_tracker_unittest.cc
index 1a4fa7c..1f64d19 100644
--- a/chrome/browser/download/download_path_reservation_tracker_unittest.cc
+++ b/chrome/browser/download/download_path_reservation_tracker_unittest.cc
@@ -5,6 +5,8 @@
 #include <stddef.h>
 #include <stdint.h>
 
+#include <memory>
+
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
@@ -19,6 +21,7 @@
 #include "chrome/browser/download/download_target_determiner.h"
 #include "content/public/test/mock_download_item.h"
 #include "content/public/test/test_browser_thread.h"
+#include "net/base/filename_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -50,7 +53,7 @@
       bool create_directory,
       DownloadPathReservationTracker::FilenameConflictAction conflict_action,
       base::FilePath* return_path,
-      bool* return_verified);
+      PathValidationResult* return_result);
 
   const base::FilePath& default_download_path() const {
     return default_download_path_;
@@ -60,7 +63,8 @@
   }
   // Creates a name of form 'a'*repeat + suffix
   base::FilePath GetLongNamePathInDownloadsDirectory(
-      size_t repeat, const base::FilePath::CharType* suffix);
+      size_t repeat,
+      const base::FilePath::CharType* suffix);
 
  protected:
   base::ScopedTempDir test_download_dir_;
@@ -71,8 +75,10 @@
 
  private:
   void TestReservedPathCallback(base::FilePath* return_path,
-                                bool* return_verified, bool* did_run_callback,
-                                const base::FilePath& path, bool verified);
+                                PathValidationResult* return_result,
+                                const base::Closure& quit_closure,
+                                PathValidationResult result,
+                                const base::FilePath& path);
 };
 
 DownloadPathReservationTrackerTest::DownloadPathReservationTrackerTest()
@@ -98,6 +104,7 @@
       .WillRepeatedly(ReturnRefOfCopy(base::FilePath()));
   EXPECT_CALL(*item, GetState())
       .WillRepeatedly(Return(DownloadItem::IN_PROGRESS));
+  EXPECT_CALL(*item, GetURL()).WillRepeatedly(ReturnRefOfCopy(GURL()));
   return item;
 }
 
@@ -117,31 +124,30 @@
     bool create_directory,
     DownloadPathReservationTracker::FilenameConflictAction conflict_action,
     base::FilePath* return_path,
-    bool* return_verified) {
+    PathValidationResult* return_result) {
   // Weak pointer factory to prevent the callback from running after this
   // function has returned.
   base::WeakPtrFactory<DownloadPathReservationTrackerTest> weak_ptr_factory(
       this);
-  bool did_run_callback = false;
+  base::RunLoop run_loop;
   DownloadPathReservationTracker::GetReservedPath(
-      download_item,
-      target_path,
-      default_download_path(),
-      create_directory,
+      download_item, target_path, default_download_path(), create_directory,
       conflict_action,
       base::Bind(&DownloadPathReservationTrackerTest::TestReservedPathCallback,
-                 weak_ptr_factory.GetWeakPtr(), return_path, return_verified,
-                 &did_run_callback));
-  base::RunLoop().RunUntilIdle();
-  EXPECT_TRUE(did_run_callback);
+                 weak_ptr_factory.GetWeakPtr(), return_path, return_result,
+                 run_loop.QuitClosure()));
+  run_loop.Run();
 }
 
 void DownloadPathReservationTrackerTest::TestReservedPathCallback(
-    base::FilePath* return_path, bool* return_verified, bool* did_run_callback,
-    const base::FilePath& path, bool verified) {
-  *did_run_callback = true;
+    base::FilePath* return_path,
+    PathValidationResult* return_result,
+    const base::Closure& quit_closure,
+    PathValidationResult result,
+    const base::FilePath& path) {
   *return_path = path;
-  *return_verified = verified;
+  *return_result = result;
+  quit_closure.Run();
 }
 
 base::FilePath
@@ -169,19 +175,14 @@
   ASSERT_FALSE(IsPathInUse(path));
 
   base::FilePath reserved_path;
-  bool verified = false;
+  PathValidationResult result = PathValidationResult::NAME_TOO_LONG;
   DownloadPathReservationTracker::FilenameConflictAction conflict_action =
-    DownloadPathReservationTracker::OVERWRITE;
+      DownloadPathReservationTracker::OVERWRITE;
   bool create_directory = false;
-  CallGetReservedPath(
-      item.get(),
-      path,
-      create_directory,
-      conflict_action,
-      &reserved_path,
-      &verified);
+  CallGetReservedPath(item.get(), path, create_directory, conflict_action,
+                      &reserved_path, &result);
   EXPECT_TRUE(IsPathInUse(path));
-  EXPECT_TRUE(verified);
+  EXPECT_EQ(PathValidationResult::SUCCESS, result);
   EXPECT_EQ(path.value(), reserved_path.value());
 
   // Destroying the item should release the reservation.
@@ -199,19 +200,14 @@
   ASSERT_FALSE(IsPathInUse(path));
 
   base::FilePath reserved_path;
-  bool verified = false;
+  PathValidationResult result = PathValidationResult::NAME_TOO_LONG;
   DownloadPathReservationTracker::FilenameConflictAction conflict_action =
-    DownloadPathReservationTracker::OVERWRITE;
+      DownloadPathReservationTracker::OVERWRITE;
   bool create_directory = false;
-  CallGetReservedPath(
-      item.get(),
-      path,
-      create_directory,
-      conflict_action,
-      &reserved_path,
-      &verified);
+  CallGetReservedPath(item.get(), path, create_directory, conflict_action,
+                      &reserved_path, &result);
   EXPECT_TRUE(IsPathInUse(path));
-  EXPECT_TRUE(verified);
+  EXPECT_EQ(PathValidationResult::SUCCESS, result);
   EXPECT_EQ(path.value(), reserved_path.value());
 
   // Once the download is interrupted, the path should become available again.
@@ -228,19 +224,14 @@
   ASSERT_FALSE(IsPathInUse(path));
 
   base::FilePath reserved_path;
-  bool verified = false;
+  PathValidationResult result = PathValidationResult::NAME_TOO_LONG;
   DownloadPathReservationTracker::FilenameConflictAction conflict_action =
-    DownloadPathReservationTracker::OVERWRITE;
+      DownloadPathReservationTracker::OVERWRITE;
   bool create_directory = false;
-  CallGetReservedPath(
-      item.get(),
-      path,
-      create_directory,
-      conflict_action,
-      &reserved_path,
-      &verified);
+  CallGetReservedPath(item.get(), path, create_directory, conflict_action,
+                      &reserved_path, &result);
   EXPECT_TRUE(IsPathInUse(path));
-  EXPECT_TRUE(verified);
+  EXPECT_EQ(PathValidationResult::SUCCESS, result);
   EXPECT_EQ(path.value(), reserved_path.value());
 
   // Once the download completes, the path should become available again. For a
@@ -268,20 +259,15 @@
   ASSERT_TRUE(IsPathInUse(path));
 
   base::FilePath reserved_path;
-  bool verified = false;
+  PathValidationResult result = PathValidationResult::NAME_TOO_LONG;
   bool create_directory = false;
   DownloadPathReservationTracker::FilenameConflictAction conflict_action =
-    DownloadPathReservationTracker::UNIQUIFY;
-  CallGetReservedPath(
-      item.get(),
-      path,
-      create_directory,
-      conflict_action,
-      &reserved_path,
-      &verified);
+      DownloadPathReservationTracker::UNIQUIFY;
+  CallGetReservedPath(item.get(), path, create_directory, conflict_action,
+                      &reserved_path, &result);
   EXPECT_TRUE(IsPathInUse(path));
   EXPECT_TRUE(IsPathInUse(reserved_path));
-  EXPECT_TRUE(verified);
+  EXPECT_EQ(PathValidationResult::SUCCESS, result);
   // The path should be uniquified, skipping over foo.txt but not over
   // "foo (1).txt.crdownload"
   EXPECT_EQ(
@@ -295,6 +281,33 @@
   EXPECT_FALSE(IsPathInUse(reserved_path));
 }
 
+// If there are conflicting files on the file system, an overwriting reservation
+// should succeed without altering the target path.
+TEST_F(DownloadPathReservationTrackerTest, ConflictingFiles_Overwrite) {
+  std::unique_ptr<MockDownloadItem> item(CreateDownloadItem(1));
+  base::FilePath path(
+      GetPathInDownloadsDirectory(FILE_PATH_LITERAL("foo.txt")));
+  // Create a file at |path|.
+  ASSERT_EQ(0, base::WriteFile(path, "", 0));
+  ASSERT_TRUE(IsPathInUse(path));
+
+  base::FilePath reserved_path;
+  PathValidationResult result = PathValidationResult::NAME_TOO_LONG;
+  bool create_directory = false;
+  DownloadPathReservationTracker::FilenameConflictAction conflict_action =
+      DownloadPathReservationTracker::OVERWRITE;
+  CallGetReservedPath(item.get(), path, create_directory, conflict_action,
+                      &reserved_path, &result);
+  EXPECT_TRUE(IsPathInUse(path));
+  EXPECT_TRUE(IsPathInUse(reserved_path));
+  EXPECT_EQ(PathValidationResult::SUCCESS, result);
+  EXPECT_EQ(path.value(), reserved_path.value());
+
+  SetDownloadItemState(item.get(), DownloadItem::COMPLETE);
+  item.reset();
+  base::RunLoop().RunUntilIdle();
+}
+
 // Multiple reservations for the same path should uniquify around each other.
 TEST_F(DownloadPathReservationTrackerTest, ConflictingReservations) {
   std::unique_ptr<MockDownloadItem> item1(CreateDownloadItem(1));
@@ -306,34 +319,23 @@
   ASSERT_FALSE(IsPathInUse(uniquified_path));
 
   base::FilePath reserved_path1;
-  bool verified = false;
+  PathValidationResult result = PathValidationResult::NAME_TOO_LONG;
   bool create_directory = false;
 
   DownloadPathReservationTracker::FilenameConflictAction conflict_action =
     DownloadPathReservationTracker::UNIQUIFY;
-  CallGetReservedPath(
-      item1.get(),
-      path,
-      create_directory,
-      conflict_action,
-      &reserved_path1,
-      &verified);
+  CallGetReservedPath(item1.get(), path, create_directory, conflict_action,
+                      &reserved_path1, &result);
   EXPECT_TRUE(IsPathInUse(path));
-  EXPECT_TRUE(verified);
-
+  EXPECT_EQ(PathValidationResult::SUCCESS, result);
 
   {
     // Requesting a reservation for the same path with uniquification results in
     // a uniquified path.
     std::unique_ptr<MockDownloadItem> item2(CreateDownloadItem(2));
     base::FilePath reserved_path2;
-    CallGetReservedPath(
-        item2.get(),
-        path,
-        create_directory,
-        conflict_action,
-        &reserved_path2,
-        &verified);
+    CallGetReservedPath(item2.get(), path, create_directory, conflict_action,
+                        &reserved_path2, &result);
     EXPECT_TRUE(IsPathInUse(path));
     EXPECT_TRUE(IsPathInUse(uniquified_path));
     EXPECT_EQ(uniquified_path.value(), reserved_path2.value());
@@ -348,13 +350,8 @@
     // for the same path should result in the same uniquified path.
     std::unique_ptr<MockDownloadItem> item2(CreateDownloadItem(2));
     base::FilePath reserved_path2;
-    CallGetReservedPath(
-        item2.get(),
-        path,
-        create_directory,
-        conflict_action,
-        &reserved_path2,
-        &verified);
+    CallGetReservedPath(item2.get(), path, create_directory, conflict_action,
+                        &reserved_path2, &result);
     EXPECT_TRUE(IsPathInUse(path));
     EXPECT_TRUE(IsPathInUse(uniquified_path));
     EXPECT_EQ(uniquified_path.value(), reserved_path2.value());
@@ -367,13 +364,8 @@
   std::unique_ptr<MockDownloadItem> item3(CreateDownloadItem(2));
   base::FilePath reserved_path3;
   conflict_action = DownloadPathReservationTracker::OVERWRITE;
-  CallGetReservedPath(
-      item3.get(),
-      path,
-      create_directory,
-      conflict_action,
-      &reserved_path3,
-      &verified);
+  CallGetReservedPath(item3.get(), path, create_directory, conflict_action,
+                      &reserved_path3, &result);
   EXPECT_TRUE(IsPathInUse(path));
   EXPECT_FALSE(IsPathInUse(uniquified_path));
 
@@ -396,12 +388,12 @@
       GetPathInDownloadsDirectory(FILE_PATH_LITERAL("Foo.txt"));
 
   base::FilePath first_reservation;
-  bool verified = false;
+  PathValidationResult result = PathValidationResult::PATH_NOT_WRITABLE;
   CallGetReservedPath(item1.get(), path_foo, false,
                       DownloadPathReservationTracker::UNIQUIFY,
-                      &first_reservation, &verified);
+                      &first_reservation, &result);
   EXPECT_TRUE(IsPathInUse(path_foo));
-  EXPECT_TRUE(verified);
+  EXPECT_EQ(PathValidationResult::SUCCESS, result);
   EXPECT_EQ(path_foo, first_reservation);
 
   // Foo should also be in use at this point.
@@ -410,8 +402,8 @@
   base::FilePath second_reservation;
   CallGetReservedPath(item2.get(), path_Foo, false,
                       DownloadPathReservationTracker::UNIQUIFY,
-                      &second_reservation, &verified);
-  EXPECT_TRUE(verified);
+                      &second_reservation, &result);
+  EXPECT_EQ(PathValidationResult::SUCCESS, result);
   EXPECT_EQ(GetPathInDownloadsDirectory(FILE_PATH_LITERAL("Foo (1).txt")),
             second_reservation);
   SetDownloadItemState(item1.get(), DownloadItem::COMPLETE);
@@ -435,7 +427,7 @@
   for (int i = 0; i <= DownloadPathReservationTracker::kMaxUniqueFiles; i++) {
     base::FilePath reserved_path;
     base::FilePath expected_path;
-    bool verified = false;
+    PathValidationResult result = PathValidationResult::NAME_TOO_LONG;
     if (i > 0) {
       expected_path =
           path.InsertBeforeExtensionASCII(base::StringPrintf(" (%d)", i));
@@ -444,30 +436,20 @@
     }
     items[i].reset(CreateDownloadItem(i));
     EXPECT_FALSE(IsPathInUse(expected_path));
-    CallGetReservedPath(
-        items[i].get(),
-        path,
-        create_directory,
-        conflict_action,
-        &reserved_path,
-        &verified);
+    CallGetReservedPath(items[i].get(), path, create_directory, conflict_action,
+                        &reserved_path, &result);
     EXPECT_TRUE(IsPathInUse(expected_path));
     EXPECT_EQ(expected_path.value(), reserved_path.value());
-    EXPECT_TRUE(verified);
+    EXPECT_EQ(PathValidationResult::SUCCESS, result);
   }
   // The next reservation for |path| will fail to be unique.
   std::unique_ptr<MockDownloadItem> item(
       CreateDownloadItem(DownloadPathReservationTracker::kMaxUniqueFiles + 1));
   base::FilePath reserved_path;
-  bool verified = true;
-  CallGetReservedPath(
-      item.get(),
-      path,
-      create_directory,
-      conflict_action,
-      &reserved_path,
-      &verified);
-  EXPECT_FALSE(verified);
+  PathValidationResult result = PathValidationResult::NAME_TOO_LONG;
+  CallGetReservedPath(item.get(), path, create_directory, conflict_action,
+                      &reserved_path, &result);
+  EXPECT_EQ(PathValidationResult::CONFLICT, result);
   EXPECT_EQ(path.value(), reserved_path.value());
 
   SetDownloadItemState(item.get(), DownloadItem::COMPLETE);
@@ -489,24 +471,15 @@
     base::FilePermissionRestorer restorer(dir);
     EXPECT_TRUE(base::MakeFileUnwritable(dir));
     base::FilePath reserved_path;
-    bool verified = true;
+    PathValidationResult result = PathValidationResult::NAME_TOO_LONG;
     DownloadPathReservationTracker::FilenameConflictAction conflict_action =
-      DownloadPathReservationTracker::OVERWRITE;
+        DownloadPathReservationTracker::OVERWRITE;
     bool create_directory = false;
-    CallGetReservedPath(
-        item.get(),
-        path,
-        create_directory,
-        conflict_action,
-        &reserved_path,
-        &verified);
+    CallGetReservedPath(item.get(), path, create_directory, conflict_action,
+                        &reserved_path, &result);
     // Verification fails.
-    EXPECT_FALSE(verified);
-#if defined(OS_ANDROID)
-    EXPECT_TRUE(reserved_path.empty());
-#else
+    EXPECT_EQ(PathValidationResult::PATH_NOT_WRITABLE, result);
     EXPECT_EQ(path.BaseName().value(), reserved_path.BaseName().value());
-#endif
   }
   SetDownloadItemState(item.get(), DownloadItem::COMPLETE);
 }
@@ -525,33 +498,23 @@
   {
     std::unique_ptr<MockDownloadItem> item(CreateDownloadItem(1));
     base::FilePath reserved_path;
-    bool verified = true;
-    CallGetReservedPath(
-        item.get(),
-        path,
-        create_directory,
-        conflict_action,
-        &reserved_path,
-        &verified);
+    PathValidationResult result = PathValidationResult::NAME_TOO_LONG;
+    CallGetReservedPath(item.get(), path, create_directory, conflict_action,
+                        &reserved_path, &result);
     // Verification fails because the directory doesn't exist.
-    EXPECT_FALSE(verified);
+    EXPECT_EQ(PathValidationResult::PATH_NOT_WRITABLE, result);
     SetDownloadItemState(item.get(), DownloadItem::COMPLETE);
   }
   ASSERT_FALSE(IsPathInUse(path));
   {
     std::unique_ptr<MockDownloadItem> item(CreateDownloadItem(1));
     base::FilePath reserved_path;
-    bool verified = true;
+    PathValidationResult result = PathValidationResult::NAME_TOO_LONG;
     set_default_download_path(dir);
-    CallGetReservedPath(
-        item.get(),
-        path,
-        create_directory,
-        conflict_action,
-        &reserved_path,
-        &verified);
+    CallGetReservedPath(item.get(), path, create_directory, conflict_action,
+                        &reserved_path, &result);
     // Verification succeeds because the directory is created.
-    EXPECT_TRUE(verified);
+    EXPECT_EQ(PathValidationResult::SUCCESS, result);
     EXPECT_TRUE(base::DirectoryExists(dir));
     SetDownloadItemState(item.get(), DownloadItem::COMPLETE);
   }
@@ -566,19 +529,14 @@
   ASSERT_FALSE(IsPathInUse(path));
 
   base::FilePath reserved_path;
-  bool verified = false;
+  PathValidationResult result = PathValidationResult::NAME_TOO_LONG;
   DownloadPathReservationTracker::FilenameConflictAction conflict_action =
-    DownloadPathReservationTracker::OVERWRITE;
+      DownloadPathReservationTracker::OVERWRITE;
   bool create_directory = false;
-  CallGetReservedPath(
-      item.get(),
-      path,
-      create_directory,
-      conflict_action,
-      &reserved_path,
-      &verified);
+  CallGetReservedPath(item.get(), path, create_directory, conflict_action,
+                      &reserved_path, &result);
   EXPECT_TRUE(IsPathInUse(path));
-  EXPECT_TRUE(verified);
+  EXPECT_EQ(PathValidationResult::SUCCESS, result);
   EXPECT_EQ(path.value(), reserved_path.value());
 
   // The target path is initially empty. If an OnDownloadUpdated() is issued in
@@ -625,19 +583,14 @@
   ASSERT_FALSE(IsPathInUse(path));
 
   base::FilePath reserved_path;
-  bool verified = false;
+  PathValidationResult result = PathValidationResult::NAME_TOO_LONG;
   DownloadPathReservationTracker::FilenameConflictAction conflict_action =
-    DownloadPathReservationTracker::OVERWRITE;
+      DownloadPathReservationTracker::OVERWRITE;
   bool create_directory = false;
-  CallGetReservedPath(
-      item.get(),
-      path,
-      create_directory,
-      conflict_action,
-      &reserved_path,
-      &verified);
+  CallGetReservedPath(item.get(), path, create_directory, conflict_action,
+                      &reserved_path, &result);
   EXPECT_TRUE(IsPathInUse(reserved_path));
-  EXPECT_TRUE(verified);
+  EXPECT_EQ(PathValidationResult::SUCCESS, result);
   // The file name length is truncated to max_length.
   EXPECT_EQ(max_length, reserved_path.BaseName().value().size());
   // But the extension is kept unchanged.
@@ -668,19 +621,14 @@
   ASSERT_EQ(0, base::WriteFile(path1, "", 0));
 
   base::FilePath reserved_path;
-  bool verified = false;
+  PathValidationResult result = PathValidationResult::NAME_TOO_LONG;
   DownloadPathReservationTracker::FilenameConflictAction conflict_action =
     DownloadPathReservationTracker::UNIQUIFY;
   bool create_directory = false;
-  CallGetReservedPath(
-      item.get(),
-      path,
-      create_directory,
-      conflict_action,
-      &reserved_path,
-      &verified);
+  CallGetReservedPath(item.get(), path, create_directory, conflict_action,
+                      &reserved_path, &result);
   EXPECT_TRUE(IsPathInUse(reserved_path));
-  EXPECT_TRUE(verified);
+  EXPECT_EQ(PathValidationResult::SUCCESS, result);
   EXPECT_EQ(path2, reserved_path);
   SetDownloadItemState(item.get(), DownloadItem::COMPLETE);
 }
@@ -698,19 +646,14 @@
   ASSERT_FALSE(IsPathInUse(path));
 
   base::FilePath reserved_path;
-  bool verified = false;
+  PathValidationResult result = PathValidationResult::SUCCESS;
   DownloadPathReservationTracker::FilenameConflictAction conflict_action =
-    DownloadPathReservationTracker::OVERWRITE;
+      DownloadPathReservationTracker::OVERWRITE;
   bool create_directory = false;
-  CallGetReservedPath(
-      item.get(),
-      path,
-      create_directory,
-      conflict_action,
-      &reserved_path,
-      &verified);
+  CallGetReservedPath(item.get(), path, create_directory, conflict_action,
+                      &reserved_path, &result);
   // We cannot truncate a path with very long extension.
-  EXPECT_FALSE(verified);
+  EXPECT_EQ(PathValidationResult::NAME_TOO_LONG, result);
   SetDownloadItemState(item.get(), DownloadItem::COMPLETE);
 }
 
diff --git a/chrome/browser/download/download_target_determiner.cc b/chrome/browser/download/download_target_determiner.cc
index 9320803b..47820a5 100644
--- a/chrome/browser/download/download_target_determiner.cc
+++ b/chrome/browser/download/download_target_determiner.cc
@@ -4,6 +4,9 @@
 
 #include "chrome/browser/download/download_target_determiner.h"
 
+#include <string>
+#include <vector>
+
 #include "base/location.h"
 #include "base/rand_util.h"
 #include "base/single_thread_task_runner.h"
@@ -11,7 +14,6 @@
 #include "base/task_scheduler/post_task.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
-#include "build/build_config.h"
 #include "chrome/browser/download/chrome_download_manager_delegate.h"
 #include "chrome/browser/download/download_crx_util.h"
 #include "chrome/browser/download/download_prefs.h"
@@ -44,11 +46,6 @@
 #include "content/public/common/webplugininfo.h"
 #endif
 
-#if defined(OS_ANDROID)
-#include "chrome/browser/android/download/download_controller.h"
-#include "chrome/browser/android/download/download_manager_service.h"
-#endif
-
 #if defined(OS_WIN)
 #include "chrome/browser/ui/pdf/adobe_reader_info_win.h"
 #endif
@@ -83,28 +80,21 @@
 
 }  // namespace
 
-DownloadTargetInfo::DownloadTargetInfo()
-    : target_disposition(DownloadItem::TARGET_DISPOSITION_OVERWRITE),
-      danger_type(content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS),
-      danger_level(DownloadFileType::NOT_DANGEROUS),
-      is_filetype_handled_safely(false) {}
-
-DownloadTargetInfo::~DownloadTargetInfo() {}
-
 DownloadTargetDeterminerDelegate::~DownloadTargetDeterminerDelegate() {
 }
 
 DownloadTargetDeterminer::DownloadTargetDeterminer(
     DownloadItem* download,
     const base::FilePath& initial_virtual_path,
+    DownloadPathReservationTracker::FilenameConflictAction conflict_action,
     DownloadPrefs* download_prefs,
     DownloadTargetDeterminerDelegate* delegate,
     const CompletionCallback& callback)
     : next_state_(STATE_GENERATE_TARGET_PATH),
-      should_prompt_(false),
+      confirmation_reason_(DownloadConfirmationReason::NONE),
       should_notify_extensions_(false),
       create_target_directory_(false),
-      conflict_action_(DownloadPathReservationTracker::OVERWRITE),
+      conflict_action_(conflict_action),
       danger_type_(download->GetDangerType()),
       danger_level_(DownloadFileType::NOT_DANGEROUS),
       virtual_path_(initial_virtual_path),
@@ -149,7 +139,7 @@
         result = DoReserveVirtualPath();
         break;
       case STATE_PROMPT_USER_FOR_DOWNLOAD_PATH:
-        result = DoPromptUserForDownloadPath();
+        result = DoRequestConfirmation();
         break;
       case STATE_DETERMINE_LOCAL_PATH:
         result = DoDetermineLocalPath();
@@ -182,16 +172,15 @@
   // determination and delete |this|.
 
   if (result == COMPLETE)
-    ScheduleCallbackAndDeleteSelf();
+    ScheduleCallbackAndDeleteSelf(content::DOWNLOAD_INTERRUPT_REASON_NONE);
 }
 
 DownloadTargetDeterminer::Result
     DownloadTargetDeterminer::DoGenerateTargetPath() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(local_path_.empty());
-  DCHECK(!should_prompt_);
+  DCHECK_EQ(confirmation_reason_, DownloadConfirmationReason::NONE);
   DCHECK(!should_notify_extensions_);
-  DCHECK_EQ(DownloadPathReservationTracker::OVERWRITE, conflict_action_);
   bool is_forced_path = !download_->GetForcedFilePath().empty();
 
   next_state_ = STATE_NOTIFY_EXTENSIONS;
@@ -200,7 +189,8 @@
     // The download is being resumed and the user has already been prompted for
     // a path. Assume that it's okay to overwrite the file if there's a conflict
     // and reuse the selection.
-    should_prompt_ = ShouldPromptForDownload(virtual_path_);
+    confirmation_reason_ = NeedsConfirmation(virtual_path_);
+    conflict_action_ = DownloadPathReservationTracker::OVERWRITE;
   } else if (!is_forced_path) {
     // If we don't have a forced path, we should construct a path for the
     // download. Forced paths are only specified for programmatic downloads
@@ -222,9 +212,9 @@
         suggested_filename,
         download_->GetMimeType(),
         default_filename);
-    should_prompt_ = ShouldPromptForDownload(generated_filename);
+    confirmation_reason_ = NeedsConfirmation(generated_filename);
     base::FilePath target_directory;
-    if (should_prompt_) {
+    if (confirmation_reason_ != DownloadConfirmationReason::NONE) {
       DCHECK(!download_prefs_->IsDownloadPathManaged());
       // If the user is going to be prompted and the user has been prompted
       // before, then always prefer the last directory that the user selected.
@@ -232,20 +222,10 @@
     } else {
       target_directory = download_prefs_->DownloadPath();
     }
-#if defined(OS_ANDROID)
-    // If |virtual_path_| is not empty, we are resuming a download which already
-    // has a target path. Don't prompt user in this case.
-    if (!virtual_path_.empty()) {
-      conflict_action_ = DownloadPathReservationTracker::UNIQUIFY;
-    } else {
-      conflict_action_ = DownloadPathReservationTracker::PROMPT;
-    }
-#else
-    conflict_action_ = DownloadPathReservationTracker::UNIQUIFY;
-#endif
     virtual_path_ = target_directory.Append(generated_filename);
     should_notify_extensions_ = true;
   } else {
+    conflict_action_ = DownloadPathReservationTracker::OVERWRITE;
     virtual_path_ = download_->GetForcedFilePath();
     // If this is a resumed download which was previously interrupted due to an
     // issue with the forced path, the user is still not prompted. If the path
@@ -325,39 +305,38 @@
 }
 
 void DownloadTargetDeterminer::ReserveVirtualPathDone(
-    const base::FilePath& path, bool verified) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+    PathValidationResult result,
+    const base::FilePath& path) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DVLOG(20) << "Reserved path: " << path.AsUTF8Unsafe()
-            << " Verified:" << verified;
+            << " Result:" << static_cast<int>(result);
   DCHECK_EQ(STATE_PROMPT_USER_FOR_DOWNLOAD_PATH, next_state_);
-#if defined(OS_ANDROID)
-  if (!verified) {
-    if (path.empty()) {
-      DownloadManagerService::OnDownloadCanceled(
-          download_, DownloadController::CANCEL_REASON_NO_EXTERNAL_STORAGE);
-      CancelOnFailureAndDeleteSelf();
-      return;
-    }
-    if (!download_->GetWebContents()) {
-      // If we cannot reserve the path and the WebContent is already gone, there
-      // is no way to prompt user for an infobar. This could happen after chrome
-      // gets killed, and user tries to resume a download while another app has
-      // created the target file (not the temporary .crdownload file).
-      DownloadManagerService::OnDownloadCanceled(
-          download_,
-          DownloadController::CANCEL_REASON_CANNOT_DETERMINE_DOWNLOAD_TARGET);
-      CancelOnFailureAndDeleteSelf();
-      return;
-    }
-  }
-#endif
-  should_prompt_ = (should_prompt_ || !verified);
+
   virtual_path_ = path;
+
+  switch (result) {
+    case PathValidationResult::SUCCESS:
+      break;
+
+    case PathValidationResult::PATH_NOT_WRITABLE:
+      confirmation_reason_ =
+          DownloadConfirmationReason::TARGET_PATH_NOT_WRITEABLE;
+      break;
+
+    case PathValidationResult::NAME_TOO_LONG:
+      confirmation_reason_ = DownloadConfirmationReason::NAME_TOO_LONG;
+      break;
+
+    case PathValidationResult::CONFLICT:
+      confirmation_reason_ = DownloadConfirmationReason::TARGET_CONFLICT;
+      break;
+  }
+
   DoLoop();
 }
 
 DownloadTargetDeterminer::Result
-    DownloadTargetDeterminer::DoPromptUserForDownloadPath() {
+DownloadTargetDeterminer::DoRequestConfirmation() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!virtual_path_.empty());
 
@@ -365,27 +344,36 @@
 
   // Avoid prompting for a download if it isn't in-progress. The user will be
   // prompted once the download is resumed and headers are available.
-  if (should_prompt_ && download_->GetState() == DownloadItem::IN_PROGRESS) {
-    delegate_->PromptUserForDownloadPath(
-        download_,
-        virtual_path_,
-        base::Bind(&DownloadTargetDeterminer::PromptUserForDownloadPathDone,
+  if (confirmation_reason_ != DownloadConfirmationReason::NONE &&
+      download_->GetState() == DownloadItem::IN_PROGRESS) {
+    delegate_->RequestConfirmation(
+        download_, virtual_path_, confirmation_reason_,
+        base::Bind(&DownloadTargetDeterminer::RequestConfirmationDone,
                    weak_ptr_factory_.GetWeakPtr()));
     return QUIT_DOLOOP;
   }
   return CONTINUE;
 }
 
-void DownloadTargetDeterminer::PromptUserForDownloadPathDone(
+void DownloadTargetDeterminer::RequestConfirmationDone(
+    DownloadConfirmationResult result,
     const base::FilePath& virtual_path) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DVLOG(20) << "User selected path:" << virtual_path.AsUTF8Unsafe();
-  if (virtual_path.empty()) {
-    CancelOnFailureAndDeleteSelf();
+  if (result == DownloadConfirmationResult::CANCELED) {
+    ScheduleCallbackAndDeleteSelf(
+        content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED);
     return;
   }
+  DCHECK(!virtual_path.empty());
   DCHECK_EQ(STATE_DETERMINE_LOCAL_PATH, next_state_);
 
+  // If the user wasn't prompted, then we need to clear the
+  // confirmation_reason_. This way it's clear that user has not given consent
+  // to download this resource.
+  if (result == DownloadConfirmationResult::CONTINUE_WITHOUT_CONFIRMATION)
+    confirmation_reason_ = DownloadConfirmationReason::NONE;
+
   virtual_path_ = virtual_path;
   download_prefs_->SetSaveFilePath(virtual_path_.DirName());
   DoLoop();
@@ -412,8 +400,12 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DVLOG(20) << "Local path: " << local_path.AsUTF8Unsafe();
   if (local_path.empty()) {
-    // Path subsitution failed.
-    CancelOnFailureAndDeleteSelf();
+    // Path subsitution failed. Usually caused by something going wrong with the
+    // Google Drive logic (e.g. filesystem error while trying to create the
+    // cache file). We are going to return a generic error here since a more
+    // specific one is unlikely to be helpful to the user.
+    ScheduleCallbackAndDeleteSelf(
+        content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED);
     return;
   }
   DCHECK_EQ(STATE_DETERMINE_MIME_TYPE, next_state_);
@@ -760,19 +752,23 @@
   return COMPLETE;
 }
 
-void DownloadTargetDeterminer::ScheduleCallbackAndDeleteSelf() {
+void DownloadTargetDeterminer::ScheduleCallbackAndDeleteSelf(
+    content::DownloadInterruptReason result) {
   DCHECK(download_);
   DVLOG(20) << "Scheduling callback. Virtual:" << virtual_path_.AsUTF8Unsafe()
             << " Local:" << local_path_.AsUTF8Unsafe()
             << " Intermediate:" << intermediate_path_.AsUTF8Unsafe()
-            << " Should prompt:" << should_prompt_
+            << " Confirmation reason:" << static_cast<int>(confirmation_reason_)
             << " Danger type:" << danger_type_
-            << " Danger level:" << danger_level_;
+            << " Danger level:" << danger_level_
+            << " Result:" << static_cast<int>(result);
   std::unique_ptr<DownloadTargetInfo> target_info(new DownloadTargetInfo);
 
   target_info->target_path = local_path_;
+  target_info->result = result;
   target_info->target_disposition =
-      (HasPromptedForPath() || should_prompt_
+      (HasPromptedForPath() ||
+               confirmation_reason_ != DownloadConfirmationReason::NONE
            ? DownloadItem::TARGET_DISPOSITION_PROMPT
            : DownloadItem::TARGET_DISPOSITION_OVERWRITE);
   target_info->danger_type = danger_type_;
@@ -787,35 +783,30 @@
   delete this;
 }
 
-void DownloadTargetDeterminer::CancelOnFailureAndDeleteSelf() {
-  // Path substitution failed.
-  virtual_path_.clear();
-  local_path_.clear();
-  intermediate_path_.clear();
-  ScheduleCallbackAndDeleteSelf();
-}
-
 Profile* DownloadTargetDeterminer::GetProfile() const {
   DCHECK(download_->GetBrowserContext());
   return Profile::FromBrowserContext(download_->GetBrowserContext());
 }
 
-bool DownloadTargetDeterminer::ShouldPromptForDownload(
+DownloadConfirmationReason DownloadTargetDeterminer::NeedsConfirmation(
     const base::FilePath& filename) const {
-#if defined(OS_ANDROID)
-  // Don't prompt user about saving path on Android.
-  // TODO(qinmin): show an error toast to warn user in certain cases.
-  return false;
-#endif
   if (is_resumption_) {
     // For resumed downloads, if the target disposition or prefs require
     // prompting, the user has already been prompted. Try to respect the user's
     // selection, unless we've discovered that the target path cannot be used
     // for some reason.
     content::DownloadInterruptReason reason = download_->GetLastReason();
-    return (reason == content::DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED ||
-            reason == content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE ||
-            reason == content::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_LARGE);
+    switch (reason) {
+      case content::DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED:
+        return DownloadConfirmationReason::TARGET_PATH_NOT_WRITEABLE;
+
+      case content::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_LARGE:
+      case content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE:
+        return DownloadConfirmationReason::TARGET_NO_SPACE;
+
+      default:
+        return DownloadConfirmationReason::NONE;
+    }
   }
 
   // If the download path is forced, don't prompt.
@@ -823,38 +814,38 @@
     // 'Save As' downloads shouldn't have a forced path.
     DCHECK(DownloadItem::TARGET_DISPOSITION_PROMPT !=
            download_->GetTargetDisposition());
-    return false;
+    return DownloadConfirmationReason::NONE;
   }
 
   // Don't ask where to save if the download path is managed. Even if the user
   // wanted to be prompted for "all" downloads, or if this was a 'Save As'
   // download.
   if (download_prefs_->IsDownloadPathManaged())
-    return false;
+    return DownloadConfirmationReason::NONE;
 
   // Prompt if this is a 'Save As' download.
   if (download_->GetTargetDisposition() ==
       DownloadItem::TARGET_DISPOSITION_PROMPT)
-    return true;
+    return DownloadConfirmationReason::SAVE_AS;
 
-  // Check if the user has the "Always prompt for download location" preference
-  // set. If so we prompt for most downloads except for the following scenarios:
-  // 1) Extension installation. Note that we only care here about the case where
-  //    an extension is installed, not when one is downloaded with "save as...".
-  // 2) Filetypes marked "always open." If the user just wants this file opened,
-  //    don't bother asking where to keep it.
-  if (download_prefs_->PromptForDownload() &&
-      !download_crx_util::IsExtensionDownload(*download_) &&
-      !filename.MatchesExtension(extensions::kExtensionFileExtension) &&
-      !download_prefs_->IsAutoOpenEnabledBasedOnExtension(filename))
-    return true;
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+  // Don't prompt for extension downloads.
+  if (download_crx_util::IsExtensionDownload(*download_) ||
+      filename.MatchesExtension(extensions::kExtensionFileExtension))
+    return DownloadConfirmationReason::NONE;
+#endif
 
-  // Otherwise, don't prompt. Note that the user might still be prompted if
-  // there are unresolved conflicts during path reservation (e.g. due to the
-  // target path being unwriteable or because there are too many conflicting
-  // files), or if an extension signals that the user be prompted on a filename
-  // conflict.
-  return false;
+  // Don't prompt for file types that are marked for opening automatically.
+  if (download_prefs_->IsAutoOpenEnabledBasedOnExtension(filename))
+    return DownloadConfirmationReason::NONE;
+
+  // For everything else, prompting is controlled by the PromptForDownload pref.
+  // The user may still be prompted even if this pref is disabled due to, for
+  // example, there being an unresolvable filename conflict or the target path
+  // is not writeable.
+  return download_prefs_->PromptForDownload()
+             ? DownloadConfirmationReason::PREFERENCE
+             : DownloadConfirmationReason::NONE;
 }
 
 bool DownloadTargetDeterminer::HasPromptedForPath() const {
@@ -869,7 +860,8 @@
   // If the user has has been prompted or will be, assume that the user has
   // approved the download. A programmatic download is considered safe unless it
   // contains malware.
-  if (HasPromptedForPath() || should_prompt_ ||
+  if (HasPromptedForPath() ||
+      confirmation_reason_ != DownloadConfirmationReason::NONE ||
       !download_->GetForcedFilePath().empty())
     return DownloadFileType::NOT_DANGEROUS;
 
@@ -919,20 +911,23 @@
     DownloadItem* download) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK_EQ(download_, download);
-  CancelOnFailureAndDeleteSelf();
+  ScheduleCallbackAndDeleteSelf(
+      content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED);
 }
 
 // static
-void DownloadTargetDeterminer::Start(content::DownloadItem* download,
-                                     const base::FilePath& initial_virtual_path,
-                                     DownloadPrefs* download_prefs,
-                                     DownloadTargetDeterminerDelegate* delegate,
-                                     const CompletionCallback& callback) {
+void DownloadTargetDeterminer::Start(
+    content::DownloadItem* download,
+    const base::FilePath& initial_virtual_path,
+    DownloadPathReservationTracker::FilenameConflictAction conflict_action,
+    DownloadPrefs* download_prefs,
+    DownloadTargetDeterminerDelegate* delegate,
+    const CompletionCallback& callback) {
   // DownloadTargetDeterminer owns itself and will self destruct when the job is
   // complete or the download item is destroyed. The callback is always invoked
   // asynchronously.
-  new DownloadTargetDeterminer(download, initial_virtual_path, download_prefs,
-                               delegate, callback);
+  new DownloadTargetDeterminer(download, initial_virtual_path, conflict_action,
+                               download_prefs, delegate, callback);
 }
 
 // static
diff --git a/chrome/browser/download/download_target_determiner.h b/chrome/browser/download/download_target_determiner.h
index 66bca7a..b565c9a95 100644
--- a/chrome/browser/download/download_target_determiner.h
+++ b/chrome/browser/download/download_target_determiner.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_DOWNLOAD_DOWNLOAD_TARGET_DETERMINER_H_
 
 #include <memory>
+#include <string>
 
 #include "base/files/file_path.h"
 #include "base/macros.h"
@@ -55,8 +56,8 @@
 class DownloadTargetDeterminer
     : public content::DownloadItem::Observer {
  public:
-  typedef base::Callback<void(std::unique_ptr<DownloadTargetInfo>)>
-      CompletionCallback;
+  using CompletionCallback =
+      base::Callback<void(std::unique_ptr<DownloadTargetInfo>)>;
 
   // Start the process of determing the target of |download|.
   //
@@ -74,11 +75,13 @@
   //   determination is complete or after |download| is destroyed.
   //
   // Start() should be called on the UI thread.
-  static void Start(content::DownloadItem* download,
-                    const base::FilePath& initial_virtual_path,
-                    DownloadPrefs* download_prefs,
-                    DownloadTargetDeterminerDelegate* delegate,
-                    const CompletionCallback& callback);
+  static void Start(
+      content::DownloadItem* download,
+      const base::FilePath& initial_virtual_path,
+      DownloadPathReservationTracker::FilenameConflictAction conflict_action,
+      DownloadPrefs* download_prefs,
+      DownloadTargetDeterminerDelegate* delegate,
+      const CompletionCallback& callback);
 
   // Returns a .crdownload intermediate path for the |suggested_path|.
   static base::FilePath GetCrDownloadPath(const base::FilePath& suggested_path);
@@ -137,11 +140,13 @@
 
   // Construct a DownloadTargetDeterminer object. Constraints on the arguments
   // are as per Start() above.
-  DownloadTargetDeterminer(content::DownloadItem* download,
-                           const base::FilePath& initial_virtual_path,
-                           DownloadPrefs* download_prefs,
-                           DownloadTargetDeterminerDelegate* delegate,
-                           const CompletionCallback& callback);
+  DownloadTargetDeterminer(
+      content::DownloadItem* download,
+      const base::FilePath& initial_virtual_path,
+      DownloadPathReservationTracker::FilenameConflictAction conflict_action,
+      DownloadPrefs* download_prefs,
+      DownloadTargetDeterminerDelegate* delegate,
+      const CompletionCallback& callback);
 
   ~DownloadTargetDeterminer() override;
 
@@ -179,16 +184,18 @@
   Result DoReserveVirtualPath();
 
   // Callback invoked after the delegate aquires a path reservation.
-  void ReserveVirtualPathDone(const base::FilePath& path, bool verified);
+  void ReserveVirtualPathDone(PathValidationResult result,
+                              const base::FilePath& path);
 
   // Presents a file picker to the user if necessary.
   // Next state:
   // - STATE_DETERMINE_LOCAL_PATH.
-  Result DoPromptUserForDownloadPath();
+  Result DoRequestConfirmation();
 
   // Callback invoked after the file picker completes. Cancels the download if
   // the user cancels the file picker.
-  void PromptUserForDownloadPathDone(const base::FilePath& virtual_path);
+  void RequestConfirmationDone(DownloadConfirmationResult result,
+                               const base::FilePath& virtual_path);
 
   // Up until this point, the path that was used is considered to be a virtual
   // path. This step determines the local file system path corresponding to this
@@ -270,19 +277,22 @@
 
   // Utilities:
 
-  void ScheduleCallbackAndDeleteSelf();
-
-  void CancelOnFailureAndDeleteSelf();
+  // Schedules the completion callback to be run on the UI thread and deletes
+  // this object. The determined target info will be passed into the callback
+  // if |interrupt_reason| is NONE. Otherwise, only the interrupt reason will be
+  // passed on.
+  void ScheduleCallbackAndDeleteSelf(content::DownloadInterruptReason result);
 
   Profile* GetProfile() const;
 
-  // Determine whether to prompt the user for the download location. For regular
+  // Determine if the download requires confirmation from the user. For regular
   // downloads, this determination is based on the target disposition, auto-open
   // behavior, among other factors. For an interrupted download, this
   // determination will be based on the interrupt reason. It is assumed that
   // download interruptions always occur after the first round of download
   // target determination is complete.
-  bool ShouldPromptForDownload(const base::FilePath& filename) const;
+  DownloadConfirmationReason NeedsConfirmation(
+      const base::FilePath& filename) const;
 
   // Returns true if the user has been prompted for this download at least once
   // prior to this target determination operation. This method is only expected
@@ -307,7 +317,7 @@
 
   // state
   State next_state_;
-  bool should_prompt_;
+  DownloadConfirmationReason confirmation_reason_;
   bool should_notify_extensions_;
   bool create_target_directory_;
   DownloadPathReservationTracker::FilenameConflictAction conflict_action_;
diff --git a/chrome/browser/download/download_target_determiner_delegate.h b/chrome/browser/download/download_target_determiner_delegate.h
index 86dc3c54..a7ae1207 100644
--- a/chrome/browser/download/download_target_determiner_delegate.h
+++ b/chrome/browser/download/download_target_determiner_delegate.h
@@ -5,8 +5,11 @@
 #ifndef CHROME_BROWSER_DOWNLOAD_DOWNLOAD_TARGET_DETERMINER_DELEGATE_H_
 #define CHROME_BROWSER_DOWNLOAD_DOWNLOAD_TARGET_DETERMINER_DELEGATE_H_
 
-#include "base/callback_forward.h"
+#include <string>
 
+#include "base/callback_forward.h"
+#include "chrome/browser/download/download_confirmation_reason.h"
+#include "chrome/browser/download/download_confirmation_result.h"
 #include "chrome/browser/download/download_path_reservation_tracker.h"
 #include "content/public/browser/download_danger_type.h"
 
@@ -33,20 +36,18 @@
       DownloadPathReservationTracker::FilenameConflictAction conflict_action)>
   NotifyExtensionsCallback;
 
-  // Callback to be invoked when ReserveVirtualPath() completes. If the path
-  // reservation is successful, then |successful| should be true and
-  // |reserved_path| should contain the reserved path. Otherwise, |successful|
-  // should be false. In the failure case, |reserved_path| is ignored.
-  typedef base::Callback<void(const base::FilePath& reserved_path,
-                              bool successful)> ReservedPathCallback;
+  // Callback to be invoked when ReserveVirtualPath() completes.
+  using ReservedPathCallback =
+      DownloadPathReservationTracker::ReservedPathCallback;
 
-  // Callback to be invoked when PromptUserForDownloadPath() completes.
+  // Callback to be invoked when RequestConfirmation() completes.
   // |virtual_path|: The path chosen by the user. If the user cancels the file
   //    selection, then this parameter will be the empty path. On Chrome OS,
   //    this path may contain virtual mount points if the user chose a virtual
   //    path (e.g. Google Drive).
-  typedef base::Callback<void(const base::FilePath& virtual_path)>
-  FileSelectedCallback;
+  typedef base::Callback<void(DownloadConfirmationResult,
+                              const base::FilePath& virtual_path)>
+      ConfirmationCallback;
 
   // Callback to be invoked when DetermineLocalPath() completes. The argument
   // should be the determined local path. It should be non-empty on success. If
@@ -94,10 +95,10 @@
 
   // Display a prompt to the user requesting that a download target be chosen.
   // Should invoke |callback| upon completion.
-  virtual void PromptUserForDownloadPath(
-      content::DownloadItem* download,
-      const base::FilePath& virtual_path,
-      const FileSelectedCallback& callback) = 0;
+  virtual void RequestConfirmation(content::DownloadItem* download,
+                                   const base::FilePath& virtual_path,
+                                   DownloadConfirmationReason reason,
+                                   const ConfirmationCallback& callback) = 0;
 
   // If |virtual_path| is not a local path, should return a possibly temporary
   // local path to use for storing the downloaded file. If |virtual_path| is
@@ -116,6 +117,7 @@
   // Get the MIME type for the given file.
   virtual void GetFileMimeType(const base::FilePath& path,
                                const GetFileMimeTypeCallback& callback) = 0;
+
  protected:
   virtual ~DownloadTargetDeterminerDelegate();
 };
diff --git a/chrome/browser/download/download_target_determiner_unittest.cc b/chrome/browser/download/download_target_determiner_unittest.cc
index 1a175578..06c410d 100644
--- a/chrome/browser/download/download_target_determiner_unittest.cc
+++ b/chrome/browser/download/download_target_determiner_unittest.cc
@@ -5,11 +5,15 @@
 #include <stddef.h>
 #include <stdint.h>
 
+#include <string>
+#include <vector>
+
 #include "base/at_exit.h"
 #include "base/files/file_path.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/location.h"
 #include "base/macros.h"
+#include "base/memory/ptr_util.h"
 #include "base/observer_list.h"
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
@@ -18,12 +22,15 @@
 #include "base/value_conversions.h"
 #include "build/build_config.h"
 #include "chrome/browser/download/chrome_download_manager_delegate.h"
+#include "chrome/browser/download/download_confirmation_result.h"
 #include "chrome/browser/download/download_prefs.h"
 #include "chrome/browser/download/download_target_determiner.h"
 #include "chrome/browser/download/download_target_info.h"
 #include "chrome/browser/history/history_service_factory.h"
+#include "chrome/common/features.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/safe_browsing/file_type_policies.h"
+#include "chrome/common/safe_browsing/file_type_policies_test_util.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/history/core/browser/history_service.h"
@@ -112,6 +119,8 @@
   EXPECT_CRDOWNLOAD,   // Expect path/to/target.crdownload.
   EXPECT_UNCONFIRMED,  // Expect path/to/Unconfirmed xxx.crdownload.
   EXPECT_LOCAL_PATH,   // Expect target path.
+  EXPECT_EMPTY,        // Expect empty path. Only for downloads which will be
+                       // marked interrupted or cancelled.
 };
 
 // Typical download test case. Used with
@@ -156,9 +165,11 @@
   MOCK_METHOD3(NotifyExtensions,
                void(content::DownloadItem*, const base::FilePath&,
                     const NotifyExtensionsCallback&));
-  MOCK_METHOD3(PromptUserForDownloadPath,
-               void(content::DownloadItem*, const base::FilePath&,
-                    const FileSelectedCallback&));
+  MOCK_METHOD4(RequestConfirmation,
+               void(content::DownloadItem*,
+                    const base::FilePath&,
+                    DownloadConfirmationReason,
+                    const ConfirmationCallback&));
   MOCK_METHOD3(DetermineLocalPath,
                void(DownloadItem*, const base::FilePath&,
                     const LocalPathCallback&));
@@ -181,9 +192,9 @@
     ON_CALL(*this, ReserveVirtualPath(_, _, _, _, _))
         .WillByDefault(Invoke(
             &MockDownloadTargetDeterminerDelegate::NullReserveVirtualPath));
-    ON_CALL(*this, PromptUserForDownloadPath(_, _, _))
-        .WillByDefault(Invoke(
-            &MockDownloadTargetDeterminerDelegate::NullPromptUser));
+    ON_CALL(*this, RequestConfirmation(_, _, _, _))
+        .WillByDefault(
+            Invoke(&MockDownloadTargetDeterminerDelegate::NullPromptUser));
     ON_CALL(*this, DetermineLocalPath(_, _, _))
         .WillByDefault(Invoke(
             &MockDownloadTargetDeterminerDelegate::NullDetermineLocalPath));
@@ -198,9 +209,10 @@
       bool create_directory,
       DownloadPathReservationTracker::FilenameConflictAction conflict_action,
       const DownloadTargetDeterminerDelegate::ReservedPathCallback& callback);
-  static void NullPromptUser(
-      DownloadItem* download, const base::FilePath& suggested_path,
-      const FileSelectedCallback& callback);
+  static void NullPromptUser(DownloadItem* download,
+                             const base::FilePath& suggested_path,
+                             DownloadConfirmationReason reason,
+                             const ConfirmationCallback& callback);
   static void NullDetermineLocalPath(
       DownloadItem* download, const base::FilePath& virtual_path,
       const LocalPathCallback& callback);
@@ -213,7 +225,7 @@
   void TearDown() override;
 
   // Creates MockDownloadItem and sets up default expectations.
-  content::MockDownloadItem* CreateActiveDownloadItem(
+  std::unique_ptr<content::MockDownloadItem> CreateActiveDownloadItem(
       int32_t id,
       const DownloadTestCase& test_case);
 
@@ -269,17 +281,15 @@
     return download_prefs_.get();
   }
 
-  // Shortcut
-  const FileTypePolicies* Policies() const {
-    return FileTypePolicies::GetInstance();
-  }
-
  private:
+  void SetUpFileTypePolicies();
+
   std::unique_ptr<DownloadPrefs> download_prefs_;
   ::testing::NiceMock<MockDownloadTargetDeterminerDelegate> delegate_;
   NullWebContentsDelegate web_contents_delegate_;
   base::ScopedTempDir test_download_dir_;
   base::FilePath test_virtual_dir_;
+  safe_browsing::FileTypePoliciesTestOverlay file_type_configuration_;
 };
 
 void DownloadTargetDeterminerTest::SetUp() {
@@ -291,6 +301,7 @@
   test_virtual_dir_ = test_download_dir().Append(FILE_PATH_LITERAL("virtual"));
   download_prefs_->SetDownloadPath(test_download_dir());
   delegate_.SetupDefaults();
+  SetUpFileTypePolicies();
 }
 
 void DownloadTargetDeterminerTest::TearDown() {
@@ -298,12 +309,12 @@
   ChromeRenderViewHostTestHarness::TearDown();
 }
 
-content::MockDownloadItem*
+std::unique_ptr<content::MockDownloadItem>
 DownloadTargetDeterminerTest::CreateActiveDownloadItem(
     int32_t id,
     const DownloadTestCase& test_case) {
-  content::MockDownloadItem* item =
-      new ::testing::NiceMock<content::MockDownloadItem>();
+  std::unique_ptr<content::MockDownloadItem> item =
+      base::MakeUnique<::testing::NiceMock<content::MockDownloadItem>>();
   GURL download_url(test_case.url);
   std::vector<GURL> url_chain;
   url_chain.push_back(download_url);
@@ -405,9 +416,9 @@
   std::unique_ptr<DownloadTargetInfo> target_info;
   base::RunLoop run_loop;
   DownloadTargetDeterminer::Start(
-      item, initial_virtual_path, download_prefs_.get(), delegate(),
-      base::Bind(&CompletionCallbackWrapper,
-                 run_loop.QuitClosure(),
+      item, initial_virtual_path, DownloadPathReservationTracker::UNIQUIFY,
+      download_prefs_.get(), delegate(),
+      base::Bind(&CompletionCallbackWrapper, run_loop.QuitClosure(),
                  &target_info));
   run_loop.Run();
   ::testing::Mock::VerifyAndClearExpectations(delegate());
@@ -418,8 +429,8 @@
     const DownloadTestCase test_cases[],
     size_t test_case_count) {
   for (size_t i = 0; i < test_case_count; ++i) {
-    std::unique_ptr<content::MockDownloadItem> item(
-        CreateActiveDownloadItem(i, test_cases[i]));
+    std::unique_ptr<content::MockDownloadItem> item =
+        CreateActiveDownloadItem(i, test_cases[i]);
     SCOPED_TRACE(testing::Message() << "Running test case " << i);
     RunTestCase(test_cases[i], base::FilePath(), item.get());
   }
@@ -465,9 +476,39 @@
       EXPECT_EQ(expected_local_path.value(),
                 target_info->intermediate_path.value());
       break;
+
+    case EXPECT_EMPTY:
+      EXPECT_TRUE(target_info->intermediate_path.empty());
+      break;
   }
 }
 
+void DownloadTargetDeterminerTest::SetUpFileTypePolicies() {
+  std::unique_ptr<safe_browsing::DownloadFileTypeConfig> fake_file_type_config =
+      base::MakeUnique<safe_browsing::DownloadFileTypeConfig>();
+  auto* file_type = fake_file_type_config->mutable_default_file_type();
+  file_type->set_uma_value(-1);
+  auto* platform_settings = file_type->add_platform_settings();
+  platform_settings->set_danger_level(DownloadFileType::NOT_DANGEROUS);
+  platform_settings->set_auto_open_hint(DownloadFileType::ALLOW_AUTO_OPEN);
+
+  file_type = fake_file_type_config->add_file_types();
+  file_type->set_extension("kindabad");
+  file_type->set_uma_value(-1);
+  platform_settings = file_type->add_platform_settings();
+  platform_settings->set_danger_level(DownloadFileType::ALLOW_ON_USER_GESTURE);
+  platform_settings->set_auto_open_hint(DownloadFileType::ALLOW_AUTO_OPEN);
+
+  file_type = fake_file_type_config->add_file_types();
+  file_type->set_extension("bad");
+  file_type->set_uma_value(-1);
+  platform_settings = file_type->add_platform_settings();
+  platform_settings->set_danger_level(DownloadFileType::DANGEROUS);
+  platform_settings->set_auto_open_hint(DownloadFileType::DISALLOW_AUTO_OPEN);
+
+  file_type_configuration_.SwapConfig(fake_file_type_config);
+}
+
 // static
 void MockDownloadTargetDeterminerDelegate::NullReserveVirtualPath(
     DownloadItem* download,
@@ -475,14 +516,16 @@
     bool create_directory,
     DownloadPathReservationTracker::FilenameConflictAction conflict_action,
     const DownloadTargetDeterminerDelegate::ReservedPathCallback& callback) {
-  callback.Run(virtual_path, true);
+  callback.Run(PathValidationResult::SUCCESS, virtual_path);
 }
 
 // static
 void MockDownloadTargetDeterminerDelegate::NullPromptUser(
-    DownloadItem* download, const base::FilePath& suggested_path,
-    const FileSelectedCallback& callback) {
-  callback.Run(suggested_path);
+    DownloadItem* download,
+    const base::FilePath& suggested_path,
+    DownloadConfirmationReason reason,
+    const ConfirmationCallback& callback) {
+  callback.Run(DownloadConfirmationResult::CONFIRMED, suggested_path);
 }
 
 // static
@@ -511,55 +554,53 @@
 
 TEST_F(DownloadTargetDeterminerTest, Basic) {
   const DownloadTestCase kBasicTestCases[] = {
-    {// 0: Automatic Safe
-     AUTOMATIC, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
-     DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.txt",
-     "text/plain", FILE_PATH_LITERAL(""),
+      {// Automatic Safe
+       AUTOMATIC, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
+       DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.txt",
+       "text/plain", FILE_PATH_LITERAL(""),
 
-     FILE_PATH_LITERAL("foo.txt"), DownloadItem::TARGET_DISPOSITION_OVERWRITE,
+       FILE_PATH_LITERAL("foo.txt"), DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-     EXPECT_CRDOWNLOAD},
+       EXPECT_CRDOWNLOAD},
 
-#if !defined(OS_ANDROID)
-    {// 1: Save_As Safe
-     SAVE_AS, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
-     DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.txt",
-     "text/plain", FILE_PATH_LITERAL(""),
+      {// Save_As Safe
+       SAVE_AS, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
+       DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.txt",
+       "text/plain", FILE_PATH_LITERAL(""),
 
-     FILE_PATH_LITERAL("foo.txt"), DownloadItem::TARGET_DISPOSITION_PROMPT,
+       FILE_PATH_LITERAL("foo.txt"), DownloadItem::TARGET_DISPOSITION_PROMPT,
 
-     EXPECT_CRDOWNLOAD},
-#endif  // !defined(OS_ANDROID)
+       EXPECT_CRDOWNLOAD},
 
-    {// 2: Automatic Dangerous
-     AUTOMATIC, content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE,
-     DownloadFileType::ALLOW_ON_USER_GESTURE, "http://example.com/foo.crx", "",
-     FILE_PATH_LITERAL(""),
+      {// Automatic Dangerous
+       AUTOMATIC, content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE,
+       DownloadFileType::ALLOW_ON_USER_GESTURE,
+       "http://example.com/foo.kindabad", "", FILE_PATH_LITERAL(""),
 
-     FILE_PATH_LITERAL("foo.crx"), DownloadItem::TARGET_DISPOSITION_OVERWRITE,
+       FILE_PATH_LITERAL("foo.kindabad"),
+       DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-     EXPECT_UNCONFIRMED},
+       EXPECT_UNCONFIRMED},
 
-    {// 3: Forced Safe
-     FORCED, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
-     DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.txt", "",
-     FILE_PATH_LITERAL("forced-foo.txt"),
+      {// Forced Safe
+       FORCED, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
+       DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.txt", "",
+       FILE_PATH_LITERAL("forced-foo.txt"),
 
-     FILE_PATH_LITERAL("forced-foo.txt"),
-     DownloadItem::TARGET_DISPOSITION_OVERWRITE,
+       FILE_PATH_LITERAL("forced-foo.txt"),
+       DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-     EXPECT_LOCAL_PATH},
+       EXPECT_LOCAL_PATH},
   };
 
-  // The test assumes that .crx files have a danger level of
+  // The test assumes that .kindabad files have a danger level of
   // ALLOW_ON_USER_GESTURE.
   ASSERT_EQ(DownloadFileType::ALLOW_ON_USER_GESTURE,
-            Policies()->GetFileDangerLevel(
-                base::FilePath(FILE_PATH_LITERAL("foo.crx"))));
+            safe_browsing::FileTypePolicies::GetInstance()->GetFileDangerLevel(
+                base::FilePath(FILE_PATH_LITERAL("foo.kindabad"))));
   RunTestCasesWithActiveItem(kBasicTestCases, arraysize(kBasicTestCases));
 }
 
-#if !defined(OS_ANDROID)
 TEST_F(DownloadTargetDeterminerTest, CancelSaveAs) {
   const DownloadTestCase kCancelSaveAsTestCases[] = {
       {// 0: Save_As Safe, Cancelled.
@@ -570,77 +611,74 @@
        FILE_PATH_LITERAL(""), DownloadItem::TARGET_DISPOSITION_PROMPT,
 
        EXPECT_LOCAL_PATH}};
-  ON_CALL(*delegate(), PromptUserForDownloadPath(_, _, _))
-      .WillByDefault(WithArg<2>(ScheduleCallback(base::FilePath())));
+  ON_CALL(*delegate(), RequestConfirmation(_, _, _, _))
+      .WillByDefault(WithArg<3>(ScheduleCallback2(
+          DownloadConfirmationResult::CANCELED, base::FilePath())));
   RunTestCasesWithActiveItem(kCancelSaveAsTestCases,
                              arraysize(kCancelSaveAsTestCases));
 }
-#endif  // !defined(OS_ANDROID)
 
 // The SafeBrowsing check is performed early. Make sure that a download item
 // that has been marked as DANGEROUS_URL behaves correctly.
 TEST_F(DownloadTargetDeterminerTest, DangerousUrl) {
   const DownloadTestCase kSafeBrowsingTestCases[] = {
-    {// 0: Automatic Dangerous URL
-     AUTOMATIC, content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL,
-     DownloadFileType::NOT_DANGEROUS, "http://phishing.example.com/foo.txt", "",
-     FILE_PATH_LITERAL(""),
+      {// 0: Automatic Dangerous URL
+       AUTOMATIC, content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL,
+       DownloadFileType::NOT_DANGEROUS, "http://phishing.example.com/foo.txt",
+       "", FILE_PATH_LITERAL(""),
 
-     FILE_PATH_LITERAL("foo.txt"), DownloadItem::TARGET_DISPOSITION_OVERWRITE,
+       FILE_PATH_LITERAL("foo.txt"), DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-     EXPECT_UNCONFIRMED},
+       EXPECT_UNCONFIRMED},
 
-#if !defined(OS_ANDROID)
-    {// 1: Save As Dangerous URL
-     SAVE_AS, content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL,
-     DownloadFileType::NOT_DANGEROUS, "http://phishing.example.com/foo.txt", "",
-     FILE_PATH_LITERAL(""),
+      {// 1: Save As Dangerous URL
+       SAVE_AS, content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL,
+       DownloadFileType::NOT_DANGEROUS, "http://phishing.example.com/foo.txt",
+       "", FILE_PATH_LITERAL(""),
 
-     FILE_PATH_LITERAL("foo.txt"), DownloadItem::TARGET_DISPOSITION_PROMPT,
+       FILE_PATH_LITERAL("foo.txt"), DownloadItem::TARGET_DISPOSITION_PROMPT,
 
-     EXPECT_UNCONFIRMED},
-#endif  // !defined(OS_ANDROID)
+       EXPECT_UNCONFIRMED},
 
-    {// 2: Forced Dangerous URL
-     FORCED, content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL,
-     DownloadFileType::NOT_DANGEROUS, "http://phishing.example.com/foo.txt", "",
-     FILE_PATH_LITERAL("forced-foo.txt"),
+      {// 2: Forced Dangerous URL
+       FORCED, content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL,
+       DownloadFileType::NOT_DANGEROUS, "http://phishing.example.com/foo.txt",
+       "", FILE_PATH_LITERAL("forced-foo.txt"),
 
-     FILE_PATH_LITERAL("forced-foo.txt"),
-     DownloadItem::TARGET_DISPOSITION_OVERWRITE,
+       FILE_PATH_LITERAL("forced-foo.txt"),
+       DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-     EXPECT_UNCONFIRMED},
+       EXPECT_UNCONFIRMED},
 
-    {// 3: Automatic Dangerous URL + Dangerous file. Dangerous URL takes
-     // precedence.
-     AUTOMATIC, content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL,
-     DownloadFileType::NOT_DANGEROUS, "http://phishing.example.com/foo.html",
-     "", FILE_PATH_LITERAL(""),
+      {// 3: Automatic Dangerous URL + Dangerous file. Dangerous URL takes
+       // precedence.
+       AUTOMATIC, content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL,
+       DownloadFileType::NOT_DANGEROUS, "http://phishing.example.com/foo.html",
+       "", FILE_PATH_LITERAL(""),
 
-     FILE_PATH_LITERAL("foo.html"), DownloadItem::TARGET_DISPOSITION_OVERWRITE,
+       FILE_PATH_LITERAL("foo.html"),
+       DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-     EXPECT_UNCONFIRMED},
+       EXPECT_UNCONFIRMED},
 
-#if !defined(OS_ANDROID)
-    {// 4: Save As Dangerous URL + Dangerous file
-     SAVE_AS, content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL,
-     DownloadFileType::NOT_DANGEROUS, "http://phishing.example.com/foo.html",
-     "", FILE_PATH_LITERAL(""),
+      {// 4: Save As Dangerous URL + Dangerous file
+       SAVE_AS, content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL,
+       DownloadFileType::NOT_DANGEROUS, "http://phishing.example.com/foo.html",
+       "", FILE_PATH_LITERAL(""),
 
-     FILE_PATH_LITERAL("foo.html"), DownloadItem::TARGET_DISPOSITION_PROMPT,
+       FILE_PATH_LITERAL("foo.html"), DownloadItem::TARGET_DISPOSITION_PROMPT,
 
-     EXPECT_UNCONFIRMED},
-#endif  // !defined(OS_ANDROID)
+       EXPECT_UNCONFIRMED},
 
-    {// 5: Forced Dangerous URL + Dangerous file
-     FORCED, content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL,
-     DownloadFileType::NOT_DANGEROUS, "http://phishing.example.com/foo.html",
-     "", FILE_PATH_LITERAL("forced-foo.html"),
+      {// 5: Forced Dangerous URL + Dangerous file
+       FORCED, content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL,
+       DownloadFileType::NOT_DANGEROUS, "http://phishing.example.com/foo.html",
+       "", FILE_PATH_LITERAL("forced-foo.html"),
 
-     FILE_PATH_LITERAL("forced-foo.html"),
-     DownloadItem::TARGET_DISPOSITION_OVERWRITE,
+       FILE_PATH_LITERAL("forced-foo.html"),
+       DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-     EXPECT_UNCONFIRMED},
+       EXPECT_UNCONFIRMED},
   };
 
   ON_CALL(*delegate(), CheckDownloadUrl(_, _, _))
@@ -654,53 +692,50 @@
 // that has been marked as MAYBE_DANGEROUS_CONTENT behaves correctly.
 TEST_F(DownloadTargetDeterminerTest, MaybeDangerousContent) {
   const DownloadTestCase kSafeBrowsingTestCases[] = {
-    {// 0: Automatic Maybe dangerous content
-     AUTOMATIC, content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT,
-     DownloadFileType::ALLOW_ON_USER_GESTURE,
-     "http://phishing.example.com/foo.crx", "", FILE_PATH_LITERAL(""),
+      {// 0: Automatic Maybe dangerous content
+       AUTOMATIC, content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT,
+       DownloadFileType::ALLOW_ON_USER_GESTURE,
+       "http://phishing.example.com/foo.kindabad", "", FILE_PATH_LITERAL(""),
 
-     FILE_PATH_LITERAL("foo.crx"), DownloadItem::TARGET_DISPOSITION_OVERWRITE,
+       FILE_PATH_LITERAL("foo.kindabad"),
+       DownloadItem::TARGET_DISPOSITION_OVERWRITE, EXPECT_UNCONFIRMED},
 
-     EXPECT_UNCONFIRMED},
+      {// 1: Automatic Maybe dangerous content with DANGEROUS type.
+       AUTOMATIC, content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT,
+       DownloadFileType::DANGEROUS, "http://phishing.example.com/foo.bad", "",
+       FILE_PATH_LITERAL(""),
 
-    {// 1: Automatic Maybe dangerous content with DANGEROUS type.
-     AUTOMATIC, content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT,
-     DownloadFileType::DANGEROUS, "http://phishing.example.com/foo.swf", "",
-     FILE_PATH_LITERAL(""),
+       FILE_PATH_LITERAL("foo.bad"), DownloadItem::TARGET_DISPOSITION_OVERWRITE,
+       EXPECT_UNCONFIRMED},
 
-     FILE_PATH_LITERAL("foo.swf"), DownloadItem::TARGET_DISPOSITION_OVERWRITE,
+      {// 2: Save As Maybe dangerous content
+       SAVE_AS, content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT,
+       DownloadFileType::NOT_DANGEROUS,
+       "http://phishing.example.com/foo.kindabad", "", FILE_PATH_LITERAL(""),
 
-     EXPECT_UNCONFIRMED},
+       FILE_PATH_LITERAL("foo.kindabad"),
+       DownloadItem::TARGET_DISPOSITION_PROMPT,
 
-#if !defined(OS_ANDROID)
-    {// 2: Save As Maybe dangerous content
-     SAVE_AS, content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT,
-     DownloadFileType::NOT_DANGEROUS, "http://phishing.example.com/foo.crx", "",
-     FILE_PATH_LITERAL(""),
+       EXPECT_UNCONFIRMED},
 
-     FILE_PATH_LITERAL("foo.crx"), DownloadItem::TARGET_DISPOSITION_PROMPT,
+      {// 3: Forced Maybe dangerous content
+       FORCED, content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT,
+       DownloadFileType::NOT_DANGEROUS,
+       "http://phishing.example.com/foo.kindabad", "",
+       FILE_PATH_LITERAL("forced-foo.kindabad"),
 
-     EXPECT_UNCONFIRMED},
-#endif  // !defined(OS_ANDROID)
+       FILE_PATH_LITERAL("forced-foo.kindabad"),
+       DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-    {// 3: Forced Maybe dangerous content
-     FORCED, content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT,
-     DownloadFileType::NOT_DANGEROUS, "http://phishing.example.com/foo.crx", "",
-     FILE_PATH_LITERAL("forced-foo.crx"),
-
-     FILE_PATH_LITERAL("forced-foo.crx"),
-     DownloadItem::TARGET_DISPOSITION_OVERWRITE,
-
-     EXPECT_UNCONFIRMED},
-  };
+       EXPECT_UNCONFIRMED}};
 
   // Test assumptions:
   ASSERT_EQ(DownloadFileType::ALLOW_ON_USER_GESTURE,
-            Policies()->GetFileDangerLevel(
-                base::FilePath(FILE_PATH_LITERAL("foo.crx"))));
+            safe_browsing::FileTypePolicies::GetInstance()->GetFileDangerLevel(
+                base::FilePath(FILE_PATH_LITERAL("foo.kindabad"))));
   ASSERT_EQ(DownloadFileType::DANGEROUS,
-            Policies()->GetFileDangerLevel(
-                base::FilePath(FILE_PATH_LITERAL("foo.swf"))));
+            safe_browsing::FileTypePolicies::GetInstance()->GetFileDangerLevel(
+                base::FilePath(FILE_PATH_LITERAL("foo.bad"))));
 
   ON_CALL(*delegate(), CheckDownloadUrl(_, _, _))
       .WillByDefault(WithArg<2>(ScheduleCallback(
@@ -709,7 +744,6 @@
                              arraysize(kSafeBrowsingTestCases));
 }
 
-#if !defined(OS_ANDROID)
 // Test whether the last saved directory is used for 'Save As' downloads.
 TEST_F(DownloadTargetDeterminerTest, LastSavePath) {
   const DownloadTestCase kLastSavePathTestCasesPre[] = {
@@ -765,7 +799,9 @@
                  << "Running with default download path");
     base::FilePath prompt_path =
         GetPathInDownloadDir(FILE_PATH_LITERAL("foo.txt"));
-    EXPECT_CALL(*delegate(), PromptUserForDownloadPath(_, prompt_path, _));
+    EXPECT_CALL(*delegate(),
+                RequestConfirmation(_, prompt_path,
+                                    DownloadConfirmationReason::SAVE_AS, _));
     RunTestCasesWithActiveItem(kLastSavePathTestCasesPre,
                                arraysize(kLastSavePathTestCasesPre));
   }
@@ -777,7 +813,9 @@
     download_prefs()->SetSaveFilePath(test_download_dir().AppendASCII("foo"));
     base::FilePath prompt_path =
         GetPathInDownloadDir(FILE_PATH_LITERAL("foo/foo.txt"));
-    EXPECT_CALL(*delegate(), PromptUserForDownloadPath(_, prompt_path, _));
+    EXPECT_CALL(*delegate(),
+                RequestConfirmation(_, prompt_path,
+                                    DownloadConfirmationReason::SAVE_AS, _));
     RunTestCasesWithActiveItem(kLastSavePathTestCasesPost,
                                arraysize(kLastSavePathTestCasesPost));
   }
@@ -789,8 +827,9 @@
     base::FilePath last_selected_dir = test_virtual_dir().AppendASCII("foo");
     base::FilePath virtual_path = last_selected_dir.AppendASCII("foo.txt");
     download_prefs()->SetSaveFilePath(last_selected_dir);
-    EXPECT_CALL(*delegate(), PromptUserForDownloadPath(
-        _, last_selected_dir.AppendASCII("foo.txt"), _));
+    EXPECT_CALL(*delegate(),
+                RequestConfirmation(_, last_selected_dir.AppendASCII("foo.txt"),
+                                    DownloadConfirmationReason::SAVE_AS, _));
     EXPECT_CALL(*delegate(), DetermineLocalPath(_, virtual_path, _))
         .WillOnce(WithArg<2>(ScheduleCallback(
             GetPathInDownloadDir(FILE_PATH_LITERAL("bar.txt")))));
@@ -798,7 +837,6 @@
                                arraysize(kLastSavePathTestCasesVirtual));
   }
 }
-#endif  // !defined(OS_ANDROID)
 
 // These tests are run with the default downloads folder set to a virtual
 // directory.
@@ -826,7 +864,6 @@
     RunTestCasesWithActiveItem(&kAutomaticDownloadToVirtualDir, 1);
   }
 
-#if !defined(OS_ANDROID)
   {
     SCOPED_TRACE(testing::Message() << "Save As to virtual directory");
     const DownloadTestCase kSaveAsToVirtualDir = {
@@ -844,10 +881,12 @@
     EXPECT_CALL(*delegate(), DetermineLocalPath(_, _, _))
         .WillOnce(WithArg<2>(ScheduleCallback(
             GetPathInDownloadDir(FILE_PATH_LITERAL("foo-local.txt")))));
-    EXPECT_CALL(*delegate(), PromptUserForDownloadPath(
-        _, test_virtual_dir().AppendASCII("bar.txt"), _))
-        .WillOnce(WithArg<2>(ScheduleCallback(
-            test_virtual_dir().AppendASCII("prompted.txt"))));
+    EXPECT_CALL(*delegate(), RequestConfirmation(
+                                 _, test_virtual_dir().AppendASCII("bar.txt"),
+                                 DownloadConfirmationReason::SAVE_AS, _))
+        .WillOnce(WithArg<3>(
+            ScheduleCallback2(DownloadConfirmationResult::CONFIRMED,
+                              test_virtual_dir().AppendASCII("prompted.txt"))));
     RunTestCasesWithActiveItem(&kSaveAsToVirtualDir, 1);
   }
 
@@ -865,13 +904,14 @@
         DownloadItem::TARGET_DISPOSITION_PROMPT,
 
         EXPECT_CRDOWNLOAD};
-    EXPECT_CALL(*delegate(), PromptUserForDownloadPath(
-        _, test_virtual_dir().AppendASCII("bar.txt"), _))
-        .WillOnce(WithArg<2>(ScheduleCallback(
+    EXPECT_CALL(*delegate(), RequestConfirmation(
+                                 _, test_virtual_dir().AppendASCII("bar.txt"),
+                                 DownloadConfirmationReason::SAVE_AS, _))
+        .WillOnce(WithArg<3>(ScheduleCallback2(
+            DownloadConfirmationResult::CONFIRMED,
             GetPathInDownloadDir(FILE_PATH_LITERAL("foo-x.txt")))));
     RunTestCasesWithActiveItem(&kSaveAsToLocalDir, 1);
   }
-#endif  // !defined(OS_ANDROID)
 
   {
     SCOPED_TRACE(testing::Message() << "Forced safe download");
@@ -894,63 +934,98 @@
 // Test that an inactive download will still get a virtual or local download
 // path.
 TEST_F(DownloadTargetDeterminerTest, InactiveDownload) {
-  const DownloadTestCase kInactiveTestCases[] = {
-    {AUTOMATIC, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
-     DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.txt",
-     "text/plain", FILE_PATH_LITERAL(""),
+  const DownloadTestCase kBaseTestCase = {
+      AUTOMATIC,
+      content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
+      DownloadFileType::NOT_DANGEROUS,
+      "http://example.com/foo.txt",
+      "text/plain",
+      FILE_PATH_LITERAL(""),
+      FILE_PATH_LITERAL("foo.txt"),
+      DownloadItem::TARGET_DISPOSITION_OVERWRITE,
+      EXPECT_CRDOWNLOAD};
 
-     FILE_PATH_LITERAL("foo.txt"), DownloadItem::TARGET_DISPOSITION_OVERWRITE,
+  const struct {
+    TestCaseType type;
+    DownloadItem::TargetDisposition disposition;
+  } kTestCases[] = {{AUTOMATIC, DownloadItem::TARGET_DISPOSITION_OVERWRITE},
+                    {SAVE_AS, DownloadItem::TARGET_DISPOSITION_PROMPT}};
 
-     EXPECT_CRDOWNLOAD},
-
-#if !defined(OS_ANDROID)
-    {SAVE_AS, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
-     DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.txt",
-     "text/plain", FILE_PATH_LITERAL(""),
-
-     FILE_PATH_LITERAL("foo.txt"), DownloadItem::TARGET_DISPOSITION_PROMPT,
-
-     EXPECT_CRDOWNLOAD}
-#endif  // !defined(OS_ANDROID)
-  };
-
-  for (size_t i = 0; i < arraysize(kInactiveTestCases); ++i) {
-    SCOPED_TRACE(testing::Message() << "Running test case " << i);
-    const DownloadTestCase& test_case = kInactiveTestCases[i];
-    std::unique_ptr<content::MockDownloadItem> item(
-        CreateActiveDownloadItem(i, test_case));
+  for (const auto& test_case : kTestCases) {
+    DownloadTestCase download_test_case = kBaseTestCase;
+    download_test_case.test_type = test_case.type;
+    download_test_case.expected_disposition = test_case.disposition;
+    std::unique_ptr<content::MockDownloadItem> item =
+        CreateActiveDownloadItem(1, download_test_case);
     EXPECT_CALL(*item.get(), GetState())
         .WillRepeatedly(Return(content::DownloadItem::CANCELLED));
 
-    EXPECT_CALL(*delegate(), PromptUserForDownloadPath(_, _, _)).Times(0);
+    EXPECT_CALL(*delegate(), RequestConfirmation(_, _, _, _)).Times(0);
     EXPECT_CALL(*delegate(), NotifyExtensions(_, _, _)).Times(0);
     EXPECT_CALL(*delegate(), ReserveVirtualPath(_, _, _, _, _)).Times(0);
     EXPECT_CALL(*delegate(), DetermineLocalPath(_, _, _)).Times(1);
-    RunTestCase(test_case, base::FilePath(), item.get());
+
+    // Each test case has a non-empty target path. The test will fail if the
+    // target determination doesn't result in that path.
+    RunTestCase(download_test_case, base::FilePath(), item.get());
   }
 }
 
 // If the reserved path could not be verified, then the user should see a
 // prompt.
-TEST_F(DownloadTargetDeterminerTest, ReservationFailed) {
-  const DownloadTestCase kReservationFailedCases[] = {
-      {// 0: Automatic download. Since the reservation fails, the disposition of
-       // the target is to prompt, but the returned path is used.
-       AUTOMATIC, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
-       DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.txt",
-       "text/plain", FILE_PATH_LITERAL(""),
+TEST_F(DownloadTargetDeterminerTest, ReservationFailed_Confirmation) {
+  DownloadTestCase download_test_case = {
+      // 0: Automatic download. Since the reservation fails, the disposition of
+      // the target is to prompt, but the returned path is used.
+      AUTOMATIC,
+      content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
+      DownloadFileType::NOT_DANGEROUS,
+      "http://example.com/foo.txt",
+      "text/plain",
+      FILE_PATH_LITERAL(""),
 
-       FILE_PATH_LITERAL("bar.txt"), DownloadItem::TARGET_DISPOSITION_PROMPT,
+      FILE_PATH_LITERAL("bar.txt"),
+      DownloadItem::TARGET_DISPOSITION_PROMPT,
 
-       EXPECT_CRDOWNLOAD},
-  };
+      EXPECT_CRDOWNLOAD};
 
-  // Setup ReserveVirtualPath() to fail.
-  ON_CALL(*delegate(), ReserveVirtualPath(_, _, _, _, _))
-      .WillByDefault(WithArg<4>(ScheduleCallback2(
-          GetPathInDownloadDir(FILE_PATH_LITERAL("bar.txt")), false)));
-  RunTestCasesWithActiveItem(kReservationFailedCases,
-                             arraysize(kReservationFailedCases));
+  struct TestCase {
+    PathValidationResult result;
+    DownloadConfirmationReason expected_confirmation_reason;
+  } kTestCases[] = {
+      {PathValidationResult::SUCCESS, DownloadConfirmationReason::NONE},
+      {PathValidationResult::CONFLICT,
+       DownloadConfirmationReason::TARGET_CONFLICT},
+      {PathValidationResult::PATH_NOT_WRITABLE,
+       DownloadConfirmationReason::TARGET_PATH_NOT_WRITEABLE},
+      {PathValidationResult::NAME_TOO_LONG,
+       DownloadConfirmationReason::NAME_TOO_LONG}};
+
+  for (const auto& test_case : kTestCases) {
+    SCOPED_TRACE(::testing::Message() << "PathValidationResult "
+                                      << static_cast<int>(test_case.result));
+    ON_CALL(*delegate(), ReserveVirtualPath(_, _, _, _, _))
+        .WillByDefault(WithArg<4>(ScheduleCallback2(
+            test_case.result,
+            GetPathInDownloadDir(FILE_PATH_LITERAL("bar.txt")))));
+    if (test_case.expected_confirmation_reason !=
+        DownloadConfirmationReason::NONE) {
+      EXPECT_CALL(
+          *delegate(),
+          RequestConfirmation(_, _, test_case.expected_confirmation_reason, _));
+    } else {
+      EXPECT_CALL(*delegate(), RequestConfirmation(_, _, _, _)).Times(0);
+    }
+
+    download_test_case.expected_disposition =
+        test_case.expected_confirmation_reason ==
+                DownloadConfirmationReason::NONE
+            ? content::DownloadItem::TARGET_DISPOSITION_OVERWRITE
+            : content::DownloadItem::TARGET_DISPOSITION_PROMPT;
+    std::unique_ptr<content::MockDownloadItem> item = CreateActiveDownloadItem(
+        static_cast<int>(test_case.result), download_test_case);
+    RunTestCase(download_test_case, base::FilePath(), item.get());
+  }
 }
 
 // If the local path could not be determined, the download should be cancelled.
@@ -981,53 +1056,58 @@
 // download and whether the referrer was visited prior to today.
 TEST_F(DownloadTargetDeterminerTest, VisitedReferrer) {
   const DownloadTestCase kVisitedReferrerCases[] = {
-    // http://visited.example.com/ is added to the history as a visit that
-    // happened prior to today.
-    {// 0: Safe download due to visiting referrer before.
-     AUTOMATIC, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
-     DownloadFileType::NOT_DANGEROUS, "http://visited.example.com/foo.crx",
-     "application/xml", FILE_PATH_LITERAL(""),
+      // http://visited.example.com/ is added to the history as a visit that
+      // happened prior to today.
+      {// 0: Safe download due to visiting referrer before.
+       AUTOMATIC, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
+       DownloadFileType::NOT_DANGEROUS,
+       "http://visited.example.com/foo.kindabad", "application/xml",
+       FILE_PATH_LITERAL(""),
 
-     FILE_PATH_LITERAL("foo.crx"), DownloadItem::TARGET_DISPOSITION_OVERWRITE,
+       FILE_PATH_LITERAL("foo.kindabad"),
+       DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-     EXPECT_CRDOWNLOAD},
+       EXPECT_CRDOWNLOAD},
 
-    {// 1: Dangerous due to not having visited referrer before.
-     AUTOMATIC, content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE,
-     DownloadFileType::ALLOW_ON_USER_GESTURE,
-     "http://not-visited.example.com/foo.crx", "application/xml",
-     FILE_PATH_LITERAL(""),
+      {// 1: Dangerous due to not having visited referrer before.
+       AUTOMATIC, content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE,
+       DownloadFileType::ALLOW_ON_USER_GESTURE,
+       "http://not-visited.example.com/foo.kindabad", "application/xml",
+       FILE_PATH_LITERAL(""),
 
-     FILE_PATH_LITERAL("foo.crx"), DownloadItem::TARGET_DISPOSITION_OVERWRITE,
+       FILE_PATH_LITERAL("foo.kindabad"),
+       DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-     EXPECT_UNCONFIRMED},
+       EXPECT_UNCONFIRMED},
 
-#if !defined(OS_ANDROID)
-    {// 2: Safe because the user is being prompted.
-     SAVE_AS, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
-     DownloadFileType::NOT_DANGEROUS, "http://not-visited.example.com/foo.crx",
-     "application/xml", FILE_PATH_LITERAL(""),
+      {// 2: Safe because the user is being prompted.
+       SAVE_AS, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
+       DownloadFileType::NOT_DANGEROUS,
+       "http://not-visited.example.com/foo.kindabad", "application/xml",
+       FILE_PATH_LITERAL(""),
 
-     FILE_PATH_LITERAL("foo.crx"), DownloadItem::TARGET_DISPOSITION_PROMPT,
+       FILE_PATH_LITERAL("foo.kindabad"),
+       DownloadItem::TARGET_DISPOSITION_PROMPT,
 
-     EXPECT_CRDOWNLOAD},
-#endif  // !defined(OS_ANDROID)
+       EXPECT_CRDOWNLOAD},
 
-    {// 3: Safe because of forced path.
-     FORCED, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
-     DownloadFileType::NOT_DANGEROUS, "http://not-visited.example.com/foo.crx",
-     "application/xml", FILE_PATH_LITERAL("foo.crx"),
+      {// 3: Safe because of forced path.
+       FORCED, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
+       DownloadFileType::NOT_DANGEROUS,
+       "http://not-visited.example.com/foo.kindabad", "application/xml",
+       FILE_PATH_LITERAL("foo.kindabad"),
 
-     FILE_PATH_LITERAL("foo.crx"), DownloadItem::TARGET_DISPOSITION_OVERWRITE,
+       FILE_PATH_LITERAL("foo.kindabad"),
+       DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-     EXPECT_LOCAL_PATH},
+       EXPECT_LOCAL_PATH},
   };
 
-  // This test assumes that the danger level of .crx files is
+  // This test assumes that the danger level of .kindabad files is
   // ALLOW_ON_USER_GESTURE.
   ASSERT_EQ(DownloadFileType::ALLOW_ON_USER_GESTURE,
-            Policies()->GetFileDangerLevel(
-                base::FilePath(FILE_PATH_LITERAL("foo.crx"))));
+            safe_browsing::FileTypePolicies::GetInstance()->GetFileDangerLevel(
+                base::FilePath(FILE_PATH_LITERAL("foo.kindabad"))));
 
   // First the history service must exist.
   ASSERT_TRUE(profile()->CreateHistoryService(false, false));
@@ -1065,11 +1145,11 @@
       AUTOMATIC,
       content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE,
       DownloadFileType::ALLOW_ON_USER_GESTURE,
-      "http://example.com/foo.crx",
+      "http://example.com/foo.kindabad",
       "application/octet-stream",
       FILE_PATH_LITERAL(""),
 
-      FILE_PATH_LITERAL("foo.crx"),
+      FILE_PATH_LITERAL("foo.kindabad"),
       DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
       EXPECT_UNCONFIRMED};
@@ -1078,11 +1158,11 @@
       AUTOMATIC,
       content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE,
       DownloadFileType::DANGEROUS,
-      "http://example.com/foo.swf",
+      "http://example.com/foo.bad",
       "application/octet-stream",
       FILE_PATH_LITERAL(""),
 
-      FILE_PATH_LITERAL("foo.swf"),
+      FILE_PATH_LITERAL("foo.bad"),
       DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
       EXPECT_UNCONFIRMED};
@@ -1135,13 +1215,13 @@
 
   // Test assumptions:
   ASSERT_EQ(DownloadFileType::ALLOW_ON_USER_GESTURE,
-            Policies()->GetFileDangerLevel(
-                base::FilePath(FILE_PATH_LITERAL("foo.crx"))));
+            safe_browsing::FileTypePolicies::GetInstance()->GetFileDangerLevel(
+                base::FilePath(FILE_PATH_LITERAL("foo.kindabad"))));
   ASSERT_EQ(DownloadFileType::DANGEROUS,
-            Policies()->GetFileDangerLevel(
-                base::FilePath(FILE_PATH_LITERAL("foo.swf"))));
+            safe_browsing::FileTypePolicies::GetInstance()->GetFileDangerLevel(
+                base::FilePath(FILE_PATH_LITERAL("foo.bad"))));
   ASSERT_EQ(DownloadFileType::NOT_DANGEROUS,
-            Policies()->GetFileDangerLevel(
+            safe_browsing::FileTypePolicies::GetInstance()->GetFileDangerLevel(
                 base::FilePath(FILE_PATH_LITERAL("foo.txt"))));
 
   for (const auto& test_case : kTestCases) {
@@ -1158,57 +1238,157 @@
       download_test_case.expected_intermediate = EXPECT_CRDOWNLOAD;
     }
 
-    std::unique_ptr<content::MockDownloadItem> item(
-        CreateActiveDownloadItem(1, download_test_case));
+    std::unique_ptr<content::MockDownloadItem> item =
+        CreateActiveDownloadItem(1, download_test_case);
     EXPECT_CALL(*item, GetTransitionType())
         .WillRepeatedly(Return(test_case.page_transition));
     RunTestCase(download_test_case, base::FilePath(), item.get());
   }
 }
 
-#if !defined(OS_ANDROID)
 // These test cases are run with "Prompt for download" user preference set to
 // true.
-TEST_F(DownloadTargetDeterminerTest, PromptAlways) {
-  const DownloadTestCase kPromptingTestCases[] = {
-      {// 0: Safe Automatic - Should prompt because of "Prompt for download"
-       //    preference setting.
-       AUTOMATIC, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
-       DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.txt",
-       "text/plain", FILE_PATH_LITERAL(""),
+TEST_F(DownloadTargetDeterminerTest, PromptAlways_SafeAutomatic) {
+  const DownloadTestCase kSafeAutomatic = {
+      // 0: Safe Automatic - Should prompt because of "Prompt for download"
+      //    preference setting.
+      AUTOMATIC,
+      content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
+      DownloadFileType::NOT_DANGEROUS,
+      "http://example.com/automatic.txt",
+      "text/plain",
+      FILE_PATH_LITERAL(""),
 
-       FILE_PATH_LITERAL("foo.txt"), DownloadItem::TARGET_DISPOSITION_PROMPT,
+      FILE_PATH_LITERAL("automatic.txt"),
+      DownloadItem::TARGET_DISPOSITION_PROMPT,
 
-       EXPECT_CRDOWNLOAD},
+      EXPECT_CRDOWNLOAD};
 
-      {// 1: Safe Forced - Shouldn't prompt.
-       FORCED, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
-       DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.txt",
-       "text/plain", FILE_PATH_LITERAL("foo.txt"),
+  SetPromptForDownload(true);
+  EXPECT_CALL(*delegate(),
+              RequestConfirmation(
+                  _, GetPathInDownloadDir(FILE_PATH_LITERAL("automatic.txt")),
+                  DownloadConfirmationReason::PREFERENCE, _));
+  RunTestCasesWithActiveItem(&kSafeAutomatic, 1);
+}
 
-       FILE_PATH_LITERAL("foo.txt"), DownloadItem::TARGET_DISPOSITION_OVERWRITE,
+TEST_F(DownloadTargetDeterminerTest, PromptAlways_SafeSaveAs) {
+  const DownloadTestCase kSafeSaveAs = {
+      // 1: Safe Save As - Should prompt because of "Save as" invocation.
+      SAVE_AS,
+      content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
+      DownloadFileType::NOT_DANGEROUS,
+      "http://example.com/save-as.txt",
+      "text/plain",
+      FILE_PATH_LITERAL(""),
 
-       EXPECT_LOCAL_PATH},
+      FILE_PATH_LITERAL("save-as.txt"),
+      DownloadItem::TARGET_DISPOSITION_PROMPT,
 
-      {// 2: Automatic - The filename extension is marked as one that we will
-       //    open automatically. Shouldn't prompt.
-       AUTOMATIC, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
-       DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.dummy", "",
-       FILE_PATH_LITERAL(""),
+      EXPECT_CRDOWNLOAD};
 
-       FILE_PATH_LITERAL("foo.dummy"),
-       DownloadItem::TARGET_DISPOSITION_OVERWRITE,
+  SetPromptForDownload(true);
+  EXPECT_CALL(*delegate(),
+              RequestConfirmation(
+                  _, GetPathInDownloadDir(FILE_PATH_LITERAL("save-as.txt")),
+                  DownloadConfirmationReason::SAVE_AS, _));
+  RunTestCasesWithActiveItem(&kSafeSaveAs, 1);
+}
 
-       EXPECT_CRDOWNLOAD},
-  };
+TEST_F(DownloadTargetDeterminerTest, PromptAlways_SafeForced) {
+  const DownloadTestCase kSafeForced = {
+      // 2: Safe Forced - Shouldn't prompt.
+      FORCED,
+      content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
+      DownloadFileType::NOT_DANGEROUS,
+      "http://example.com/foo.txt",
+      "text/plain",
+      FILE_PATH_LITERAL("foo.txt"),
 
+      FILE_PATH_LITERAL("foo.txt"),
+      DownloadItem::TARGET_DISPOSITION_OVERWRITE,
+
+      EXPECT_LOCAL_PATH};
+
+  SetPromptForDownload(true);
+  RunTestCasesWithActiveItem(&kSafeForced, 1);
+}
+
+TEST_F(DownloadTargetDeterminerTest, PromptAlways_AutoOpen) {
+  const DownloadTestCase kAutoOpen = {
+      // 3: Automatic - The filename extension is marked as one that we will
+      //    open automatically. Shouldn't prompt.
+      AUTOMATIC,
+      content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
+      DownloadFileType::NOT_DANGEROUS,
+      "http://example.com/foo.dummy",
+      "",
+      FILE_PATH_LITERAL(""),
+
+      FILE_PATH_LITERAL("foo.dummy"),
+      DownloadItem::TARGET_DISPOSITION_OVERWRITE,
+
+      EXPECT_CRDOWNLOAD};
   SetPromptForDownload(true);
   EnableAutoOpenBasedOnExtension(
       base::FilePath(FILE_PATH_LITERAL("dummy.dummy")));
-  RunTestCasesWithActiveItem(kPromptingTestCases,
-                             arraysize(kPromptingTestCases));
+  RunTestCasesWithActiveItem(&kAutoOpen, 1);
 }
-#endif  // !defined(OS_ANDROID)
+
+// If an embedder responds to a RequestConfirmation with a new path and a
+// CONTINUE_WITHOUT_CONFIRMATION, then we shouldn't consider the file as safe.
+TEST_F(DownloadTargetDeterminerTest, ContinueWithoutConfirmation_SaveAs) {
+  const DownloadTestCase kTestCase = {
+      SAVE_AS,
+      content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE,
+      DownloadFileType::ALLOW_ON_USER_GESTURE,
+      "http://example.com/save-as.kindabad",
+      "text/plain",
+      FILE_PATH_LITERAL(""),
+
+      FILE_PATH_LITERAL("foo.kindabad"),
+      DownloadItem::TARGET_DISPOSITION_OVERWRITE,
+
+      EXPECT_UNCONFIRMED};
+
+  EXPECT_CALL(
+      *delegate(),
+      RequestConfirmation(
+          _, GetPathInDownloadDir(FILE_PATH_LITERAL("save-as.kindabad")),
+          DownloadConfirmationReason::SAVE_AS, _))
+      .WillOnce(WithArg<3>(ScheduleCallback2(
+          DownloadConfirmationResult::CONTINUE_WITHOUT_CONFIRMATION,
+          GetPathInDownloadDir(FILE_PATH_LITERAL("foo.kindabad")))));
+  RunTestCasesWithActiveItem(&kTestCase, 1);
+}
+
+// Same as ContinueWithoutConfirmation_SaveAs, but the embedder response
+// indicates that the user confirmed the path. Hence the danger level of the
+// download and the disposition should be updated accordingly.
+TEST_F(DownloadTargetDeterminerTest, ContinueWithConfirmation_SaveAs) {
+  const DownloadTestCase kTestCase = {
+      SAVE_AS,
+      content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
+      DownloadFileType::NOT_DANGEROUS,
+      "http://example.com/save-as.kindabad",
+      "text/plain",
+      FILE_PATH_LITERAL(""),
+
+      FILE_PATH_LITERAL("foo.kindabad"),
+      DownloadItem::TARGET_DISPOSITION_PROMPT,
+
+      EXPECT_CRDOWNLOAD};
+
+  EXPECT_CALL(
+      *delegate(),
+      RequestConfirmation(
+          _, GetPathInDownloadDir(FILE_PATH_LITERAL("save-as.kindabad")),
+          DownloadConfirmationReason::SAVE_AS, _))
+      .WillOnce(WithArg<3>(ScheduleCallback2(
+          DownloadConfirmationResult::CONFIRMED,
+          GetPathInDownloadDir(FILE_PATH_LITERAL("foo.kindabad")))));
+  RunTestCasesWithActiveItem(&kTestCase, 1);
+}
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 // These test cases are run with "Prompt for download" user preference set to
@@ -1216,40 +1396,29 @@
 // Android doesn't support extensions.
 TEST_F(DownloadTargetDeterminerTest, PromptAlways_Extension) {
   const DownloadTestCase kPromptingTestCases[] = {
-    {// 0: Automatic Browser Extension download. - Shouldn't prompt for browser
-     //    extension downloads even if "Prompt for download" preference is set.
-     AUTOMATIC, content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE,
-     DownloadFileType::ALLOW_ON_USER_GESTURE, "http://example.com/foo.crx",
-     extensions::Extension::kMimeType, FILE_PATH_LITERAL(""),
+      {// 0: Automatic Browser Extension download. - Shouldn't prompt for
+       //    browser extension downloads even if "Prompt for download"
+       //    preference is set.
+       AUTOMATIC, content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE,
+       DownloadFileType::ALLOW_ON_USER_GESTURE,
+       "http://example.com/foo.kindabad", extensions::Extension::kMimeType,
+       FILE_PATH_LITERAL(""),
 
-     FILE_PATH_LITERAL("foo.crx"), DownloadItem::TARGET_DISPOSITION_OVERWRITE,
+       FILE_PATH_LITERAL("foo.kindabad"),
+       DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-     EXPECT_UNCONFIRMED},
+       EXPECT_UNCONFIRMED},
 
-#if defined(OS_WIN)
-    {// 1: Automatic User Script - Shouldn't prompt for user script downloads
-     //    even if "Prompt for download" preference is set. ".js" files are
-     //    considered dangerous on Windows.
-     AUTOMATIC, content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE,
-     DownloadFileType::ALLOW_ON_USER_GESTURE, "http://example.com/foo.user.js", "",
-     FILE_PATH_LITERAL(""),
+      {// 1: Automatic User Script - Shouldn't prompt for user script downloads
+       //    even if "Prompt for download" preference is set.
+       AUTOMATIC, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
+       DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.user.js", "",
+       FILE_PATH_LITERAL(""),
 
-     FILE_PATH_LITERAL("foo.user.js"),
-     DownloadItem::TARGET_DISPOSITION_OVERWRITE,
+       FILE_PATH_LITERAL("foo.user.js"),
+       DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-     EXPECT_UNCONFIRMED},
-#else
-    {// 1: Automatic User Script - Shouldn't prompt for user script downloads
-     //    even if "Prompt for download" preference is set.
-     AUTOMATIC, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
-     DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.user.js", "",
-     FILE_PATH_LITERAL(""),
-
-     FILE_PATH_LITERAL("foo.user.js"),
-     DownloadItem::TARGET_DISPOSITION_OVERWRITE,
-
-     EXPECT_CRDOWNLOAD},
-#endif
+       EXPECT_CRDOWNLOAD},
   };
 
   SetPromptForDownload(true);
@@ -1291,47 +1460,45 @@
 // filenames.
 TEST_F(DownloadTargetDeterminerTest, NotifyExtensionsSafe) {
   const DownloadTestCase kNotifyExtensionsTestCases[] = {
-    {// 0: Automatic Safe
-     AUTOMATIC, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
-     DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.txt",
-     "text/plain", FILE_PATH_LITERAL(""),
+      {// 0: Automatic Safe
+       AUTOMATIC, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
+       DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.txt",
+       "text/plain", FILE_PATH_LITERAL(""),
 
-     FILE_PATH_LITERAL("overridden/foo.txt"),
-     DownloadItem::TARGET_DISPOSITION_OVERWRITE,
+       FILE_PATH_LITERAL("overridden/foo.txt"),
+       DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-     EXPECT_CRDOWNLOAD},
+       EXPECT_CRDOWNLOAD},
 
-#if !defined(OS_ANDROID)
-    {// 1: Save_As Safe
-     SAVE_AS, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
-     DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.txt",
-     "text/plain", FILE_PATH_LITERAL(""),
+      {// 1: Save_As Safe
+       SAVE_AS, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
+       DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.txt",
+       "text/plain", FILE_PATH_LITERAL(""),
 
-     FILE_PATH_LITERAL("overridden/foo.txt"),
-     DownloadItem::TARGET_DISPOSITION_PROMPT,
+       FILE_PATH_LITERAL("overridden/foo.txt"),
+       DownloadItem::TARGET_DISPOSITION_PROMPT,
 
-     EXPECT_CRDOWNLOAD},
-#endif  // !defined(OS_ANDROID)
+       EXPECT_CRDOWNLOAD},
 
-    {// 2: Automatic Dangerous
-     AUTOMATIC, content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE,
-     DownloadFileType::ALLOW_ON_USER_GESTURE, "http://example.com/foo.crx", "",
-     FILE_PATH_LITERAL(""),
+      {// 2: Automatic Dangerous
+       AUTOMATIC, content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE,
+       DownloadFileType::ALLOW_ON_USER_GESTURE,
+       "http://example.com/foo.kindabad", "", FILE_PATH_LITERAL(""),
 
-     FILE_PATH_LITERAL("overridden/foo.crx"),
-     DownloadItem::TARGET_DISPOSITION_OVERWRITE,
+       FILE_PATH_LITERAL("overridden/foo.kindabad"),
+       DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-     EXPECT_UNCONFIRMED},
+       EXPECT_UNCONFIRMED},
 
-    {// 3: Forced Safe
-     FORCED, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
-     DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.txt", "",
-     FILE_PATH_LITERAL("forced-foo.txt"),
+      {// 3: Forced Safe
+       FORCED, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
+       DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.txt", "",
+       FILE_PATH_LITERAL("forced-foo.txt"),
 
-     FILE_PATH_LITERAL("forced-foo.txt"),
-     DownloadItem::TARGET_DISPOSITION_OVERWRITE,
+       FILE_PATH_LITERAL("forced-foo.txt"),
+       DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-     EXPECT_LOCAL_PATH},
+       EXPECT_LOCAL_PATH},
   };
 
   ON_CALL(*delegate(), NotifyExtensions(_, _, _))
@@ -1347,11 +1514,11 @@
       AUTOMATIC,
       content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE,
       DownloadFileType::ALLOW_ON_USER_GESTURE,
-      "http://example.com/foo.crx.remove",
+      "http://example.com/foo.kindabad.remove",
       "text/plain",
       FILE_PATH_LITERAL(""),
 
-      FILE_PATH_LITERAL("overridden/foo.crx"),
+      FILE_PATH_LITERAL("overridden/foo.kindabad"),
       DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
       EXPECT_UNCONFIRMED};
@@ -1360,11 +1527,11 @@
       AUTOMATIC,
       content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT,
       DownloadFileType::ALLOW_ON_USER_GESTURE,
-      "http://example.com/foo.crx.remove",
+      "http://example.com/foo.kindabad.remove",
       "text/plain",
       FILE_PATH_LITERAL(""),
 
-      FILE_PATH_LITERAL("overridden/foo.crx"),
+      FILE_PATH_LITERAL("overridden/foo.kindabad"),
       DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
       EXPECT_UNCONFIRMED};
@@ -1396,8 +1563,8 @@
       EXPECT_CRDOWNLOAD};
 
   const DownloadTestCase& test_case = kNotifyExtensionsTestCase;
-  std::unique_ptr<content::MockDownloadItem> item(
-      CreateActiveDownloadItem(0, test_case));
+  std::unique_ptr<content::MockDownloadItem> item =
+      CreateActiveDownloadItem(0, test_case);
   base::FilePath overridden_path(FILE_PATH_LITERAL("overridden/foo.txt"));
   base::FilePath full_overridden_path =
       GetPathInDownloadDir(overridden_path.value());
@@ -1407,10 +1574,11 @@
       .WillOnce(WithArg<2>(
           ScheduleCallback2(overridden_path,
                             DownloadPathReservationTracker::OVERWRITE)));
-  EXPECT_CALL(*delegate(), ReserveVirtualPath(
-      _, full_overridden_path, true, DownloadPathReservationTracker::OVERWRITE,
-      _)).WillOnce(WithArg<4>(
-          ScheduleCallback2(full_overridden_path, true)));
+  EXPECT_CALL(*delegate(),
+              ReserveVirtualPath(_, full_overridden_path, true,
+                                 DownloadPathReservationTracker::OVERWRITE, _))
+      .WillOnce(WithArg<4>(ScheduleCallback2(PathValidationResult::SUCCESS,
+                                             full_overridden_path)));
 
   RunTestCase(test_case, base::FilePath(), item.get());
 
@@ -1419,19 +1587,19 @@
       .WillOnce(WithArg<2>(
           ScheduleCallback2(overridden_path,
                             DownloadPathReservationTracker::PROMPT)));
-  EXPECT_CALL(*delegate(), ReserveVirtualPath(
-      _, full_overridden_path, true, DownloadPathReservationTracker::PROMPT, _))
-      .WillOnce(WithArg<4>(
-          ScheduleCallback2(full_overridden_path, true)));
+  EXPECT_CALL(*delegate(),
+              ReserveVirtualPath(_, full_overridden_path, true,
+                                 DownloadPathReservationTracker::PROMPT, _))
+      .WillOnce(WithArg<4>(ScheduleCallback2(PathValidationResult::SUCCESS,
+                                             full_overridden_path)));
   RunTestCase(test_case, base::FilePath(), item.get());
 }
 
-#if !defined(OS_ANDROID)
 // Test that relative paths returned by extensions are always relative to the
 // default downloads path.
 TEST_F(DownloadTargetDeterminerTest, NotifyExtensionsDefaultPath) {
   const DownloadTestCase kNotifyExtensionsTestCase = {
-      SAVE_AS,
+      AUTOMATIC,
       content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
       DownloadFileType::NOT_DANGEROUS,
       "http://example.com/foo.txt",
@@ -1439,13 +1607,13 @@
       FILE_PATH_LITERAL(""),
 
       FILE_PATH_LITERAL("overridden/foo.txt"),
-      DownloadItem::TARGET_DISPOSITION_PROMPT,
+      DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
       EXPECT_CRDOWNLOAD};
 
   const DownloadTestCase& test_case = kNotifyExtensionsTestCase;
-  std::unique_ptr<content::MockDownloadItem> item(
-      CreateActiveDownloadItem(0, test_case));
+  std::unique_ptr<content::MockDownloadItem> item =
+      CreateActiveDownloadItem(0, test_case);
   base::FilePath overridden_path(FILE_PATH_LITERAL("overridden/foo.txt"));
   base::FilePath full_overridden_path =
       GetPathInDownloadDir(overridden_path.value());
@@ -1457,13 +1625,8 @@
       .WillOnce(WithArg<2>(
           ScheduleCallback2(overridden_path,
                             DownloadPathReservationTracker::UNIQUIFY)));
-  EXPECT_CALL(*delegate(),
-              PromptUserForDownloadPath(_, full_overridden_path, _))
-      .WillOnce(WithArg<2>(
-          ScheduleCallback(full_overridden_path)));
   RunTestCase(test_case, base::FilePath(), item.get());
 }
-#endif  // !defined(OS_ANDROID)
 
 TEST_F(DownloadTargetDeterminerTest, InitialVirtualPathUnsafe) {
   const base::FilePath::CharType* kInitialPath =
@@ -1486,8 +1649,8 @@
       EXPECT_CRDOWNLOAD};
 
   const DownloadTestCase& test_case = kInitialPathTestCase;
-  std::unique_ptr<content::MockDownloadItem> item(
-      CreateActiveDownloadItem(1, test_case));
+  std::unique_ptr<content::MockDownloadItem> item =
+      CreateActiveDownloadItem(1, test_case);
   EXPECT_CALL(*item, GetLastReason())
       .WillRepeatedly(Return(
           content::DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED));
@@ -1511,8 +1674,8 @@
       {// 0: Automatic Safe: Initial path is ignored since the user has not been
        // prompted before.
        AUTOMATIC, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
-       DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.txt", "text/plain",
-       FILE_PATH_LITERAL(""),
+       DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.txt",
+       "text/plain", FILE_PATH_LITERAL(""),
 
        FILE_PATH_LITERAL("foo.txt"), DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
@@ -1520,8 +1683,8 @@
 
       {// 1: Save_As Safe: Initial path used.
        SAVE_AS, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
-       DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.txt", "text/plain",
-       FILE_PATH_LITERAL(""),
+       DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.txt",
+       "text/plain", FILE_PATH_LITERAL(""),
 
        kInitialPath, DownloadItem::TARGET_DISPOSITION_PROMPT,
 
@@ -1530,10 +1693,11 @@
       {// 2: Automatic Dangerous: Initial path is ignored since the user hasn't
        // been prompted before.
        AUTOMATIC, content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE,
-       DownloadFileType::ALLOW_ON_USER_GESTURE, "http://example.com/foo.crx", "",
-       FILE_PATH_LITERAL(""),
+       DownloadFileType::ALLOW_ON_USER_GESTURE,
+       "http://example.com/foo.kindabad", "", FILE_PATH_LITERAL(""),
 
-       FILE_PATH_LITERAL("foo.crx"), DownloadItem::TARGET_DISPOSITION_OVERWRITE,
+       FILE_PATH_LITERAL("foo.kindabad"),
+       DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
        EXPECT_UNCONFIRMED},
 
@@ -1548,16 +1712,16 @@
        EXPECT_LOCAL_PATH},
   };
 
-  // The test assumes that .crx files have a danger level of
+  // The test assumes that .kindabad files have a danger level of
   // ALLOW_ON_USER_GESTURE.
   ASSERT_EQ(DownloadFileType::ALLOW_ON_USER_GESTURE,
-            Policies()->GetFileDangerLevel(
-                base::FilePath(FILE_PATH_LITERAL("foo.crx"))));
+            safe_browsing::FileTypePolicies::GetInstance()->GetFileDangerLevel(
+                base::FilePath(FILE_PATH_LITERAL("foo.kindabad"))));
   for (size_t i = 0; i < arraysize(kResumedTestCases); ++i) {
     SCOPED_TRACE(testing::Message() << "Running test case " << i);
     const DownloadTestCase& test_case = kResumedTestCases[i];
-    std::unique_ptr<content::MockDownloadItem> item(
-        CreateActiveDownloadItem(i, test_case));
+    std::unique_ptr<content::MockDownloadItem> item =
+        CreateActiveDownloadItem(i, test_case);
     base::FilePath expected_path =
         GetPathInDownloadDir(test_case.expected_local_path);
     ON_CALL(*item.get(), GetLastReason())
@@ -1568,15 +1732,8 @@
     // type == AUTOMATIC.
     EXPECT_CALL(*delegate(), NotifyExtensions(_, _, _))
         .Times(test_case.test_type == AUTOMATIC ? 1 : 0);
-    // When resuming an AUTOMATIC download with non-empty initial path, the file
-    // name conflict action should be UNIQUIFY.
-    DownloadPathReservationTracker::FilenameConflictAction action =
-        test_case.test_type == AUTOMATIC ?
-            DownloadPathReservationTracker::UNIQUIFY :
-            DownloadPathReservationTracker::OVERWRITE;
-    EXPECT_CALL(*delegate(), ReserveVirtualPath(
-        _, expected_path, false, action, _));
-    EXPECT_CALL(*delegate(), PromptUserForDownloadPath(_, expected_path, _))
+    EXPECT_CALL(*delegate(), ReserveVirtualPath(_, expected_path, false, _, _));
+    EXPECT_CALL(*delegate(), RequestConfirmation(_, expected_path, _, _))
         .Times(0);
     EXPECT_CALL(*delegate(), DetermineLocalPath(_, expected_path, _));
     EXPECT_CALL(*delegate(), CheckDownloadUrl(_, expected_path, _));
@@ -1606,15 +1763,14 @@
   const DownloadTestCase& test_case = kResumedForcedDownload;
   base::FilePath expected_path =
       GetPathInDownloadDir(test_case.expected_local_path);
-  std::unique_ptr<content::MockDownloadItem> item(
-      CreateActiveDownloadItem(0, test_case));
+  std::unique_ptr<content::MockDownloadItem> item =
+      CreateActiveDownloadItem(0, test_case);
   ON_CALL(*item.get(), GetLastReason())
       .WillByDefault(Return(content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE));
   EXPECT_CALL(*delegate(), NotifyExtensions(_, _, _))
       .Times(test_case.test_type == AUTOMATIC ? 1 : 0);
   EXPECT_CALL(*delegate(), ReserveVirtualPath(_, expected_path, false, _, _));
-  EXPECT_CALL(*delegate(), PromptUserForDownloadPath(_, _, _))
-      .Times(0);
+  EXPECT_CALL(*delegate(), RequestConfirmation(_, _, _, _)).Times(0);
   EXPECT_CALL(*delegate(), DetermineLocalPath(_, expected_path, _));
   EXPECT_CALL(*delegate(), CheckDownloadUrl(_, expected_path, _));
   RunTestCase(test_case, GetPathInDownloadDir(kInitialPath), item.get());
@@ -1637,12 +1793,7 @@
        DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.txt",
        "text/plain", FILE_PATH_LITERAL(""),
 
-       FILE_PATH_LITERAL("foo.txt"),
-#if defined(OS_ANDROID)
-       DownloadItem::TARGET_DISPOSITION_OVERWRITE,
-#else
-       DownloadItem::TARGET_DISPOSITION_PROMPT,
-#endif
+       FILE_PATH_LITERAL("foo.txt"), DownloadItem::TARGET_DISPOSITION_PROMPT,
        EXPECT_CRDOWNLOAD},
 
       {// 1: Save_As Safe
@@ -1656,53 +1807,43 @@
 
       {
           // 2: Automatic Dangerous
-          AUTOMATIC,
-#if defined(OS_ANDROID)
-          // If we don't prompt user, the file will be treated as dangerous.
-          content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE,
-          DownloadFileType::ALLOW_ON_USER_GESTURE,
-#else
-          content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
-          DownloadFileType::NOT_DANGEROUS,
-#endif
-          "http://example.com/foo.crx", "", FILE_PATH_LITERAL(""),
+          AUTOMATIC, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
+          DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.kindabad",
+          "", FILE_PATH_LITERAL(""),
 
-          FILE_PATH_LITERAL("foo.crx"),
-#if defined(OS_ANDROID)
-          DownloadItem::TARGET_DISPOSITION_OVERWRITE,
-          // Dangerous download will have an unconfirmed intermediate file name.
-          EXPECT_UNCONFIRMED,
-#else
+          FILE_PATH_LITERAL("foo.kindabad"),
           DownloadItem::TARGET_DISPOSITION_PROMPT, EXPECT_CRDOWNLOAD,
-#endif
+      },
+
+      {
+          // 3: Automatic Dangerous
+          AUTOMATIC, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
+          DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.bad", "",
+          FILE_PATH_LITERAL(""),
+
+          FILE_PATH_LITERAL("foo.bad"), DownloadItem::TARGET_DISPOSITION_PROMPT,
+          EXPECT_CRDOWNLOAD,
       },
   };
 
-  // The test assumes that .xml files have a danger level of
-  // ALLOW_ON_USER_GESTURE.
   ASSERT_EQ(DownloadFileType::ALLOW_ON_USER_GESTURE,
-            Policies()->GetFileDangerLevel(
-                base::FilePath(FILE_PATH_LITERAL("foo.crx"))));
+            safe_browsing::FileTypePolicies::GetInstance()->GetFileDangerLevel(
+                base::FilePath(FILE_PATH_LITERAL("foo.kindabad"))));
   for (size_t i = 0; i < arraysize(kResumedTestCases); ++i) {
     SCOPED_TRACE(testing::Message() << "Running test case " << i);
     download_prefs()->SetSaveFilePath(test_download_dir());
     const DownloadTestCase& test_case = kResumedTestCases[i];
     base::FilePath expected_path =
         GetPathInDownloadDir(test_case.expected_local_path);
-    std::unique_ptr<content::MockDownloadItem> item(
-        CreateActiveDownloadItem(i, test_case));
+    std::unique_ptr<content::MockDownloadItem> item =
+        CreateActiveDownloadItem(i, test_case);
     ON_CALL(*item.get(), GetLastReason())
         .WillByDefault(Return(
             content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE));
     EXPECT_CALL(*delegate(), NotifyExtensions(_, _, _))
         .Times(test_case.test_type == AUTOMATIC ? 1 : 0);
     EXPECT_CALL(*delegate(), ReserveVirtualPath(_, expected_path, false, _, _));
-#if defined(OS_ANDROID)
-    EXPECT_CALL(*delegate(), PromptUserForDownloadPath(_, expected_path, _))
-        .Times(0);
-#else
-    EXPECT_CALL(*delegate(), PromptUserForDownloadPath(_, expected_path, _));
-#endif
+    EXPECT_CALL(*delegate(), RequestConfirmation(_, expected_path, _, _));
     EXPECT_CALL(*delegate(), DetermineLocalPath(_, expected_path, _));
     EXPECT_CALL(*delegate(), CheckDownloadUrl(_, expected_path, _));
     RunTestCase(test_case, GetPathInDownloadDir(kInitialPath), item.get());
@@ -1753,10 +1894,10 @@
 
       {{// 2: Automatic Dangerous
         AUTOMATIC, content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE,
-        DownloadFileType::ALLOW_ON_USER_GESTURE, "http://example.com/foo.crx",
-        "", FILE_PATH_LITERAL(""),
+        DownloadFileType::ALLOW_ON_USER_GESTURE,
+        "http://example.com/foo.kindabad", "", FILE_PATH_LITERAL(""),
 
-        FILE_PATH_LITERAL("foo.crx"),
+        FILE_PATH_LITERAL("foo.kindabad"),
         DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
         EXPECT_UNCONFIRMED},
@@ -1765,10 +1906,10 @@
 
       {{// 3: Automatic Dangerous
         AUTOMATIC, content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE,
-        DownloadFileType::ALLOW_ON_USER_GESTURE, "http://example.com/foo.crx",
-        "", FILE_PATH_LITERAL(""),
+        DownloadFileType::ALLOW_ON_USER_GESTURE,
+        "http://example.com/foo.kindabad", "", FILE_PATH_LITERAL(""),
 
-        FILE_PATH_LITERAL("foo.crx"),
+        FILE_PATH_LITERAL("foo.kindabad"),
         DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
         EXPECT_UNCONFIRMED},
@@ -1792,17 +1933,17 @@
        FILE_PATH_LITERAL("forced-foo.txt")},
   };
 
-  // The test assumes that .crx files have a danger level of
+  // The test assumes that .kindabad files have a danger level of
   // ALLOW_ON_USER_GESTURE.
   ASSERT_EQ(DownloadFileType::ALLOW_ON_USER_GESTURE,
-            Policies()->GetFileDangerLevel(
-                base::FilePath(FILE_PATH_LITERAL("foo.crx"))));
+            safe_browsing::FileTypePolicies::GetInstance()->GetFileDangerLevel(
+                base::FilePath(FILE_PATH_LITERAL("foo.kindabad"))));
 
   for (size_t i = 0; i < arraysize(kIntermediateNameTestCases); ++i) {
     SCOPED_TRACE(testing::Message() << "Running test case " << i);
     const IntermediateNameTestCase& test_case = kIntermediateNameTestCases[i];
-    std::unique_ptr<content::MockDownloadItem> item(
-        CreateActiveDownloadItem(i, test_case.general));
+    std::unique_ptr<content::MockDownloadItem> item =
+        CreateActiveDownloadItem(i, test_case.general);
 
     ON_CALL(*item.get(), GetLastReason())
         .WillByDefault(Return(
@@ -1898,7 +2039,6 @@
 
         EXPECT_CRDOWNLOAD},
        ""},
-
   };
 
   ON_CALL(*delegate(), GetFileMimeType(
@@ -1909,8 +2049,8 @@
   for (size_t i = 0; i < arraysize(kMIMETypeTestCases); ++i) {
     SCOPED_TRACE(testing::Message() << "Running test case " << i);
     const MIMETypeTestCase& test_case = kMIMETypeTestCases[i];
-    std::unique_ptr<content::MockDownloadItem> item(
-        CreateActiveDownloadItem(i, test_case.general));
+    std::unique_ptr<content::MockDownloadItem> item =
+        CreateActiveDownloadItem(i, test_case.general);
     std::unique_ptr<DownloadTargetInfo> target_info =
         RunDownloadTargetDeterminer(GetPathInDownloadDir(kInitialPath),
                                     item.get());
@@ -1949,10 +2089,9 @@
       .WillByDefault(Return(content::DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED));
   EXPECT_CALL(*delegate(), NotifyExtensions(_, _, _));
   EXPECT_CALL(*delegate(), ReserveVirtualPath(_, expected_path, false, _, _));
-  EXPECT_CALL(*delegate(), PromptUserForDownloadPath(_, expected_path, _))
-      .Times(0);
   EXPECT_CALL(*delegate(), DetermineLocalPath(_, expected_path, _));
   EXPECT_CALL(*delegate(), CheckDownloadUrl(_, expected_path, _)).Times(0);
+  EXPECT_CALL(*delegate(), RequestConfirmation(_, _, _, _)).Times(0);
   RunTestCase(test_case, GetPathInDownloadDir(kInitialPath), item.get());
 }
 
@@ -2106,8 +2245,8 @@
       GetPathInDownloadDir(FILE_PATH_LITERAL("foo.fakeext")), _))
       .WillByDefault(WithArg<1>(
           ScheduleCallback(kTestMIMEType)));
-  std::unique_ptr<content::MockDownloadItem> item(
-      CreateActiveDownloadItem(1, kSecureHandlingTestCase));
+  std::unique_ptr<content::MockDownloadItem> item =
+      CreateActiveDownloadItem(1, kSecureHandlingTestCase);
   std::unique_ptr<DownloadTargetInfo> target_info = RunDownloadTargetDeterminer(
       GetPathInDownloadDir(kInitialPath), item.get());
   EXPECT_FALSE(target_info->is_filetype_handled_safely);
@@ -2175,8 +2314,8 @@
       GetPathInDownloadDir(FILE_PATH_LITERAL("foo.fakeext")), _))
       .WillByDefault(WithArg<1>(
           ScheduleCallback(kTestMIMEType)));
-  std::unique_ptr<content::MockDownloadItem> item(
-      CreateActiveDownloadItem(1, kSecureHandlingTestCase));
+  std::unique_ptr<content::MockDownloadItem> item =
+      CreateActiveDownloadItem(1, kSecureHandlingTestCase);
   std::unique_ptr<DownloadTargetInfo> target_info = RunDownloadTargetDeterminer(
       GetPathInDownloadDir(kInitialPath), item.get());
   EXPECT_FALSE(target_info->is_filetype_handled_safely);
diff --git a/chrome/browser/download/download_target_info.cc b/chrome/browser/download/download_target_info.cc
new file mode 100644
index 0000000..b1bb713
--- /dev/null
+++ b/chrome/browser/download/download_target_info.cc
@@ -0,0 +1,16 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/download/download_target_info.h"
+
+#include "chrome/common/safe_browsing/file_type_policies.h"
+
+DownloadTargetInfo::DownloadTargetInfo()
+    : target_disposition(content::DownloadItem::TARGET_DISPOSITION_OVERWRITE),
+      danger_type(content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS),
+      danger_level(safe_browsing::DownloadFileType::NOT_DANGEROUS),
+      is_filetype_handled_safely(false),
+      result(content::DOWNLOAD_INTERRUPT_REASON_NONE) {}
+
+DownloadTargetInfo::~DownloadTargetInfo() {}
diff --git a/chrome/browser/download/download_target_info.h b/chrome/browser/download/download_target_info.h
index f82d6b0..6e3a492 100644
--- a/chrome/browser/download/download_target_info.h
+++ b/chrome/browser/download/download_target_info.h
@@ -5,9 +5,12 @@
 #ifndef CHROME_BROWSER_DOWNLOAD_DOWNLOAD_TARGET_INFO_H_
 #define CHROME_BROWSER_DOWNLOAD_DOWNLOAD_TARGET_INFO_H_
 
+#include <string>
+
 #include "base/files/file_path.h"
 #include "chrome/common/safe_browsing/download_file_types.pb.h"
 #include "content/public/browser/download_danger_type.h"
+#include "content/public/browser/download_interrupt_reasons.h"
 #include "content/public/browser/download_item.h"
 
 struct DownloadTargetInfo {
@@ -22,6 +25,8 @@
   // Disposition. This will be TARGET_DISPOSITION_PROMPT if the user was
   // prompted during the process of determining the download target. Otherwise
   // it will be TARGET_DISPOSITION_OVERWRITE.
+  // TODO(asanka): This should be has_user_confirmation or somesuch that
+  // indicates that the user has seen and confirmed the download path.
   content::DownloadItem::TargetDisposition target_disposition;
 
   // Danger type of the download.
@@ -69,6 +74,9 @@
   // opens should be handled. The file is considered to be handled safely if the
   // filetype is supported by the renderer or a sandboxed plugin.
   bool is_filetype_handled_safely;
+
+  // Result of the download target determination.
+  content::DownloadInterruptReason result;
 };
 
 #endif  // CHROME_BROWSER_DOWNLOAD_DOWNLOAD_TARGET_INFO_H_
diff --git a/chrome/browser/download/download_test_file_activity_observer.cc b/chrome/browser/download/download_test_file_activity_observer.cc
index 5039abf..3537054 100644
--- a/chrome/browser/download/download_test_file_activity_observer.cc
+++ b/chrome/browser/download/download_test_file_activity_observer.cc
@@ -52,15 +52,17 @@
   }
 
  protected:
-  void PromptUserForDownloadPath(
-      content::DownloadItem* item,
-      const base::FilePath& suggested_path,
-      const FileSelectedCallback& callback) override {
+  void RequestConfirmation(content::DownloadItem* item,
+                           const base::FilePath& suggested_path,
+                           DownloadConfirmationReason reason,
+                           const ConfirmationCallback& callback) override {
     file_chooser_displayed_ = true;
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE,
-        base::Bind(callback, (file_chooser_enabled_ ? suggested_path
-                                                    : base::FilePath())));
+        FROM_HERE, base::Bind(callback,
+                              (file_chooser_enabled_
+                                   ? DownloadConfirmationResult::CONFIRMED
+                                   : DownloadConfirmationResult::CANCELED),
+                              suggested_path));
   }
 
   void OpenDownload(content::DownloadItem* item) override {}
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 0421950..11cb19d 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -1860,12 +1860,12 @@
 
 //  Site per process mode
 
-const char kSitePerProcessName[] = "Out of process iframes";
+const char kSitePerProcessName[] = "Strict site isolation";
 
 const char kSitePerProcessDescription[] =
-    "Highly experimental support for rendering cross-site iframes in "
-    "separate processes. In this mode, documents will share a renderer "
-    "process only if they are from the same web site.";
+    "Highly experimental security mode that ensures each renderer process "
+    "contains pages from at most one site. In this mode, out-of-process "
+    "iframes will be used whenever an iframe is cross-site.";
 
 //  Top document isolation mode
 
diff --git a/chrome/browser/notifications/notification_platform_bridge_mac.mm b/chrome/browser/notifications/notification_platform_bridge_mac.mm
index 16220a75..291d8bc 100644
--- a/chrome/browser/notifications/notification_platform_bridge_mac.mm
+++ b/chrome/browser/notifications/notification_platform_bridge_mac.mm
@@ -9,6 +9,7 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/callback.h"
+#include "base/i18n/number_formatting.h"
 #include "base/mac/bundle_locations.h"
 #include "base/mac/foundation_util.h"
 #include "base/mac/mac_util.h"
@@ -115,6 +116,16 @@
                             XPC_CONNECTION_EVENT_COUNT);
 }
 
+base::string16 CreateNotificationTitle(const Notification& notification) {
+  base::string16 title;
+  if (notification.progress() > 0) {
+    title += base::FormatPercent(notification.progress());
+    title += base::UTF8ToUTF16(" - ");
+  }
+  title += notification.title();
+  return title;
+}
+
 }  // namespace
 
 // A Cocoa class that represents the delegate of NSUserNotificationCenter and
@@ -169,7 +180,8 @@
            settingsLabel:l10n_util::GetNSString(
                              IDS_NOTIFICATION_BUTTON_SETTINGS)]);
 
-  [builder setTitle:base::SysUTF16ToNSString(notification.title())];
+  [builder
+      setTitle:base::SysUTF16ToNSString(CreateNotificationTitle(notification))];
   [builder setContextMessage:base::SysUTF16ToNSString(notification.message())];
 
   bool requires_attribution =
diff --git a/chrome/browser/notifications/notification_platform_bridge_mac_unittest.mm b/chrome/browser/notifications/notification_platform_bridge_mac_unittest.mm
index e1a819f..e7bbd4d 100644
--- a/chrome/browser/notifications/notification_platform_bridge_mac_unittest.mm
+++ b/chrome/browser/notifications/notification_platform_bridge_mac_unittest.mm
@@ -6,8 +6,10 @@
 #import <objc/runtime.h>
 
 #include "base/bind.h"
+#include "base/i18n/number_formatting.h"
 #include "base/mac/scoped_nsobject.h"
 #include "base/run_loop.h"
+#include "base/strings/sys_string_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/notifications/notification.h"
 #include "chrome/browser/notifications/notification_platform_bridge_mac.h"
@@ -241,6 +243,29 @@
   EXPECT_NSEQ(@"More", [delivered_notification actionButtonTitle]);
 }
 
+TEST_F(NotificationPlatformBridgeMacTest, TestDisplayProgress) {
+  std::unique_ptr<Notification> notification =
+      CreateBanner("Title", "Context", "https://gmail.com", nullptr, nullptr);
+  const int kSamplePercent = 10;
+
+  notification->set_progress(kSamplePercent);
+
+  std::unique_ptr<NotificationPlatformBridgeMac> bridge(
+      new NotificationPlatformBridgeMac(notification_center(),
+                                        alert_dispatcher()));
+  bridge->Display(NotificationCommon::PERSISTENT, "notification_id",
+                  "profile_id", false, *notification);
+  NSArray* notifications = [notification_center() deliveredNotifications];
+
+  EXPECT_EQ(1u, [notifications count]);
+
+  NSUserNotification* delivered_notification = [notifications objectAtIndex:0];
+  base::string16 expected =
+      base::FormatPercent(kSamplePercent) + base::UTF8ToUTF16(" - Title");
+  EXPECT_NSEQ(base::SysUTF16ToNSString(expected),
+              [delivered_notification title]);
+}
+
 TEST_F(NotificationPlatformBridgeMacTest, TestCloseNotification) {
   std::unique_ptr<Notification> notification = CreateBanner(
       "Title", "Context", "https://gmail.com", "Button 1", nullptr);
diff --git a/chrome/browser/resources/chromeos/chromevox/braille/braille_translator_manager.js b/chrome/browser/resources/chromeos/chromevox/braille/braille_translator_manager.js
index 2a152b2..c1d472b 100644
--- a/chrome/browser/resources/chromeos/chromevox/braille/braille_translator_manager.js
+++ b/chrome/browser/resources/chromeos/chromevox/braille/braille_translator_manager.js
@@ -82,14 +82,21 @@
    * Refreshes the braille translator(s) used for input and output.  This
    * should be called when something has changed (such as a preference) to
    * make sure that the correct translator is used.
+   * @param {string} brailleTable The table for this translator to use.
+   * @param {string=} opt_brailleTable8 Optionally specify an uncontracted
+   * table.
    */
-  refresh: function() {
+  refresh: function(brailleTable, opt_brailleTable8) {
+    if (brailleTable && brailleTable === this.defaultTableId_) {
+      return;
+    }
+
     var tables = this.tables_;
     if (tables.length == 0)
       return;
 
-    // First, see if we have a braille table set previously.
-    var table = cvox.BrailleTable.forId(tables, localStorage['brailleTable']);
+    // Look for the table requested.
+    var table = cvox.BrailleTable.forId(tables, brailleTable);
     if (!table) {
       // Match table against current locale.
       var currentLocale = chrome.i18n.getMessage('@@ui_locale').split(/[_-]/);
@@ -112,30 +119,13 @@
     if (!table)
       table = cvox.BrailleTable.forId(tables, 'en-US-comp8');
 
-    // TODO(plundblad): Only update when user explicitly selects a table
-    // so that switching locales changes table by default.  crbug.com/441206.
-    localStorage['brailleTable'] = table.id;
-    if (!localStorage['brailleTable6'])
-      localStorage['brailleTable6'] = 'en-US-g1';
-    if (!localStorage['brailleTable8'])
-      localStorage['brailleTable8'] = 'en-US-comp8';
-
-    if (table.dots == '6') {
-      localStorage['brailleTableType'] = 'brailleTable6';
-      localStorage['brailleTable6'] = table.id;
-    } else {
-      localStorage['brailleTableType'] = 'brailleTable8';
-      localStorage['brailleTable8'] = table.id;
-    }
-
     // If the user explicitly set an 8 dot table, use that when looking
     // for an uncontracted table.  Otherwise, use the current table and let
     // getUncontracted find an appropriate corresponding table.
-    var table8Dot = cvox.BrailleTable.forId(tables,
-                                            localStorage['brailleTable8']);
+    var table8Dot = opt_brailleTable8 ?
+        cvox.BrailleTable.forId(tables, opt_brailleTable8) : null;
     var uncontractedTable = cvox.BrailleTable.getUncontracted(
         tables, table8Dot || table);
-
     var newDefaultTableId = table.id;
     var newUncontractedTableId = table.id === uncontractedTable.id ?
         null : uncontractedTable.id;
@@ -200,7 +190,9 @@
   fetchTables_: function() {
     cvox.BrailleTable.getAll(function(tables) {
       this.tables_ = tables;
-      this.refresh();
+
+      // Initial refresh; set options from user preferences.
+      this.refresh(localStorage['brailleTable'], localStorage['brailleTable8']);
     }.bind(this));
   },
 
diff --git a/chrome/browser/resources/chromeos/chromevox/braille/braille_translator_manager_test.extjs b/chrome/browser/resources/chromeos/chromevox/braille/braille_translator_manager_test.extjs
index 24a414f9..ea5dbdda 100644
--- a/chrome/browser/resources/chromeos/chromevox/braille/braille_translator_manager_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/braille/braille_translator_manager_test.extjs
@@ -97,7 +97,7 @@
     this.manager.addChangeListener(function() {
       assertNotReached('Refresh should not be called without a change.');
     });
-    this.manager.refresh();
+    this.manager.refresh(localStorage['brailleTable']);
   });
 });
 
@@ -107,10 +107,9 @@
     assertNotEquals(null, this.manager.getExpandingTranslator());
     this.addChangeListener(function() {
       assertEquals('en-UEB-g2', this.manager.getDefaultTranslator().table.id);
-      assertEquals('en-US-comp8',
+      assertEquals('en-UEB-g1',
                    this.manager.getUncontractedTranslator().table.id);
     });
-    localStorage['brailleTable'] = 'en-UEB-g2';
-    this.manager.refresh();
+    this.manager.refresh('en-UEB-g2');
   });
 });
diff --git a/chrome/browser/resources/chromeos/chromevox/chromevox/background/background.js b/chrome/browser/resources/chromeos/chromevox/chromevox/background/background.js
index 9810413..6fdbcb9 100644
--- a/chrome/browser/resources/chromeos/chromevox/chromevox/background/background.js
+++ b/chrome/browser/resources/chromeos/chromevox/chromevox/background/background.js
@@ -154,7 +154,7 @@
    * @type {cvox.BrailleBackground}
    * @private
    */
-  this.backgroundBraille_ = new cvox.BrailleBackground();
+  this.backgroundBraille_ = cvox.BrailleBackground.getInstance();
 
   this.tabsApiHandler_ = new cvox.TabsApiHandler();
 
diff --git a/chrome/browser/resources/chromeos/chromevox/chromevox/background/options.js b/chrome/browser/resources/chromeos/chromevox/chromevox/background/options.js
index 35d3ac6c..7c8e8fca 100644
--- a/chrome/browser/resources/chromeos/chromevox/chromevox/background/options.js
+++ b/chrome/browser/resources/chromeos/chromevox/chromevox/background/options.js
@@ -266,7 +266,8 @@
       var sel = node.options[selIndex];
       localStorage['brailleTable'] = sel.id;
       localStorage[node.id] = sel.id;
-      cvox.OptionsPage.getBrailleTranslatorManager().refresh();
+      cvox.OptionsPage.getBrailleTranslatorManager().refresh(
+          localStorage['brailleTable']);
     };
   };
 
@@ -297,7 +298,8 @@
       tableTypeButton.textContent =
           Msgs.getMsg('options_braille_table_type_8');
     }
-    cvox.OptionsPage.getBrailleTranslatorManager().refresh();
+    cvox.OptionsPage.getBrailleTranslatorManager().refresh(
+        localStorage['brailleTable']);
   };
   updateTableType(false);
 
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/background.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/background.js
index 1e765b6..c271af5 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/background.js
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/background.js
@@ -393,6 +393,9 @@
       return;
 
     this.currentRange_ = newRange;
+    ChromeVoxState.observers.forEach(function(observer) {
+      observer.onCurrentRangeChanged(newRange);
+    });
     var oldMode = this.mode_;
     var newMode = this.getMode();
     if (oldMode != newMode) {
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/chromevox_state.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/chromevox_state.js
index 8cbf6bb..3373797 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/chromevox_state.js
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/chromevox_state.js
@@ -10,8 +10,10 @@
 
 goog.provide('ChromeVoxMode');
 goog.provide('ChromeVoxState');
+goog.provide('ChromeVoxStateObserver');
 
 goog.require('cursors.Cursor');
+goog.require('cursors.Range');
 
 /**
  * All possible modes ChromeVox can run.
@@ -25,6 +27,20 @@
 };
 
 /**
+ * An interface implemented by objects that want to observe ChromeVox state
+ * changes.
+ * @interface
+ */
+ChromeVoxStateObserver = function() {};
+
+ChromeVoxStateObserver.prototype = {
+  /**
+   * @param {cursors.Range} range The new range.
+   */
+  onCurrentRangeChanged: function(range) {}
+};
+
+/**
  * ChromeVox2 state object.
  * @constructor
  */
@@ -80,5 +96,15 @@
   /**
    * @param {cursors.Range} newRange The new range.
    */
-  setCurrentRange: goog.abstractMethod,
+  setCurrentRange: goog.abstractMethod
+};
+
+/** @type {!Array<ChromeVoxStateObserver>} */
+ChromeVoxState.observers = [];
+
+/**
+ * @param {ChromeVoxStateObserver} observer
+ */
+ChromeVoxState.addObserver = function(observer) {
+  this.observers.push(observer);
 };
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/editing.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/editing.js
index e4bd5831..43b60fa 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/editing.js
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/editing.js
@@ -15,6 +15,7 @@
 goog.require('Output.EventType');
 goog.require('cursors.Cursor');
 goog.require('cursors.Range');
+goog.require('cvox.BrailleBackground');
 goog.require('cvox.ChromeVoxEditableTextBase');
 
 goog.scope(function() {
@@ -219,4 +220,35 @@
   return null;
 };
 
+/**
+ * An observer that reacts to ChromeVox range changes that modifies braille
+ * table output when over email or url text fields.
+ * @constructor
+ * @implements {ChromeVoxStateObserver}
+ */
+editing.EditingChromeVoxStateObserver = function() {
+  ChromeVoxState.addObserver(this);
+};
+
+editing.EditingChromeVoxStateObserver.prototype = {
+  __proto__: ChromeVoxStateObserver,
+
+  /** @override */
+  onCurrentRangeChanged: function(range) {
+    var inputType = range && range.start.node.inputType;
+    if (inputType == 'email' || inputType == 'url') {
+      cvox.BrailleBackground.getInstance().getTranslatorManager().refresh(
+          localStorage['brailleTable8']);
+      return;
+    }
+    cvox.BrailleBackground.getInstance().getTranslatorManager().refresh(
+        localStorage['brailleTable']);
+  }
+};
+
+/**
+ * @private {ChromeVoxStateObserver}
+ */
+editing.observer_ = new editing.EditingChromeVoxStateObserver();
+
 });
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/output_test.extjs b/chrome/browser/resources/chromeos/chromevox/cvox2/background/output_test.extjs
index 7a98411..176ed77 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/output_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/output_test.extjs
@@ -303,7 +303,7 @@
       // space for the cursor in edit fields.
       var expectedBrailleValues = [
         ' ed',
-        ' @ed',
+        ' @ed 8dot',
         ' pwded',
         ' #ed',
         {string_: '0 min:0 max:0 spnbtn'},
diff --git a/chrome/browser/resources/chromeos/chromevox/host/chrome/braille_background.js b/chrome/browser/resources/chromeos/chromevox/host/chrome/braille_background.js
index d168672..45383bc 100644
--- a/chrome/browser/resources/chromeos/chromevox/host/chrome/braille_background.js
+++ b/chrome/browser/resources/chromeos/chromevox/host/chrome/braille_background.js
@@ -62,6 +62,7 @@
   /** @private {boolean} */
   this.frozen_ = false;
 };
+goog.addSingletonGetter(cvox.BrailleBackground);
 
 
 /** @override */
diff --git a/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd b/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd
index 4245c29..57b49979 100644
--- a/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd
+++ b/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd
@@ -1633,8 +1633,8 @@
       <message desc="Describes an &lt;input&gt; element with type=email." name="IDS_CHROMEVOX_INPUT_TYPE_EMAIL">
         Edit text, email entry
       </message>
-      <message desc="Shown on a braille display for a text field that accept an email address as input. When translating, keep the @ sign and try to find an abbreviation for 'edit field'. If reasonable, use all lowercase and avoid punctuation to keep the number of characters as low as possible." name="IDS_CHROMEVOX_INPUT_TYPE_EMAIL_BRL">
-        @ed
+      <message desc="Shown on a braille display for a text field that accept an email address as input. When translating, keep the @ sign and try to find an abbreviation for 'edit field 8 dot' according to local conventions; 8 dot refers to the braille table chosen by the user that includes 8 dots in a braille cell appropriate for writing computer symbols. If reasonable, use all lowercase and avoid punctuation to keep the number of characters as low as possible." name="IDS_CHROMEVOX_INPUT_TYPE_EMAIL_BRL">
+        @ed 8dot
       </message>
       <message desc="Describes an &lt;input&gt; element with type=file." name="IDS_CHROMEVOX_INPUT_TYPE_FILE">
         File selection
@@ -1681,8 +1681,8 @@
       <message desc="Describes an &lt;input&gt; element with type=url." name="IDS_CHROMEVOX_INPUT_TYPE_URL">
         Edit text, URL entry
       </message>
-      <message desc="Shown on a braille display to describe a text field for entering a URL. When translating, try to find a contracted form of the translation for 'url edit field' according to local conventions. If reasonable, use all lowercase and avoid punctuation to keep the number of characters as low as possible." name="IDS_CHROMEVOX_INPUT_TYPE_URL_BRL">
-        urled
+      <message desc="Shown on a braille display to describe a text field for entering a URL. When translating, try to find a contracted form of the translation for 'url edit field 8 dot' according to local conventions; 8 dot refers to the braille table chosen by the user that includes 8 dots in a braille cell appropriate for writing computer symbols. If reasonable, use all lowercase and avoid punctuation to keep the number of characters as low as possible." name="IDS_CHROMEVOX_INPUT_TYPE_URL_BRL">
+        urled 8dot
       </message>
       <message desc="Describes an &lt;input&gt; element with type=week." name="IDS_CHROMEVOX_INPUT_TYPE_WEEK">
         Week of the year control
diff --git a/chrome/browser/resources/settings/people_page/user_list.html b/chrome/browser/resources/settings/people_page/user_list.html
index 1030ba8..3303a465 100644
--- a/chrome/browser/resources/settings/people_page/user_list.html
+++ b/chrome/browser/resources/settings/people_page/user_list.html
@@ -30,7 +30,7 @@
       }
 
       .user-info {
-        -webkit-padding-start: 16px;
+        -webkit-padding-start: 20px;
       }
     </style>
     <div class="user-list">
diff --git a/chrome/browser/resources/settings/people_page/users_page.html b/chrome/browser/resources/settings/people_page/users_page.html
index 6316f4a..318d936 100644
--- a/chrome/browser/resources/settings/people_page/users_page.html
+++ b/chrome/browser/resources/settings/people_page/users_page.html
@@ -11,20 +11,13 @@
 <dom-module id="settings-users-page">
   <template>
     <style include="settings-shared action-link">
-      .users {
-        /* The users box must line up with the checkbox text. */
-        -webkit-margin-start: var(--settings-indent-width);
-      }
-
       .settings-box:first-of-type {
         border-top: none;
       }
 
       #add-user-button {
         /* Add user button must be lined up with the start of users' names. */
-        /* TODO(dschuyler): Should this be var(--settings-box-row-indent) rather
-           than 56px? */
-        -webkit-margin-start: 56px;
+        -webkit-margin-start: var(--settings-box-row-indent);
       }
     </style>
     <template is="dom-if" if="[[isWhitelistManaged_]]">
@@ -58,18 +51,18 @@
           disabled="[[isEditingDisabled_(isOwner_, isWhitelistManaged_)]]"
           inverted>
       </settings-toggle-button>
-      <div class="users">
-        <settings-user-list prefs="[[prefs]]"
-            disabled="[[isEditingUsersDisabled_(isOwner_, isWhitelistManaged_,
-                prefs.cros.accounts.allowGuest.value)]]">
-        </settings-user-list>
-        <div id="add-user-button" class="list-item"
-            hidden="[[isEditingUsersDisabled_(isOwner_, isWhitelistManaged_,
-                prefs.cros.accounts.allowGuest.value)]]">
-          <a is="action-link" class="list-button" on-tap="openAddUserDialog_">
-            $i18n{addUsers}
-          </a>
-        </div>
+    </div>
+    <div class="list-frame">
+      <settings-user-list prefs="[[prefs]]"
+          disabled="[[isEditingUsersDisabled_(isOwner_, isWhitelistManaged_,
+              prefs.cros.accounts.allowGuest.value)]]">
+      </settings-user-list>
+      <div id="add-user-button" class="list-item"
+          hidden="[[isEditingUsersDisabled_(isOwner_, isWhitelistManaged_,
+              prefs.cros.accounts.allowGuest.value)]]">
+        <a is="action-link" class="list-button" on-tap="openAddUserDialog_">
+          $i18n{addUsers}
+        </a>
       </div>
     </div>
     <settings-users-add-user-dialog id="addUserDialog"
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_page.html b/chrome/browser/resources/settings/privacy_page/privacy_page.html
index add253bb..bb86f4c 100644
--- a/chrome/browser/resources/settings/privacy_page/privacy_page.html
+++ b/chrome/browser/resources/settings/privacy_page/privacy_page.html
@@ -221,7 +221,8 @@
             id="site-settings"
             page-title="[[siteSettingsPageTitle_()]]"
             learn-more-url="$i18n{exceptionsLearnMoreURL}">
-          <settings-site-settings-page></settings-site-settings-page>
+          <settings-site-settings-page focus-config="[[focusConfig_]]">
+          </settings-site-settings-page>
         </settings-subpage>
       </template>
 
diff --git a/chrome/browser/resources/settings/settings_page/settings_animated_pages.js b/chrome/browser/resources/settings/settings_page/settings_animated_pages.js
index 18fe508..5e71132e 100644
--- a/chrome/browser/resources/settings/settings_page/settings_animated_pages.js
+++ b/chrome/browser/resources/settings/settings_page/settings_animated_pages.js
@@ -56,8 +56,13 @@
    * @private
    */
   onIronSelect_: function(e) {
-    if (!this.focusConfig || !this.previousRoute_ ||
-        e.detail.item.tagName != 'NEON-ANIMATABLE') {
+    if (!this.focusConfig || !this.previousRoute_)
+      return;
+
+    // Only handle iron-select events from neon-animatable elements and the
+    // SITE_SETTINGS subpage only.
+    if (!e.detail.item.matches(
+        'neon-animatable, settings-subpage#site-settings')) {
       return;
     }
 
diff --git a/chrome/browser/resources/settings/settings_shared_css.html b/chrome/browser/resources/settings/settings_shared_css.html
index 9283f0dd..d153a27 100644
--- a/chrome/browser/resources/settings/settings_shared_css.html
+++ b/chrome/browser/resources/settings/settings_shared_css.html
@@ -135,6 +135,9 @@
       paper-radio-button {
         --paper-radio-button-checked-color: var(--google-blue-500);
         --paper-radio-button-label-spacing: 22px;
+        --paper-radio-button-radio-container: {
+          flex-shrink: 0;
+        };
         --paper-radio-button-unchecked-color: var(--paper-grey-600);
         -webkit-margin-start: 2px;
         align-items: center;
diff --git a/chrome/browser/resources/settings/site_settings_page/site_settings_page.html b/chrome/browser/resources/settings/site_settings_page/site_settings_page.html
index 7dee54ee..8451c84 100644
--- a/chrome/browser/resources/settings/site_settings_page/site_settings_page.html
+++ b/chrome/browser/resources/settings/site_settings_page/site_settings_page.html
@@ -26,7 +26,7 @@
       <div class="settings-box line-only">
       </div>
     </template>
-    <div class="settings-box two-line first"
+    <div id="cookies" class="settings-box two-line first"
         category$="[[ContentSettingsTypes.COOKIES]]"
         data-route="SITE_SETTINGS_COOKIES" on-tap="onTapNavigate_" actionable>
       <iron-icon icon="settings:cookie"></iron-icon>
@@ -44,7 +44,7 @@
           aria-label="$i18n{siteSettingsCookies}"
           aria-describedby="cookiesSecondary"></button>
     </div>
-    <div class="settings-box two-line"
+    <div id="location" class="settings-box two-line"
         category$="[[ContentSettingsTypes.GEOLOCATION]]"
         data-route="SITE_SETTINGS_LOCATION" on-tap="onTapNavigate_" actionable>
       <iron-icon icon="settings:location-on"></iron-icon>
@@ -61,7 +61,7 @@
           aria-label="$i18n{siteSettingsLocation}"
           aria-describedby="locationSecondary"></button>
     </div>
-    <div class="settings-box two-line"
+    <div id="camera" class="settings-box two-line"
         category$="[[ContentSettingsTypes.CAMERA]]"
         data-route="SITE_SETTINGS_CAMERA"
         on-tap="onTapNavigate_" actionable>
@@ -79,7 +79,8 @@
           aria-label="$i18n{siteSettingsCamera}"
           aria-describedby="cameraSecondary"></button>
     </div>
-    <div class="settings-box two-line" category$="[[ContentSettingsTypes.MIC]]"
+    <div id="microphone" class="settings-box two-line"
+        category$="[[ContentSettingsTypes.MIC]]"
         data-route="SITE_SETTINGS_MICROPHONE" on-tap="onTapNavigate_"
         actionable>
       <iron-icon icon="settings:mic"></iron-icon>
@@ -96,7 +97,7 @@
           aria-label="$i18n{siteSettingsMic}"
           aria-describedby="micSecondary"></button>
     </div>
-    <div class="settings-box two-line"
+    <div id="notifications" class="settings-box two-line"
         category$="[[ContentSettingsTypes.NOTIFICATIONS]]"
         data-route="SITE_SETTINGS_NOTIFICATIONS" on-tap="onTapNavigate_"
         actionable>
@@ -114,7 +115,7 @@
           aria-label="$i18n{siteSettingsNotifications}"
           aria-describedby="notificationsSecondary"></button>
     </div>
-    <div class="settings-box two-line"
+    <div id="javascript" class="settings-box two-line"
         category$="[[ContentSettingsTypes.JAVASCRIPT]]"
         data-route="SITE_SETTINGS_JAVASCRIPT" on-tap="onTapNavigate_"
         actionable>
@@ -132,7 +133,7 @@
           aria-label="$i18n{siteSettingsJavascript}"
           aria-describedby="javascriptSecondary"></button>
     </div>
-    <div class="settings-box two-line"
+    <div id="flash" class="settings-box two-line"
         category$="[[ContentSettingsTypes.PLUGINS]]"
         data-route="SITE_SETTINGS_FLASH" on-tap="onTapNavigate_" actionable>
       <iron-icon icon="cr:extension"></iron-icon>
@@ -150,7 +151,7 @@
           aria-label="$i18n{siteSettingsFlash}"
           aria-describedby="flashSecondary"></button>
     </div>
-    <div class="settings-box two-line"
+    <div id="images" class="settings-box two-line"
         category$="[[ContentSettingsTypes.IMAGES]]"
         data-route="SITE_SETTINGS_IMAGES" on-tap="onTapNavigate_" actionable>
       <iron-icon icon="settings:photo"></iron-icon>
@@ -167,7 +168,7 @@
           aria-label="$i18n{siteSettingsImages}"
           aria-describedby="imagesSecondary"></button>
     </div>
-    <div category$="[[ContentSettingsTypes.POPUPS]]"
+    <div id="popups" category$="[[ContentSettingsTypes.POPUPS]]"
         class="settings-box two-line" data-route="SITE_SETTINGS_POPUPS"
         on-tap="onTapNavigate_" actionable>
       <iron-icon icon="cr:open-in-new"></iron-icon>
@@ -185,7 +186,7 @@
           aria-describedby="popupsSecondary"></button>
     </div>
     <template is="dom-if" if="[[enableSafeBrowsingSubresourceFilter_]]">
-      <div class="settings-box two-line"
+      <div id="subresource-filter" class="settings-box two-line"
           category$="[[ContentSettingsTypes.SUBRESOURCE_FILTER]]"
           data-route="SITE_SETTINGS_SUBRESOURCE_FILTER" on-tap="onTapNavigate_"
           actionable>
@@ -204,7 +205,7 @@
             aria-describedby="subresourceFilterSecondary"></button>
       </div>
     </template>
-    <div class="settings-box two-line"
+    <div id="background-sync" class="settings-box two-line"
         category$="[[ContentSettingsTypes.BACKGROUND_SYNC]]"
         data-route="SITE_SETTINGS_BACKGROUND_SYNC" on-tap="onTapNavigate_"
         actionable>
@@ -222,7 +223,7 @@
           aria-label="$i18n{siteSettingsBackgroundSync}"
           aria-describedby="backgroundSyncSecondary"></button>
     </div>
-    <div class="settings-box two-line"
+    <div id="automatic-downloads" class="settings-box two-line"
         category$="[[ContentSettingsTypes.AUTOMATIC_DOWNLOADS]]"
         data-route="SITE_SETTINGS_AUTOMATIC_DOWNLOADS"
         on-tap="onTapNavigate_" actionable>
@@ -240,7 +241,7 @@
           aria-label="$i18n{siteSettingsAutomaticDownloads}"
           aria-describedby="automaticDownloadsSecondary"></button>
     </div>
-    <div class="settings-box two-line"
+    <div id="unsandboxed-plugins" class="settings-box two-line"
         category$="[[ContentSettingsTypes.UNSANDBOXED_PLUGINS]]"
         data-route="SITE_SETTINGS_UNSANDBOXED_PLUGINS"
         on-tap="onTapNavigate_" actionable>
@@ -259,7 +260,7 @@
           aria-describedby="unsandboxedPluginsSecondary"></button>
     </div>
     <template is="dom-if" if="[[!isGuest_]]">
-      <div class="settings-box two-line"
+      <div id="protocol-handlers" class="settings-box two-line"
           category$="[[ContentSettingsTypes.PROTOCOL_HANDLERS]]"
           data-route="SITE_SETTINGS_HANDLERS"
           on-tap="onTapNavigate_" actionable>
@@ -278,7 +279,7 @@
             aria-describedby="handlersSecondary"></button>
       </div>
     </template>
-    <div class="settings-box two-line"
+    <div id="midi-devices" class="settings-box two-line"
         category$="[[ContentSettingsTypes.MIDI_DEVICES]]"
         data-route="SITE_SETTINGS_MIDI_DEVICES"
         on-tap="onTapNavigate_" actionable>
@@ -296,31 +297,35 @@
           aria-label="$i18n{siteSettingsMidiDevices}"
           aria-describedby="midiDevicesSecondary"></button>
     </div>
-    <div class="settings-box" category$="[[ContentSettingsTypes.ZOOM_LEVELS]]"
-        data-route="SITE_SETTINGS_ZOOM_LEVELS"
-        on-tap="onTapNavigate_" actionable>
+    <div id="zoom-levels" class="settings-box"
+        category$="[[ContentSettingsTypes.ZOOM_LEVELS]]"
+        data-route="SITE_SETTINGS_ZOOM_LEVELS" on-tap="onTapNavigate_"
+        actionable>
       <iron-icon icon="settings:zoom-in"></iron-icon>
       <div class="middle">$i18n{siteSettingsZoomLevels}</div>
       <button class="subpage-arrow" is="paper-icon-button-light"
           aria-label="$i18n{siteSettingsZoomLevels}"></button>
     </div>
-    <div class="settings-box" category$="[[ContentSettingsTypes.USB_DEVICES]]"
-        data-route="SITE_SETTINGS_USB_DEVICES"
-        on-tap="onTapNavigate_" actionable>
+    <div id="usb-devices" class="settings-box"
+        category$="[[ContentSettingsTypes.USB_DEVICES]]"
+        data-route="SITE_SETTINGS_USB_DEVICES" on-tap="onTapNavigate_"
+        actionable>
       <iron-icon icon="settings:usb"></iron-icon>
       <div class="middle">$i18n{siteSettingsUsbDevices}</div>
       <button class="subpage-arrow" is="paper-icon-button-light"
           aria-label="$i18n{siteSettingsUsbDevices}"></button>
     </div>
-    <div class="settings-box" data-route="SITE_SETTINGS_PDF_DOCUMENTS"
-        on-tap="onTapNavigate_" actionable>
+    <div id="pdf-documents" class="settings-box"
+        data-route="SITE_SETTINGS_PDF_DOCUMENTS" on-tap="onTapNavigate_"
+        actionable>
       <iron-icon icon="settings:pdf"></iron-icon>
       <div class="middle">$i18n{siteSettingsPdfDocuments}</div>
       <button class="subpage-arrow" is="paper-icon-button-light"
           aria-label="$i18n{siteSettingsPdfDocuments}"></button>
     </div>
-    <div class="settings-box" data-route="SITE_SETTINGS_PROTECTED_CONTENT"
-        on-tap="onTapNavigate_" actionable>
+    <div id="protected-content" class="settings-box"
+        data-route="SITE_SETTINGS_PROTECTED_CONTENT" on-tap="onTapNavigate_"
+        actionable>
       <iron-icon icon="settings:security"></iron-icon>
       <div class="middle">$i18n{siteSettingsProtectedContent}</div>
       <button class="subpage-arrow" is="paper-icon-button-light"
diff --git a/chrome/browser/resources/settings/site_settings_page/site_settings_page.js b/chrome/browser/resources/settings/site_settings_page/site_settings_page.js
index b1e40a4..5c489d72 100644
--- a/chrome/browser/resources/settings/site_settings_page/site_settings_page.js
+++ b/chrome/browser/resources/settings/site_settings_page/site_settings_page.js
@@ -49,6 +49,54 @@
       }
     },
 
+
+    /** @type {!Map<string, string>} */
+    focusConfig: {
+      type: Object,
+      observer: 'focusConfigChanged_',
+    },
+  },
+
+  /**
+   * @param {!Map<string, string>} newConfig
+   * @param {?Map<string, string>} oldConfig
+   * @private
+   */
+  focusConfigChanged_: function(newConfig, oldConfig) {
+    // focusConfig is set only once on the parent, so this observer should only
+    // fire once.
+    assert(!oldConfig);
+
+    // Populate the |focusConfig| map of the parent <settings-animated-pages>
+    // element, with additional entries that correspond to subpage trigger
+    // elements residing in this element's Shadow DOM.
+    var R = settings.Route;
+    [
+      [R.SITE_SETTINGS_COOKIES, 'cookies'],
+      [R.SITE_SETTINGS_LOCATION, 'location'],
+      [R.SITE_SETTINGS_CAMERA, 'camera'],
+      [R.SITE_SETTINGS_MICROPHONE, 'microphone'],
+      [R.SITE_SETTINGS_NOTIFICATIONS, 'notifications'],
+      [R.SITE_SETTINGS_JAVASCRIPT,'javascript'],
+      [R.SITE_SETTINGS_FLASH,'flash'],
+      [R.SITE_SETTINGS_IMAGES,'images'],
+      [R.SITE_SETTINGS_POPUPS,'popups'],
+      [R.SITE_SETTINGS_BACKGROUND_SYNC,'background-sync'],
+      [R.SITE_SETTINGS_AUTOMATIC_DOWNLOADS,'automatic-downloads'],
+      [R.SITE_SETTINGS_UNSANDBOXED_PLUGINS,'unsandboxed-plugins'],
+      [R.SITE_SETTINGS_HANDLERS,'protocol-handlers'],
+      [R.SITE_SETTINGS_MIDI_DEVICES,'midi-devices'],
+      [R.SITE_SETTINGS_SUBRESOURCE_FILTER,'subresource-filter'],
+      [R.SITE_SETTINGS_ZOOM_LEVELS,'zoom-levels'],
+      [R.SITE_SETTINGS_USB_DEVICES,'usb-devices'],
+      [R.SITE_SETTINGS_PDF_DOCUMENTS,'pdf-documents'],
+      [R.SITE_SETTINGS_PROTECTED_CONTENT,'protected-content'],
+    ].forEach(function(pair) {
+      var route = pair[0];
+      var id = pair[1];
+      this.focusConfig.set(
+          route.path, '* /deep/ #' + id + ' .subpage-arrow');
+    }.bind(this));
   },
 
   /** @override */
diff --git a/chrome/browser/ui/ash/palette_delegate_chromeos.cc b/chrome/browser/ui/ash/palette_delegate_chromeos.cc
index 3f06c3d..b9a52a7 100644
--- a/chrome/browser/ui/ash/palette_delegate_chromeos.cc
+++ b/chrome/browser/ui/ash/palette_delegate_chromeos.cc
@@ -12,11 +12,16 @@
 #include "ash/utility/screenshot_controller.h"
 #include "base/memory/ptr_util.h"
 #include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/chromeos/arc/arc_util.h"
+#include "chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_framework_service.h"
 #include "chrome/browser/chromeos/note_taking_helper.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/common/pref_names.h"
+#include "components/arc/arc_bridge_service.h"
+#include "components/arc/arc_service_manager.h"
+#include "components/arc/common/voice_interaction_framework.mojom.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "components/prefs/pref_service.h"
 #include "components/user_manager/user_manager.h"
@@ -25,6 +30,37 @@
 
 namespace chromeos {
 
+class VoiceInteractionScreenshotDelegate : public ash::ScreenshotDelegate {
+ public:
+  VoiceInteractionScreenshotDelegate() {}
+  ~VoiceInteractionScreenshotDelegate() override {}
+
+ private:
+  void HandleTakeScreenshotForAllRootWindows() override { NOTIMPLEMENTED(); }
+
+  void HandleTakePartialScreenshot(aura::Window* window,
+                                   const gfx::Rect& rect) override {
+    arc::mojom::VoiceInteractionFrameworkInstance* framework =
+        ARC_GET_INSTANCE_FOR_METHOD(arc::ArcServiceManager::Get()
+                                        ->arc_bridge_service()
+                                        ->voice_interaction_framework(),
+                                    StartVoiceInteractionSessionForRegion);
+    if (!framework)
+      return;
+    double device_scale_factor = window->layer()->device_scale_factor();
+    framework->StartVoiceInteractionSessionForRegion(
+        gfx::ScaleToEnclosingRect(rect, device_scale_factor));
+  }
+
+  void HandleTakeWindowScreenshot(aura::Window* window) override {
+    NOTIMPLEMENTED();
+  }
+
+  bool CanTakeScreenshot() override { return true; }
+
+  DISALLOW_COPY_AND_ASSIGN(VoiceInteractionScreenshotDelegate);
+};
+
 PaletteDelegateChromeOS::PaletteDelegateChromeOS() : weak_factory_(this) {
   registrar_.Add(this, chrome::NOTIFICATION_SESSION_STARTED,
                  content::NotificationService::AllSources());
@@ -141,9 +177,22 @@
 
 void PaletteDelegateChromeOS::TakePartialScreenshot(const base::Closure& done) {
   auto* screenshot_controller = ash::Shell::Get()->screenshot_controller();
-  auto* screenshot_delegate = ash::ShellPortClassic::Get()
-                                  ->accelerator_controller_delegate()
-                                  ->screenshot_delegate();
+
+  ash::ScreenshotDelegate* screenshot_delegate;
+  if (arc::ArcVoiceInteractionFrameworkService::IsVoiceInteractionEnabled() &&
+      arc::IsArcAllowedForProfile(profile_)) {
+    // This is an experimental mode. It will be either taken out or grow
+    // into a separate tool next to "Capture region".
+    if (!voice_interaction_screenshot_delegate_) {
+      voice_interaction_screenshot_delegate_ =
+          base::MakeUnique<VoiceInteractionScreenshotDelegate>();
+    }
+    screenshot_delegate = voice_interaction_screenshot_delegate_.get();
+  } else {
+    screenshot_delegate = ash::ShellPortClassic::Get()
+                              ->accelerator_controller_delegate()
+                              ->screenshot_delegate();
+  }
 
   screenshot_controller->set_pen_events_only(true);
   screenshot_controller->StartPartialScreenshotSession(
diff --git a/chrome/browser/ui/ash/palette_delegate_chromeos.h b/chrome/browser/ui/ash/palette_delegate_chromeos.h
index e153148f..ac059a8 100644
--- a/chrome/browser/ui/ash/palette_delegate_chromeos.h
+++ b/chrome/browser/ui/ash/palette_delegate_chromeos.h
@@ -19,6 +19,10 @@
 class PrefChangeRegistrar;
 class Profile;
 
+namespace ash {
+class ScreenshotDelegate;
+}
+
 namespace chromeos {
 
 // A class which allows the Ash palette to perform chrome actions.
@@ -65,6 +69,9 @@
       session_state_observer_;
   content::NotificationRegistrar registrar_;
 
+  std::unique_ptr<ash::ScreenshotDelegate>
+      voice_interaction_screenshot_delegate_;
+
   base::WeakPtrFactory<PaletteDelegateChromeOS> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(PaletteDelegateChromeOS);
diff --git a/chrome/browser/ui/cocoa/history_menu_bridge.h b/chrome/browser/ui/cocoa/history_menu_bridge.h
index fa71b371..a7a9669 100644
--- a/chrome/browser/ui/cocoa/history_menu_bridge.h
+++ b/chrome/browser/ui/cocoa/history_menu_bridge.h
@@ -138,6 +138,10 @@
   // corresponding HistoryItem.
   HistoryItem* HistoryItemForMenuItem(NSMenuItem* item);
 
+  // Called by HistoryMenuCocoaController when the menu begins and ends
+  // tracking, to block updates when it is open.
+  void SetIsMenuOpen(bool flag);
+
   // I wish I has a "friend @class" construct. These are used by the HMCC
   // to access model information when responding to actions.
   history::HistoryService* service();
@@ -235,6 +239,10 @@
   bool create_in_progress_;
   bool need_recreate_;
 
+  // In order to not jarringly refresh the menu while the user has it open,
+  // updates are blocked while the menu is tracking.
+  bool is_menu_open_;
+
   // The default favicon if a HistoryItem does not have one.
   base::scoped_nsobject<NSImage> default_favicon_;
 
diff --git a/chrome/browser/ui/cocoa/history_menu_bridge.mm b/chrome/browser/ui/cocoa/history_menu_bridge.mm
index 3da84d6..904ff50 100644
--- a/chrome/browser/ui/cocoa/history_menu_bridge.mm
+++ b/chrome/browser/ui/cocoa/history_menu_bridge.mm
@@ -11,6 +11,7 @@
 #include "base/strings/string_util.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/threading/thread_task_runner_handle.h"
 #include "chrome/app/chrome_command_ids.h"  // IDC_HISTORY_MENU
 #include "chrome/browser/favicon/favicon_service_factory.h"
 #include "chrome/browser/history/history_service_factory.h"
@@ -104,6 +105,7 @@
   NSMenuItem* item = [HistoryMenu() itemWithTag:IDC_SHOW_HISTORY];
   [item setImage:rb.GetNativeImageNamed(IDR_HISTORY_FAVICON).ToNSImage()];
 
+  [HistoryMenu() setDelegate:controller_];
 }
 
 // Note that all requests sent to either the history service or the favicon
@@ -238,6 +240,15 @@
   return NULL;
 }
 
+void HistoryMenuBridge::SetIsMenuOpen(bool flag) {
+  is_menu_open_ = flag;
+  if (!is_menu_open_ && need_recreate_) {
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE,
+        base::Bind(&HistoryMenuBridge::CreateMenu, base::Unretained(this)));
+  }
+}
+
 history::HistoryService* HistoryMenuBridge::service() {
   return history_service_;
 }
@@ -322,7 +333,8 @@
 
 void HistoryMenuBridge::CreateMenu() {
   // If we're currently running CreateMenu(), wait until it finishes.
-  if (create_in_progress_)
+  // If the menu is currently open, wait until it closes.
+  if (create_in_progress_ || is_menu_open_)
     return;
   create_in_progress_ = true;
   need_recreate_ = false;
diff --git a/chrome/browser/ui/cocoa/history_menu_cocoa_controller.h b/chrome/browser/ui/cocoa/history_menu_cocoa_controller.h
index f29057b..2ba2cd1 100644
--- a/chrome/browser/ui/cocoa/history_menu_cocoa_controller.h
+++ b/chrome/browser/ui/cocoa/history_menu_cocoa_controller.h
@@ -11,7 +11,7 @@
 // Controller (MVC) for the history menu. All history menu item commands get
 // directed here. This class only responds to menu events, but the actual
 // creation and maintenance of the menu happens in the Bridge.
-@interface HistoryMenuCocoaController : NSObject {
+@interface HistoryMenuCocoaController : NSObject<NSMenuDelegate> {
  @private
   HistoryMenuBridge* bridge_;  // weak; owns us
 }
diff --git a/chrome/browser/ui/cocoa/history_menu_cocoa_controller.mm b/chrome/browser/ui/cocoa/history_menu_cocoa_controller.mm
index 0f7c796..92ac9094 100644
--- a/chrome/browser/ui/cocoa/history_menu_cocoa_controller.mm
+++ b/chrome/browser/ui/cocoa/history_menu_cocoa_controller.mm
@@ -68,4 +68,14 @@
   [self openURLForItem:item];
 }
 
+// NSMenuDelegate:
+
+- (void)menuWillOpen:(NSMenu*)menu {
+  bridge_->SetIsMenuOpen(true);
+}
+
+- (void)menuDidClose:(NSMenu*)menu {
+  bridge_->SetIsMenuOpen(false);
+}
+
 @end  // HistoryMenuCocoaController
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
index 6fc3b895..389e0db1 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
@@ -705,6 +705,7 @@
   node_data->role = ui::AX_ROLE_TEXT_FIELD;
   node_data->SetName(l10n_util::GetStringUTF8(IDS_ACCNAME_LOCATION));
   node_data->SetValue(GetText());
+  node_data->html_attributes.push_back(std::make_pair("type", "url"));
 
   base::string16::size_type entry_start;
   base::string16::size_type entry_end;
diff --git a/chrome/common/extensions/api/_permission_features.json b/chrome/common/extensions/api/_permission_features.json
index 86d2d69..7f1f660 100644
--- a/chrome/common/extensions/api/_permission_features.json
+++ b/chrome/common/extensions/api/_permission_features.json
@@ -386,11 +386,11 @@
       "3F65507A3B39259B38C8173C6FFA3D12DF64CCE9"   // http://crbug.com/371562
     ]
   }],
-  "fileSystem.requestFileSystem": [{
+  "fileSystem.requestFileSystem": {
     "channel": "stable",
     "extension_types": ["platform_app"],
     "platforms": ["chromeos"]
-  }],
+  },
   "fileSystemProvider": [{
     "channel": "stable",
     "extension_types": ["extension", "platform_app"],
@@ -730,7 +730,7 @@
       "0136FCB13DB29FD5CD442F56E59E53B61F1DF96F"   // http://crbug.com/642141
     ]
   },
-  "inlineInstallPrivate":[{
+  "inlineInstallPrivate": {
     "channel": "dev",
     "extension_types": ["platform_app"],
     "whitelist": [
@@ -738,7 +738,7 @@
       "3A78E13285C1949EF84AA85E3BF65D1E83A3D9AB", // Test Extension
       "4477F0B4FE934D0A8C88922C0986DA7B25D881E1"  // API Test
     ]
-  }],
+  },
   "resourcesPrivate": {
     "channel": "stable",
     "extension_types": [
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/signin/SigninTestUtil.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/signin/SigninTestUtil.java
index d570002..8b81b73 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/signin/SigninTestUtil.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/signin/SigninTestUtil.java
@@ -53,14 +53,6 @@
     }
 
     /**
-     * Tears down the test authentication environment.
-     */
-    public static void tearDownAuthForTest() {
-        AccountManagerHelper.resetAccountManagerHelperForTests();
-        sContext = null;
-    }
-
-    /**
      * Returns the currently signed in account.
      */
     public static Account getCurrentAccount() {
diff --git a/chrome/test/chromedriver/test/run_py_tests.py b/chrome/test/chromedriver/test/run_py_tests.py
index 081f9d7..a717053a 100755
--- a/chrome/test/chromedriver/test/run_py_tests.py
+++ b/chrome/test/chromedriver/test/run_py_tests.py
@@ -214,6 +214,9 @@
         'ChromeDriverTest.testAlertOnNewWindow',
         'ChromeDesiredCapabilityTest.testUnexpectedAlertBehaviour',
         'ChromeDriverTest.testAlertHandlingOnPageUnload',
+        'ChromeDriverTest.testClickElementAfterNavigation',
+        'ChromeDriverTest.testGetLogOnWindowWithAlert',
+        'ChromeDriverTest.testUnexpectedAlertOpenExceptionMessage',
     ]
 )
 
diff --git a/components/BUILD.gn b/components/BUILD.gn
index 323b3336..d596dca5 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -249,6 +249,7 @@
       "//components/gcm_driver/instance_id/android:instance_id_driver_test_support_java",
       "//components/invalidation/impl",
       "//components/invalidation/impl:java",
+      "//components/payments/android:unit_tests",
       "//components/policy/android:policy_java",
       "//components/safe_json",
       "//components/safe_json/android:safe_json_java",
diff --git a/components/arc/common/voice_interaction_framework.mojom b/components/arc/common/voice_interaction_framework.mojom
index 7206c78..adf25f4 100644
--- a/components/arc/common/voice_interaction_framework.mojom
+++ b/components/arc/common/voice_interaction_framework.mojom
@@ -2,23 +2,35 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
-// Next MinVersion: 1
+// Next MinVersion: 2
 
 module arc.mojom;
 
+import "screen_rect.mojom";
+
 // Handles voice interaction queries from Android.
-// Next method ID: 1
+// Next method ID: 2
 interface VoiceInteractionFrameworkHost {
   // Returns a screenshot of currently focused window or empty array if
   // no window is focused.
   CaptureFocusedWindow@0() => (array<uint8> png_data);
+
+  // Returns a fullscreen screenshot of the primary display.
+  [MinVersion=1]CaptureFullscreen@1() => (array<uint8> png_data);
 };
 
 // Connects with Android system server.
-// Next method ID:2
+// Next method ID:4
 interface VoiceInteractionFrameworkInstance {
   Init@0(VoiceInteractionFrameworkHost host_ptr);
 
   // Starts the voice interaction session in container.
   StartVoiceInteractionSession@1();
+
+  // Starts the voice interaction session in container, with a screen region
+  // selected.
+  [MinVersion=1] StartVoiceInteractionSessionForRegion@2(ScreenRect region);
+
+  // Toggles the metalayer.
+  [MinVersion=1] ToggleMetalayer@3();
 };
diff --git a/components/autofill/android/java/src/org/chromium/components/autofill/AutofillDelegate.java b/components/autofill/android/java/src/org/chromium/components/autofill/AutofillDelegate.java
index 1c5fab7..e50c430 100644
--- a/components/autofill/android/java/src/org/chromium/components/autofill/AutofillDelegate.java
+++ b/components/autofill/android/java/src/org/chromium/components/autofill/AutofillDelegate.java
@@ -25,4 +25,10 @@
      * @param listIndex The index of the suggestion to delete.
      */
     public void deleteSuggestion(int listIndex);
+
+    /**
+     * Informs the controller the AutofillPopup received a
+     * {@code TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED} accessibility event.
+     */
+    public void accessibilityFocusCleared();
 }
diff --git a/components/autofill/android/java/src/org/chromium/components/autofill/AutofillPopup.java b/components/autofill/android/java/src/org/chromium/components/autofill/AutofillPopup.java
index 47f13dd..7906d25 100644
--- a/components/autofill/android/java/src/org/chromium/components/autofill/AutofillPopup.java
+++ b/components/autofill/android/java/src/org/chromium/components/autofill/AutofillPopup.java
@@ -8,6 +8,9 @@
 import android.content.Context;
 import android.graphics.Color;
 import android.view.View;
+import android.view.View.AccessibilityDelegate;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
 import android.widget.AdapterView;
 import android.widget.PopupWindow;
 
@@ -34,10 +37,26 @@
      */
     private static final int ITEM_ID_SEPARATOR_ENTRY = -3;
 
+    /**
+     * We post a delayed runnable to clear accessibility focus from the autofill popup's list view
+     * when we receive a {@code TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED} event because we receive a
+     * {@code TYPE_VIEW_ACCESSIBILITY_FOCUSED} for the same list view if user navigates to a
+     * different suggestion. On the other hand, if user navigates out of the popup we do not receive
+     * a {@code TYPE_VIEW_ACCESSIBILITY_FOCUSED} in immediate succession.
+     */
+    private static final long CLEAR_ACCESSIBILITY_FOCUS_DELAY_MS = 100;
+
     private final Context mContext;
     private final AutofillDelegate mAutofillDelegate;
     private List<AutofillSuggestion> mSuggestions;
 
+    private final Runnable mClearAccessibilityFocusRunnable = new Runnable() {
+        @Override
+        public void run() {
+            mAutofillDelegate.accessibilityFocusCleared();
+        }
+    };
+
     /**
      * Creates an AutofillWindow with specified parameters.
      * @param context Application context.
@@ -94,6 +113,19 @@
         setRtl(isRtl);
         show();
         getListView().setOnItemLongClickListener(this);
+        getListView().setAccessibilityDelegate(new AccessibilityDelegate() {
+            @Override
+            public boolean onRequestSendAccessibilityEvent(
+                    ViewGroup host, View child, AccessibilityEvent event) {
+                getListView().removeCallbacks(mClearAccessibilityFocusRunnable);
+                if (event.getEventType()
+                        == AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED) {
+                    getListView().postDelayed(
+                            mClearAccessibilityFocusRunnable, CLEAR_ACCESSIBILITY_FOCUS_DELAY_MS);
+                }
+                return super.onRequestSendAccessibilityEvent(host, child, event);
+            }
+        });
     }
 
     @Override
diff --git a/components/autofill/core/browser/autofill_metrics.h b/components/autofill/core/browser/autofill_metrics.h
index 15ee9a1..256ee78 100644
--- a/components/autofill/core/browser/autofill_metrics.h
+++ b/components/autofill/core/browser/autofill_metrics.h
@@ -351,6 +351,10 @@
     USER_DID_EDIT_AUTOFILLED_FIELD,
     // Same as above, but only logged once per page load.
     USER_DID_EDIT_AUTOFILLED_FIELD_ONCE,
+
+    // User entered form data that appears to be a UPI Virtual Payment Address.
+    USER_DID_ENTER_UPI_VPA,
+
     NUM_USER_HAPPINESS_METRICS,
   };
 
diff --git a/components/autofill/core/browser/autofill_metrics_unittest.cc b/components/autofill/core/browser/autofill_metrics_unittest.cc
index c9fd25a..b5cdaca 100644
--- a/components/autofill/core/browser/autofill_metrics_unittest.cc
+++ b/components/autofill/core/browser/autofill_metrics_unittest.cc
@@ -852,6 +852,47 @@
       GetFieldTypeGroupMetric(NAME_MIDDLE, AutofillMetrics::TYPE_MISMATCH), 1);
 }
 
+// Test that we log UPI Virtual Payment Address.
+TEST_F(AutofillMetricsTest, UpiVirtualPaymentAddress) {
+  // Set up our form data.
+  FormData form;
+  form.name = ASCIIToUTF16("TestForm");
+  form.origin = GURL("http://example.com/form.html");
+  form.action = GURL("http://example.com/submit.html");
+
+  std::vector<ServerFieldType> heuristic_types, server_types;
+  FormFieldData field;
+
+  // Heuristic value will match with Autocomplete attribute.
+  test::CreateTestFormField("Last Name", "lastname", "", "text", &field);
+  form.fields.push_back(field);
+  heuristic_types.push_back(NAME_LAST);
+  server_types.push_back(NAME_LAST);
+
+  // Heuristic value will NOT match with Autocomplete attribute.
+  test::CreateTestFormField("First Name", "firstname", "", "text", &field);
+  form.fields.push_back(field);
+  heuristic_types.push_back(NAME_FIRST);
+  server_types.push_back(NAME_FIRST);
+
+  // Heuristic value will NOT match with Autocomplete attribute.
+  test::CreateTestFormField("Payment Address", "payment_address", "user@upi",
+                            "text", &field);
+  form.fields.push_back(field);
+  heuristic_types.push_back(UNKNOWN_TYPE);
+  server_types.push_back(NO_SERVER_DATA);
+
+  // Simulate having seen this form on page load.
+  autofill_manager_->AddSeenForm(form, heuristic_types, server_types);
+
+  // Simulate form submission.
+  base::HistogramTester histogram_tester;
+  autofill_manager_->SubmitForm(form, TimeTicks::Now());
+
+  histogram_tester.ExpectBucketCount(
+      "Autofill.UserHappiness", AutofillMetrics::USER_DID_ENTER_UPI_VPA, 1);
+}
+
 // Test that we do not log RAPPOR metrics when the number of mismatches is not
 // high enough.
 TEST_F(AutofillMetricsTest, Rappor_LowMismatchRate_NoMetricsReported) {
diff --git a/components/autofill/core/browser/autofill_regex_constants.cc b/components/autofill/core/browser/autofill_regex_constants.cc
index e5587be..57b961d 100644
--- a/components/autofill/core/browser/autofill_regex_constants.cc
+++ b/components/autofill/core/browser/autofill_regex_constants.cc
@@ -309,5 +309,34 @@
 const char kPhoneExtensionRe[] =
     "\\bext|ext\\b|extension"
     "|ramal";  // pt-BR, pt-PT
+const char kUPIVirtualPaymentAddressRe[] =
+    "^\\w+@("
+    "upi|"         // BHIM Bharat Interface for Money
+    "allbank|"     // Allahabad Bank UPI
+    "andb|"        // Andhra Bank ONE
+    "axisbank|"    // Axis Pay
+    "barodampay|"  // Baroda MPay
+    "mahb|"        // MAHAUPI
+    "cnrb|"        // Canara Bank UPI - Empower
+    "csbpay|"      // CSB UPI
+    "dcb|"         // DCB Bank
+    "federal|"     // Lotza
+    "hdfcbank|"    // HDFC Bank MobileBanking
+    "pockets|"     // Pockets- ICICI Bank
+    "icici|"       // Pockets- ICICI Bank
+    "idfcbank|"    // IDFC Bank UPI App
+    "indus|"       // Indus Pay
+    "kbl|"         // KBL Smartz
+    "kaypay|"      // KayPay
+    "pnb|"         // PNB UPI
+    "sib|"         // SIB M-Pay (UPI Pay)
+    "sbi|"         // SBI Pay
+    "tjsp|"        // TranZapp
+    "uco|"         // UCO UPI
+    "unionbank|"   // Union Bank UPI
+    "united|"      // United UPI
+    "vijb|"        // Vijaya UPI App
+    "ybl"          // Yes Pay
+    ")$";
 
 }  // namespace autofill
diff --git a/components/autofill/core/browser/autofill_regex_constants.h b/components/autofill/core/browser/autofill_regex_constants.h
index db85f540..8be7b2c 100644
--- a/components/autofill/core/browser/autofill_regex_constants.h
+++ b/components/autofill/core/browser/autofill_regex_constants.h
@@ -56,6 +56,12 @@
 extern const char kPhoneSuffixRe[];
 extern const char kPhoneExtensionRe[];
 
+// Used to match field data that might be a UPI Virtual Payment Address.
+// See:
+//   - http://crbug.com/702220
+//   - https://upipayments.co.in/virtual-payment-address-vpa/
+extern const char kUPIVirtualPaymentAddressRe[];
+
 }  // namespace autofill
 
 #endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_REGEX_CONSTANTS_H_
diff --git a/components/autofill/core/browser/form_structure.cc b/components/autofill/core/browser/form_structure.cc
index 0896bb4f..65d699a 100644
--- a/components/autofill/core/browser/form_structure.cc
+++ b/components/autofill/core/browser/form_structure.cc
@@ -27,6 +27,7 @@
 #include "components/autofill/core/browser/field_candidates.h"
 #include "components/autofill/core/browser/field_types.h"
 #include "components/autofill/core/browser/form_field.h"
+#include "components/autofill/core/browser/validation.h"
 #include "components/autofill/core/common/autofill_constants.h"
 #include "components/autofill/core/common/autofill_util.h"
 #include "components/autofill/core/common/form_data.h"
@@ -702,6 +703,10 @@
     if (field->form_control_type == "password")
       continue;
 
+    if (IsUPIVirtualPaymentAddress(field->value)) {
+      AutofillMetrics::LogUserHappinessMetric(
+          AutofillMetrics::USER_DID_ENTER_UPI_VPA);
+    }
     // We count fields that were autofilled but later modified, regardless of
     // whether the data now in the field is recognized.
     if (field->previously_autofilled())
diff --git a/components/autofill/core/browser/validation.cc b/components/autofill/core/browser/validation.cc
index 2f7d2a1..e7237f7 100644
--- a/components/autofill/core/browser/validation.cc
+++ b/components/autofill/core/browser/validation.cc
@@ -13,6 +13,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
 #include "components/autofill/core/browser/autofill_data_util.h"
+#include "components/autofill/core/browser/autofill_regex_constants.h"
 #include "components/autofill/core/browser/credit_card.h"
 #include "components/autofill/core/browser/phone_number_i18n.h"
 #include "components/autofill/core/browser/state_names.h"
@@ -321,4 +322,8 @@
   return GENERAL_CVC_LENGTH;
 }
 
+bool IsUPIVirtualPaymentAddress(const base::string16& value) {
+  return MatchesPattern(value, base::ASCIIToUTF16(kUPIVirtualPaymentAddressRe));
+}
+
 }  // namespace autofill
diff --git a/components/autofill/core/browser/validation.h b/components/autofill/core/browser/validation.h
index 076663d..93a52e59 100644
--- a/components/autofill/core/browser/validation.h
+++ b/components/autofill/core/browser/validation.h
@@ -70,6 +70,10 @@
 // Returns the expected CVC length based on the |card_type|.
 size_t GetCvcLengthForCardType(const base::StringPiece card_type);
 
+// Returns true if |value| appears to be a UPI Virtual Payment Address.
+// https://upipayments.co.in/virtual-payment-address-vpa/
+bool IsUPIVirtualPaymentAddress(const base::string16& value);
+
 }  // namespace autofill
 
 #endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_VALIDATION_H_
diff --git a/components/autofill/core/browser/validation_unittest.cc b/components/autofill/core/browser/validation_unittest.cc
index a6c85c8..ee36fbc4 100644
--- a/components/autofill/core/browser/validation_unittest.cc
+++ b/components/autofill/core/browser/validation_unittest.cc
@@ -443,4 +443,50 @@
         GetCvcLengthForCardTypeCase{kUnionPay, GENERAL_CVC_LENGTH},
         GetCvcLengthForCardTypeCase{kVisaCard, GENERAL_CVC_LENGTH}));
 
+class AutofillIsUPIVirtualPaymentAddress
+    : public testing::TestWithParam<std::string> {};
+
+TEST_P(AutofillIsUPIVirtualPaymentAddress, IsUPIVirtualPaymentAddress) {
+  // Expected format is user@bank
+  EXPECT_TRUE(IsUPIVirtualPaymentAddress(ASCIIToUTF16("user@" + GetParam())));
+
+  // Deviations should not match: bank, @bank, user@prefixbank, user@banksuffix.
+  EXPECT_FALSE(IsUPIVirtualPaymentAddress(ASCIIToUTF16(GetParam())));
+  EXPECT_FALSE(IsUPIVirtualPaymentAddress(ASCIIToUTF16(GetParam() + "@")));
+  EXPECT_FALSE(IsUPIVirtualPaymentAddress(ASCIIToUTF16("@" + GetParam())));
+  EXPECT_FALSE(
+      IsUPIVirtualPaymentAddress(ASCIIToUTF16("user@invalid" + GetParam())));
+  EXPECT_FALSE(
+      IsUPIVirtualPaymentAddress(ASCIIToUTF16("user@" + GetParam() + ".com")));
+}
+
+INSTANTIATE_TEST_CASE_P(UPIVirtualPaymentAddress,
+                        AutofillIsUPIVirtualPaymentAddress,
+                        testing::Values("upi",
+                                        "allbank",
+                                        "andb",
+                                        "axisbank",
+                                        "barodampay",
+                                        "mahb",
+                                        "cnrb",
+                                        "csbpay",
+                                        "dcb",
+                                        "federal",
+                                        "hdfcbank",
+                                        "pockets",
+                                        "icici",
+                                        "idfcbank",
+                                        "indus",
+                                        "kbl",
+                                        "kaypay",
+                                        "pnb",
+                                        "sib",
+                                        "sbi",
+                                        "tjsp",
+                                        "uco",
+                                        "unionbank",
+                                        "united",
+                                        "vijb",
+                                        "ybl"));
+
 }  // namespace autofill
diff --git a/components/crash/content/app/run_as_crashpad_handler_win.cc b/components/crash/content/app/run_as_crashpad_handler_win.cc
index 0724eac..6ea9348 100644
--- a/components/crash/content/app/run_as_crashpad_handler_win.cc
+++ b/components/crash/content/app/run_as_crashpad_handler_win.cc
@@ -65,7 +65,7 @@
   argv_as_utf8[argv.size()] = nullptr;
   argv.clear();
   return crashpad::HandlerMain(static_cast<int>(storage.size()),
-                               argv_as_utf8.get());
+                               argv_as_utf8.get(), nullptr);
 }
 
 }  // namespace crash_reporter
diff --git a/components/offline_pages/core/offline_page_archiver.h b/components/offline_pages/core/offline_page_archiver.h
index 31f2d33..cfada7b4 100644
--- a/components/offline_pages/core/offline_page_archiver.h
+++ b/components/offline_pages/core/offline_page_archiver.h
@@ -50,6 +50,8 @@
     ERROR_ARCHIVE_CREATION_FAILED,  // Creation of archive failed.
     ERROR_SECURITY_CERTIFICATE,     // Page was loaded on secure connection, but
                                     // there was a security error.
+    ERROR_ERROR_PAGE,               // We detected an error page.
+    ERROR_INTERSTITIAL_PAGE,        // We detected an interstitial page.
   };
 
   // Describes the parameters to control how to create an archive.
diff --git a/components/offline_pages/core/offline_page_model_impl.cc b/components/offline_pages/core/offline_page_model_impl.cc
index 2338be5..fd8c266 100644
--- a/components/offline_pages/core/offline_page_model_impl.cc
+++ b/components/offline_pages/core/offline_page_model_impl.cc
@@ -72,6 +72,11 @@
     case ArchiverResult::ERROR_SECURITY_CERTIFICATE:
       result = SavePageResult::SECURITY_CERTIFICATE_ERROR;
       break;
+    case ArchiverResult::ERROR_ERROR_PAGE:
+      result = SavePageResult::ERROR_PAGE;
+      break;
+    case ArchiverResult::ERROR_INTERSTITIAL_PAGE:
+      result = SavePageResult::INTERSTITIAL_PAGE;
     default:
       NOTREACHED();
       result = SavePageResult::CONTENT_UNAVAILABLE;
diff --git a/components/offline_pages/core/offline_page_types.h b/components/offline_pages/core/offline_page_types.h
index ae9e298a..4bb3342 100644
--- a/components/offline_pages/core/offline_page_types.h
+++ b/components/offline_pages/core/offline_page_types.h
@@ -34,6 +34,10 @@
   // are already locally accessible.
   SKIPPED,
   SECURITY_CERTIFICATE_ERROR,
+  // Returned when we detect trying to save a chrome error page.
+  ERROR_PAGE,
+  // Returned when we detect trying to save a chrome interstitial page.
+  INTERSTITIAL_PAGE,
   // NOTE: always keep this entry at the end. Add new result types only
   // immediately above this line. Make sure to update the corresponding
   // histogram enum accordingly.
diff --git a/components/omnibox/browser/BUILD.gn b/components/omnibox/browser/BUILD.gn
index 77c4626..a6fc2365 100644
--- a/components/omnibox/browser/BUILD.gn
+++ b/components/omnibox/browser/BUILD.gn
@@ -239,8 +239,8 @@
   sources = [
     "//components/test/data/omnibox/Shortcuts.no_fill_into_edit.sql",
     "//components/test/data/omnibox/Shortcuts.v0.sql",
-    "//components/test/data/omnibox/in_memory_url_index_test.db.txt",
-    "//components/test/data/omnibox/in_memory_url_index_test_limited.db.txt",
+    "//components/test/data/omnibox/in_memory_url_index_test.sql",
+    "//components/test/data/omnibox/in_memory_url_index_test_limited.sql",
   ]
   outputs = [
     "{{bundle_resources_dir}}/" +
diff --git a/components/omnibox/browser/in_memory_url_index_unittest.cc b/components/omnibox/browser/in_memory_url_index_unittest.cc
index 14996f64d..36320e9f4 100644
--- a/components/omnibox/browser/in_memory_url_index_unittest.cc
+++ b/components/omnibox/browser/in_memory_url_index_unittest.cc
@@ -242,79 +242,42 @@
   history::HistoryBackend* backend = history_service_->history_backend_.get();
   history_database_ = backend->db();
 
-  // Create and populate a working copy of the URL history database.
-  base::FilePath history_proto_path;
-  PathService::Get(base::DIR_SOURCE_ROOT, &history_proto_path);
-  history_proto_path = history_proto_path.AppendASCII("components");
-  history_proto_path = history_proto_path.AppendASCII("test");
-  history_proto_path = history_proto_path.AppendASCII("data");
-  history_proto_path = history_proto_path.AppendASCII("omnibox");
-  history_proto_path = history_proto_path.Append(TestDBName());
-  ASSERT_TRUE(base::PathExists(history_proto_path));
+  // TODO(shess): If/when this code gets refactored, consider including the
+  // schema in the golden file (as sqlite3 .dump would generate) and using
+  // sql::test::CreateDatabaseFromSQL() to load it.  The code above which
+  // creates the database can change in ways which may not reliably represent
+  // user databases on disks in the fleet.
 
-  std::ifstream proto_file(history_proto_path.value().c_str());
-  static const size_t kCommandBufferMaxSize = 2048;
-  char sql_cmd_line[kCommandBufferMaxSize];
-
+  // Execute the contents of a golden file to populate the [urls] and [visits]
+  // tables.
+  base::FilePath golden_path;
+  PathService::Get(base::DIR_SOURCE_ROOT, &golden_path);
+  golden_path = golden_path.AppendASCII("components/test/data/omnibox");
+  golden_path = golden_path.Append(TestDBName());
+  ASSERT_TRUE(base::PathExists(golden_path));
+  std::string sql;
+  ASSERT_TRUE(base::ReadFileToString(golden_path, &sql));
   sql::Connection& db(GetDB());
   ASSERT_TRUE(db.is_open());
-  {
-    sql::Transaction transaction(&db);
-    transaction.Begin();
-    while (!proto_file.eof()) {
-      proto_file.getline(sql_cmd_line, kCommandBufferMaxSize);
-      if (!proto_file.eof()) {
-        // We only process lines which begin with a upper-case letter.
-        if (base::IsAsciiUpper(sql_cmd_line[0])) {
-          std::string sql_cmd(sql_cmd_line);
-          sql::Statement sql_stmt(db.GetUniqueStatement(sql_cmd_line));
-          EXPECT_TRUE(sql_stmt.Run());
-        }
-      }
-    }
-    transaction.Commit();
-  }
+  ASSERT_TRUE(db.Execute(sql.c_str()));
 
-  // Update the last_visit_time table column in the "urls" table
-  // such that it represents a time relative to 'now'.
-  sql::Statement statement(db.GetUniqueStatement(
-      "SELECT" HISTORY_URL_ROW_FIELDS "FROM urls;"));
-  ASSERT_TRUE(statement.is_valid());
+  // Update [urls.last_visit_time] and [visits.visit_time] to represent a time
+  // relative to 'now'.
   base::Time time_right_now = base::Time::NowFromSystemTime();
   base::TimeDelta day_delta = base::TimeDelta::FromDays(1);
   {
-    sql::Transaction transaction(&db);
-    transaction.Begin();
-    while (statement.Step()) {
-      history::URLRow row;
-      history_database_->FillURLRow(statement, &row);
-      base::Time last_visit = time_right_now;
-      for (int64_t i = row.last_visit().ToInternalValue(); i > 0; --i)
-        last_visit -= day_delta;
-      row.set_last_visit(last_visit);
-      history_database_->UpdateURLRow(row.id(), row);
-    }
-    transaction.Commit();
+    sql::Statement s(db.GetUniqueStatement(
+        "UPDATE urls SET last_visit_time = ? - ? * last_visit_time"));
+    s.BindInt64(0, time_right_now.ToInternalValue());
+    s.BindInt64(1, day_delta.ToInternalValue());
+    ASSERT_TRUE(s.Run());
   }
-
-  // Update the visit_time table column in the "visits" table
-  // such that it represents a time relative to 'now'.
-  statement.Assign(db.GetUniqueStatement(
-      "SELECT" HISTORY_VISIT_ROW_FIELDS "FROM visits;"));
-  ASSERT_TRUE(statement.is_valid());
   {
-    sql::Transaction transaction(&db);
-    transaction.Begin();
-    while (statement.Step()) {
-      history::VisitRow row;
-      history_database_->FillVisitRow(statement, &row);
-      base::Time last_visit = time_right_now;
-      for (int64_t i = row.visit_time.ToInternalValue(); i > 0; --i)
-        last_visit -= day_delta;
-      row.visit_time = last_visit;
-      history_database_->UpdateVisitRow(row);
-    }
-    transaction.Commit();
+    sql::Statement s(db.GetUniqueStatement(
+        "UPDATE visits SET visit_time = ? - ? * visit_time"));
+    s.BindInt64(0, time_right_now.ToInternalValue());
+    s.BindInt64(1, day_delta.ToInternalValue());
+    ASSERT_TRUE(s.Run());
   }
 
   // Set up a simple template URL service with a default search engine.
@@ -336,7 +299,7 @@
 }
 
 base::FilePath::StringType InMemoryURLIndexTest::TestDBName() const {
-    return FILE_PATH_LITERAL("in_memory_url_index_test.db.txt");
+  return FILE_PATH_LITERAL("in_memory_url_index_test.sql");
 }
 
 bool InMemoryURLIndexTest::InitializeInMemoryURLIndexInSetUp() const {
@@ -494,7 +457,7 @@
 };
 
 base::FilePath::StringType LimitedInMemoryURLIndexTest::TestDBName() const {
-  return FILE_PATH_LITERAL("in_memory_url_index_test_limited.db.txt");
+  return FILE_PATH_LITERAL("in_memory_url_index_test_limited.sql");
 }
 
 bool LimitedInMemoryURLIndexTest::InitializeInMemoryURLIndexInSetUp() const {
diff --git a/components/payments/android/BUILD.gn b/components/payments/android/BUILD.gn
new file mode 100644
index 0000000..d83c9ec3
--- /dev/null
+++ b/components/payments/android/BUILD.gn
@@ -0,0 +1,29 @@
+# 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.
+
+static_library("android") {
+  sources = [
+    "web_app_manifest_section_table.cc",
+    "web_app_manifest_section_table.h",
+  ]
+
+  deps = [
+    "//components/payments/content:mojom_parser",
+    "//components/webdata/common",
+    "//sql",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "web_app_manifest_section_table_unittest.cc",
+  ]
+
+  deps = [
+    ":android",
+    "//base",
+    "//testing/gtest",
+  ]
+}
diff --git a/components/payments/android/DEPS b/components/payments/android/DEPS
new file mode 100644
index 0000000..0d58d2f
--- /dev/null
+++ b/components/payments/android/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+  "+components/payments/content",
+  "+components/webdata/common",
+  "+sql",
+]
\ No newline at end of file
diff --git a/components/payments/android/web_app_manifest_section_table.cc b/components/payments/android/web_app_manifest_section_table.cc
new file mode 100644
index 0000000..6094bc3
--- /dev/null
+++ b/components/payments/android/web_app_manifest_section_table.cc
@@ -0,0 +1,146 @@
+// 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/payments/android/web_app_manifest_section_table.h"
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/logging.h"
+#include "sql/statement.h"
+#include "sql/transaction.h"
+
+namespace payments {
+namespace {
+
+// Note that the fingerprint is calculated with SHA-256.
+const size_t kFingerPrintLength = 32;
+
+WebDatabaseTable::TypeKey GetKey() {
+  // We just need a unique constant. Use the address of a static that
+  // COMDAT folding won't touch in an optimizing linker.
+  static int table_key = 0;
+  return reinterpret_cast<void*>(&table_key);
+}
+
+// Converts 2-dimensional vector |fingerprints| to 1-dimesional vector.
+std::unique_ptr<std::vector<uint8_t>> SerializeFingerPrints(
+    const std::vector<std::vector<uint8_t>>& fingerprints) {
+  auto serialized_fingerprints = base::MakeUnique<std::vector<uint8_t>>();
+
+  for (const auto& fingerprint : fingerprints) {
+    DCHECK_EQ(fingerprint.size(), kFingerPrintLength);
+    serialized_fingerprints->insert(serialized_fingerprints->end(),
+                                    fingerprint.begin(), fingerprint.end());
+  }
+
+  return serialized_fingerprints;
+}
+
+// Converts 1-dimensional vector created by SerializeFingerPrints back to
+// 2-dimensional vector. Each vector of the second dimensional vector has exact
+// kFingerPrintLength number of elements.
+bool DeserializeFingerPrints(
+    const std::vector<uint8_t>& fingerprints,
+    std::vector<std::vector<uint8_t>>& deserialized_fingerprints) {
+  if (fingerprints.size() % kFingerPrintLength != 0)
+    return false;
+
+  for (size_t i = 0; i < fingerprints.size(); i += kFingerPrintLength) {
+    deserialized_fingerprints.emplace_back(
+        fingerprints.begin() + i,
+        fingerprints.begin() + i + kFingerPrintLength);
+  }
+  return true;
+}
+
+}  // namespace
+
+WebAppManifestSectionTable::WebAppManifestSectionTable() {}
+
+WebAppManifestSectionTable::~WebAppManifestSectionTable() {}
+
+WebAppManifestSectionTable* WebAppManifestSectionTable::FromWebDatabase(
+    WebDatabase* db) {
+  return static_cast<WebAppManifestSectionTable*>(db->GetTable(GetKey()));
+}
+
+WebDatabaseTable::TypeKey WebAppManifestSectionTable::GetTypeKey() const {
+  return GetKey();
+}
+
+bool WebAppManifestSectionTable::CreateTablesIfNecessary() {
+  if (!db_->Execute("CREATE TABLE IF NOT EXISTS web_app_manifest_section ( "
+                    "id VARCHAR PRIMARY KEY, "
+                    "min_version INTEGER NOT NULL DEFAULT 0, "
+                    "fingerprints BLOB) ")) {
+    NOTREACHED();
+    return false;
+  }
+
+  return true;
+}
+
+bool WebAppManifestSectionTable::IsSyncable() {
+  return false;
+}
+
+bool WebAppManifestSectionTable::MigrateToVersion(
+    int version,
+    bool* update_compatible_version) {
+  return true;
+}
+
+bool WebAppManifestSectionTable::AddWebAppManifest(
+    mojom::WebAppManifestSection* manifest) {
+  DCHECK(manifest);
+  DCHECK(!manifest->id.empty());
+
+  sql::Statement s(
+      db_->GetUniqueStatement("INSERT OR REPLACE INTO web_app_manifest_section "
+                              "(id, min_version, fingerprints) "
+                              "VALUES (?, ?, ?)"));
+  int index = 0;
+  s.BindString(index++, manifest->id);
+  s.BindInt64(index++, manifest->min_version);
+  std::unique_ptr<std::vector<uint8_t>> serialized_fingerprints =
+      SerializeFingerPrints(manifest->fingerprints);
+  s.BindBlob(index, serialized_fingerprints->data(),
+             serialized_fingerprints->size());
+  if (!s.Run())
+    return false;
+
+  return true;
+}
+
+mojom::WebAppManifestSectionPtr WebAppManifestSectionTable::GetWebAppManifest(
+    const std::string& web_app) {
+  sql::Statement s(
+      db_->GetUniqueStatement("SELECT id, min_version, fingerprints "
+                              "FROM web_app_manifest_section "
+                              "WHERE id=?"));
+  s.BindString(0, web_app);
+
+  if (!s.Step())
+    return nullptr;
+
+  mojom::WebAppManifestSectionPtr manifest =
+      mojom::WebAppManifestSection::New();
+
+  int index = 0;
+  manifest->id = s.ColumnString(index++);
+  manifest->min_version = s.ColumnInt64(index++);
+
+  std::vector<uint8_t> fingerprints;
+  if (!s.ColumnBlobAsVector(index, &fingerprints))
+    return nullptr;
+
+  if (!DeserializeFingerPrints(fingerprints, manifest->fingerprints))
+    return nullptr;
+
+  return manifest;
+}
+
+}  // payments
diff --git a/components/payments/android/web_app_manifest_section_table.h b/components/payments/android/web_app_manifest_section_table.h
new file mode 100644
index 0000000..44a5c046
--- /dev/null
+++ b/components/payments/android/web_app_manifest_section_table.h
@@ -0,0 +1,57 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_PAYMENTS_ANDROID_WEB_APP_MANIFEST_SECTION_TABLE_H_
+#define COMPONENTS_PAYMENTS_ANDROID_WEB_APP_MANIFEST_SECTION_TABLE_H_
+
+#include <string>
+#include <vector>
+
+#include "components/payments/content/payment_manifest_parser.mojom.h"
+#include "components/webdata/common/web_database.h"
+#include "components/webdata/common/web_database_table.h"
+
+namespace payments {
+
+// This class manages web_app_manifest_section table in SQLite database. It
+// expects the following schema.
+// The interfaces should only be accessed on DB thread.
+//
+// web_app_manifest_section The table stores the contents in
+//                          WebAppManifestSection.
+//
+//  id                      The package name of the app.
+//  min_version             Minimum version number of the app.
+//  fingerprints            The result of SHA256(signing certificate bytes) for
+//                          each certificate in the app.
+//
+class WebAppManifestSectionTable : public WebDatabaseTable {
+ public:
+  WebAppManifestSectionTable();
+  ~WebAppManifestSectionTable() override;
+
+  // Retrieves the WebAppManifestSectionTable* owned by |db|.
+  static WebAppManifestSectionTable* FromWebDatabase(WebDatabase* db);
+
+  // WebDatabaseTable:
+  WebDatabaseTable::TypeKey GetTypeKey() const override;
+  bool CreateTablesIfNecessary() override;
+  bool IsSyncable() override;
+  bool MigrateToVersion(int version, bool* update_compatible_version) override;
+
+  // Adds the web app |*manifest|. Note that the previous web app manifest will
+  // be deleted.
+  bool AddWebAppManifest(mojom::WebAppManifestSection* manifest);
+
+  // Gets manifest of the |web_app|. Returns nullptr if no manifest exists for
+  // the |web_app|.
+  mojom::WebAppManifestSectionPtr GetWebAppManifest(const std::string& web_app);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(WebAppManifestSectionTable);
+};
+
+}  // namespace payments
+
+#endif  // COMPONENTS_PAYMENTS_ANDROID_WEB_APP_MANIFEST_SECTION_TABLE_H_
diff --git a/components/payments/android/web_app_manifest_section_table_unittest.cc b/components/payments/android/web_app_manifest_section_table_unittest.cc
new file mode 100644
index 0000000..842c7e82
--- /dev/null
+++ b/components/payments/android/web_app_manifest_section_table_unittest.cc
@@ -0,0 +1,138 @@
+// 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/payments/android/web_app_manifest_section_table.h"
+
+#include "base/files/scoped_temp_dir.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace payments {
+namespace {
+
+class WebAppManifestSectionTableTest : public testing::Test {
+ public:
+  WebAppManifestSectionTableTest() {}
+  ~WebAppManifestSectionTableTest() override {}
+
+ protected:
+  void SetUp() override {
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+    file_ = temp_dir_.GetPath().AppendASCII("TestWebDatabase");
+
+    table_.reset(new WebAppManifestSectionTable);
+    db_.reset(new WebDatabase);
+    db_->AddTable(table_.get());
+    ASSERT_EQ(sql::INIT_OK, db_->Init(file_));
+  }
+
+  void TearDown() override {}
+
+  std::vector<uint8_t> GenerateFingerprint(uint8_t seed) {
+    std::vector<uint8_t> fingerprint;
+    // Note that the fingerprint is calculated with SHA-256, so the length is
+    // 32.
+    for (size_t i = 0; i < 32U; i++) {
+      fingerprint.push_back((seed + i) % 256U);
+    }
+    return fingerprint;
+  }
+
+  base::FilePath file_;
+  base::ScopedTempDir temp_dir_;
+  std::unique_ptr<WebAppManifestSectionTable> table_;
+  std::unique_ptr<WebDatabase> db_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(WebAppManifestSectionTableTest);
+};
+
+TEST_F(WebAppManifestSectionTableTest, GetNonExistManifest) {
+  WebAppManifestSectionTable* web_app_manifest_section_table =
+      WebAppManifestSectionTable::FromWebDatabase(db_.get());
+  mojom::WebAppManifestSectionPtr retrieved_manifest =
+      web_app_manifest_section_table->GetWebAppManifest("https://bobpay.com");
+  ASSERT_TRUE(retrieved_manifest.get() == nullptr);
+}
+
+TEST_F(WebAppManifestSectionTableTest, AddAndGetManifest) {
+  std::vector<uint8_t> fingerprint_one = GenerateFingerprint(1);
+  std::vector<uint8_t> fingerprint_two = GenerateFingerprint(32);
+
+  // create a bobpay web app manifest.
+  mojom::WebAppManifestSectionPtr manifest =
+      mojom::WebAppManifestSection::New();
+  manifest->id = "com.bobpay";
+  manifest->min_version = static_cast<int64_t>(1);
+  manifest->fingerprints.push_back(fingerprint_one);
+  manifest->fingerprints.push_back(fingerprint_two);
+
+  // Adds the manifest to the table.
+  WebAppManifestSectionTable* web_app_manifest_section_table =
+      WebAppManifestSectionTable::FromWebDatabase(db_.get());
+  ASSERT_TRUE(
+      web_app_manifest_section_table->AddWebAppManifest(manifest.get()));
+
+  // Gets and verifys the manifest.
+  mojom::WebAppManifestSectionPtr retrieved_manifest =
+      web_app_manifest_section_table->GetWebAppManifest("com.bobpay");
+  ASSERT_EQ(retrieved_manifest->id, "com.bobpay");
+  ASSERT_EQ(retrieved_manifest->min_version, 1);
+  ASSERT_EQ(retrieved_manifest->fingerprints.size(), 2U);
+
+  // Verify the two fingerprints.
+  ASSERT_TRUE(retrieved_manifest->fingerprints[0] == fingerprint_one);
+  ASSERT_TRUE(retrieved_manifest->fingerprints[1] == fingerprint_two);
+}
+
+TEST_F(WebAppManifestSectionTableTest, AddAndGetMultipleManifests) {
+  std::vector<uint8_t> fingerprint_one = GenerateFingerprint(1);
+  std::vector<uint8_t> fingerprint_two = GenerateFingerprint(32);
+  std::vector<uint8_t> fingerprint_three = GenerateFingerprint(2);
+  std::vector<uint8_t> fingerprint_four = GenerateFingerprint(30);
+
+  WebAppManifestSectionTable* web_app_manifest_section_table =
+      WebAppManifestSectionTable::FromWebDatabase(db_.get());
+
+  // Adds bobpay manifest to the table.
+  mojom::WebAppManifestSectionPtr manifest_1 =
+      mojom::WebAppManifestSection::New();
+  manifest_1->id = "com.bobpay";
+  manifest_1->min_version = static_cast<int64_t>(1);
+  // Adds two finger prints.
+  manifest_1->fingerprints.push_back(fingerprint_one);
+  manifest_1->fingerprints.push_back(fingerprint_two);
+  ASSERT_TRUE(
+      web_app_manifest_section_table->AddWebAppManifest(manifest_1.get()));
+
+  // Adds alicepay manifest to the table.
+  mojom::WebAppManifestSectionPtr manifest_2 =
+      mojom::WebAppManifestSection::New();
+  manifest_2->id = "com.alicepay";
+  manifest_2->min_version = static_cast<int64_t>(2);
+  // Adds two finger prints.
+  manifest_2->fingerprints.push_back(fingerprint_three);
+  manifest_2->fingerprints.push_back(fingerprint_four);
+  ASSERT_TRUE(
+      web_app_manifest_section_table->AddWebAppManifest(manifest_2.get()));
+
+  // Verifys bobpay manifest.
+  mojom::WebAppManifestSectionPtr bobpay_manifest =
+      web_app_manifest_section_table->GetWebAppManifest("com.bobpay");
+  ASSERT_EQ(bobpay_manifest->id, "com.bobpay");
+  ASSERT_EQ(bobpay_manifest->min_version, 1);
+  ASSERT_EQ(bobpay_manifest->fingerprints.size(), 2U);
+  ASSERT_TRUE(bobpay_manifest->fingerprints[0] == fingerprint_one);
+  ASSERT_TRUE(bobpay_manifest->fingerprints[1] == fingerprint_two);
+
+  // Verifys alicepay manifest.
+  mojom::WebAppManifestSectionPtr alicepay_manifest =
+      web_app_manifest_section_table->GetWebAppManifest("com.alicepay");
+  ASSERT_EQ(alicepay_manifest->id, "com.alicepay");
+  ASSERT_EQ(alicepay_manifest->min_version, 2);
+  ASSERT_EQ(alicepay_manifest->fingerprints.size(), 2U);
+  ASSERT_TRUE(alicepay_manifest->fingerprints[0] == fingerprint_three);
+  ASSERT_TRUE(alicepay_manifest->fingerprints[1] == fingerprint_four);
+}
+}
+}  // payments
diff --git a/components/payments/core/address_normalizer.cc b/components/payments/core/address_normalizer.cc
index 5fdd2558..3114c5a 100644
--- a/components/payments/core/address_normalizer.cc
+++ b/components/payments/core/address_normalizer.cc
@@ -153,8 +153,9 @@
   }
 }
 
-void AddressNormalizer::OnAddressRulesLoaded(const std::string& region_code,
-                                             bool success) {
+void AddressNormalizer::OnAddressValidationRulesLoaded(
+    const std::string& region_code,
+    bool success) {
   // Check if an address normalization is pending.
   auto it = pending_normalization_.find(region_code);
   if (it != pending_normalization_.end()) {
@@ -165,4 +166,4 @@
   }
 }
 
-}  // namespace payments
+}  // namespace payments
\ No newline at end of file
diff --git a/components/payments/core/address_normalizer.h b/components/payments/core/address_normalizer.h
index 2d5b1d5..f3d2a24 100644
--- a/components/payments/core/address_normalizer.h
+++ b/components/payments/core/address_normalizer.h
@@ -76,8 +76,8 @@
  private:
   // Called when the validation rules for the |region_code| have finished
   // loading. Implementation of the LoadRulesListener interface.
-  void OnAddressRulesLoaded(const std::string& region_code,
-                            bool success) override;
+  void OnAddressValidationRulesLoaded(const std::string& region_code,
+                                      bool success) override;
 
   // Map associating a region code to pending normalizations.
   std::map<std::string, std::vector<std::unique_ptr<Request>>>
diff --git a/components/search_engines/template_url.cc b/components/search_engines/template_url.cc
index da62aaa..a3a162a 100644
--- a/components/search_engines/template_url.cc
+++ b/components/search_engines/template_url.cc
@@ -505,7 +505,9 @@
           // suffix, then this is not a match.
           base::StringPiece search_term =
               base::StringPiece(source).substr(value.begin, value.len);
-          if (!search_term.starts_with(search_term_value_prefix_) ||
+          if (search_term.size() < (search_term_value_prefix_.size() +
+                                    search_term_value_suffix_.size()) ||
+              !search_term.starts_with(search_term_value_prefix_) ||
               !search_term.ends_with(search_term_value_suffix_))
             continue;
 
diff --git a/components/search_engines/template_url_unittest.cc b/components/search_engines/template_url_unittest.cc
index 170807f1..3177462 100644
--- a/components/search_engines/template_url_unittest.cc
+++ b/components/search_engines/template_url_unittest.cc
@@ -1267,6 +1267,14 @@
   // Don't match if the prefix and suffix aren't there.
   EXPECT_FALSE(url.ExtractSearchTermsFromURL(
       GURL("http://www.example.com/?q=invalid"), search_terms_data_, &result));
+
+  // Don't match if the prefix and suffix overlap.
+  TemplateURLData data_with_overlap;
+  data.alternate_urls.push_back(
+      "http://www.example.com/?q=goo{searchTerms}oogle");
+  TemplateURL url_with_overlap(data);
+  EXPECT_FALSE(url_with_overlap.ExtractSearchTermsFromURL(
+      GURL("http://www.example.com/?q=google"), search_terms_data_, &result));
 }
 
 TEST_F(TemplateURLTest, HasSearchTermsReplacementKey) {
diff --git a/components/signin/core/browser/account_tracker_service_unittest.cc b/components/signin/core/browser/account_tracker_service_unittest.cc
index 284cefb..9fae742 100644
--- a/components/signin/core/browser/account_tracker_service_unittest.cc
+++ b/components/signin/core/browser/account_tracker_service_unittest.cc
@@ -15,7 +15,6 @@
 #include "components/signin/core/browser/account_fetcher_service.h"
 #include "components/signin/core/browser/account_info.h"
 #include "components/signin/core/browser/account_tracker_service.h"
-#include "components/signin/core/browser/child_account_info_fetcher.h"
 #include "components/signin/core/browser/fake_account_fetcher_service.h"
 #include "components/signin/core/browser/test_signin_client.h"
 #include "components/signin/core/common/signin_pref_names.h"
@@ -256,8 +255,6 @@
   ~AccountTrackerServiceTest() override {}
 
   void SetUp() override {
-    ChildAccountInfoFetcher::InitializeForTests();
-
     fake_oauth2_token_service_.reset(new FakeOAuth2TokenService());
 
     pref_service_.registry()->RegisterListPref(
diff --git a/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerHelper.java b/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerHelper.java
index 3ec989d..c8d409c 100644
--- a/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerHelper.java
+++ b/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerHelper.java
@@ -21,14 +21,12 @@
 import java.util.Locale;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
 import java.util.regex.Pattern;
 
 /**
  * AccountManagerHelper wraps our access of AccountManager in Android.
  *
- * Use the {@link #initializeAccountManagerHelper} to instantiate it.
- * After initialization, instance get be acquired by calling {@link #get}.
+ * Use the AccountManagerHelper.get(someContext) to instantiate it
  */
 public class AccountManagerHelper {
     private static final String TAG = "Sync_Signin";
@@ -48,7 +46,9 @@
     @VisibleForTesting
     public static final String FEATURE_IS_CHILD_ACCOUNT_KEY = "service_uca";
 
-    private static final AtomicReference<AccountManagerHelper> sInstance = new AtomicReference<>();
+    private static final Object sLock = new Object();
+
+    private static AccountManagerHelper sAccountManagerHelper;
 
     private final AccountManagerDelegate mAccountManager;
 
@@ -87,21 +87,26 @@
      * @param delegate the custom AccountManagerDelegate to use.
      */
     public static void initializeAccountManagerHelper(AccountManagerDelegate delegate) {
-        if (!sInstance.compareAndSet(null, new AccountManagerHelper(delegate))) {
-            throw new IllegalStateException("AccountManagerHelper is already initialized!");
+        synchronized (sLock) {
+            assert sAccountManagerHelper == null;
+            sAccountManagerHelper = new AccountManagerHelper(delegate);
         }
     }
 
     /**
-     * Singleton instance getter. Singleton must be initialized before calling this
-     * (by initializeAccountManagerHelper or overrideAccountManagerHelperForTests).
+     * A getter method for AccountManagerHelper singleton which also initializes it if not wasn't
+     * already initialized.
      *
-     * @return a singleton instance
+     * @return a singleton instance of the AccountManagerHelper
      */
     public static AccountManagerHelper get() {
-        AccountManagerHelper instance = sInstance.get();
-        assert instance != null : "AccountManagerHelper is not initialized!";
-        return instance;
+        synchronized (sLock) {
+            if (sAccountManagerHelper == null) {
+                sAccountManagerHelper =
+                        new AccountManagerHelper(new SystemAccountManagerDelegate());
+            }
+        }
+        return sAccountManagerHelper;
     }
 
     /**
@@ -115,16 +120,9 @@
     @VisibleForTesting
     public static void overrideAccountManagerHelperForTests(
             Context context, AccountManagerDelegate delegate) {
-        sInstance.set(new AccountManagerHelper(delegate));
-    }
-
-    /**
-     * Resets custom AccountManagerHelper set with {@link #overrideAccountManagerHelperForTests}.
-     * Only for use in Tests.
-     */
-    @VisibleForTesting
-    public static void resetAccountManagerHelperForTests() {
-        sInstance.set(null);
+        synchronized (sLock) {
+            sAccountManagerHelper = new AccountManagerHelper(delegate);
+        }
     }
 
     /**
diff --git a/components/signin/core/browser/android/java/src/org/chromium/components/signin/ChildAccountInfoFetcher.java b/components/signin/core/browser/android/java/src/org/chromium/components/signin/ChildAccountInfoFetcher.java
index 42969b7..b2e8ae1 100644
--- a/components/signin/core/browser/android/java/src/org/chromium/components/signin/ChildAccountInfoFetcher.java
+++ b/components/signin/core/browser/android/java/src/org/chromium/components/signin/ChildAccountInfoFetcher.java
@@ -89,13 +89,6 @@
         nativeSetIsChildAccount(mNativeAccountFetcherService, mAccountId, isChildAccount);
     }
 
-    @CalledByNative
-    private static void initializeForTests() {
-        Context context = ContextUtils.getApplicationContext();
-        AccountManagerDelegate delegate = new SystemAccountManagerDelegate();
-        AccountManagerHelper.overrideAccountManagerHelperForTests(context, delegate);
-    }
-
     private static native void nativeSetIsChildAccount(
             long accountFetcherServicePtr, String accountId, boolean isChildAccount);
 }
diff --git a/components/signin/core/browser/android/javatests/src/org/chromium/components/signin/test/AccountManagerHelperTest.java b/components/signin/core/browser/android/javatests/src/org/chromium/components/signin/test/AccountManagerHelperTest.java
index 7efc048..0a73fe86 100644
--- a/components/signin/core/browser/android/javatests/src/org/chromium/components/signin/test/AccountManagerHelperTest.java
+++ b/components/signin/core/browser/android/javatests/src/org/chromium/components/signin/test/AccountManagerHelperTest.java
@@ -31,12 +31,6 @@
         mHelper = AccountManagerHelper.get();
     }
 
-    @Override
-    protected void tearDown() throws Exception {
-        AccountManagerHelper.resetAccountManagerHelperForTests();
-        super.tearDown();
-    }
-
     @SmallTest
     public void testCanonicalAccount() throws InterruptedException {
         addTestAccount("test@gmail.com", "password");
diff --git a/components/signin/core/browser/child_account_info_fetcher.cc b/components/signin/core/browser/child_account_info_fetcher.cc
index 804444b..15bc0538 100644
--- a/components/signin/core/browser/child_account_info_fetcher.cc
+++ b/components/signin/core/browser/child_account_info_fetcher.cc
@@ -31,9 +31,3 @@
 
 ChildAccountInfoFetcher::~ChildAccountInfoFetcher() {
 }
-
-void ChildAccountInfoFetcher::InitializeForTests() {
-#if defined(OS_ANDROID)
-  ChildAccountInfoFetcherAndroid::InitializeForTests();
-#endif
-}
diff --git a/components/signin/core/browser/child_account_info_fetcher.h b/components/signin/core/browser/child_account_info_fetcher.h
index 0b3623d..1c35c48d 100644
--- a/components/signin/core/browser/child_account_info_fetcher.h
+++ b/components/signin/core/browser/child_account_info_fetcher.h
@@ -34,8 +34,6 @@
       net::URLRequestContextGetter* request_context_getter,
       invalidation::InvalidationService* invalidation_service);
   virtual ~ChildAccountInfoFetcher();
-
-  static void InitializeForTests();
 };
 
 #endif  // COMPONENTS_SIGNIN_CORE_BROWSER_CHILD_ACCOUNT_INFO_FETCHER_H_
diff --git a/components/signin/core/browser/child_account_info_fetcher_android.cc b/components/signin/core/browser/child_account_info_fetcher_android.cc
index 34446da..654a815 100644
--- a/components/signin/core/browser/child_account_info_fetcher_android.cc
+++ b/components/signin/core/browser/child_account_info_fetcher_android.cc
@@ -29,11 +29,6 @@
       new ChildAccountInfoFetcherAndroid(service, account_id, account_name));
 }
 
-void ChildAccountInfoFetcherAndroid::InitializeForTests() {
-  Java_ChildAccountInfoFetcher_initializeForTests(
-      base::android::AttachCurrentThread());
-}
-
 ChildAccountInfoFetcherAndroid::ChildAccountInfoFetcherAndroid(
     AccountFetcherService* service,
     const std::string& account_id,
diff --git a/components/signin/core/browser/child_account_info_fetcher_android.h b/components/signin/core/browser/child_account_info_fetcher_android.h
index 05f30a1..cb06fedb 100644
--- a/components/signin/core/browser/child_account_info_fetcher_android.h
+++ b/components/signin/core/browser/child_account_info_fetcher_android.h
@@ -20,8 +20,6 @@
       AccountFetcherService* service,
       const std::string& account_id);
 
-  static void InitializeForTests();
-
   // Register JNI methods.
   static bool Register(JNIEnv* env);
 
diff --git a/components/sync/android/javatests/src/org/chromium/components/sync/AndroidSyncSettingsTest.java b/components/sync/android/javatests/src/org/chromium/components/sync/AndroidSyncSettingsTest.java
index af72394..29d952b 100644
--- a/components/sync/android/javatests/src/org/chromium/components/sync/AndroidSyncSettingsTest.java
+++ b/components/sync/android/javatests/src/org/chromium/components/sync/AndroidSyncSettingsTest.java
@@ -126,12 +126,6 @@
         return account;
     }
 
-    @Override
-    protected void tearDown() throws Exception {
-        AccountManagerHelper.resetAccountManagerHelperForTests();
-        super.tearDown();
-    }
-
     private void enableChromeSyncOnUiThread() {
         ThreadUtils.runOnUiThreadBlocking(new Runnable() {
             @Override
diff --git a/components/sync/base/cryptographer.cc b/components/sync/base/cryptographer.cc
index 67e24ad9..c585965 100644
--- a/components/sync/base/cryptographer.cc
+++ b/components/sync/base/cryptographer.cc
@@ -34,10 +34,10 @@
       default_nigori_name_(other.default_nigori_name_) {
   for (NigoriMap::const_iterator it = other.nigoris_.begin();
        it != other.nigoris_.end(); ++it) {
-    std::string encryption_key, mac_key;
-    it->second->ExportKeys(&encryption_key, &mac_key);
+    std::string user_key, encryption_key, mac_key;
+    it->second->ExportKeys(&user_key, &encryption_key, &mac_key);
     linked_ptr<Nigori> nigori_copy(new Nigori());
-    nigori_copy->InitByImport(encryption_key, mac_key);
+    nigori_copy->InitByImport(user_key, encryption_key, mac_key);
     nigoris_.insert(std::make_pair(it->first, nigori_copy));
   }
 
@@ -150,7 +150,8 @@
     const Nigori& nigori = *it->second;
     sync_pb::NigoriKey* key = bag.add_key();
     key->set_name(it->first);
-    nigori.ExportKeys(key->mutable_encryption_key(), key->mutable_mac_key());
+    nigori.ExportKeys(key->mutable_user_key(), key->mutable_encryption_key(),
+                      key->mutable_mac_key());
   }
 
   // Encrypt the bag with the default Nigori.
@@ -305,7 +306,8 @@
     // Only use this key if we don't already know about it.
     if (nigoris_.end() == nigoris_.find(key.name())) {
       std::unique_ptr<Nigori> new_nigori(new Nigori);
-      if (!new_nigori->InitByImport(key.encryption_key(), key.mac_key())) {
+      if (!new_nigori->InitByImport(key.user_key(), key.encryption_key(),
+                                    key.mac_key())) {
         NOTREACHED();
         continue;
       }
@@ -346,7 +348,8 @@
   if (iter == nigoris_.end())
     return std::string();
   sync_pb::NigoriKey key;
-  if (!iter->second->ExportKeys(key.mutable_encryption_key(),
+  if (!iter->second->ExportKeys(key.mutable_user_key(),
+                                key.mutable_encryption_key(),
                                 key.mutable_mac_key()))
     return std::string();
   return key.SerializeAsString();
@@ -361,7 +364,8 @@
     return false;
 
   std::unique_ptr<Nigori> nigori(new Nigori);
-  if (!nigori->InitByImport(key.encryption_key(), key.mac_key())) {
+  if (!nigori->InitByImport(key.user_key(), key.encryption_key(),
+                            key.mac_key())) {
     NOTREACHED();
     return false;
   }
diff --git a/components/sync/base/nigori.cc b/components/sync/base/nigori.cc
index 03c61b2..53f32baf7 100644
--- a/components/sync/base/nigori.cc
+++ b/components/sync/base/nigori.cc
@@ -79,6 +79,12 @@
   if (!user_salt->GetRawKey(&raw_user_salt))
     return false;
 
+  // Kuser = PBKDF2(P, Suser, Nuser, 16)
+  user_key_ = SymmetricKey::DeriveKeyFromPassword(
+      SymmetricKey::AES, password, raw_user_salt, kUserIterations,
+      kDerivedKeySizeInBits);
+  DCHECK(user_key_);
+
   // Kenc = PBKDF2(P, Suser, Nenc, 16)
   encryption_key_ = SymmetricKey::DeriveKeyFromPassword(
       SymmetricKey::AES, password, raw_user_salt, kEncryptionIterations,
@@ -91,11 +97,14 @@
       kDerivedKeySizeInBits);
   DCHECK(mac_key_);
 
-  return encryption_key_ && mac_key_;
+  return user_key_ && encryption_key_ && mac_key_;
 }
 
-bool Nigori::InitByImport(const std::string& encryption_key,
+bool Nigori::InitByImport(const std::string& user_key,
+                          const std::string& encryption_key,
                           const std::string& mac_key) {
+  user_key_ = SymmetricKey::Import(SymmetricKey::AES, user_key);
+
   encryption_key_ = SymmetricKey::Import(SymmetricKey::AES, encryption_key);
   DCHECK(encryption_key_);
 
@@ -223,11 +232,15 @@
   return true;
 }
 
-bool Nigori::ExportKeys(std::string* encryption_key,
+bool Nigori::ExportKeys(std::string* user_key,
+                        std::string* encryption_key,
                         std::string* mac_key) const {
   DCHECK(encryption_key);
   DCHECK(mac_key);
 
+  if (user_key_)
+    user_key_->GetRawKey(user_key);
+
   return encryption_key_->GetRawKey(encryption_key) &&
          mac_key_->GetRawKey(mac_key);
 }
diff --git a/components/sync/base/nigori.h b/components/sync/base/nigori.h
index d0088c3..92c77b40 100644
--- a/components/sync/base/nigori.h
+++ b/components/sync/base/nigori.h
@@ -41,7 +41,8 @@
 
   // Initialize the client by importing the given keys instead of deriving new
   // ones.
-  bool InitByImport(const std::string& encryption_key,
+  bool InitByImport(const std::string& user_key,
+                    const std::string& encryption_key,
                     const std::string& mac_key);
 
   // Derives a secure lookup name from |type| and |name|. If |hostname|,
@@ -59,7 +60,9 @@
   bool Decrypt(const std::string& value, std::string* decrypted) const;
 
   // Exports the raw derived keys.
-  bool ExportKeys(std::string* encryption_key, std::string* mac_key) const;
+  bool ExportKeys(std::string* user_key,
+                  std::string* encryption_key,
+                  std::string* mac_key) const;
 
   static const char kSaltSalt[];  // The salt used to derive the user salt.
   static const size_t kSaltKeySizeInBits = 128;
@@ -68,10 +71,16 @@
   static const size_t kHashSize = 32;
 
   static const size_t kSaltIterations = 1001;
+  static const size_t kUserIterations = 1002;
   static const size_t kEncryptionIterations = 1003;
   static const size_t kSigningIterations = 1004;
 
  private:
+  // user_key isn't used any more, but legacy clients will fail to import a
+  // nigori node without one. We preserve it for the sake of those clients, but
+  // it should be removed once enough clients have upgraded to code that doesn't
+  // enforce its presence.
+  std::unique_ptr<crypto::SymmetricKey> user_key_;
   std::unique_ptr<crypto::SymmetricKey> encryption_key_;
   std::unique_ptr<crypto::SymmetricKey> mac_key_;
 };
diff --git a/components/sync/base/nigori_unittest.cc b/components/sync/base/nigori_unittest.cc
index 09bbcd75..9f1c1ba 100644
--- a/components/sync/base/nigori_unittest.cc
+++ b/components/sync/base/nigori_unittest.cc
@@ -126,12 +126,13 @@
   Nigori nigori1;
   EXPECT_TRUE(nigori1.InitByDerivation("example.com", "username", "password"));
 
+  std::string user_key;
   std::string encryption_key;
   std::string mac_key;
-  EXPECT_TRUE(nigori1.ExportKeys(&encryption_key, &mac_key));
+  EXPECT_TRUE(nigori1.ExportKeys(&user_key, &encryption_key, &mac_key));
 
   Nigori nigori2;
-  EXPECT_TRUE(nigori2.InitByImport(encryption_key, mac_key));
+  EXPECT_TRUE(nigori2.InitByImport(user_key, encryption_key, mac_key));
 
   std::string original("test");
   std::string plaintext;
@@ -151,5 +152,32 @@
   EXPECT_EQ(permuted1, permuted2);
 }
 
+TEST(SyncNigoriTest, InitByDerivationSetsUserKey) {
+  Nigori nigori;
+  EXPECT_TRUE(nigori.InitByDerivation("example.com", "username", "password"));
+
+  std::string user_key = "";
+  std::string encryption_key;
+  std::string mac_key;
+  EXPECT_TRUE(nigori.ExportKeys(&user_key, &encryption_key, &mac_key));
+
+  EXPECT_NE(user_key, "");
+}
+
+TEST(SyncNigoriTest, ToleratesEmptyUserKey) {
+  Nigori nigori1;
+  EXPECT_TRUE(nigori1.InitByDerivation("example.com", "username", "password"));
+
+  std::string user_key;
+  std::string encryption_key;
+  std::string mac_key;
+  EXPECT_TRUE(nigori1.ExportKeys(&user_key, &encryption_key, &mac_key));
+
+  Nigori nigori2;
+  EXPECT_TRUE(nigori2.InitByImport("", encryption_key, mac_key));
+
+  EXPECT_TRUE(nigori2.ExportKeys(&user_key, &encryption_key, &mac_key));
+}
+
 }  // anonymous namespace
 }  // namespace syncer
diff --git a/components/sync/engine_impl/model_type_worker_unittest.cc b/components/sync/engine_impl/model_type_worker_unittest.cc
index 486afe1..cc4fd1bd 100644
--- a/components/sync/engine_impl/model_type_worker_unittest.cc
+++ b/components/sync/engine_impl/model_type_worker_unittest.cc
@@ -216,7 +216,8 @@
       sync_pb::NigoriKey* key = bag.add_key();
 
       key->set_name(GetNigoriName(nigori));
-      nigori.ExportKeys(key->mutable_encryption_key(), key->mutable_mac_key());
+      nigori.ExportKeys(key->mutable_user_key(), key->mutable_encryption_key(),
+                        key->mutable_mac_key());
     }
 
     // Re-create the last nigori from that loop.
diff --git a/components/test/data/omnibox/in_memory_url_index_test.db.txt b/components/test/data/omnibox/in_memory_url_index_test.db.txt
deleted file mode 100644
index fcbcf84..0000000
--- a/components/test/data/omnibox/in_memory_url_index_test.db.txt
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
-  This file contains test data used
-  chrome/browser/history/in_memory_url_index_unittest.cc
-
-  It contains data for two types of history database tables:
-  1. the history URL database.
-  2. the visit database.
-
-  1.
-  The schema of the URL database is defined by HISTORY_URL_ROW_FIELDS found in
-  url_database.h and is equivalent to:
-
-  CREATE TABLE urls(id INTEGER PRIMARY KEY AUTOINCREMENT,
-                    url LONGVARCHAR,
-                    title LONGVARCHAR,
-                    visit_count INTEGER DEFAULT 0 NOT NULL,
-                    typed_count INTEGER DEFAULT 0 NOT NULL,
-                    last_visit_time INTEGER NOT NULL,
-                    hidden INTEGER DEFAULT 0 NOT NULL);
-
-  The quick history autocomplete provider filters out history items that:
-    1) have not been visited in kLowQualityMatchAgeLimitInDays, AND
-    2) for which the URL was not explicitly typed at least
-       kLowQualityMatchTypedLimit + 1 times, AND
-    3) have not been visited at least kLowQualityMatchVisitLimit + 1 times.
-  So we create history items in all of those combinations.
-
-  Note that the last_visit_time column for this test table represents the
-  relative number of days prior to 'today' to which the final column
-  value will be set during test setup. Beware: Do not set this number
-  to be equal to kLowQualityMatchAgeLimitInDays.
-
-  The ordering, URLs and titles must be kept in sync with the unit tests found
-  in in_memory_url_index_unittest.cc.
-
-  2.
-  The schema of the visit database is defined by HISTORY_VISIT_ROW_FIELDS
-  found in visit_database.h and is equivalent to:
-
-  CREATE TABLE visits(visit_id INTEGER PRIMARY KEY,
-                      url_id INTEGER NOT NULL,
-                      visit_time INTEGER NOT NULL,
-                      from_visit INTEGER,
-                      transition INTEGER DEFAULT 0 NOT NULL,
-                      segment_id INTEGER,
-                      is_indexed BOOLEAN,
-                      visit_duration INTEGER DEFAULT 0 NOT NULL)
-*/
-INSERT INTO "urls" VALUES(1,'http://www.reuters.com/article/idUSN0839880620100708','UPDATE 1-US 30-yr mortgage rate drops to new record low | Reuters',3,1,2,0);  // Qualifies
-INSERT INTO "urls" VALUES(2,'http://www.golfweek.com/news/2010/jul/08/goydos-opens-john-deere-classic-59/','Goydos opens John Deere Classic with 59',3,1,4,0);  // Qualifies
-INSERT INTO "urls" VALUES(3,'http://www.businessandmedia.org/articles/2010/20100708120415.aspx','LeBronomics: Could High Taxes Influence James'' Team Decision?',4,1,2,0);  // Qualifies
-INSERT INTO "urls" VALUES(4,'http://www.realclearmarkets.com/articles/2010/07/08/diversity_in_the_financial_sector_98562.html','RealClearMarkets - Racial, Gender Quotas in the Financial Bill?',4,1,4,0);  // Qualifies
-INSERT INTO "urls" VALUES(5,'http://drudgereport.com/','DRUDGE REPORT 2010',3,2,2,0);  // Qualifies
-INSERT INTO "urls" VALUES(6,'http://totalfinder.binaryage.com/','TotalFinder brings tabs to your native Finder and more!',3,2,4,0);  // Qualifies
-INSERT INTO "urls" VALUES(7,'http://getsharekit.com/','ShareKit : Drop-in Share Features for all iOS Apps',4,2,4,0);  // Qualifies
-INSERT INTO "urls" VALUES(8,'http://getsharekit.com/index.html','ShareKit : Drop-in Share Features for all iOS Apps',3,0,4,0);
-INSERT INTO "urls" VALUES(9,'http://en.wikipedia.org/wiki/Control-Z','Control-Z - Wikipedia, the free encyclopedia',0,0,6,0);
-INSERT INTO "urls" VALUES(10,'http://vmware.com/info?id=724','VMware Account Management Login',1,0,6,0);
-INSERT INTO "urls" VALUES(11,'http://www.tech-recipes.com/rx/2621/os_x_change_path_environment_variable/','OS X: Change your PATH environment variable | Mac system administration | Tech-Recipes',0,1,6,0);  // Qualifies
-INSERT INTO "urls" VALUES(12,'http://view.atdmt.com/PPJ/iview/194841301/direct;wi.160;hi.600/01?click=','',6,6,0,1);  // Qualifies
-INSERT INTO "urls" VALUES(15,'http://www.cnn.com/','CNN.com International - Breaking, World, Business, Sports, Entertainment and Video News',6,6,0,0);  // Qualifies
-INSERT INTO "urls" VALUES(16,'http://www.zdnet.com/','Technology News, Analysis, Comments and Product Reviews for IT Professionals | ZDNet',6,6,0,0);  // Qualifies
-INSERT INTO "urls" VALUES(17,'http://www.crash.net/','Crash.Net | Formula 1 & MotoGP | Motorsport News',6,6,0,0);  // Qualifies
-INSERT INTO "urls" VALUES(18,'http://www.theinquirer.net/','THE INQUIRER - Microprocessor, Server, Memory, PCS, Graphics, Networking, Storage',6,6,0,0);  // Qualifies
-INSERT INTO "urls" VALUES(19,'http://www.theregister.co.uk/','The Register: Sci/Tech News for the World',6,6,0,0);  // Qualifies
-INSERT INTO "urls" VALUES(20,'http://blogs.technet.com/markrussinovich/','Mark''s Blog - Site Home - TechNet Blogs',6,6,0,0);  // Qualifies
-INSERT INTO "urls" VALUES(21,'http://www.icu-project.org/','ICU Home Page (ICU - International Components for Unicode)',6,6,0,0);  // Qualifies
-INSERT INTO "urls" VALUES(22,'http://site.icu-project.org/','ICU Home Page (ICU - International Components for Unicode)',6,6,0,0);  // Qualifies
-INSERT INTO "urls" VALUES(23,'http://icu-project.org/apiref/icu4c/','ICU 4.2: Main Page',6,6,0,0);  // Qualifies
-INSERT INTO "urls" VALUES(24,'http://www.danilatos.com/event-test/ExperimentTest.html','Experimentation Harness',6,6,0,0);  // Qualifies
-INSERT INTO "urls" VALUES(25,'http://www.codeguru.com/','CodeGuru : codeguru',6,6,0,0);  // Qualifies
-INSERT INTO "urls" VALUES(26,'http://www.codeproject.com/','Your Development Resource - CodeProject',6,6,0,0);  // Qualifies
-INSERT INTO "urls" VALUES(27,'http://www.tomshardware.com/us/#redir','Tom''s Hardware: Hardware News, Tests and Reviews',6,6,0,0);  // Qualifies
-INSERT INTO "urls" VALUES(28,'http://www.ddj.com/windows/184416623','Dr. ABRACADABRA''s | Avoiding the Visual C++ Runtime Library | 2 1, 2003',6,6,0,0);  // Qualifies
-INSERT INTO "urls" VALUES(29,'http://svcs.cnn.com/weather/getForecast?time=34&mode=json_html&zipCode=336736767676&locCode=EGLL&celcius=true&csiID=csi2','',6,6,0,1);  // Qualifies
-INSERT INTO "urls" VALUES(30,'http://www.drudgery.com/Dogs%20and%20Mice','Life in the Slow Lane',8,2,2,0);  // Qualifies
-INSERT INTO "urls" VALUES(31,'http://www.redrudgerydo.com/','Music of the Wild Landscape',0,0,6,0);
-INSERT INTO "urls" VALUES(32,'https://NearlyPerfectResult.com/','Practically Perfect Search Result',99,99,0,0);  // Qualifies
-INSERT INTO "urls" VALUES(33,'http://QuiteUselessSearchResultxyz.com/','Practically Useless Search Result',4,0,99,0);  // Qualifies
-INSERT INTO "urls" VALUES(34,'http://FubarFubarAndFubar.com/','Situation Normal -- FUBARED',99,99,0,0);  // Qualifies
-INSERT INTO "urls" VALUES(35,'http://en.wikipedia.org/wiki/1%25_rule_(Internet_culture)','Do Not Need Title',2,2,0,0);  // Qualifies
-INSERT INTO "urls" VALUES(36,'http://default-engine.com?q=query','Query Query Query',2,2,0,0);  // Qualifies
-
-/*
- This file creates some visits, enough to test (in InMemoryURLIndexTest)
- the visits functionality, certainly not as many visits as are implied
- by the visit counts associated with the URLs above.
-*/
-INSERT INTO "visits" VALUES(1, 1, 2, 4, 0, 0, 1);
-INSERT INTO "visits" VALUES(2, 1, 5, 0, 1, 0, 1);
-INSERT INTO "visits" VALUES(3, 1, 12, 0, 0, 0, 1);
-INSERT INTO "visits" VALUES(4, 32, 1, 0, 0, 0, 1);
-INSERT INTO "visits" VALUES(5, 32, 2, 0, 0, 0, 1);
-INSERT INTO "visits" VALUES(6, 32, 3, 0, 0, 0, 1);
-INSERT INTO "visits" VALUES(7, 32, 4, 0, 0, 0, 1);
-INSERT INTO "visits" VALUES(8, 32, 5, 0, 0, 0, 1);
-INSERT INTO "visits" VALUES(9, 32, 6, 0, 0, 0, 1);
-INSERT INTO "visits" VALUES(10, 32, 7, 0, 0, 0, 1);
-INSERT INTO "visits" VALUES(11, 32, 8, 0, 0, 0, 1);
-INSERT INTO "visits" VALUES(12, 32, 9, 0, 0, 0, 1);
-INSERT INTO "visits" VALUES(13, 32, 10, 0, 0, 0, 1);
-INSERT INTO "visits" VALUES(14, 32, 11, 0, 0, 0, 1);
-INSERT INTO "visits" VALUES(15, 32, 12, 0, 0, 0, 1);
-INSERT INTO "visits" VALUES(16, 32, 13, 0, 0, 0, 1);
-INSERT INTO "visits" VALUES(17, 32, 14, 0, 0, 0, 1);
-INSERT INTO "visits" VALUES(18, 32, 15, 0, 1, 0, 1);
-INSERT INTO "visits" VALUES(19, 35, 0, 0, 1, 0, 1);
-INSERT INTO "visits" VALUES(20, 35, 7, 0, 1, 0, 1);
-INSERT INTO "visits" VALUES(21, 36, 1, 0, 1, 0, 1);
-INSERT INTO "visits" VALUES(22, 36, 2, 0, 1, 0, 1);
diff --git a/components/test/data/omnibox/in_memory_url_index_test.sql b/components/test/data/omnibox/in_memory_url_index_test.sql
new file mode 100644
index 0000000..93386ab
--- /dev/null
+++ b/components/test/data/omnibox/in_memory_url_index_test.sql
@@ -0,0 +1,106 @@
+-- This file contains test data used
+-- chrome/browser/history/in_memory_url_index_unittest.cc
+--
+-- It contains data for two types of history database tables:
+-- 1. the history URL database.
+-- 2. the visit database.
+--
+-- 1.
+-- The schema of the URL database is defined by HISTORY_URL_ROW_FIELDS found in
+-- url_database.h and is equivalent to:
+--
+-- CREATE TABLE urls(id INTEGER PRIMARY KEY AUTOINCREMENT,
+--                   url LONGVARCHAR,
+--                   title LONGVARCHAR,
+--                   visit_count INTEGER DEFAULT 0 NOT NULL,
+--                   typed_count INTEGER DEFAULT 0 NOT NULL,
+--                   last_visit_time INTEGER NOT NULL,
+--                   hidden INTEGER DEFAULT 0 NOT NULL);
+--
+-- The quick history autocomplete provider filters out history items that:
+--   1) have not been visited in kLowQualityMatchAgeLimitInDays, AND
+--   2) for which the URL was not explicitly typed at least
+--      kLowQualityMatchTypedLimit + 1 times, AND
+--   3) have not been visited at least kLowQualityMatchVisitLimit + 1 times.
+-- So we create history items in all of those combinations.
+--
+-- Note that the last_visit_time column for this test table represents the
+-- relative number of days prior to 'today' to which the final column
+-- value will be set during test setup. Beware: Do not set this number
+-- to be equal to kLowQualityMatchAgeLimitInDays.
+--
+-- The ordering, URLs and titles must be kept in sync with the unit tests found
+-- in in_memory_url_index_unittest.cc.
+--
+-- 2.
+-- The schema of the visit database is defined by HISTORY_VISIT_ROW_FIELDS
+-- found in visit_database.h and is equivalent to:
+--
+-- CREATE TABLE visits(visit_id INTEGER PRIMARY KEY,
+--                     url_id INTEGER NOT NULL,
+--                     visit_time INTEGER NOT NULL,
+--                     from_visit INTEGER,
+--                     transition INTEGER DEFAULT 0 NOT NULL,
+--                     segment_id INTEGER,
+--                     is_indexed BOOLEAN,
+--                     visit_duration INTEGER DEFAULT 0 NOT NULL)
+INSERT INTO "urls" VALUES(1,'http://www.reuters.com/article/idUSN0839880620100708','UPDATE 1-US 30-yr mortgage rate drops to new record low | Reuters',3,1,2,0);  -- Qualifies
+INSERT INTO "urls" VALUES(2,'http://www.golfweek.com/news/2010/jul/08/goydos-opens-john-deere-classic-59/','Goydos opens John Deere Classic with 59',3,1,4,0);  -- Qualifies
+INSERT INTO "urls" VALUES(3,'http://www.businessandmedia.org/articles/2010/20100708120415.aspx','LeBronomics: Could High Taxes Influence James'' Team Decision?',4,1,2,0);  -- Qualifies
+INSERT INTO "urls" VALUES(4,'http://www.realclearmarkets.com/articles/2010/07/08/diversity_in_the_financial_sector_98562.html','RealClearMarkets - Racial, Gender Quotas in the Financial Bill?',4,1,4,0);  -- Qualifies
+INSERT INTO "urls" VALUES(5,'http://drudgereport.com/','DRUDGE REPORT 2010',3,2,2,0);  -- Qualifies
+INSERT INTO "urls" VALUES(6,'http://totalfinder.binaryage.com/','TotalFinder brings tabs to your native Finder and more!',3,2,4,0);  -- Qualifies
+INSERT INTO "urls" VALUES(7,'http://getsharekit.com/','ShareKit : Drop-in Share Features for all iOS Apps',4,2,4,0);  -- Qualifies
+INSERT INTO "urls" VALUES(8,'http://getsharekit.com/index.html','ShareKit : Drop-in Share Features for all iOS Apps',3,0,4,0);
+INSERT INTO "urls" VALUES(9,'http://en.wikipedia.org/wiki/Control-Z','Control-Z - Wikipedia, the free encyclopedia',0,0,6,0);
+INSERT INTO "urls" VALUES(10,'http://vmware.com/info?id=724','VMware Account Management Login',1,0,6,0);
+INSERT INTO "urls" VALUES(11,'http://www.tech-recipes.com/rx/2621/os_x_change_path_environment_variable/','OS X: Change your PATH environment variable | Mac system administration | Tech-Recipes',0,1,6,0);  -- Qualifies
+INSERT INTO "urls" VALUES(12,'http://view.atdmt.com/PPJ/iview/194841301/direct;wi.160;hi.600/01?click=','',6,6,0,1);  -- Qualifies
+INSERT INTO "urls" VALUES(15,'http://www.cnn.com/','CNN.com International - Breaking, World, Business, Sports, Entertainment and Video News',6,6,0,0);  -- Qualifies
+INSERT INTO "urls" VALUES(16,'http://www.zdnet.com/','Technology News, Analysis, Comments and Product Reviews for IT Professionals | ZDNet',6,6,0,0);  -- Qualifies
+INSERT INTO "urls" VALUES(17,'http://www.crash.net/','Crash.Net | Formula 1 & MotoGP | Motorsport News',6,6,0,0);  -- Qualifies
+INSERT INTO "urls" VALUES(18,'http://www.theinquirer.net/','THE INQUIRER - Microprocessor, Server, Memory, PCS, Graphics, Networking, Storage',6,6,0,0);  -- Qualifies
+INSERT INTO "urls" VALUES(19,'http://www.theregister.co.uk/','The Register: Sci/Tech News for the World',6,6,0,0);  -- Qualifies
+INSERT INTO "urls" VALUES(20,'http://blogs.technet.com/markrussinovich/','Mark''s Blog - Site Home - TechNet Blogs',6,6,0,0);  -- Qualifies
+INSERT INTO "urls" VALUES(21,'http://www.icu-project.org/','ICU Home Page (ICU - International Components for Unicode)',6,6,0,0);  -- Qualifies
+INSERT INTO "urls" VALUES(22,'http://site.icu-project.org/','ICU Home Page (ICU - International Components for Unicode)',6,6,0,0);  -- Qualifies
+INSERT INTO "urls" VALUES(23,'http://icu-project.org/apiref/icu4c/','ICU 4.2: Main Page',6,6,0,0);  -- Qualifies
+INSERT INTO "urls" VALUES(24,'http://www.danilatos.com/event-test/ExperimentTest.html','Experimentation Harness',6,6,0,0);  -- Qualifies
+INSERT INTO "urls" VALUES(25,'http://www.codeguru.com/','CodeGuru : codeguru',6,6,0,0);  -- Qualifies
+INSERT INTO "urls" VALUES(26,'http://www.codeproject.com/','Your Development Resource - CodeProject',6,6,0,0);  -- Qualifies
+INSERT INTO "urls" VALUES(27,'http://www.tomshardware.com/us/#redir','Tom''s Hardware: Hardware News, Tests and Reviews',6,6,0,0);  -- Qualifies
+INSERT INTO "urls" VALUES(28,'http://www.ddj.com/windows/184416623','Dr. ABRACADABRA''s | Avoiding the Visual C++ Runtime Library | 2 1, 2003',6,6,0,0);  -- Qualifies
+INSERT INTO "urls" VALUES(29,'http://svcs.cnn.com/weather/getForecast?time=34&mode=json_html&zipCode=336736767676&locCode=EGLL&celcius=true&csiID=csi2','',6,6,0,1);  -- Qualifies
+INSERT INTO "urls" VALUES(30,'http://www.drudgery.com/Dogs%20and%20Mice','Life in the Slow Lane',8,2,2,0);  -- Qualifies
+INSERT INTO "urls" VALUES(31,'http://www.redrudgerydo.com/','Music of the Wild Landscape',0,0,6,0);
+INSERT INTO "urls" VALUES(32,'https://NearlyPerfectResult.com/','Practically Perfect Search Result',99,99,0,0);  -- Qualifies
+INSERT INTO "urls" VALUES(33,'http://QuiteUselessSearchResultxyz.com/','Practically Useless Search Result',4,0,99,0);  -- Qualifies
+INSERT INTO "urls" VALUES(34,'http://FubarFubarAndFubar.com/','Situation Normal -- FUBARED',99,99,0,0);  -- Qualifies
+INSERT INTO "urls" VALUES(35,'http://en.wikipedia.org/wiki/1%25_rule_(Internet_culture)','Do Not Need Title',2,2,0,0);  -- Qualifies
+INSERT INTO "urls" VALUES(36,'http://default-engine.com?q=query','Query Query Query',2,2,0,0);  -- Qualifies
+
+-- This file creates some visits, enough to test (in InMemoryURLIndexTest)
+-- the visits functionality, certainly not as many visits as are implied
+-- by the visit counts associated with the URLs above.
+INSERT INTO "visits" VALUES(1, 1, 2, 4, 0, 0, 1);
+INSERT INTO "visits" VALUES(2, 1, 5, 0, 1, 0, 1);
+INSERT INTO "visits" VALUES(3, 1, 12, 0, 0, 0, 1);
+INSERT INTO "visits" VALUES(4, 32, 1, 0, 0, 0, 1);
+INSERT INTO "visits" VALUES(5, 32, 2, 0, 0, 0, 1);
+INSERT INTO "visits" VALUES(6, 32, 3, 0, 0, 0, 1);
+INSERT INTO "visits" VALUES(7, 32, 4, 0, 0, 0, 1);
+INSERT INTO "visits" VALUES(8, 32, 5, 0, 0, 0, 1);
+INSERT INTO "visits" VALUES(9, 32, 6, 0, 0, 0, 1);
+INSERT INTO "visits" VALUES(10, 32, 7, 0, 0, 0, 1);
+INSERT INTO "visits" VALUES(11, 32, 8, 0, 0, 0, 1);
+INSERT INTO "visits" VALUES(12, 32, 9, 0, 0, 0, 1);
+INSERT INTO "visits" VALUES(13, 32, 10, 0, 0, 0, 1);
+INSERT INTO "visits" VALUES(14, 32, 11, 0, 0, 0, 1);
+INSERT INTO "visits" VALUES(15, 32, 12, 0, 0, 0, 1);
+INSERT INTO "visits" VALUES(16, 32, 13, 0, 0, 0, 1);
+INSERT INTO "visits" VALUES(17, 32, 14, 0, 0, 0, 1);
+INSERT INTO "visits" VALUES(18, 32, 15, 0, 1, 0, 1);
+INSERT INTO "visits" VALUES(19, 35, 0, 0, 1, 0, 1);
+INSERT INTO "visits" VALUES(20, 35, 7, 0, 1, 0, 1);
+INSERT INTO "visits" VALUES(21, 36, 1, 0, 1, 0, 1);
+INSERT INTO "visits" VALUES(22, 36, 2, 0, 1, 0, 1);
diff --git a/components/test/data/omnibox/in_memory_url_index_test_limited.db.txt b/components/test/data/omnibox/in_memory_url_index_test_limited.sql
similarity index 68%
rename from components/test/data/omnibox/in_memory_url_index_test_limited.db.txt
rename to components/test/data/omnibox/in_memory_url_index_test_limited.sql
index d47dca01..38ff2c3 100644
--- a/components/test/data/omnibox/in_memory_url_index_test_limited.db.txt
+++ b/components/test/data/omnibox/in_memory_url_index_test_limited.sql
@@ -1,4 +1,2 @@
-/*
-  See url_history_provider_test.db.txt for a schema definition.
-*/
+-- See url_history_provider_test.db.txt for a schema definition.
 INSERT INTO "urls" VALUES(1, 'http://def.ghi?jkl=mno&pqr0123456789stu#vwx!yz=words', 'This example has 19 WORDS and 35 unique characters', 3, 1, 2, 0);
diff --git a/components/webdata_services/BUILD.gn b/components/webdata_services/BUILD.gn
index 1818a437..aa4b4a20 100644
--- a/components/webdata_services/BUILD.gn
+++ b/components/webdata_services/BUILD.gn
@@ -21,6 +21,10 @@
     "//components/webdata/common",
     "//sql",
   ]
+
+  if (is_android) {
+    deps += [ "//components/payments/android" ]
+  }
 }
 
 static_library("test_support") {
diff --git a/components/webdata_services/DEPS b/components/webdata_services/DEPS
index ff4a094..2b14aa6 100644
--- a/components/webdata_services/DEPS
+++ b/components/webdata_services/DEPS
@@ -2,6 +2,7 @@
   "+components/autofill/core/browser/webdata",
   "+components/keyed_service/core",
   "+components/password_manager/core/browser/webdata",
+  "+components/payments/android",
   "+components/search_engines/keyword_table.h",
   "+components/search_engines/keyword_web_data_service.h",
   "+components/signin/core/browser/webdata",
diff --git a/components/webdata_services/web_data_service_wrapper.cc b/components/webdata_services/web_data_service_wrapper.cc
index a8ff689..bc68374 100644
--- a/components/webdata_services/web_data_service_wrapper.cc
+++ b/components/webdata_services/web_data_service_wrapper.cc
@@ -31,6 +31,10 @@
 #include "components/password_manager/core/browser/webdata/password_web_data_service_win.h"
 #endif
 
+#if defined(OS_ANDROID)
+#include "components/payments/android/web_app_manifest_section_table.h"
+#endif
+
 namespace {
 
 void InitSyncableServicesOnDBThread(
@@ -86,13 +90,17 @@
 
   // All tables objects that participate in managing the database must
   // be added here.
-  web_database_->AddTable(base::WrapUnique(new autofill::AutofillTable));
-  web_database_->AddTable(base::WrapUnique(new KeywordTable));
+  web_database_->AddTable(base::MakeUnique<autofill::AutofillTable>());
+  web_database_->AddTable(base::MakeUnique<KeywordTable>());
   // TODO(mdm): We only really need the LoginsTable on Windows for IE7 password
   // access, but for now, we still create it on all platforms since it deletes
   // the old logins table. We can remove this after a while, e.g. in M22 or so.
-  web_database_->AddTable(base::WrapUnique(new LoginsTable));
-  web_database_->AddTable(base::WrapUnique(new TokenServiceTable));
+  web_database_->AddTable(base::MakeUnique<LoginsTable>());
+  web_database_->AddTable(base::MakeUnique<TokenServiceTable>());
+#if defined(OS_ANDROID)
+  web_database_->AddTable(
+      base::MakeUnique<payments::WebAppManifestSectionTable>());
+#endif
   web_database_->LoadDatabase();
 
   autofill_web_data_ = new autofill::AutofillWebDataService(
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index a405429f..ef9f563 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -847,6 +847,8 @@
     "loader/navigation_url_loader_impl.h",
     "loader/navigation_url_loader_impl_core.cc",
     "loader/navigation_url_loader_impl_core.h",
+    "loader/navigation_url_loader_network_service.cc",
+    "loader/navigation_url_loader_network_service.h",
     "loader/netlog_observer.cc",
     "loader/netlog_observer.h",
     "loader/null_resource_controller.cc",
diff --git a/content/browser/accessibility/browser_accessibility_manager_android.cc b/content/browser/accessibility/browser_accessibility_manager_android.cc
index d2b5e70..5923de3 100644
--- a/content/browser/accessibility/browser_accessibility_manager_android.cc
+++ b/content/browser/accessibility/browser_accessibility_manager_android.cc
@@ -123,6 +123,10 @@
 // The element in the document for which we may be displaying an autofill popup.
 int32_t g_element_hosting_autofill_popup_unique_id = -1;
 
+// The element in the document that is the next element after
+// |g_element_hosting_autofill_popup_unique_id|.
+int32_t g_element_after_element_hosting_autofill_popup_unique_id = -1;
+
 // Autofill popup will not be part of the |AXTree| that is sent by renderer.
 // Hence, we need a proxy |AXNode| to represent the autofill popup.
 BrowserAccessibility* g_autofill_popup_proxy_node = nullptr;
@@ -765,14 +769,6 @@
     jint start_id,
     const JavaParamRef<jstring>& element_type_str,
     jboolean forwards) {
-  // Navigate forwards to the autofill popup's proxy node if focus is currently
-  // on the element hosting the autofill popup. Once within the popup, a back
-  // press will navigate back to the element hosting the popup.
-  if (forwards && start_id == g_element_hosting_autofill_popup_unique_id &&
-      g_autofill_popup_proxy_node) {
-    return g_autofill_popup_proxy_node->unique_id();
-  }
-
   BrowserAccessibilityAndroid* start_node = GetFromUniqueID(start_id);
   if (!start_node)
     return 0;
@@ -802,7 +798,21 @@
   if (tree_search.CountMatches() == 0)
     return 0;
 
-  return tree_search.GetMatchAtIndex(0)->unique_id();
+  int32_t element_id = tree_search.GetMatchAtIndex(0)->unique_id();
+
+  // Navigate forwards to the autofill popup's proxy node if focus is currently
+  // on the element hosting the autofill popup. Once within the popup, a back
+  // press will navigate back to the element hosting the popup. If user swipes
+  // past last suggestion in the popup, or swipes left from the first suggestion
+  // in the popup, we will navigate to the element that is the next element in
+  // the document after the element hosting the popup.
+  if (forwards && start_id == g_element_hosting_autofill_popup_unique_id &&
+      g_autofill_popup_proxy_node) {
+    g_element_after_element_hosting_autofill_popup_unique_id = element_id;
+    return g_autofill_popup_proxy_node->unique_id();
+  }
+
+  return element_id;
 }
 
 jboolean BrowserAccessibilityManagerAndroid::NextAtGranularity(
@@ -994,9 +1004,23 @@
     JNIEnv* env,
     const JavaParamRef<jobject>& obj) {
   g_element_hosting_autofill_popup_unique_id = -1;
+  g_element_after_element_hosting_autofill_popup_unique_id = -1;
   DeleteAutofillPopupProxy();
 }
 
+jint BrowserAccessibilityManagerAndroid::
+    GetIdForElementAfterElementHostingAutofillPopup(
+        JNIEnv* env,
+        const JavaParamRef<jobject>& obj) {
+  if (!base::FeatureList::IsEnabled(features::kAndroidAutofillAccessibility) ||
+      g_element_after_element_hosting_autofill_popup_unique_id == -1 ||
+      GetFromUniqueID(
+          g_element_after_element_hosting_autofill_popup_unique_id) == nullptr)
+    return 0;
+
+  return g_element_after_element_hosting_autofill_popup_unique_id;
+}
+
 jboolean BrowserAccessibilityManagerAndroid::IsAutofillPopupNode(
     JNIEnv* env,
     const JavaParamRef<jobject>& obj,
diff --git a/content/browser/accessibility/browser_accessibility_manager_android.h b/content/browser/accessibility/browser_accessibility_manager_android.h
index 48ff7c3..5d15faf2 100644
--- a/content/browser/accessibility/browser_accessibility_manager_android.h
+++ b/content/browser/accessibility/browser_accessibility_manager_android.h
@@ -230,6 +230,9 @@
   void OnAutofillPopupDismissed(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& obj);
+  jint GetIdForElementAfterElementHostingAutofillPopup(
+      JNIEnv* env,
+      const base::android::JavaParamRef<jobject>& obj);
   jboolean IsAutofillPopupNode(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& obj,
diff --git a/content/browser/loader/navigation_url_loader.cc b/content/browser/loader/navigation_url_loader.cc
index 7c21576..cca051b7 100644
--- a/content/browser/loader/navigation_url_loader.cc
+++ b/content/browser/loader/navigation_url_loader.cc
@@ -6,10 +6,14 @@
 
 #include <utility>
 
+#include "base/command_line.h"
+#include "base/memory/ptr_util.h"
 #include "content/browser/frame_host/navigation_request_info.h"
 #include "content/browser/loader/navigation_url_loader_factory.h"
 #include "content/browser/loader/navigation_url_loader_impl.h"
+#include "content/browser/loader/navigation_url_loader_network_service.h"
 #include "content/public/browser/navigation_ui_data.h"
+#include "content/public/common/content_switches.h"
 
 namespace content {
 
@@ -28,10 +32,18 @@
         resource_context, storage_partition, std::move(request_info),
         std::move(navigation_ui_data), service_worker_handle, delegate);
   }
-  return std::unique_ptr<NavigationURLLoader>(new NavigationURLLoaderImpl(
-      resource_context, storage_partition, std::move(request_info),
-      std::move(navigation_ui_data), service_worker_handle, appcache_handle,
-      delegate));
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kEnableNetworkService)) {
+    return base::MakeUnique<NavigationURLLoaderNetworkService>(
+        resource_context, storage_partition, std::move(request_info),
+        std::move(navigation_ui_data), service_worker_handle, appcache_handle,
+        delegate);
+  } else {
+    return base::MakeUnique<NavigationURLLoaderImpl>(
+        resource_context, storage_partition, std::move(request_info),
+        std::move(navigation_ui_data), service_worker_handle, appcache_handle,
+        delegate);
+  }
 }
 
 void NavigationURLLoader::SetFactoryForTesting(
diff --git a/content/browser/loader/navigation_url_loader_network_service.cc b/content/browser/loader/navigation_url_loader_network_service.cc
new file mode 100644
index 0000000..b29827b
--- /dev/null
+++ b/content/browser/loader/navigation_url_loader_network_service.cc
@@ -0,0 +1,99 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/loader/navigation_url_loader_network_service.h"
+
+#include "base/memory/ptr_util.h"
+#include "content/browser/frame_host/navigation_request_info.h"
+#include "content/browser/loader/navigation_url_loader_delegate.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/navigation_ui_data.h"
+#include "content/public/common/service_manager_connection.h"
+#include "content/public/common/service_names.mojom.h"
+#include "net/url_request/url_request_context.h"
+#include "services/service_manager/public/cpp/connector.h"
+
+namespace content {
+
+NavigationURLLoaderNetworkService::NavigationURLLoaderNetworkService(
+    ResourceContext* resource_context,
+    StoragePartition* storage_partition,
+    std::unique_ptr<NavigationRequestInfo> request_info,
+    std::unique_ptr<NavigationUIData> navigation_ui_data,
+    ServiceWorkerNavigationHandle* service_worker_handle,
+    AppCacheNavigationHandle* appcache_handle,
+    NavigationURLLoaderDelegate* delegate)
+    : delegate_(delegate), binding_(this) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  // TODO(scottmg): Maybe some of this setup should be done only once, instead
+  // of every time.
+  url_loader_factory_request_ = mojo::MakeRequest(&url_loader_factory_);
+
+  ServiceManagerConnection::GetForProcess()->GetConnector()->BindInterface(
+      mojom::kNetworkServiceName, &url_loader_factory_);
+
+  // TODO(scottmg): Port over stuff from RDHI::BeginNavigationRequest() here.
+  auto new_request = base::MakeUnique<ResourceRequest>();
+  new_request->method = "GET";
+  new_request->url = request_info->common_params.url;
+  new_request->priority = net::HIGHEST;
+
+  mojom::URLLoaderClientPtr url_loader_client_ptr;
+  mojom::URLLoaderClientRequest url_loader_client_request =
+      mojo::MakeRequest(&url_loader_client_ptr);
+  mojom::URLLoaderClientPtr url_loader_client_ptr_to_pass;
+  binding_.Bind(&url_loader_client_ptr_to_pass);
+
+  url_loader_factory_->CreateLoaderAndStart(
+      mojo::MakeRequest(&url_loader_associated_ptr_), 0 /* routing_id? */,
+      0 /* request_id? */, *new_request,
+      std::move(url_loader_client_ptr_to_pass));
+}
+
+NavigationURLLoaderNetworkService::~NavigationURLLoaderNetworkService() {}
+
+void NavigationURLLoaderNetworkService::FollowRedirect() {
+  url_loader_associated_ptr_->FollowRedirect();
+}
+
+void NavigationURLLoaderNetworkService::ProceedWithResponse() {}
+
+void NavigationURLLoaderNetworkService::OnReceiveResponse(
+    const ResourceResponseHead& head,
+    mojom::DownloadedTempFilePtr downloaded_file) {
+  // TODO(scottmg): This should mirror code in
+  // NavigationResourceHandler::OnReponseStarted().
+}
+
+void NavigationURLLoaderNetworkService::OnReceiveRedirect(
+    const net::RedirectInfo& redirect_info,
+    const ResourceResponseHead& head) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  scoped_refptr<ResourceResponse> response(new ResourceResponse());
+  response->head = head;
+  delegate_->OnRequestRedirected(redirect_info, response);
+}
+
+void NavigationURLLoaderNetworkService::OnDataDownloaded(
+    int64_t data_length,
+    int64_t encoded_length) {}
+
+void NavigationURLLoaderNetworkService::OnUploadProgress(
+    int64_t current_position,
+    int64_t total_size,
+    const OnUploadProgressCallback& callback) {}
+
+void NavigationURLLoaderNetworkService::OnReceiveCachedMetadata(
+    const std::vector<uint8_t>& data) {}
+
+void NavigationURLLoaderNetworkService::OnTransferSizeUpdated(
+    int32_t transfer_size_diff) {}
+
+void NavigationURLLoaderNetworkService::OnStartLoadingResponseBody(
+    mojo::ScopedDataPipeConsumerHandle body) {}
+
+void NavigationURLLoaderNetworkService::OnComplete(
+    const ResourceRequestCompletionStatus& completion_status) {}
+
+}  // namespace content
diff --git a/content/browser/loader/navigation_url_loader_network_service.h b/content/browser/loader/navigation_url_loader_network_service.h
new file mode 100644
index 0000000..b301769
--- /dev/null
+++ b/content/browser/loader/navigation_url_loader_network_service.h
@@ -0,0 +1,75 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_LOADER_NAVIGATION_URL_LOADER_NETWORK_SERVICE_H_
+#define CONTENT_BROWSER_LOADER_NAVIGATION_URL_LOADER_NETWORK_SERVICE_H_
+
+#include "base/macros.h"
+#include "content/browser/loader/navigation_url_loader.h"
+#include "content/common/url_loader.mojom.h"
+#include "content/common/url_loader_factory.mojom.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "services/service_manager/public/cpp/connector.h"
+
+namespace net {
+struct RedirectInfo;
+}
+
+namespace content {
+
+class ResourceContext;
+
+// This is an implementation of NavigationURLLoader used when
+// --enable-network-service is used.
+class NavigationURLLoaderNetworkService : public NavigationURLLoader,
+                                          public mojom::URLLoaderClient {
+ public:
+  // The caller is responsible for ensuring that |delegate| outlives the loader.
+  NavigationURLLoaderNetworkService(
+      ResourceContext* resource_context,
+      StoragePartition* storage_partition,
+      std::unique_ptr<NavigationRequestInfo> request_info,
+      std::unique_ptr<NavigationUIData> navigation_ui_data,
+      ServiceWorkerNavigationHandle* service_worker_handle,
+      AppCacheNavigationHandle* appcache_handle,
+      NavigationURLLoaderDelegate* delegate);
+  ~NavigationURLLoaderNetworkService() override;
+
+  // NavigationURLLoader implementation:
+  void FollowRedirect() override;
+  void ProceedWithResponse() override;
+
+  // mojom::URLLoaderClient implementation:
+  void OnReceiveResponse(const ResourceResponseHead& head,
+                         mojom::DownloadedTempFilePtr downloaded_file) override;
+  void OnReceiveRedirect(const net::RedirectInfo& redirect_info,
+                         const ResourceResponseHead& head) override;
+  void OnDataDownloaded(int64_t data_length, int64_t encoded_length) override;
+  void OnUploadProgress(int64_t current_position,
+                        int64_t total_size,
+                        const OnUploadProgressCallback& callback) override;
+  void OnReceiveCachedMetadata(const std::vector<uint8_t>& data) override;
+  void OnTransferSizeUpdated(int32_t transfer_size_diff) override;
+  void OnStartLoadingResponseBody(
+      mojo::ScopedDataPipeConsumerHandle body) override;
+  void OnComplete(
+      const ResourceRequestCompletionStatus& completion_status) override;
+
+ private:
+  void ConnectURLLoaderFactory(
+      std::unique_ptr<service_manager::Connector> connector);
+
+  NavigationURLLoaderDelegate* delegate_;
+
+  mojom::URLLoaderFactoryRequest url_loader_factory_request_;
+  mojom::URLLoaderFactoryPtr url_loader_factory_;
+  mojo::Binding<mojom::URLLoaderClient> binding_;
+  mojom::URLLoaderAssociatedPtr url_loader_associated_ptr_;
+
+  DISALLOW_COPY_AND_ASSIGN(NavigationURLLoaderNetworkService);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_LOADER_NAVIGATION_URL_LOADER_NETWORK_SERVICE_H_
diff --git a/content/browser/tracing/memory_tracing_browsertest.cc b/content/browser/tracing/memory_tracing_browsertest.cc
index a5f8b8a2..2e222a85 100644
--- a/content/browser/tracing/memory_tracing_browsertest.cc
+++ b/content/browser/tracing/memory_tracing_browsertest.cc
@@ -42,9 +42,10 @@
 
 class MemoryTracingTest : public ContentBrowserTest {
  public:
-  void DoRequestGlobalDump(const MemoryDumpType& dump_type,
-                           const MemoryDumpLevelOfDetail& level_of_detail,
-                           const base::trace_event::MemoryDumpCallback& cb) {
+  void DoRequestGlobalDump(
+      const MemoryDumpType& dump_type,
+      const MemoryDumpLevelOfDetail& level_of_detail,
+      const base::trace_event::GlobalMemoryDumpCallback& cb) {
     MemoryDumpManager::GetInstance()->RequestGlobalDump(dump_type,
                                                         level_of_detail, cb);
   }
@@ -78,7 +79,7 @@
       const MemoryDumpLevelOfDetail& level_of_detail,
       const base::Closure& closure) {
     uint32_t request_index = next_request_index_++;
-    base::trace_event::MemoryDumpCallback callback = base::Bind(
+    base::trace_event::GlobalMemoryDumpCallback callback = base::Bind(
         &MemoryTracingTest::OnGlobalMemoryDumpDone, base::Unretained(this),
         base::ThreadTaskRunnerHandle::Get(), closure, request_index);
     if (from_renderer_thread) {
diff --git a/content/public/android/java/src/org/chromium/content/browser/accessibility/BrowserAccessibilityManager.java b/content/public/android/java/src/org/chromium/content/browser/accessibility/BrowserAccessibilityManager.java
index 54ad7171..e2a8622 100644
--- a/content/public/android/java/src/org/chromium/content/browser/accessibility/BrowserAccessibilityManager.java
+++ b/content/public/android/java/src/org/chromium/content/browser/accessibility/BrowserAccessibilityManager.java
@@ -368,6 +368,16 @@
         }
     }
 
+    public void onAutofillPopupAccessibilityFocusCleared() {
+        if (mAccessibilityManager.isEnabled() && mNativeObj != 0) {
+            int id = nativeGetIdForElementAfterElementHostingAutofillPopup(mNativeObj);
+            if (id == 0) return;
+
+            moveAccessibilityFocusToId(id);
+            nativeScrollToMakeNodeVisible(mNativeObj, mAccessibilityFocusId);
+        }
+    }
+
     /**
      * @see View#onHoverEvent(MotionEvent)
      */
@@ -431,8 +441,7 @@
         if (id == 0) return false;
 
         moveAccessibilityFocusToId(id);
-        nativeScrollToMakeNodeVisible(
-                mNativeObj, mAccessibilityFocusId);
+        nativeScrollToMakeNodeVisible(mNativeObj, mAccessibilityFocusId);
         return true;
     }
 
@@ -1178,6 +1187,8 @@
             long nativeBrowserAccessibilityManagerAndroid);
     private native void nativeOnAutofillPopupDismissed(
             long nativeBrowserAccessibilityManagerAndroid);
+    private native int nativeGetIdForElementAfterElementHostingAutofillPopup(
+            long nativeBrowserAccessibilityManagerAndroid);
     private native int nativeGetRootId(long nativeBrowserAccessibilityManagerAndroid);
     private native boolean nativeIsNodeValid(long nativeBrowserAccessibilityManagerAndroid, int id);
     private native boolean nativeIsAutofillPopupNode(
diff --git a/content/public/app/mojo/content_browser_manifest.json b/content/public/app/mojo/content_browser_manifest.json
index 6e3177d..0fedcfd 100644
--- a/content/public/app/mojo/content_browser_manifest.json
+++ b/content/public/app/mojo/content_browser_manifest.json
@@ -60,6 +60,7 @@
         "device": [ "device:wake_lock" ],
         "file": [ "file:filesystem", "file:leveldb" ],
         "media": [ "media:media" ],
+        "network": [ "url_loader" ],
         "ui": [ "display_output_protection" ],
         "service_manager": [
           "service_manager:client_process",
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 8322aab..68111f4 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -117,7 +117,7 @@
 
 // Enables the old algorithm for processing video constraints in getUserMedia().
 const base::Feature kMediaStreamOldVideoConstraints{
-    "MediaStreamOldVideoConstraints", base::FEATURE_ENABLED_BY_DEFAULT};
+    "MediaStreamOldVideoConstraints", base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Enables the memory coordinator.
 // WARNING:
diff --git a/content/renderer/media/media_stream_constraints_util_video_content.cc b/content/renderer/media/media_stream_constraints_util_video_content.cc
index a47db2a..2f9d0822 100644
--- a/content/renderer/media/media_stream_constraints_util_video_content.cc
+++ b/content/renderer/media/media_stream_constraints_util_video_content.cc
@@ -4,6 +4,7 @@
 
 #include "content/renderer/media/media_stream_constraints_util_video_content.h"
 
+#include <algorithm>
 #include <cmath>
 #include <utility>
 #include <vector>
@@ -16,14 +17,27 @@
 
 namespace content {
 
+const int kMinScreenCastDimension = 1;
+// Use kMaxDimension/2 as maximum to ensure selected resolutions have area less
+// than media::limits::kMaxCanvas.
+const int kMaxScreenCastDimension = media::limits::kMaxDimension / 2;
+static_assert(kMaxScreenCastDimension * kMaxScreenCastDimension <
+                  media::limits::kMaxCanvas,
+              "Invalid kMaxScreenCastDimension");
+
 const int kDefaultScreenCastWidth = 2880;
 const int kDefaultScreenCastHeight = 1800;
 const double kDefaultScreenCastAspectRatio =
     static_cast<double>(kDefaultScreenCastWidth) / kDefaultScreenCastHeight;
+static_assert(kDefaultScreenCastWidth <= kMaxScreenCastDimension,
+              "Invalid kDefaultScreenCastWidth");
+static_assert(kDefaultScreenCastHeight <= kMaxScreenCastDimension,
+              "Invalid kDefaultScreenCastHeight");
+
+const double kMinScreenCastFrameRate = 1.0 / 60.0;
+const double kMaxScreenCastFrameRate = 120.0;
 const double kDefaultScreenCastFrameRate =
     MediaStreamVideoSource::kDefaultFrameRate;
-const int kMinScreenCastDimension = 1;
-const int kMaxScreenCastDimension = media::limits::kMaxDimension - 1;
 
 namespace {
 
@@ -31,9 +45,6 @@
 using StringSet = DiscreteSet<std::string>;
 using BoolSet = DiscreteSet<bool>;
 
-// Hard upper and lower bound frame rates for tab/desktop capture.
-const double kMaxScreenCastFrameRate = 120.0;
-const double kMinScreenCastFrameRate = 1.0 / 60.0;
 
 constexpr double kMinScreenCastAspectRatio =
     static_cast<double>(kMinScreenCastDimension) /
@@ -65,15 +76,28 @@
 class VideoContentCaptureCandidates {
  public:
   VideoContentCaptureCandidates()
-      : device_id_set(StringSet::UniversalSet()),
-        noise_reduction_set(BoolSet::UniversalSet()) {}
+      : has_explicit_max_height_(false),
+        has_explicit_max_width_(false),
+        has_explicit_max_frame_rate_(false),
+        device_id_set_(StringSet::UniversalSet()),
+        noise_reduction_set_(BoolSet::UniversalSet()) {}
   explicit VideoContentCaptureCandidates(
       const blink::WebMediaTrackConstraintSet& constraint_set)
-      : resolution_set(ResolutionSet::FromConstraintSet(constraint_set)),
-        frame_rate_set(
+      : resolution_set_(ResolutionSet::FromConstraintSet(constraint_set)),
+        has_explicit_max_height_(ConstraintHasMax(constraint_set.height) &&
+                                 ConstraintMax(constraint_set.height) <=
+                                     kMaxScreenCastDimension),
+        has_explicit_max_width_(ConstraintHasMax(constraint_set.width) &&
+                                ConstraintMax(constraint_set.width) <=
+                                    kMaxScreenCastDimension),
+        frame_rate_set_(
             DoubleRangeSet::FromConstraint(constraint_set.frame_rate)),
-        device_id_set(StringSetFromConstraint(constraint_set.device_id)),
-        noise_reduction_set(
+        has_explicit_max_frame_rate_(
+            ConstraintHasMax(constraint_set.frame_rate) &&
+            ConstraintMax(constraint_set.frame_rate) <=
+                kMaxScreenCastFrameRate),
+        device_id_set_(StringSetFromConstraint(constraint_set.device_id)),
+        noise_reduction_set_(
             BoolSetFromConstraint(constraint_set.goog_noise_reduction)) {}
 
   VideoContentCaptureCandidates(VideoContentCaptureCandidates&& other) =
@@ -82,28 +106,50 @@
       VideoContentCaptureCandidates&& other) = default;
 
   bool IsEmpty() const {
-    return resolution_set.IsEmpty() || frame_rate_set.IsEmpty() ||
-           device_id_set.IsEmpty() || noise_reduction_set.IsEmpty();
+    return resolution_set_.IsEmpty() || frame_rate_set_.IsEmpty() ||
+           device_id_set_.IsEmpty() || noise_reduction_set_.IsEmpty();
   }
 
   VideoContentCaptureCandidates Intersection(
       const VideoContentCaptureCandidates& other) {
     VideoContentCaptureCandidates intersection;
-    intersection.resolution_set =
-        resolution_set.Intersection(other.resolution_set);
-    intersection.frame_rate_set =
-        frame_rate_set.Intersection(other.frame_rate_set);
-    intersection.device_id_set =
-        device_id_set.Intersection(other.device_id_set);
-    intersection.noise_reduction_set =
-        noise_reduction_set.Intersection(other.noise_reduction_set);
+    intersection.resolution_set_ =
+        resolution_set_.Intersection(other.resolution_set_);
+    intersection.has_explicit_max_height_ =
+        has_explicit_max_height_ || other.has_explicit_max_height_;
+    intersection.has_explicit_max_width_ =
+        has_explicit_max_width_ || other.has_explicit_max_width_;
+    intersection.frame_rate_set_ =
+        frame_rate_set_.Intersection(other.frame_rate_set_);
+    intersection.has_explicit_max_frame_rate_ =
+        has_explicit_max_frame_rate_ || other.has_explicit_max_frame_rate_;
+    intersection.device_id_set_ =
+        device_id_set_.Intersection(other.device_id_set_);
+    intersection.noise_reduction_set_ =
+        noise_reduction_set_.Intersection(other.noise_reduction_set_);
     return intersection;
   }
 
-  ResolutionSet resolution_set;
-  DoubleRangeSet frame_rate_set;
-  StringSet device_id_set;
-  BoolSet noise_reduction_set;
+  const ResolutionSet& resolution_set() const { return resolution_set_; }
+  bool has_explicit_max_height() const { return has_explicit_max_height_; }
+  bool has_explicit_max_width() const { return has_explicit_max_width_; }
+  const DoubleRangeSet& frame_rate_set() const { return frame_rate_set_; }
+  bool has_explicit_max_frame_rate() const {
+    return has_explicit_max_frame_rate_;
+  }
+  const StringSet& device_id_set() const { return device_id_set_; }
+  const BoolSet& noise_reduction_set() const { return noise_reduction_set_; }
+  void set_resolution_set(const ResolutionSet& set) { resolution_set_ = set; }
+  void set_frame_rate_set(const DoubleRangeSet& set) { frame_rate_set_ = set; }
+
+ private:
+  ResolutionSet resolution_set_;
+  bool has_explicit_max_height_;
+  bool has_explicit_max_width_;
+  DoubleRangeSet frame_rate_set_;
+  bool has_explicit_max_frame_rate_;
+  StringSet device_id_set_;
+  BoolSet noise_reduction_set_;
 };
 
 ResolutionSet ScreenCastResolutionCapabilities() {
@@ -173,16 +219,16 @@
     int default_width,
     double default_frame_rate) {
   double requested_frame_rate = SelectFrameRateFromCandidates(
-      candidates.frame_rate_set, basic_constraint_set, default_frame_rate);
+      candidates.frame_rate_set(), basic_constraint_set, default_frame_rate);
   Point requested_resolution =
-      candidates.resolution_set.SelectClosestPointToIdeal(
+      candidates.resolution_set().SelectClosestPointToIdeal(
           basic_constraint_set, default_height, default_width);
   media::VideoCaptureParams params;
   params.requested_format = media::VideoCaptureFormat(
       ToGfxSize(requested_resolution), static_cast<float>(requested_frame_rate),
       media::PIXEL_FORMAT_I420);
   params.resolution_change_policy =
-      SelectResolutionPolicyFromCandidates(candidates.resolution_set);
+      SelectResolutionPolicyFromCandidates(candidates.resolution_set());
   // Content capture always uses default power-line frequency.
   DCHECK(params.IsValid());
 
@@ -232,11 +278,19 @@
   return base::Optional<bool>(candidates.FirstElement());
 }
 
+int ClampToValidDimension(int value) {
+  if (value > kMaxScreenCastDimension)
+    return kMaxScreenCastDimension;
+  else if (value < kMinScreenCastDimension)
+    return kMinScreenCastDimension;
+  return value;
+}
+
 VideoCaptureSettings SelectResultFromCandidates(
     const VideoContentCaptureCandidates& candidates,
     const blink::WebMediaTrackConstraintSet& basic_constraint_set) {
-  std::string device_id = SelectDeviceIDFromCandidates(candidates.device_id_set,
-                                                       basic_constraint_set);
+  std::string device_id = SelectDeviceIDFromCandidates(
+      candidates.device_id_set(), basic_constraint_set);
   // If a maximum width or height is explicitly given, use them as default.
   // If only one of them is given, use the default aspect ratio to determine the
   // other default value.
@@ -244,55 +298,65 @@
   // http://crbug.com/257097
   int default_height = kDefaultScreenCastHeight;
   int default_width = kDefaultScreenCastWidth;
-  bool has_explicit_max_height =
-      candidates.resolution_set.max_height() < kMaxScreenCastDimension;
-  bool has_explicit_max_width =
-      candidates.resolution_set.max_width() < kMaxScreenCastDimension;
-  if (has_explicit_max_height && has_explicit_max_width) {
-    default_height = candidates.resolution_set.max_height();
-    default_width = candidates.resolution_set.max_width();
-  } else if (has_explicit_max_height) {
-    default_height = candidates.resolution_set.max_height();
+  if (candidates.has_explicit_max_height() &&
+      candidates.has_explicit_max_width()) {
+    default_height = candidates.resolution_set().max_height();
+    default_width = candidates.resolution_set().max_width();
+  } else if (candidates.has_explicit_max_height()) {
+    default_height = candidates.resolution_set().max_height();
     default_width = static_cast<int>(
         std::round(default_height * kDefaultScreenCastAspectRatio));
-  } else if (has_explicit_max_width) {
-    default_width = candidates.resolution_set.max_width();
+  } else if (candidates.has_explicit_max_width()) {
+    default_width = candidates.resolution_set().max_width();
     default_height = static_cast<int>(
         std::round(default_width / kDefaultScreenCastAspectRatio));
   }
+  // When the given maximum values are large, the computed values using default
+  // aspect ratio may fall out of range. Ensure the defaults are in the valid
+  // range.
+  default_height = ClampToValidDimension(default_height);
+  default_width = ClampToValidDimension(default_width);
+
+  // If a maximum frame rate is explicitly given, use it as default for
+  // better compatibility with the old constraints algorithm.
+  // TODO(guidou): Use the actual default when applications migrate to the new
+  // constraint syntax.  http://crbug.com/710800
+  double default_frame_rate = candidates.has_explicit_max_frame_rate()
+                                  ? candidates.frame_rate_set().Max()
+                                  : kDefaultScreenCastFrameRate;
   media::VideoCaptureParams capture_params =
       SelectVideoCaptureParamsFromCandidates(candidates, basic_constraint_set,
                                              default_height, default_width,
-                                             kDefaultScreenCastFrameRate);
+                                             default_frame_rate);
 
   base::Optional<bool> noise_reduction = SelectNoiseReductionFromCandidates(
-      candidates.noise_reduction_set, basic_constraint_set);
+      candidates.noise_reduction_set(), basic_constraint_set);
 
   auto track_adapter_settings = SelectVideoTrackAdapterSettings(
-      basic_constraint_set, candidates.resolution_set,
-      candidates.frame_rate_set, capture_params.requested_format);
+      basic_constraint_set, candidates.resolution_set(),
+      candidates.frame_rate_set(), capture_params.requested_format);
 
   return VideoCaptureSettings(std::move(device_id), capture_params,
                               noise_reduction, track_adapter_settings,
-                              candidates.frame_rate_set.Min());
+                              candidates.frame_rate_set().Min());
 }
 
 VideoCaptureSettings UnsatisfiedConstraintsResult(
     const VideoContentCaptureCandidates& candidates,
     const blink::WebMediaTrackConstraintSet& constraint_set) {
   DCHECK(candidates.IsEmpty());
-  if (candidates.resolution_set.IsHeightEmpty()) {
+  if (candidates.resolution_set().IsHeightEmpty()) {
     return VideoCaptureSettings(constraint_set.height.GetName());
-  } else if (candidates.resolution_set.IsWidthEmpty()) {
+  } else if (candidates.resolution_set().IsWidthEmpty()) {
     return VideoCaptureSettings(constraint_set.width.GetName());
-  } else if (candidates.resolution_set.IsAspectRatioEmpty()) {
+  } else if (candidates.resolution_set().IsAspectRatioEmpty()) {
     return VideoCaptureSettings(constraint_set.aspect_ratio.GetName());
-  } else if (candidates.frame_rate_set.IsEmpty()) {
+  } else if (candidates.frame_rate_set().IsEmpty()) {
     return VideoCaptureSettings(constraint_set.frame_rate.GetName());
-  } else if (candidates.noise_reduction_set.IsEmpty()) {
+  } else if (candidates.noise_reduction_set().IsEmpty()) {
     return VideoCaptureSettings(constraint_set.goog_noise_reduction.GetName());
   } else {
-    DCHECK(candidates.device_id_set.IsEmpty());
+    DCHECK(candidates.device_id_set().IsEmpty());
     return VideoCaptureSettings(constraint_set.device_id.GetName());
   }
 }
@@ -302,9 +366,9 @@
 VideoCaptureSettings SelectSettingsVideoContentCapture(
     const blink::WebMediaConstraints& constraints) {
   VideoContentCaptureCandidates candidates;
-  candidates.resolution_set = ScreenCastResolutionCapabilities();
-  candidates.frame_rate_set =
-      DoubleRangeSet(kMinScreenCastFrameRate, kMaxScreenCastFrameRate);
+  candidates.set_resolution_set(ScreenCastResolutionCapabilities());
+  candidates.set_frame_rate_set(
+      DoubleRangeSet(kMinScreenCastFrameRate, kMaxScreenCastFrameRate));
   // candidates.device_id_set and candidates.noise_reduction_set are
   // automatically initialized with the universal set.
 
diff --git a/content/renderer/media/media_stream_constraints_util_video_content.h b/content/renderer/media/media_stream_constraints_util_video_content.h
index 28f5b739..c0522a1 100644
--- a/content/renderer/media/media_stream_constraints_util_video_content.h
+++ b/content/renderer/media/media_stream_constraints_util_video_content.h
@@ -17,12 +17,15 @@
 
 namespace content {
 
+CONTENT_EXPORT extern const int kMinScreenCastDimension;
+CONTENT_EXPORT extern const int kMaxScreenCastDimension;
 CONTENT_EXPORT extern const int kDefaultScreenCastWidth;
 CONTENT_EXPORT extern const int kDefaultScreenCastHeight;
 CONTENT_EXPORT extern const double kDefaultScreenCastAspectRatio;
+
+CONTENT_EXPORT extern const double kMinScreenCastFrameRate;
+CONTENT_EXPORT extern const double kMaxScreenCastFrameRate;
 CONTENT_EXPORT extern const double kDefaultScreenCastFrameRate;
-CONTENT_EXPORT extern const int kMinScreenCastDimension;
-CONTENT_EXPORT extern const int kMaxScreenCastDimension;
 
 // This function performs source, source-settings and track-settings selection
 // for content video capture based on the given |constraints|.
diff --git a/content/renderer/media/media_stream_constraints_util_video_content_unittest.cc b/content/renderer/media/media_stream_constraints_util_video_content_unittest.cc
index d28c8b7..7f7913d 100644
--- a/content/renderer/media/media_stream_constraints_util_video_content_unittest.cc
+++ b/content/renderer/media/media_stream_constraints_util_video_content_unittest.cc
@@ -78,21 +78,21 @@
 // constraint results in failure to select a candidate.
 TEST_F(MediaStreamConstraintsUtilVideoContentTest, OverconstrainedOnHeight) {
   constraint_factory_.Reset();
-  constraint_factory_.basic().height.SetExact(123467890);
+  constraint_factory_.basic().height.SetExact(kMaxScreenCastDimension + 1);
   auto result = SelectSettings();
   EXPECT_FALSE(result.HasValue());
   EXPECT_EQ(constraint_factory_.basic().height.GetName(),
             result.failed_constraint_name());
 
   constraint_factory_.Reset();
-  constraint_factory_.basic().height.SetMin(123467890);
+  constraint_factory_.basic().height.SetMin(kMaxScreenCastDimension + 1);
   result = SelectSettings();
   EXPECT_FALSE(result.HasValue());
   EXPECT_EQ(constraint_factory_.basic().height.GetName(),
             result.failed_constraint_name());
 
   constraint_factory_.Reset();
-  constraint_factory_.basic().height.SetMax(0);
+  constraint_factory_.basic().height.SetMax(kMinScreenCastDimension - 1);
   result = SelectSettings();
   EXPECT_FALSE(result.HasValue());
   EXPECT_EQ(constraint_factory_.basic().height.GetName(),
@@ -101,21 +101,21 @@
 
 TEST_F(MediaStreamConstraintsUtilVideoContentTest, OverconstrainedOnWidth) {
   constraint_factory_.Reset();
-  constraint_factory_.basic().width.SetExact(123467890);
+  constraint_factory_.basic().width.SetExact(kMaxScreenCastDimension + 1);
   auto result = SelectSettings();
   EXPECT_FALSE(result.HasValue());
   EXPECT_EQ(constraint_factory_.basic().width.GetName(),
             result.failed_constraint_name());
 
   constraint_factory_.Reset();
-  constraint_factory_.basic().width.SetMin(123467890);
+  constraint_factory_.basic().width.SetMin(kMaxScreenCastDimension + 1);
   result = SelectSettings();
   EXPECT_FALSE(result.HasValue());
   EXPECT_EQ(constraint_factory_.basic().width.GetName(),
             result.failed_constraint_name());
 
   constraint_factory_.Reset();
-  constraint_factory_.basic().width.SetMax(0);
+  constraint_factory_.basic().width.SetMax(kMinScreenCastDimension - 1);
   result = SelectSettings();
   EXPECT_FALSE(result.HasValue());
   EXPECT_EQ(constraint_factory_.basic().width.GetName(),
@@ -148,21 +148,22 @@
 
 TEST_F(MediaStreamConstraintsUtilVideoContentTest, OverconstrainedOnFrameRate) {
   constraint_factory_.Reset();
-  constraint_factory_.basic().frame_rate.SetExact(123467890.0);
+  constraint_factory_.basic().frame_rate.SetExact(kMaxScreenCastFrameRate +
+                                                  0.1);
   auto result = SelectSettings();
   EXPECT_FALSE(result.HasValue());
   EXPECT_EQ(constraint_factory_.basic().frame_rate.GetName(),
             result.failed_constraint_name());
 
   constraint_factory_.Reset();
-  constraint_factory_.basic().frame_rate.SetMin(123467890.0);
+  constraint_factory_.basic().frame_rate.SetMin(kMaxScreenCastFrameRate + 0.1);
   result = SelectSettings();
   EXPECT_FALSE(result.HasValue());
   EXPECT_EQ(constraint_factory_.basic().frame_rate.GetName(),
             result.failed_constraint_name());
 
   constraint_factory_.Reset();
-  constraint_factory_.basic().frame_rate.SetMax(0.0);
+  constraint_factory_.basic().frame_rate.SetMax(kMinScreenCastFrameRate - 0.1);
   result = SelectSettings();
   EXPECT_FALSE(result.HasValue());
   EXPECT_EQ(constraint_factory_.basic().frame_rate.GetName(),
@@ -334,7 +335,7 @@
   // kMaxHeight greater than the maximum allowed.
   {
     constraint_factory_.Reset();
-    constraint_factory_.basic().height.SetMax(kMaxScreenCastDimension + 100);
+    constraint_factory_.basic().height.SetMax(kMaxScreenCastDimension + 1);
     auto result = SelectSettings();
     EXPECT_TRUE(result.HasValue());
     EXPECT_EQ(kDefaultScreenCastHeight, result.Height());
@@ -348,6 +349,25 @@
               result.track_adapter_settings().max_aspect_ratio);
     CheckTrackAdapterSettingsEqualsFormat(result);
   }
+
+  // kMaxHeight equal to the maximum allowed.
+  {
+    constraint_factory_.Reset();
+    const int kMaxHeight = kMaxScreenCastDimension;
+    constraint_factory_.basic().height.SetMax(kMaxHeight);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    EXPECT_EQ(kMaxHeight, result.Height());
+    // Since the given max is too large, the default aspect ratio cannot be
+    // used and the width is clamped to the maximum.
+    EXPECT_EQ(kMaxScreenCastDimension, result.Width());
+    CheckNonResolutionDefaults(result);
+    EXPECT_EQ(1.0 / kMaxHeight,
+              result.track_adapter_settings().min_aspect_ratio);
+    EXPECT_EQ(kMaxScreenCastDimension,
+              result.track_adapter_settings().max_aspect_ratio);
+    CheckTrackAdapterSettingsEqualsFormat(result);
+  }
 }
 
 TEST_F(MediaStreamConstraintsUtilVideoContentTest, MandatoryHeightRange) {
@@ -599,7 +619,7 @@
     constraint_factory_.basic().width.SetMax(kMaxWidth);
     auto result = SelectSettings();
     EXPECT_TRUE(result.HasValue());
-    // kSmallWidth is less that the default, so expect kSmallWidth.
+    // If max is provided, max is used as default.
     EXPECT_EQ(kMaxWidth, result.Width());
     EXPECT_EQ(std::round(kMaxWidth / kDefaultScreenCastAspectRatio),
               result.Height());
@@ -618,7 +638,7 @@
     constraint_factory_.basic().width.SetMax(kMaxWidth);
     auto result = SelectSettings();
     EXPECT_TRUE(result.HasValue());
-    // kSmallWidth is less that the default, so expect kSmallWidth.
+    // If max is provided, max is used as default.
     EXPECT_EQ(kMaxWidth, result.Width());
     EXPECT_EQ(std::round(kMaxWidth / kDefaultScreenCastAspectRatio),
               result.Height());
@@ -633,10 +653,10 @@
   // kMaxWidth greater than the maximum allowed (gets ignored).
   {
     constraint_factory_.Reset();
-    constraint_factory_.basic().width.SetMax(kMaxScreenCastDimension + 100);
+    constraint_factory_.basic().width.SetMax(kMaxScreenCastDimension + 1);
     auto result = SelectSettings();
     EXPECT_TRUE(result.HasValue());
-    // kSmallWidth is less that the default, so expect kSmallWidth.
+    // Expect the default, since the given max value cannot be used as default.
     EXPECT_EQ(kDefaultScreenCastWidth, result.Width());
     EXPECT_EQ(
         std::round(kDefaultScreenCastWidth / kDefaultScreenCastAspectRatio),
@@ -649,6 +669,24 @@
         result.track_adapter_settings().max_aspect_ratio);
     CheckTrackAdapterSettingsEqualsFormat(result);
   }
+
+  // kMaxWidth equal to the maximum allowed.
+  {
+    constraint_factory_.Reset();
+    const int kMaxWidth = kMaxScreenCastDimension;
+    constraint_factory_.basic().width.SetMax(kMaxWidth);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    EXPECT_EQ(kMaxWidth, result.Width());
+    EXPECT_EQ(std::round(kMaxWidth / kDefaultScreenCastAspectRatio),
+              result.Height());
+    CheckNonResolutionDefaults(result);
+    EXPECT_EQ(1.0 / kMaxScreenCastDimension,
+              result.track_adapter_settings().min_aspect_ratio);
+    EXPECT_EQ(static_cast<double>(kMaxWidth) / kMinScreenCastDimension,
+              result.track_adapter_settings().max_aspect_ratio);
+    CheckTrackAdapterSettingsEqualsFormat(result);
+  }
 }
 
 TEST_F(MediaStreamConstraintsUtilVideoContentTest, MandatoryWidthRange) {
@@ -1237,23 +1275,53 @@
 
 TEST_F(MediaStreamConstraintsUtilVideoContentTest, MandatoryMaxFrameRate) {
   constraint_factory_.Reset();
-  const double kFrameRate = 45.0;
-  constraint_factory_.basic().frame_rate.SetMax(kFrameRate);
-  auto result = SelectSettings();
-  EXPECT_TRUE(result.HasValue());
-  // kFrameRate is greater that the default, so expect the default.
-  EXPECT_EQ(kDefaultScreenCastFrameRate, result.FrameRate());
-  CheckNonFrameRateDefaults(result);
-  CheckTrackAdapterSettingsEqualsFormatDefaultAspectRatio(result);
+  // kMaxFrameRate greater than default
+  {
+    const double kMaxFrameRate = 45.0;
+    constraint_factory_.basic().frame_rate.SetMax(kMaxFrameRate);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    // If max frame rate is provided, it is used as default.
+    EXPECT_EQ(kMaxFrameRate, result.FrameRate());
+    CheckNonFrameRateDefaults(result);
+    CheckTrackAdapterSettingsEqualsFormatDefaultAspectRatio(result);
+  }
 
-  const double kSmallFrameRate = 5.0;
-  constraint_factory_.basic().frame_rate.SetMax(kSmallFrameRate);
-  result = SelectSettings();
-  EXPECT_TRUE(result.HasValue());
-  // kFrameRate is less that the default, so expect kFrameRate.
-  EXPECT_EQ(kSmallFrameRate, result.FrameRate());
-  CheckNonFrameRateDefaults(result);
-  CheckTrackAdapterSettingsEqualsFormatDefaultAspectRatio(result);
+  // kMaxFrameRate less than default
+  {
+    const double kMaxFrameRate = 5.0;
+    constraint_factory_.basic().frame_rate.SetMax(kMaxFrameRate);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    // If max frame rate is provided, it is used as default.
+    EXPECT_EQ(kMaxFrameRate, result.FrameRate());
+    CheckNonFrameRateDefaults(result);
+    CheckTrackAdapterSettingsEqualsFormatDefaultAspectRatio(result);
+  }
+
+  // kMaxFrameRate greater than the maximum allowed
+  {
+    const double kMaxFrameRate = kMaxScreenCastFrameRate + 0.1;
+    constraint_factory_.basic().frame_rate.SetMax(kMaxFrameRate);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    // Expect the default, since the given maximum is invalid.
+    EXPECT_EQ(kDefaultScreenCastFrameRate, result.FrameRate());
+    CheckNonFrameRateDefaults(result);
+    CheckTrackAdapterSettingsEqualsFormatDefaultAspectRatio(result);
+  }
+
+  // kMaxFrameRate equal to the maximum allowed
+  {
+    const double kMaxFrameRate = kMaxScreenCastFrameRate;
+    constraint_factory_.basic().frame_rate.SetMax(kMaxFrameRate);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    // If max frame rate is provided, it is used as default.
+    EXPECT_EQ(kMaxFrameRate, result.FrameRate());
+    CheckNonFrameRateDefaults(result);
+    CheckTrackAdapterSettingsEqualsFormatDefaultAspectRatio(result);
+  }
 }
 
 TEST_F(MediaStreamConstraintsUtilVideoContentTest, MandatoryRangeFrameRate) {
@@ -1265,8 +1333,8 @@
     constraint_factory_.basic().frame_rate.SetMax(kMaxFrameRate);
     auto result = SelectSettings();
     EXPECT_TRUE(result.HasValue());
-    // The range includes the default, so expect the default.
-    EXPECT_EQ(kDefaultScreenCastFrameRate, result.FrameRate());
+    // If max frame rate is provided, it is used as default.
+    EXPECT_EQ(kMaxFrameRate, result.FrameRate());
     CheckNonFrameRateDefaults(result);
     CheckTrackAdapterSettingsEqualsFormatDefaultAspectRatio(result);
   }
@@ -1278,8 +1346,8 @@
     constraint_factory_.basic().frame_rate.SetMax(kMaxFrameRate);
     auto result = SelectSettings();
     EXPECT_TRUE(result.HasValue());
-    // The whole range is greater that the default, so expect the minimum.
-    EXPECT_EQ(kDefaultScreenCastFrameRate, result.FrameRate());
+    // If max frame rate is provided, it is used as default.
+    EXPECT_EQ(kMaxFrameRate, result.FrameRate());
     CheckNonFrameRateDefaults(result);
     CheckTrackAdapterSettingsEqualsFormatDefaultAspectRatio(result);
   }
@@ -1291,7 +1359,7 @@
     constraint_factory_.basic().frame_rate.SetMax(kMaxFrameRate);
     auto result = SelectSettings();
     EXPECT_TRUE(result.HasValue());
-    // The whole range is less that the default, so expect the maximum.
+    // If max frame rate is provided, it is used as default.
     EXPECT_EQ(kMaxFrameRate, result.FrameRate());
     CheckNonFrameRateDefaults(result);
     CheckTrackAdapterSettingsEqualsFormatDefaultAspectRatio(result);
@@ -1674,17 +1742,19 @@
   constraint_factory_.Reset();
   blink::WebMediaTrackConstraintSet& advanced1 =
       constraint_factory_.AddAdvanced();
-  advanced1.aspect_ratio.SetExact(10.0);
+  const double kMinAspectRatio = 5.0;
+  advanced1.aspect_ratio.SetExact(kMinAspectRatio);
   blink::WebMediaTrackConstraintSet& advanced2 =
       constraint_factory_.AddAdvanced();
   advanced2.aspect_ratio.SetExact(3.0);
   auto result = SelectSettings();
   EXPECT_TRUE(result.HasValue());
-  EXPECT_EQ(std::round(kDefaultScreenCastHeight * 10.0), result.Width());
+  EXPECT_EQ(std::round(kDefaultScreenCastHeight * kMinAspectRatio),
+            result.Width());
   EXPECT_EQ(kDefaultScreenCastHeight, result.Height());
   CheckNonResolutionDefaults(result);
-  EXPECT_EQ(10.0, result.track_adapter_settings().min_aspect_ratio);
-  EXPECT_EQ(10.0, result.track_adapter_settings().max_aspect_ratio);
+  EXPECT_EQ(kMinAspectRatio, result.track_adapter_settings().min_aspect_ratio);
+  EXPECT_EQ(kMinAspectRatio, result.track_adapter_settings().max_aspect_ratio);
   CheckTrackAdapterSettingsEqualsFormat(result);
 }
 
@@ -1693,16 +1763,18 @@
   constraint_factory_.Reset();
   blink::WebMediaTrackConstraintSet& advanced1 =
       constraint_factory_.AddAdvanced();
-  advanced1.aspect_ratio.SetMin(10.0);
+  const double kMinAspectRatio = 5.0;
+  advanced1.aspect_ratio.SetMin(kMinAspectRatio);
   blink::WebMediaTrackConstraintSet& advanced2 =
       constraint_factory_.AddAdvanced();
   advanced2.aspect_ratio.SetMax(3.0);
   auto result = SelectSettings();
   EXPECT_TRUE(result.HasValue());
-  EXPECT_EQ(std::round(kDefaultScreenCastHeight * 10.0), result.Width());
+  EXPECT_EQ(std::round(kDefaultScreenCastHeight * kMinAspectRatio),
+            result.Width());
   EXPECT_EQ(kDefaultScreenCastHeight, result.Height());
   CheckNonResolutionDefaults(result);
-  EXPECT_EQ(10.0, result.track_adapter_settings().min_aspect_ratio);
+  EXPECT_EQ(kMinAspectRatio, result.track_adapter_settings().min_aspect_ratio);
   EXPECT_EQ(
       kMaxScreenCastDimension / static_cast<double>(kMinScreenCastDimension),
       result.track_adapter_settings().max_aspect_ratio);
diff --git a/content/renderer/media/webmediaplayer_ms.cc b/content/renderer/media/webmediaplayer_ms.cc
index e2adb547..98f53696 100644
--- a/content/renderer/media/webmediaplayer_ms.cc
+++ b/content/renderer/media/webmediaplayer_ms.cc
@@ -578,9 +578,6 @@
 bool WebMediaPlayerMS::CopyVideoTextureToPlatformTexture(
     gpu::gles2::GLES2Interface* gl,
     unsigned int texture,
-    unsigned internal_format,
-    unsigned format,
-    unsigned type,
     bool premultiply_alpha,
     bool flip_y) {
   TRACE_EVENT0("media", "WebMediaPlayerMS:copyVideoTextureToPlatformTexture");
@@ -601,8 +598,7 @@
   context_3d = media::Context3D(provider->ContextGL(), provider->GrContext());
   DCHECK(context_3d.gl);
   return video_renderer_.CopyVideoFrameTexturesToGLTexture(
-      context_3d, gl, video_frame.get(), texture, internal_format, format, type,
-      premultiply_alpha, flip_y);
+      context_3d, gl, video_frame.get(), texture, premultiply_alpha, flip_y);
 }
 
 bool WebMediaPlayerMS::TexImageImpl(TexImageFunctionID functionID,
diff --git a/content/renderer/media/webmediaplayer_ms.h b/content/renderer/media/webmediaplayer_ms.h
index d44d94e..a3600eb 100644
--- a/content/renderer/media/webmediaplayer_ms.h
+++ b/content/renderer/media/webmediaplayer_ms.h
@@ -155,9 +155,6 @@
 
   bool CopyVideoTextureToPlatformTexture(gpu::gles2::GLES2Interface* gl,
                                          unsigned int texture,
-                                         unsigned internal_format,
-                                         unsigned format,
-                                         unsigned type,
                                          bool premultiply_alpha,
                                          bool flip_y) override;
 
diff --git a/content/test/gpu/generate_buildbot_json.py b/content/test/gpu/generate_buildbot_json.py
index 559103a..5178161 100755
--- a/content/test/gpu/generate_buildbot_json.py
+++ b/content/test/gpu/generate_buildbot_json.py
@@ -1510,6 +1510,15 @@
         'os_types': ['win']
       },
     ],
+    # Windows Intel doesn't have the GL extensions to support this test
+    'disabled_tester_configs': [
+      {
+        'names': [
+          'Win10 Debug (Intel HD 530)',
+          'Win10 Release (Intel HD 530)',
+        ],
+      },
+    ],
     'args': [
       '--use-angle=gl',
       '--use-test-data-path',
diff --git a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
index c32c1cb1..507f23b 100644
--- a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
+++ b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
@@ -44,6 +44,10 @@
     self.Flaky('conformance2/query/occlusion-query.html', bug=603168)
     self.Fail('conformance2/glsl3/tricky-loop-conditions.html', bug=483282)
 
+    # Temporary suppression; will be removed after bug fix.
+    self.Fail('conformance/textures/misc/texture-corner-case-videos.html',
+              bug=701060)
+
     # canvas.commit() promise synchronization isn't fully reliable yet.
     self.Fail('conformance/offscreencanvas/offscreencanvas-resize.html',
               bug=709484)
diff --git a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
index a314343..cdd41f4 100644
--- a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
+++ b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
@@ -101,6 +101,10 @@
     self.Fail('conformance/textures/misc/tex-sub-image-2d-bad-args.html',
         bug=625738)
 
+    # Temporary suppression; will be removed after bug fix.
+    self.Fail('conformance/textures/misc/texture-corner-case-videos.html',
+              bug=701060)
+
     # canvas.commit() promise synchronization isn't fully reliable yet.
     self.Fail('conformance/offscreencanvas/offscreencanvas-resize.html',
               bug=709484)
diff --git a/extensions/common/api/_permission_features.json b/extensions/common/api/_permission_features.json
index 967f3cb..05925a0d 100644
--- a/extensions/common/api/_permission_features.json
+++ b/extensions/common/api/_permission_features.json
@@ -62,18 +62,16 @@
     "channel": "stable",
     "extension_types": ["platform_app"]
   },
-  "app.window.ime": [
-    {
-       "channel": "stable",
-       "extension_types": ["extension"],
-       "platforms": ["chromeos"],
-       "whitelist": [
-         "06BE211D5F014BAB34BC22D9DDA09C63A81D828E",
-         "F94EE6AB36D6C6588670B2B01EB65212D9C64E33",
-         "B9EF10DDFEA11EF77873CC5009809E5037FC4C7A"   // http://crbug.com/435380
-       ]
-    }
-  ],
+  "app.window.ime": {
+     "channel": "stable",
+     "extension_types": ["extension"],
+     "platforms": ["chromeos"],
+     "whitelist": [
+       "06BE211D5F014BAB34BC22D9DDA09C63A81D828E",
+       "F94EE6AB36D6C6588670B2B01EB65212D9C64E33",
+       "B9EF10DDFEA11EF77873CC5009809E5037FC4C7A"   // http://crbug.com/435380
+     ]
+  },
   "appview": {
     "channel": "stable",
     "extension_types": ["platform_app"]
@@ -460,20 +458,18 @@
       "whitelist": ["B44D08FD98F1523ED5837D78D0A606EA9D6206E5"]  // Web Store
     }
   ],
-  "u2fDevices": [
-    {
-      "channel": "stable",
-      "extension_types": ["extension", "platform_app"],
-      "whitelist": [
-        "496B6890097EB6E19809ADEADD095A8721FBB2E0",  // FIDO U2F APIs
-        "AD8ED80B705E1818AAD4684F9FF62B43D6D79620",  // FIDO U2F APIs (dev)
-        "E24F1786D842E91E74C27929B0B3715A4689A473",  // CryptoToken
-        "A28C9619C4C41306FA5236FB4D94DA812F504DE8",  // CryptoToken (dev)
-        "6F9E349A0561C78A0D3F41496FE521C5151C7F71",  // Security Key
-        "C06709A259378015404ED20F75C7D08547E0F10B"   // Security Key (dev)
-      ]
-    }
-  ],
+  "u2fDevices": {
+    "channel": "stable",
+    "extension_types": ["extension", "platform_app"],
+    "whitelist": [
+      "496B6890097EB6E19809ADEADD095A8721FBB2E0",  // FIDO U2F APIs
+      "AD8ED80B705E1818AAD4684F9FF62B43D6D79620",  // FIDO U2F APIs (dev)
+      "E24F1786D842E91E74C27929B0B3715A4689A473",  // CryptoToken
+      "A28C9619C4C41306FA5236FB4D94DA812F504DE8",  // CryptoToken (dev)
+      "6F9E349A0561C78A0D3F41496FE521C5151C7F71",  // Security Key
+      "C06709A259378015404ED20F75C7D08547E0F10B"   // Security Key (dev)
+    ]
+  },
   "unlimitedStorage": {
     "channel": "stable",
     "extension_types": [
diff --git a/extensions/common/features/complex_feature.cc b/extensions/common/features/complex_feature.cc
index ed129da..b5ea0ff0 100644
--- a/extensions/common/features/complex_feature.cc
+++ b/extensions/common/features/complex_feature.cc
@@ -7,7 +7,7 @@
 namespace extensions {
 
 ComplexFeature::ComplexFeature(std::vector<Feature*>* features) {
-  DCHECK_GT(features->size(), 0UL);
+  DCHECK_GT(features->size(), 1UL);
   for (Feature* f : *features)
     features_.push_back(std::unique_ptr<Feature>(f));
   features->clear();
diff --git a/ios/chrome/app/safe_mode/safe_mode_coordinator.h b/ios/chrome/app/safe_mode/safe_mode_coordinator.h
index bf571733..499e924 100644
--- a/ios/chrome/app/safe_mode/safe_mode_coordinator.h
+++ b/ios/chrome/app/safe_mode/safe_mode_coordinator.h
@@ -5,7 +5,7 @@
 #ifndef IOS_CHROME_APP_SAFE_MODE_SAFE_MODE_COORDINATOR_H_
 #define IOS_CHROME_APP_SAFE_MODE_SAFE_MODE_COORDINATOR_H_
 
-#import "ios/chrome/browser/root_coordinator.h"
+#import "ios/chrome/browser/chrome_root_coordinator.h"
 
 #import <UIKit/UIKit.h>
 
@@ -18,7 +18,7 @@
 // Coordinator to manage the Safe Mode UI. This should be self-contained.
 // While this is a ChromeCoordinator, it doesn't support (and will DCHECK) using
 // child coordinators.
-@interface SafeModeCoordinator : RootCoordinator
+@interface SafeModeCoordinator : ChromeRootCoordinator
 
 // Delegate for this coordinator.
 @property(nonatomic, nullable, assign) id<SafeModeCoordinatorDelegate> delegate;
diff --git a/ios/chrome/browser/BUILD.gn b/ios/chrome/browser/BUILD.gn
index c0be92b..de6beb48 100644
--- a/ios/chrome/browser/BUILD.gn
+++ b/ios/chrome/browser/BUILD.gn
@@ -28,6 +28,8 @@
     "chrome_paths.h",
     "chrome_paths.mm",
     "chrome_paths_internal.h",
+    "chrome_root_coordinator.h",
+    "chrome_root_coordinator.mm",
     "chrome_switches.cc",
     "chrome_switches.h",
     "chrome_url_constants.cc",
@@ -57,8 +59,6 @@
     "pref_names.cc",
     "pref_names.h",
     "procedural_block_types.h",
-    "root_coordinator.h",
-    "root_coordinator.mm",
     "tab_parenting_global_observer.cc",
     "tab_parenting_global_observer.h",
     "web_data_service_factory.cc",
diff --git a/ios/chrome/browser/autofill/form_input_accessory_view_controller.mm b/ios/chrome/browser/autofill/form_input_accessory_view_controller.mm
index ae1102c..04e8077 100644
--- a/ios/chrome/browser/autofill/form_input_accessory_view_controller.mm
+++ b/ios/chrome/browser/autofill/form_input_accessory_view_controller.mm
@@ -93,7 +93,6 @@
   return descendants;
 }
 
-#if defined(__IPHONE_9_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_9_0
 NSArray* FindDescendantToolbarItemsForActionName(
     UITextInputAssistantItem* inputAssistantItem,
     NSString* actionName) {
@@ -117,7 +116,6 @@
 
   return toolbarItems;
 }
-#endif
 
 // Computes the frame of each part of the accessory view of the keyboard. It is
 // assumed that the keyboard has either two parts (when it is split) or one part
diff --git a/ios/chrome/browser/root_coordinator.h b/ios/chrome/browser/chrome_root_coordinator.h
similarity index 75%
rename from ios/chrome/browser/root_coordinator.h
rename to ios/chrome/browser/chrome_root_coordinator.h
index c9c6a829..e4d9311 100644
--- a/ios/chrome/browser/root_coordinator.h
+++ b/ios/chrome/browser/chrome_root_coordinator.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 IOS_CHROME_BROWSER_ROOT_COORDINATOR_H_
-#define IOS_CHROME_BROWSER_ROOT_COORDINATOR_H_
+#ifndef IOS_CHROME_BROWSER_CHROME_ROOT_COORDINATOR_H_
+#define IOS_CHROME_BROWSER_CHROME_ROOT_COORDINATOR_H_
 
 #import <UIKit/UIKit.h>
 
@@ -12,7 +12,7 @@
 // A coordinator specialization for the case where the coordinator is
 // creating and managing the root view controller for a UIWindow.
 
-@interface RootCoordinator : ChromeCoordinator
+@interface ChromeRootCoordinator : ChromeCoordinator
 
 - (nullable instancetype)initWithWindow:(nullable UIWindow*)window
     NS_DESIGNATED_INITIALIZER;
@@ -24,4 +24,4 @@
 
 @end
 
-#endif  // IOS_CHROME_BROWSER_ROOT_COORDINATOR_H_
+#endif  // IOS_CHROME_BROWSER_CHROME_ROOT_COORDINATOR_H_
diff --git a/ios/chrome/browser/root_coordinator.mm b/ios/chrome/browser/chrome_root_coordinator.mm
similarity index 71%
rename from ios/chrome/browser/root_coordinator.mm
rename to ios/chrome/browser/chrome_root_coordinator.mm
index 4604fc8..945f542 100644
--- a/ios/chrome/browser/root_coordinator.mm
+++ b/ios/chrome/browser/chrome_root_coordinator.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/root_coordinator.h"
+#import "ios/chrome/browser/chrome_root_coordinator.h"
 
 #include "base/logging.h"
 
@@ -10,7 +10,7 @@
 #error "This file requires ARC support."
 #endif
 
-@implementation RootCoordinator
+@implementation ChromeRootCoordinator
 @synthesize window = _window;
 
 - (instancetype)initWithWindow:(UIWindow*)window {
@@ -20,9 +20,4 @@
   return self;
 }
 
-- (instancetype)initWithBaseViewController:(UIViewController*)viewController {
-  NOTREACHED();
-  return nil;
-}
-
 @end
diff --git a/ios/chrome/browser/tabs/BUILD.gn b/ios/chrome/browser/tabs/BUILD.gn
index da726f3..59d8e13 100644
--- a/ios/chrome/browser/tabs/BUILD.gn
+++ b/ios/chrome/browser/tabs/BUILD.gn
@@ -24,6 +24,7 @@
     "//components/sync_sessions",
     "//ios/net",
     "//ios/web",
+    "//ios/web:user_agent",
     "//ui/base",
   ]
   libs = [ "UIKit.framework" ]
@@ -105,6 +106,7 @@
     "//ios/public/provider/chrome/browser",
     "//ios/public/provider/chrome/browser/native_app_launcher",
     "//ios/web",
+    "//ios/web:user_agent",
     "//net",
     "//ui/base",
     "//url",
diff --git a/ios/chrome/browser/tabs/tab.h b/ios/chrome/browser/tabs/tab.h
index ddc417f..a21bab0 100644
--- a/ios/chrome/browser/tabs/tab.h
+++ b/ios/chrome/browser/tabs/tab.h
@@ -12,6 +12,7 @@
 
 #import "components/signin/ios/browser/manage_accounts_delegate.h"
 #include "ios/net/request_tracker.h"
+#include "ios/web/public/user_agent.h"
 #import "ios/web/public/web_state/ui/crw_web_delegate.h"
 #include "ui/base/page_transition_types.h"
 
@@ -238,9 +239,10 @@
 // current content.
 - (void)switchToReaderMode;
 
-// Remove the UIWebView and reload the current url.  Used by request desktop
-// so the updated user agent is used.
-- (void)reloadForDesktopUserAgent;
+// Loads the original url of the last non-redirect item (including non-history
+// items). Used by request desktop/mobile site so that the updated user agent is
+// used.
+- (void)reloadWithUserAgentType:(web::UserAgentType)userAgentType;
 
 // Ensures the toolbar visibility matches |visible|.
 - (void)updateFullscreenWithToolbarVisible:(BOOL)visible;
diff --git a/ios/chrome/browser/tabs/tab.mm b/ios/chrome/browser/tabs/tab.mm
index 3b94a7e5..29e6c97c 100644
--- a/ios/chrome/browser/tabs/tab.mm
+++ b/ios/chrome/browser/tabs/tab.mm
@@ -198,6 +198,15 @@
           state == UIApplicationStateInactive);
 }
 
+// Returns true if |item| is the result of a HTTP redirect.
+// Returns false if |item| is nullptr;
+bool IsItemRedirectItem(web::NavigationItem* item) {
+  if (!item)
+    return false;
+
+  return (ui::PageTransition::PAGE_TRANSITION_IS_REDIRECT_MASK &
+          item->GetTransitionType()) == 0;
+}
 }  // namespace
 
 @interface Tab ()<CRWWebStateObserver,
@@ -1358,7 +1367,7 @@
          visibleItem->GetUserAgentType() == web::UserAgentType::DESKTOP;
 }
 
-- (void)reloadForDesktopUserAgent {
+- (void)reloadWithUserAgentType:(web::UserAgentType)userAgentType {
   // This removes the web view, which will be recreated at the end of this.
   [self.webController requirePageReconstruction];
 
@@ -1367,25 +1376,41 @@
   // navigation from new navigations.
   web::NavigationManager* navigationManager = [self navigationManager];
   DCHECK(navigationManager);
-  web::NavigationItem* lastNonRedirectedItem =
-      GetLastCommittedNonRedirectedItem(navigationManager);
-  if (!lastNonRedirectedItem)
+
+  web::NavigationItem* lastNonRedirectItem =
+      navigationManager->GetTransientItem();
+  if (!lastNonRedirectItem || IsItemRedirectItem(lastNonRedirectItem))
+    lastNonRedirectItem = navigationManager->GetVisibleItem();
+  if (!lastNonRedirectItem || IsItemRedirectItem(lastNonRedirectItem))
+    lastNonRedirectItem = GetLastCommittedNonRedirectedItem(navigationManager);
+
+  if (!lastNonRedirectItem)
     return;
 
   // |reloadURL| will be empty if a page was open by DOM.
-  GURL reloadURL(lastNonRedirectedItem->GetOriginalRequestURL());
+  GURL reloadURL(lastNonRedirectItem->GetOriginalRequestURL());
   if (reloadURL.is_empty()) {
     DCHECK(self.webState && self.webState->HasOpener());
-    reloadURL = lastNonRedirectedItem->GetVirtualURL();
+    reloadURL = lastNonRedirectItem->GetVirtualURL();
   }
 
   web::NavigationManager::WebLoadParams params(reloadURL);
-  params.referrer = lastNonRedirectedItem->GetReferrer();
-  // A new navigation is needed here for reloading with desktop User-Agent.
-  params.user_agent_override_option =
-      web::NavigationManager::UserAgentOverrideOption::DESKTOP;
-  params.transition_type =
-      ui::PageTransitionFromInt(ui::PAGE_TRANSITION_FORM_SUBMIT);
+  params.referrer = lastNonRedirectItem->GetReferrer();
+  params.transition_type = ui::PAGE_TRANSITION_RELOAD;
+
+  switch (userAgentType) {
+    case web::UserAgentType::DESKTOP:
+      params.user_agent_override_option =
+          web::NavigationManager::UserAgentOverrideOption::DESKTOP;
+      break;
+    case web::UserAgentType::MOBILE:
+      params.user_agent_override_option =
+          web::NavigationManager::UserAgentOverrideOption::MOBILE;
+      break;
+    case web::UserAgentType::NONE:
+      NOTREACHED();
+  }
+
   navigationManager->LoadURLWithParams(params);
 }
 
diff --git a/ios/chrome/browser/ui/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view_controller.mm
index a95912d..222276a 100644
--- a/ios/chrome/browser/ui/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view_controller.mm
@@ -724,13 +724,6 @@
 - (void)tabLoadComplete:(Tab*)tab withSuccess:(BOOL)success;
 // Evaluates Javascript asynchronously using the current page context.
 - (void)openJavascript:(NSString*)javascript;
-
-// Sets the desktop user agent flag and reloads the current page.
-- (void)enableDesktopUserAgent;
-
-// Sets the desktop user agent flag and reloads the current page.
-- (void)enableMobileUserAgent;
-
 // Helper methods used by ShareToDelegate methods.
 // Shows an alert with the given title and message id.
 - (void)showErrorAlert:(int)titleMessageId message:(int)messageId;
@@ -4059,10 +4052,10 @@
       [[_model currentTab] switchToReaderMode];
       break;
     case IDC_REQUEST_DESKTOP_SITE:
-      [self enableDesktopUserAgent];
+      [[_model currentTab] reloadWithUserAgentType:web::UserAgentType::DESKTOP];
       break;
     case IDC_REQUEST_MOBILE_SITE:
-      [self enableMobileUserAgent];
+      [[_model currentTab] reloadWithUserAgentType:web::UserAgentType::MOBILE];
       break;
     case IDC_SHOW_TOOLS_MENU: {
       [self showToolsMenuPopup];
@@ -4271,16 +4264,6 @@
                   appendTo:kCurrentTab];
 }
 
-- (void)enableDesktopUserAgent {
-  [[_model currentTab] reloadForDesktopUserAgent];
-}
-
-// TODO(crbug.com/692303): Implement the actual functionality of
-// "Request Mobile Site", and also refactoring the user agent related function
-// names to improve readability.
-- (void)enableMobileUserAgent {
-}
-
 - (void)resetAllWebViews {
   [_dialogPresenter cancelAllDialogs];
   [_model resetAllWebViews];
diff --git a/ios/chrome/browser/ui/downloads/BUILD.gn b/ios/chrome/browser/ui/downloads/BUILD.gn
index fc0f249..9272668 100644
--- a/ios/chrome/browser/ui/downloads/BUILD.gn
+++ b/ios/chrome/browser/ui/downloads/BUILD.gn
@@ -66,6 +66,7 @@
 }
 
 source_set("unit_tests") {
+  configs += [ "//build/config/compiler:enable_arc" ]
   testonly = true
   sources = [
     "download_manager_controller_unittest.mm",
diff --git a/ios/chrome/browser/ui/downloads/download_manager_controller_unittest.mm b/ios/chrome/browser/ui/downloads/download_manager_controller_unittest.mm
index d816a0b1..f0c35ab 100644
--- a/ios/chrome/browser/ui/downloads/download_manager_controller_unittest.mm
+++ b/ios/chrome/browser/ui/downloads/download_manager_controller_unittest.mm
@@ -8,7 +8,6 @@
 
 #include <memory>
 
-#import "base/mac/scoped_nsobject.h"
 #include "base/message_loop/message_loop.h"
 #import "ios/chrome/browser/store_kit/store_kit_launcher.h"
 #import "ios/chrome/browser/store_kit/store_kit_tab_helper.h"
@@ -23,6 +22,10 @@
 #include "testing/platform_test.h"
 #import "third_party/ocmock/OCMock/OCMock.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 using net::HttpResponseHeaders;
 using net::URLRequestStatus;
 
@@ -58,13 +61,12 @@
     id mock_launcher =
         [OCMockObject niceMockForProtocol:@protocol(StoreKitLauncher)];
     helper->SetLauncher(mock_launcher);
-    _controller.reset([[DownloadManagerController alloc]
-        initWithWebState:web_state()
-             downloadURL:kTestURL]);
+    _controller =
+        [[DownloadManagerController alloc] initWithWebState:web_state()
+                                                downloadURL:kTestURL];
   }
-
   std::unique_ptr<net::TestURLFetcherFactory> _fetcher_factory;
-  base::scoped_nsobject<DownloadManagerController> _controller;
+  __strong DownloadManagerController* _controller;
 };
 
 TEST_F(DownloadManagerControllerTest, TestXibViewConnections) {
diff --git a/ios/chrome/browser/ui/keyboard/UIKeyCommand+Chrome.mm b/ios/chrome/browser/ui/keyboard/UIKeyCommand+Chrome.mm
index b9897e6..736e0dad 100644
--- a/ios/chrome/browser/ui/keyboard/UIKeyCommand+Chrome.mm
+++ b/ios/chrome/browser/ui/keyboard/UIKeyCommand+Chrome.mm
@@ -97,10 +97,7 @@
       [self keyCommandWithInput:input
                   modifierFlags:modifierFlags
                          action:@selector(cr_handleKeyCommand:)];
-#if defined(__IPHONE_9_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_9_0
-  if ([keyCommand respondsToSelector:@selector(discoverabilityTitle)])
-    keyCommand.discoverabilityTitle = discoveryTitle;
-#endif
+  keyCommand.discoverabilityTitle = discoveryTitle;
   keyCommand.cr_action = action;
   return keyCommand;
 }
diff --git a/ios/chrome/browser/ui/main/main_coordinator.h b/ios/chrome/browser/ui/main/main_coordinator.h
index 89dff560..9734551 100644
--- a/ios/chrome/browser/ui/main/main_coordinator.h
+++ b/ios/chrome/browser/ui/main/main_coordinator.h
@@ -5,13 +5,13 @@
 #ifndef IOS_CHROME_BROWSER_UI_MAIN_MAIN_COORDINATOR_H_
 #define IOS_CHROME_BROWSER_UI_MAIN_MAIN_COORDINATOR_H_
 
-#import "ios/chrome/browser/root_coordinator.h"
+#import "ios/chrome/browser/chrome_root_coordinator.h"
 
 #import <Foundation/Foundation.h>
 
 @class MainViewController;
 
-@interface MainCoordinator : RootCoordinator
+@interface MainCoordinator : ChromeRootCoordinator
 
 // The view controller this coordinator creates and manages.
 // (This is only public while the view controller architecture is being
diff --git a/ios/chrome/browser/ui/tools_menu/BUILD.gn b/ios/chrome/browser/ui/tools_menu/BUILD.gn
index 0a4e712..6ffcd2d 100644
--- a/ios/chrome/browser/ui/tools_menu/BUILD.gn
+++ b/ios/chrome/browser/ui/tools_menu/BUILD.gn
@@ -67,6 +67,7 @@
   configs += [ "//build/config/compiler:enable_arc" ]
   testonly = true
   sources = [
+    "request_desktop_mobile_site_egtest.mm",
     "tools_popup_menu_egtest.mm",
   ]
   deps = [
diff --git a/ios/chrome/browser/ui/tools_menu/request_desktop_mobile_site_egtest.mm b/ios/chrome/browser/ui/tools_menu/request_desktop_mobile_site_egtest.mm
new file mode 100644
index 0000000..d88e97e
--- /dev/null
+++ b/ios/chrome/browser/ui/tools_menu/request_desktop_mobile_site_egtest.mm
@@ -0,0 +1,293 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import <EarlGrey/EarlGrey.h>
+#import <XCTest/XCTest.h>
+
+#include "base/strings/sys_string_conversions.h"
+#include "components/strings/grit/components_strings.h"
+#import "ios/chrome/browser/ui/chrome_web_view_factory.h"
+#import "ios/chrome/browser/ui/toolbar/toolbar_controller.h"
+#include "ios/chrome/browser/ui/tools_menu/tools_menu_constants.h"
+#import "ios/chrome/browser/ui/uikit_ui_util.h"
+#include "ios/chrome/grit/ios_strings.h"
+#import "ios/chrome/test/earl_grey/accessibility_util.h"
+#import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
+#import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
+#import "ios/chrome/test/earl_grey/chrome_matchers.h"
+#import "ios/chrome/test/earl_grey/chrome_test_case.h"
+#import "ios/web/public/test/http_server.h"
+#include "ios/web/public/test/http_server_util.h"
+#include "ios/web/public/test/response_providers/data_response_provider.h"
+#include "ui/base/l10n/l10n_util.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+using chrome_test_util::WebViewContainingText;
+
+namespace {
+
+const char kUserAgentTestURL[] =
+    "http://ios/testing/data/http_server_files/user_agent_test_page.html";
+
+const char kMobileSiteLabel[] = "Mobile";
+
+const char kDesktopSiteLabel[] = "Desktop";
+
+// Matcher for the button to request desktop site.
+id<GREYMatcher> RequestDesktopButton() {
+  return grey_accessibilityID(kToolsMenuRequestDesktopId);
+}
+
+// Matcher for the button to request mobile site.
+id<GREYMatcher> RequestMobileButton() {
+  return grey_accessibilityID(kToolsMenuRequestMobileId);
+}
+
+// A ResponseProvider that provides user agent for httpServer request.
+class UserAgentResponseProvider : public web::DataResponseProvider {
+ public:
+  bool CanHandleRequest(const Request& request) override { return true; }
+
+  void GetResponseHeadersAndBody(
+      const Request& request,
+      scoped_refptr<net::HttpResponseHeaders>* headers,
+      std::string* response_body) override {
+    // Do not return anything if static plist file has been requested,
+    // as plain text is not a valid property list content.
+    if ([[base::SysUTF8ToNSString(request.url.spec()) pathExtension]
+            isEqualToString:@"plist"]) {
+      *headers =
+          web::ResponseProvider::GetResponseHeaders("", net::HTTP_NO_CONTENT);
+      return;
+    }
+
+    *headers = web::ResponseProvider::GetDefaultResponseHeaders();
+    std::string userAgent;
+    const std::string kDesktopUserAgent =
+        base::SysNSStringToUTF8(ChromeWebView::kDesktopUserAgent);
+    if (request.headers.GetHeader("User-Agent", &userAgent) &&
+        userAgent == kDesktopUserAgent) {
+      response_body->assign("Desktop");
+    } else {
+      response_body->assign("Mobile");
+    }
+  }
+};
+}  // namespace
+
+// Tests for the tools popup menu.
+@interface RequestDesktopMobileSiteTestCase : ChromeTestCase
+@end
+
+@implementation RequestDesktopMobileSiteTestCase
+
+// Tests that requesting desktop site of a page works and the user agent
+// propagates to the next navigations in the same tab.
+- (void)testRequestDesktopSitePropagatesToNextNavigations {
+  std::unique_ptr<web::DataResponseProvider> provider(
+      new UserAgentResponseProvider());
+  web::test::SetUpHttpServer(std::move(provider));
+
+  [ChromeEarlGrey loadURL:web::test::HttpServer::MakeUrl("http://1.com")];
+  // Verify initial reception of the mobile site.
+  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kMobileSiteLabel)]
+      assertWithMatcher:grey_notNil()];
+
+  // Request and verify reception of the desktop site.
+  [ChromeEarlGreyUI openToolsMenu];
+  [[EarlGrey selectElementWithMatcher:RequestDesktopButton()]
+      performAction:grey_tap()];
+  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kDesktopSiteLabel)]
+      assertWithMatcher:grey_notNil()];
+
+  // Verify that desktop user agent propagates.
+  [ChromeEarlGrey loadURL:web::test::HttpServer::MakeUrl("http://2.com")];
+  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kDesktopSiteLabel)]
+      assertWithMatcher:grey_notNil()];
+}
+
+// Tests that requesting desktop site of a page works and desktop user agent
+// does not propagate to next the new tab.
+- (void)testRequestDesktopSiteDoesNotPropagateToNewTab {
+  std::unique_ptr<web::DataResponseProvider> provider(
+      new UserAgentResponseProvider());
+  web::test::SetUpHttpServer(std::move(provider));
+
+  [ChromeEarlGrey loadURL:web::test::HttpServer::MakeUrl("http://1.com")];
+  // Verify initial reception of the mobile site.
+  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kMobileSiteLabel)]
+      assertWithMatcher:grey_notNil()];
+
+  // Request and verify reception of the desktop site.
+  [ChromeEarlGreyUI openToolsMenu];
+  [[EarlGrey selectElementWithMatcher:RequestDesktopButton()]
+      performAction:grey_tap()];
+  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kDesktopSiteLabel)]
+      assertWithMatcher:grey_notNil()];
+
+  // Verify that desktop user agent does not propagate to new tab.
+  [ChromeEarlGreyUI openNewTab];
+  [ChromeEarlGrey loadURL:web::test::HttpServer::MakeUrl("http://2.com")];
+  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kMobileSiteLabel)]
+      assertWithMatcher:grey_notNil()];
+}
+
+// Tests that requesting desktop site of a page works and going back re-opens
+// mobile version of the page.
+- (void)testRequestDesktopSiteGoBackToMobile {
+  std::unique_ptr<web::DataResponseProvider> provider(
+      new UserAgentResponseProvider());
+  web::test::SetUpHttpServer(std::move(provider));
+
+  [ChromeEarlGrey loadURL:web::test::HttpServer::MakeUrl("http://1.com")];
+  // Verify initial reception of the mobile site.
+  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kMobileSiteLabel)]
+      assertWithMatcher:grey_notNil()];
+
+  // Request and verify reception of the desktop site.
+  [ChromeEarlGreyUI openToolsMenu];
+  [[EarlGrey selectElementWithMatcher:RequestDesktopButton()]
+      performAction:grey_tap()];
+  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kDesktopSiteLabel)]
+      assertWithMatcher:grey_notNil()];
+
+  // Verify that going back returns to the mobile site.
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::BackButton()]
+      performAction:grey_tap()];
+  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kMobileSiteLabel)]
+      assertWithMatcher:grey_notNil()];
+}
+
+// Tests that requesting mobile site of a page works and the user agent
+// propagates to the next navigations in the same tab.
+- (void)testRequestMobileSitePropagatesToNextNavigations {
+  std::unique_ptr<web::DataResponseProvider> provider(
+      new UserAgentResponseProvider());
+  web::test::SetUpHttpServer(std::move(provider));
+
+  [ChromeEarlGrey loadURL:web::test::HttpServer::MakeUrl("http://1.com")];
+  // Verify initial reception of the mobile site.
+  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kMobileSiteLabel)]
+      assertWithMatcher:grey_notNil()];
+
+  // Request and verify reception of the desktop site.
+  [ChromeEarlGreyUI openToolsMenu];
+  [[EarlGrey selectElementWithMatcher:RequestDesktopButton()]
+      performAction:grey_tap()];
+  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kDesktopSiteLabel)]
+      assertWithMatcher:grey_notNil()];
+
+  // Request and verify reception of the mobile site.
+  [ChromeEarlGreyUI openToolsMenu];
+  [[EarlGrey selectElementWithMatcher:RequestMobileButton()]
+      performAction:grey_tap()];
+  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kMobileSiteLabel)]
+      assertWithMatcher:grey_notNil()];
+
+  // Verify that mobile user agent propagates.
+  [ChromeEarlGrey loadURL:web::test::HttpServer::MakeUrl("http://2.com")];
+  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kMobileSiteLabel)]
+      assertWithMatcher:grey_notNil()];
+}
+
+// Tests that requesting mobile site of a page works and going back re-opens
+// desktop version of the page.
+- (void)testRequestMobileSiteGoBackToDesktop {
+  std::unique_ptr<web::DataResponseProvider> provider(
+      new UserAgentResponseProvider());
+  web::test::SetUpHttpServer(std::move(provider));
+
+  [ChromeEarlGrey loadURL:web::test::HttpServer::MakeUrl("http://1.com")];
+  // Verify initial reception of the mobile site.
+  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kMobileSiteLabel)]
+      assertWithMatcher:grey_notNil()];
+
+  // Request and verify reception of the desktop site.
+  [ChromeEarlGreyUI openToolsMenu];
+  [[EarlGrey selectElementWithMatcher:RequestDesktopButton()]
+      performAction:grey_tap()];
+  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kDesktopSiteLabel)]
+      assertWithMatcher:grey_notNil()];
+
+  // Request and verify reception of the mobile site.
+  [ChromeEarlGreyUI openToolsMenu];
+  [[EarlGrey selectElementWithMatcher:RequestMobileButton()]
+      performAction:grey_tap()];
+  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kMobileSiteLabel)]
+      assertWithMatcher:grey_notNil()];
+
+  // Verify that going back returns to the desktop site.
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::BackButton()]
+      performAction:grey_tap()];
+  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kDesktopSiteLabel)]
+      assertWithMatcher:grey_notNil()];
+}
+
+// Tests that requesting desktop site button is not enabled on new tab pages.
+- (void)testRequestDesktopSiteNotEnabledOnNewTabPage {
+  // Verify tapping on request desktop button is no-op.
+  [ChromeEarlGreyUI openToolsMenu];
+  [[[EarlGrey selectElementWithMatcher:RequestDesktopButton()]
+      assertWithMatcher:grey_notNil()] performAction:grey_tap()];
+  [[EarlGrey selectElementWithMatcher:RequestDesktopButton()]
+      assertWithMatcher:grey_notNil()];
+}
+
+// Tests that requesting desktop site button is not enabled on WebUI pages.
+- (void)testRequestDesktopSiteNotEnabledOnWebUIPage {
+  [ChromeEarlGrey loadURL:GURL("chrome://version")];
+
+  // Verify tapping on request desktop button is no-op.
+  [ChromeEarlGreyUI openToolsMenu];
+  [[[EarlGrey selectElementWithMatcher:RequestDesktopButton()]
+      assertWithMatcher:grey_notNil()] performAction:grey_tap()];
+  [[EarlGrey selectElementWithMatcher:RequestDesktopButton()]
+      assertWithMatcher:grey_notNil()];
+}
+
+// Tests that navigator.appVersion JavaScript API returns correct string for
+// desktop User Agent.
+- (void)testAppVersionJSAPIWithDesktopUserAgent {
+  web::test::SetUpFileBasedHttpServer();
+  [ChromeEarlGrey loadURL:web::test::HttpServer::MakeUrl(kUserAgentTestURL)];
+  // Verify initial reception of the mobile site.
+  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kMobileSiteLabel)]
+      assertWithMatcher:grey_notNil()];
+
+  // Request and verify reception of the desktop site.
+  [ChromeEarlGreyUI openToolsMenu];
+  [[EarlGrey selectElementWithMatcher:RequestDesktopButton()]
+      performAction:grey_tap()];
+  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kDesktopSiteLabel)]
+      assertWithMatcher:grey_notNil()];
+}
+
+// Tests that navigator.appVersion JavaScript API returns correct string for
+// mobile User Agent.
+- (void)testAppVersionJSAPIWithMobileUserAgent {
+  web::test::SetUpFileBasedHttpServer();
+  [ChromeEarlGrey loadURL:web::test::HttpServer::MakeUrl(kUserAgentTestURL)];
+  // Verify initial reception of the mobile site.
+  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kMobileSiteLabel)]
+      assertWithMatcher:grey_notNil()];
+
+  // Request and verify reception of the desktop site.
+  [ChromeEarlGreyUI openToolsMenu];
+  [[EarlGrey selectElementWithMatcher:RequestDesktopButton()]
+      performAction:grey_tap()];
+  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kDesktopSiteLabel)]
+      assertWithMatcher:grey_notNil()];
+
+  // Request and verify reception of the mobile site.
+  [ChromeEarlGreyUI openToolsMenu];
+  [[EarlGrey selectElementWithMatcher:RequestMobileButton()]
+      performAction:grey_tap()];
+  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kMobileSiteLabel)]
+      assertWithMatcher:grey_notNil()];
+}
+
+@end
diff --git a/ios/chrome/browser/ui/tools_menu/tools_menu_configuration.h b/ios/chrome/browser/ui/tools_menu/tools_menu_configuration.h
index 1c485c6..aaf14a12 100644
--- a/ios/chrome/browser/ui/tools_menu/tools_menu_configuration.h
+++ b/ios/chrome/browser/ui/tools_menu/tools_menu_configuration.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 IOS_CHROME_BROWSER_UI_TOOLS_MENU_TOOLS_MENU_CONTEXT_H_
-#define IOS_CHROME_BROWSER_UI_TOOLS_MENU_TOOLS_MENU_CONTEXT_H_
+#ifndef IOS_CHROME_BROWSER_UI_TOOLS_MENU_TOOLS_MENU_CONFIGURATION_H_
+#define IOS_CHROME_BROWSER_UI_TOOLS_MENU_TOOLS_MENU_CONFIGURATION_H_
 
 #import <Foundation/Foundation.h>
 #import <UIKit/UIKit.h>
@@ -54,4 +54,4 @@
 
 @end
 
-#endif  // IOS_CHROME_BROWSER_UI_TOOLS_MENU_TOOLS_MENU_CONTEXT_H_
+#endif  // IOS_CHROME_BROWSER_UI_TOOLS_MENU_TOOLS_MENU_CONFIGURATION_H_
diff --git a/ios/chrome/browser/ui/tools_menu/tools_menu_model.h b/ios/chrome/browser/ui/tools_menu/tools_menu_model.h
index 5b64a84..9b40414 100644
--- a/ios/chrome/browser/ui/tools_menu/tools_menu_model.h
+++ b/ios/chrome/browser/ui/tools_menu/tools_menu_model.h
@@ -10,7 +10,7 @@
 #import "ios/chrome/browser/ui/tools_menu/tools_menu_configuration.h"
 
 // Total number of possible menu items.
-const int kToolsMenuNumberOfItems = 15;
+const int kToolsMenuNumberOfItems = 16;
 
 // Initialization table for all possible commands to initialize the
 // tools menu at run time. Data initialized into this structure is not mutable.
diff --git a/ios/chrome/browser/ui/tools_menu/tools_menu_model.mm b/ios/chrome/browser/ui/tools_menu/tools_menu_model.mm
index 6dc52d1..9a0ebd1 100644
--- a/ios/chrome/browser/ui/tools_menu/tools_menu_model.mm
+++ b/ios/chrome/browser/ui/tools_menu/tools_menu_model.mm
@@ -15,10 +15,6 @@
 #import "ios/public/provider/chrome/browser/user_feedback/user_feedback_provider.h"
 #include "ios/web/public/user_agent.h"
 
-// TODO(crbug.com/678047) Remove this switch when request mobile site
-// functionality is implemented.
-#define HIDE_REQUEST_MOBILE_SITE_CELL
-
 // Menu items can be marked as visible or not when Incognito is enabled.
 // The following bits are used for |visibility| field in |MenuItemInfo|.
 const NSInteger kVisibleIncognitoOnly = 1 << 0;
@@ -64,6 +60,9 @@
   { IDS_IOS_TOOLS_MENU_REQUEST_DESKTOP_SITE, kToolsMenuRequestDesktopId,
     IDC_REQUEST_DESKTOP_SITE,             ToolbarTypeWebAll,
     0,                                    nil },
+  { IDS_IOS_TOOLS_MENU_REQUEST_MOBILE_SITE, kToolsMenuRequestMobileId,
+    IDC_REQUEST_MOBILE_SITE,              ToolbarTypeWebAll,
+    0,                                    nil },
   { IDS_IOS_TOOLS_MENU_READER_MODE,       kToolsMenuReaderMode,
     IDC_READER_MODE,                      ToolbarTypeWebAll,
     0,                                    nil },
@@ -104,19 +103,9 @@
     // flag should stick when going backward and which cell should be visible
     // when navigating to native pages).
     case IDS_IOS_TOOLS_MENU_REQUEST_DESKTOP_SITE:
-#ifdef HIDE_REQUEST_MOBILE_SITE_CELL
-      return true;
-#else
       return (configuration.userAgentType != web::UserAgentType::DESKTOP);
-#endif
     case IDS_IOS_TOOLS_MENU_REQUEST_MOBILE_SITE:
-// TODO(crbug.com/678047) Remove this switch when request mobile site
-// functionality is implemented.
-#ifdef HIDE_REQUEST_MOBILE_SITE_CELL
-      return false;
-#else
       return (configuration.userAgentType == web::UserAgentType::DESKTOP);
-#endif
     default:
       return true;
   }
diff --git a/ios/chrome/browser/ui/tools_menu/tools_menu_view_controller.mm b/ios/chrome/browser/ui/tools_menu/tools_menu_view_controller.mm
index 6b8b3ba..2d578f37 100644
--- a/ios/chrome/browser/ui/tools_menu/tools_menu_view_controller.mm
+++ b/ios/chrome/browser/ui/tools_menu/tools_menu_view_controller.mm
@@ -37,10 +37,6 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/l10n/l10n_util_mac.h"
 
-// TODO(crbug.com/678047) Remove this switch when request mobile site
-// functionality is implemented.
-#define HIDE_REQUEST_MOBILE_SITE_CELL
-
 using ios::material::TimingFunction;
 
 namespace {
@@ -273,12 +269,6 @@
       break;
     case web::UserAgentType::DESKTOP:
       [self setItemEnabled:YES withTag:IDC_REQUEST_MOBILE_SITE];
-
-// TODO(crbug.com/678047) Remove this switch when request mobile site
-// functionality is implemented.
-#ifdef HIDE_REQUEST_MOBILE_SITE_CELL
-      [self setItemEnabled:NO withTag:IDC_REQUEST_DESKTOP_SITE];
-#endif
       break;
   }
 
diff --git a/ios/chrome/browser/ui/tools_menu/tools_menu_view_controller_unittest.mm b/ios/chrome/browser/ui/tools_menu/tools_menu_view_controller_unittest.mm
index 3e2e9a1..2a7d7a5 100644
--- a/ios/chrome/browser/ui/tools_menu/tools_menu_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/tools_menu/tools_menu_view_controller_unittest.mm
@@ -69,19 +69,19 @@
   EXPECT_FALSE(mobile_item);
 }
 
-// Tests that "Request Desktop Site" is visible and not enabled, and
-// "Request Mobile Site" is invisible when the current page is a web page and
-// uses DESKTOP user agent.
+// Tests that "Request Desktop Site" is invisible, and "Request Mobile Site" is
+// visible and enabled when the current page is a web page and uses DESKTOP user
+// agent.
 TEST_F(ToolsMenuViewControllerTest, TestUserAgentTypeDESKTOP) {
   [configuration_ setUserAgentType:web::UserAgentType::DESKTOP];
   [controller_ initializeMenuWithConfiguration:configuration_.get()];
 
   ToolsMenuViewItem* desktop_item =
       GetToolsMenuViewItemWithTag(IDC_REQUEST_DESKTOP_SITE);
-  ASSERT_TRUE(desktop_item);
-  EXPECT_FALSE(desktop_item.active);
+  EXPECT_FALSE(desktop_item);
 
   ToolsMenuViewItem* mobile_item =
       GetToolsMenuViewItemWithTag(IDC_REQUEST_MOBILE_SITE);
-  EXPECT_FALSE(mobile_item);
+  ASSERT_TRUE(mobile_item);
+  EXPECT_TRUE(mobile_item.active);
 }
diff --git a/ios/chrome/browser/ui/tools_menu/tools_popup_menu_egtest.mm b/ios/chrome/browser/ui/tools_menu/tools_popup_menu_egtest.mm
index 79784cc..0b52bc28 100644
--- a/ios/chrome/browser/ui/tools_menu/tools_popup_menu_egtest.mm
+++ b/ios/chrome/browser/ui/tools_menu/tools_popup_menu_egtest.mm
@@ -5,9 +5,6 @@
 #import <EarlGrey/EarlGrey.h>
 #import <XCTest/XCTest.h>
 
-#include "base/strings/sys_string_conversions.h"
-#include "components/strings/grit/components_strings.h"
-#import "ios/chrome/browser/ui/chrome_web_view_factory.h"
 #import "ios/chrome/browser/ui/toolbar/toolbar_controller.h"
 #include "ios/chrome/browser/ui/tools_menu/tools_menu_constants.h"
 #import "ios/chrome/browser/ui/uikit_ui_util.h"
@@ -19,7 +16,6 @@
 #import "ios/chrome/test/earl_grey/chrome_test_case.h"
 #import "ios/web/public/test/http_server.h"
 #include "ios/web/public/test/http_server_util.h"
-#include "ios/web/public/test/response_providers/data_response_provider.h"
 #include "ui/base/l10n/l10n_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -28,90 +24,21 @@
 
 namespace {
 
-// A ResponseProvider that provides user agent for httpServer request.
-class UserAgentResponseProvider : public web::DataResponseProvider {
- public:
-  bool CanHandleRequest(const Request& request) override { return true; }
-
-  void GetResponseHeadersAndBody(
-      const Request& request,
-      scoped_refptr<net::HttpResponseHeaders>* headers,
-      std::string* response_body) override {
-    // Do not return anything if static plist file has been requested,
-    // as plain text is not a valid property list content.
-    if ([[base::SysUTF8ToNSString(request.url.spec()) pathExtension]
-            isEqualToString:@"plist"]) {
-      *headers =
-          web::ResponseProvider::GetResponseHeaders("", net::HTTP_NO_CONTENT);
-      return;
-    }
-
-    *headers = web::ResponseProvider::GetDefaultResponseHeaders();
-    std::string userAgent;
-    const std::string kDesktopUserAgent =
-        base::SysNSStringToUTF8(ChromeWebView::kDesktopUserAgent);
-    if (request.headers.GetHeader("User-Agent", &userAgent) &&
-        userAgent == kDesktopUserAgent) {
-      response_body->assign("Desktop");
-    } else {
-      response_body->assign("Mobile");
-    }
-  }
-};
+const char kPDFURL[] = "http://ios/testing/data/http_server_files/testpage.pdf";
 
 // Matcher for the button to find in page.
 id<GREYMatcher> FindInPageButton() {
   return chrome_test_util::ButtonWithAccessibilityLabel(
       l10n_util::GetNSStringWithFixup(IDS_IOS_TOOLS_MENU_FIND_IN_PAGE));
 }
-
-// Matcher for the button to request desktop version.
-id<GREYMatcher> RequestDesktopButton() {
-  return grey_accessibilityID(kToolsMenuRequestDesktopId);
-}
-
-const char kPDFURL[] = "http://ios/testing/data/http_server_files/testpage.pdf";
-
 }  // namespace
 
 // Tests for the tools popup menu.
 @interface ToolsPopupMenuTestCase : ChromeTestCase
-- (void)verifyMobileAndDesktopVersions:(const GURL&)url;
 @end
 
 @implementation ToolsPopupMenuTestCase
 
-// Verify that requesting desktop and mobile versions works.
-- (void)verifyMobileAndDesktopVersions:(const GURL&)url {
-  NSString* const kMobileSiteLabel = @"Mobile";
-  NSString* const kDesktopSiteLabel = @"Desktop";
-
-  [ChromeEarlGrey loadURL:url];
-
-  // Verify initial reception of the mobile site.
-  [[EarlGrey
-      selectElementWithMatcher:chrome_test_util::WebViewContainingText(
-                                   base::SysNSStringToUTF8(kMobileSiteLabel))]
-      assertWithMatcher:grey_notNil()];
-
-  // Request and verify reception of the desktop site.
-  [ChromeEarlGreyUI openToolsMenu];
-  [[EarlGrey selectElementWithMatcher:RequestDesktopButton()]
-      performAction:grey_tap()];
-  [[EarlGrey
-      selectElementWithMatcher:chrome_test_util::WebViewContainingText(
-                                   base::SysNSStringToUTF8(kDesktopSiteLabel))]
-      assertWithMatcher:grey_notNil()];
-
-  // Verify that going back returns to the mobile site.
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::BackButton()]
-      performAction:grey_tap()];
-  [[EarlGrey
-      selectElementWithMatcher:chrome_test_util::WebViewContainingText(
-                                   base::SysNSStringToUTF8(kMobileSiteLabel))]
-      assertWithMatcher:grey_notNil()];
-}
-
 // Tests that the menu is closed when tapping the close button.
 - (void)testOpenAndCloseToolsMenu {
   [ChromeEarlGreyUI openToolsMenu];
@@ -145,28 +72,6 @@
                             UIAccessibilityTraitNotEnabled)];
 }
 
-// Test requesting desktop version of page works and going back re-opens mobile
-// version of page.
-- (void)testToolsMenuRequestDesktopNetwork {
-  std::unique_ptr<web::DataResponseProvider> provider(
-      new UserAgentResponseProvider());
-  web::test::SetUpHttpServer(std::move(provider));
-
-  const GURL networkLayerTestURL =
-      web::test::HttpServer::MakeUrl("http://network");
-  [self verifyMobileAndDesktopVersions:networkLayerTestURL];
-}
-
-// Test requesting the desktop version of a page works correctly for
-// script-based desktop/mobile differentation.
-- (void)testToolsMenuRequestDesktopScript {
-  web::test::SetUpFileBasedHttpServer();
-  const GURL scriptLayerTestURL = web::test::HttpServer::MakeUrl(
-      "http://ios/testing/data/http_server_files/"
-      "request_desktop_test_page.html");
-  [self verifyMobileAndDesktopVersions:scriptLayerTestURL];
-}
-
 // Open tools menu and verify elements are accessible.
 - (void)testAccessibilityOnToolsMenu {
   [ChromeEarlGreyUI openToolsMenu];
diff --git a/ios/chrome/browser/web/forms_egtest.mm b/ios/chrome/browser/web/forms_egtest.mm
index ca9cf191..cb0ca64 100644
--- a/ios/chrome/browser/web/forms_egtest.mm
+++ b/ios/chrome/browser/web/forms_egtest.mm
@@ -4,18 +4,16 @@
 
 #import <XCTest/XCTest.h>
 
-#include "base/mac/scoped_nsobject.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/sys_string_conversions.h"
 #include "components/strings/grit/components_strings.h"
-#import "ios/chrome/browser/ui/commands/generic_chrome_command.h"
-#include "ios/chrome/browser/ui/commands/ios_command_ids.h"
 #include "ios/chrome/browser/ui/ui_util.h"
 #import "ios/chrome/test/app/chrome_test_util.h"
 #include "ios/chrome/test/app/navigation_test_util.h"
 #include "ios/chrome/test/app/web_view_interaction_test_util.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
+#import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
 #import "ios/chrome/test/earl_grey/chrome_matchers.h"
 #import "ios/chrome/test/earl_grey/chrome_test_case.h"
 #import "ios/testing/wait_util.h"
@@ -24,56 +22,55 @@
 #include "ios/web/public/test/response_providers/data_response_provider.h"
 #include "ios/web/public/test/url_test_util.h"
 
+using chrome_test_util::ButtonWithAccessibilityLabelId;
 using chrome_test_util::OmniboxText;
 using chrome_test_util::WebViewContainingText;
 
 namespace {
 
-// URL for a generic website in the user navigation flow.
-const char kGenericUrl[] = "http://generic";
-
-// URL to print the HTTP method and the request body.
-const char kPrintFormDataUrl[] = "http://printFormData";
-
-// URL that redirects to kPrintPostData with a 302.
-const char kRedirectUrl[] = "http://redirect";
-
-// URL to return a page that posts a form with some data to
-// |kPrintPostData|.
-const char kFormUrl[] = "http://formURL";
-
-// URL to return a page that posts to |kRedirect|.
-const char kRedirectFormUrl[] = "http://redirectFormURL";
+// Response shown on the page of |GetDestinationUrl|.
+const char kDestinationText[] = "bar!";
 
 // Label for the button in the form.
-const char kSubmitButton[] = "Submit";
+const char kSubmitButtonLabel[] = "submit";
 
-// Expected response from the server.
-const char kExpectedPostData[] = "POST Data=Unicorn";
+// Html form template with a submission button named "submit".
+const char* kFormHtmlTemplate =
+    "<form method='post' action='%s'> submit: "
+    "<input value='textfield' id='textfield' type='text'></label>"
+    "<input type='submit' value='submit' id='submit'>"
+    "</form>";
 
-#pragma mark - TestResponseProvider
+// GURL of a generic website in the user navigation flow.
+const GURL GetGenericUrl() {
+  return web::test::HttpServer::MakeUrl("http://generic");
+}
+
+// GURL of a page with a form that posts data to |GetDestinationUrl|.
+const GURL GetFormUrl() {
+  return web::test::HttpServer::MakeUrl("http://form");
+}
+
+// GURL of the page to which the |GetFormUrl| posts data to.
+const GURL GetDestinationUrl() {
+  return web::test::HttpServer::MakeUrl("http://destination");
+}
+
+#pragma mark - TestFormRedirectResponseProvider
+
+// URL that redirects to |GetDestinationUrl| with a 302.
+const GURL GetRedirectUrl() {
+  return web::test::HttpServer::MakeUrl("http://redirect");
+}
+
+// URL to return a page that posts to |GetRedirectUrl|.
+const GURL GetRedirectFormUrl() {
+  return web::test::HttpServer::MakeUrl("http://formRedirect");
+}
 
 // A ResponseProvider that provides html response or a redirect.
-class TestResponseProvider : public web::DataResponseProvider {
+class TestFormRedirectResponseProvider : public web::DataResponseProvider {
  public:
-  // URL for the server at |kGenericUrl|.
-  static GURL GetGenericUrl() {
-    return web::test::HttpServer::MakeUrl(kGenericUrl);
-  }
-  // URL for the server at |kPrintFormDataUrl|.
-  static GURL GetPrintFormDataUrl() {
-    return web::test::HttpServer::MakeUrl(kPrintFormDataUrl);
-  }
-  // URL for the server at |kRedirectUrl|.
-  static GURL GetRedirectUrl() {
-    return web::test::HttpServer::MakeUrl(kRedirectUrl);
-  }
-  // URL for the server at |kFormUrl|.
-  static GURL GetFormUrl() { return web::test::HttpServer::MakeUrl(kFormUrl); }
-  // URL for the server at |kRedirectFormUrl|.
-  static GURL GetRedirectFormUrl() {
-    return web::test::HttpServer::MakeUrl(kRedirectFormUrl);
-  }
   // TestResponseProvider implementation.
   bool CanHandleRequest(const Request& request) override;
   void GetResponseHeadersAndBody(
@@ -82,43 +79,30 @@
       std::string* response_body) override;
 };
 
-bool TestResponseProvider::CanHandleRequest(const Request& request) {
+bool TestFormRedirectResponseProvider::CanHandleRequest(
+    const Request& request) {
   const GURL& url = request.url;
-  return url == TestResponseProvider::GetPrintFormDataUrl() ||
-         url == TestResponseProvider::GetRedirectUrl() ||
-         url == TestResponseProvider::GetFormUrl() ||
-         url == TestResponseProvider::GetRedirectFormUrl();
+  return url == GetDestinationUrl() || url == GetRedirectUrl() ||
+         url == GetRedirectFormUrl();
 }
 
-void TestResponseProvider::GetResponseHeadersAndBody(
+void TestFormRedirectResponseProvider::GetResponseHeadersAndBody(
     const Request& request,
     scoped_refptr<net::HttpResponseHeaders>* headers,
     std::string* response_body) {
   const GURL& url = request.url;
-  if (url == TestResponseProvider::GetRedirectUrl()) {
+  if (url == GetRedirectUrl()) {
     *headers = web::ResponseProvider::GetRedirectResponseHeaders(
-        TestResponseProvider::GetPrintFormDataUrl().spec(), net::HTTP_FOUND);
+        GetDestinationUrl().spec(), net::HTTP_FOUND);
     return;
   }
 
-  const char* form_html =
-      "<form method=\"post\" action=\"%s\">"
-      "<textarea rows=\"1\" name=\"Data\">Unicorn</textarea>"
-      "<input type=\"submit\" value=\"%s\" id=\"%s\">"
-      "</form>";
-
   *headers = web::ResponseProvider::GetDefaultResponseHeaders();
-  if (url == TestResponseProvider::GetFormUrl()) {
-    *response_body = base::StringPrintf(
-        form_html, TestResponseProvider::GetPrintFormDataUrl().spec().c_str(),
-        kSubmitButton, kSubmitButton);
+  if (url == GetRedirectFormUrl()) {
+    *response_body =
+        base::StringPrintf(kFormHtmlTemplate, GetRedirectUrl().spec().c_str());
     return;
-  } else if (url == TestResponseProvider::GetRedirectFormUrl()) {
-    *response_body = base::StringPrintf(
-        form_html, TestResponseProvider::GetRedirectUrl().spec().c_str(),
-        kSubmitButton, kSubmitButton);
-    return;
-  } else if (url == TestResponseProvider::GetPrintFormDataUrl()) {
+  } else if (url == GetDestinationUrl()) {
     *response_body = request.method + std::string(" ") + request.body;
     return;
   }
@@ -133,39 +117,6 @@
 
 @implementation FormsTestCase
 
-// Sets up server urls and responses.
-- (void)setUp {
-  [super setUp];
-
-  web::test::SetUpHttpServer(base::MakeUnique<TestResponseProvider>());
-}
-
-// Submits the html form and verifies the destination url.
-- (void)submitForm {
-  chrome_test_util::TapWebViewElementWithId(kSubmitButton);
-
-  GURL url = TestResponseProvider::GetPrintFormDataUrl();
-  [[EarlGrey selectElementWithMatcher:OmniboxText(url.GetContent())]
-      assertWithMatcher:grey_notNil()];
-}
-
-// Waits for the |expectedResponse| within the web view.
-- (void)waitForExpectedResponse:(std::string)expectedResponse {
-  GREYCondition* condition = [GREYCondition
-      conditionWithName:@"Waiting for webview to display resulting text."
-                  block:^BOOL {
-                    id<GREYMatcher> webViewMatcher =
-                        chrome_test_util::WebViewContainingText(
-                            expectedResponse);
-                    NSError* error = nil;
-                    [[EarlGrey selectElementWithMatcher:webViewMatcher]
-                        assertWithMatcher:grey_notNil()
-                                    error:&error];
-                    return error == nil;
-                  }];
-  GREYAssert([condition waitWithTimeout:5], @"Webview text was not displayed.");
-}
-
 // Waits for view with Tab History accessibility ID.
 - (void)waitForTabHistoryView {
   GREYCondition* condition = [GREYCondition
@@ -179,26 +130,8 @@
                                     error:&error];
                     return error == nil;
                   }];
-  GREYAssert([condition waitWithTimeout:5], @"Tab History View not displayed.");
-}
-
-// Reloads the web view and waits for the loading to complete.
-// TODO(crbug.com/638674): Evaluate if this can move to shared code
-- (void)reloadPage {
-  base::scoped_nsobject<GenericChromeCommand> reloadCommand(
-      [[GenericChromeCommand alloc] initWithTag:IDC_RELOAD]);
-  chrome_test_util::RunCommandWithActiveViewController(reloadCommand);
-
-  [ChromeEarlGrey waitForPageToFinishLoading];
-}
-
-// Navigates back to the previous webpage.
-- (void)goBack {
-  base::scoped_nsobject<GenericChromeCommand> backCommand(
-      [[GenericChromeCommand alloc] initWithTag:IDC_BACK]);
-  chrome_test_util::RunCommandWithActiveViewController(backCommand);
-
-  [ChromeEarlGrey waitForPageToFinishLoading];
+  GREYAssert([condition waitWithTimeout:testing::kWaitForUIElementTimeout],
+             @"Tab History View not displayed.");
 }
 
 // Open back navigation history.
@@ -207,16 +140,6 @@
       performAction:grey_longPress()];
 }
 
-// Navigates forward to a previous webpage.
-// TODO(crbug.com/638674): Evaluate if this can move to shared code
-- (void)goForward {
-  base::scoped_nsobject<GenericChromeCommand> forwardCommand(
-      [[GenericChromeCommand alloc] initWithTag:IDC_FORWARD]);
-  chrome_test_util::RunCommandWithActiveViewController(forwardCommand);
-
-  [ChromeEarlGrey waitForPageToFinishLoading];
-}
-
 // Accepts the warning that the form POST data will be reposted.
 - (void)confirmResendWarning {
   id<GREYMatcher> resendWarning =
@@ -226,78 +149,126 @@
       performAction:grey_longPress()];
 }
 
-// Tests whether the request data is reposted correctly.
-- (void)testRepostForm {
-  [ChromeEarlGrey loadURL:TestResponseProvider::GetFormUrl()];
+// Sets up a basic simple http server for form test with a form located at
+// |GetFormUrl|, and posts data to |GetDestinationUrl| upon submission.
+- (void)setUpFormTestSimpleHttpServer {
+  std::map<GURL, std::string> responses;
+  responses[GetGenericUrl()] = "A generic page";
+  responses[GetFormUrl()] =
+      base::StringPrintf(kFormHtmlTemplate, GetDestinationUrl().spec().c_str());
+  responses[GetDestinationUrl()] = kDestinationText;
+  web::test::SetUpSimpleHttpServer(responses);
+}
 
-  [self submitForm];
-  [self waitForExpectedResponse:kExpectedPostData];
+// Tests that a POST followed by reloading the destination page resends data.
+- (void)testRepostFormAfterReload {
+  [self setUpFormTestSimpleHttpServer];
+  const GURL destinationURL = GetDestinationUrl();
 
-  [self reloadPage];
+  [ChromeEarlGrey loadURL:GetFormUrl()];
+  chrome_test_util::TapWebViewElementWithId(kSubmitButtonLabel);
+  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kDestinationText)]
+      assertWithMatcher:grey_notNil()];
+  [[EarlGrey selectElementWithMatcher:OmniboxText(destinationURL.GetContent())]
+      assertWithMatcher:grey_notNil()];
+
+  [ChromeEarlGrey reload];
   [self confirmResendWarning];
 
-  [self waitForExpectedResponse:kExpectedPostData];
+  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kDestinationText)]
+      assertWithMatcher:grey_notNil()];
+  [[EarlGrey selectElementWithMatcher:OmniboxText(destinationURL.GetContent())]
+      assertWithMatcher:grey_notNil()];
 }
 
 // Tests that a POST followed by navigating to a new page and then tapping back
 // to the form result page resends data.
 - (void)testRepostFormAfterTappingBack {
-  [ChromeEarlGrey loadURL:TestResponseProvider::GetFormUrl()];
+  [self setUpFormTestSimpleHttpServer];
+  const GURL destinationURL = GetDestinationUrl();
 
-  [self submitForm];
+  [ChromeEarlGrey loadURL:GetFormUrl()];
+  chrome_test_util::TapWebViewElementWithId(kSubmitButtonLabel);
+  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kDestinationText)]
+      assertWithMatcher:grey_notNil()];
+  [[EarlGrey selectElementWithMatcher:OmniboxText(destinationURL.GetContent())]
+      assertWithMatcher:grey_notNil()];
 
-  // Go to a new page.
-  [ChromeEarlGrey loadURL:TestResponseProvider::GetGenericUrl()];
-
-  // Go back and check that the data is reposted.
-  [self goBack];
+  // Go to a new page and go back and check that the data is reposted.
+  [ChromeEarlGrey loadURL:GetGenericUrl()];
+  [ChromeEarlGrey goBack];
   [self confirmResendWarning];
-  [self waitForExpectedResponse:kExpectedPostData];
+  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kDestinationText)]
+      assertWithMatcher:grey_notNil()];
+  [[EarlGrey selectElementWithMatcher:OmniboxText(destinationURL.GetContent())]
+      assertWithMatcher:grey_notNil()];
 }
 
 // Tests that a POST followed by tapping back to the form page and then tapping
 // forward to the result page resends data.
 - (void)testRepostFormAfterTappingBackAndForward {
-  [ChromeEarlGrey loadURL:TestResponseProvider::GetFormUrl()];
-  [self submitForm];
+  [self setUpFormTestSimpleHttpServer];
+  const GURL destinationURL = GetDestinationUrl();
 
-  [self goBack];
-  [self goForward];
+  [ChromeEarlGrey loadURL:GetFormUrl()];
+  chrome_test_util::TapWebViewElementWithId(kSubmitButtonLabel);
+  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kDestinationText)]
+      assertWithMatcher:grey_notNil()];
+  [[EarlGrey selectElementWithMatcher:OmniboxText(destinationURL.GetContent())]
+      assertWithMatcher:grey_notNil()];
+
+  [ChromeEarlGrey goBack];
+  [ChromeEarlGrey goForward];
   [self confirmResendWarning];
-  [self waitForExpectedResponse:kExpectedPostData];
+  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kDestinationText)]
+      assertWithMatcher:grey_notNil()];
+  [[EarlGrey selectElementWithMatcher:OmniboxText(destinationURL.GetContent())]
+      assertWithMatcher:grey_notNil()];
 }
 
 // Tests that a POST followed by a new request and then index navigation to get
 // back to the result page resends data.
 - (void)testRepostFormAfterIndexNavigation {
-  [ChromeEarlGrey loadURL:TestResponseProvider::GetFormUrl()];
-  [self submitForm];
+  [self setUpFormTestSimpleHttpServer];
+  const GURL destinationURL = GetDestinationUrl();
 
-  // Go to a new page.
-  [ChromeEarlGrey loadURL:TestResponseProvider::GetGenericUrl()];
+  [ChromeEarlGrey loadURL:GetFormUrl()];
+  chrome_test_util::TapWebViewElementWithId(kSubmitButtonLabel);
+  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kDestinationText)]
+      assertWithMatcher:grey_notNil()];
+  [[EarlGrey selectElementWithMatcher:OmniboxText(destinationURL.GetContent())]
+      assertWithMatcher:grey_notNil()];
 
+  // Go to a new page and go back to destination through back history.
+  [ChromeEarlGrey loadURL:GetGenericUrl()];
   [self openBackHistory];
   [self waitForTabHistoryView];
-
-  GURL history_url = TestResponseProvider::GetPrintFormDataUrl();
   id<GREYMatcher> historyItem = grey_text(
-      base::SysUTF16ToNSString(web::GetDisplayTitleForUrl(history_url)));
+      base::SysUTF16ToNSString(web::GetDisplayTitleForUrl(destinationURL)));
   [[EarlGrey selectElementWithMatcher:historyItem] performAction:grey_tap()];
-
   [ChromeEarlGrey waitForPageToFinishLoading];
 
   [self confirmResendWarning];
-  [self waitForExpectedResponse:kExpectedPostData];
+  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kDestinationText)]
+      assertWithMatcher:grey_notNil()];
+  [[EarlGrey selectElementWithMatcher:OmniboxText(destinationURL.GetContent())]
+      assertWithMatcher:grey_notNil()];
 }
 
 // When data is not reposted, the request is canceled.
 - (void)testRepostFormCancelling {
-  GURL formURL = TestResponseProvider::GetFormUrl();
-  [ChromeEarlGrey loadURL:formURL];
-  [self submitForm];
+  [self setUpFormTestSimpleHttpServer];
+  const GURL destinationURL = GetDestinationUrl();
 
-  [self goBack];
-  [self goForward];
+  [ChromeEarlGrey loadURL:GetFormUrl()];
+  chrome_test_util::TapWebViewElementWithId(kSubmitButtonLabel);
+  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kDestinationText)]
+      assertWithMatcher:grey_notNil()];
+  [[EarlGrey selectElementWithMatcher:OmniboxText(destinationURL.GetContent())]
+      assertWithMatcher:grey_notNil()];
+
+  [ChromeEarlGrey goBack];
+  [ChromeEarlGrey goForward];
 
   // Abort the reload.
   // TODO (crbug.com/705020): Use something like ElementToDismissContextMenu
@@ -319,52 +290,65 @@
     [[EarlGrey selectElementWithMatcher:chrome_test_util::CancelButton()]
         performAction:grey_tap()];
   }
+  [ChromeEarlGrey waitForPageToFinishLoading];
 
   // Verify that navigation was cancelled, and forward navigation is possible.
-  [ChromeEarlGrey waitForPageToFinishLoading];
-  [[EarlGrey selectElementWithMatcher:OmniboxText(formURL.GetContent())]
+  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kSubmitButtonLabel)]
+      assertWithMatcher:grey_notNil()];
+  [[EarlGrey selectElementWithMatcher:OmniboxText(GetFormUrl().GetContent())]
       assertWithMatcher:grey_notNil()];
   [[EarlGrey selectElementWithMatcher:chrome_test_util::ForwardButton()]
       assertWithMatcher:grey_interactable()];
 }
 
-// Tests that a POST followed by a redirect does not show the popup.
-- (void)testRepostFormCancellingAfterRedirect {
-  [ChromeEarlGrey loadURL:TestResponseProvider::GetRedirectFormUrl()];
-  // Submit the form, which redirects before printing the data.
-  [self submitForm];
-  // Check that the redirect changes the POST to a GET.
-  [self waitForExpectedResponse:"GET"];
-  [self reloadPage];
-
-  // Check that the popup did not show
-  id<GREYMatcher> resendWarning =
-      chrome_test_util::ButtonWithAccessibilityLabelId(
-          IDS_HTTP_POST_WARNING_RESEND);
-  [[EarlGrey selectElementWithMatcher:resendWarning]
-      assertWithMatcher:grey_nil()];
-
-  [self waitForExpectedResponse:"GET"];
-}
-
 // Tests that pressing the button on a POST-based form changes the page and that
 // the back button works as expected afterwards.
 - (void)testGoBackButtonAfterFormSubmission {
-  GURL formURL = TestResponseProvider::GetFormUrl();
-  GURL destinationURL = TestResponseProvider::GetPrintFormDataUrl();
+  [self setUpFormTestSimpleHttpServer];
+  GURL destinationURL = GetDestinationUrl();
 
-  [ChromeEarlGrey loadURL:formURL];
-  chrome_test_util::TapWebViewElementWithId(kSubmitButton);
-  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kExpectedPostData)]
+  [ChromeEarlGrey loadURL:GetFormUrl()];
+  chrome_test_util::TapWebViewElementWithId(kSubmitButtonLabel);
+  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kDestinationText)]
       assertWithMatcher:grey_notNil()];
   [[EarlGrey selectElementWithMatcher:OmniboxText(destinationURL.GetContent())]
       assertWithMatcher:grey_notNil()];
 
   // Go back and verify the browser navigates to the original URL.
-  [self goBack];
-  [[EarlGrey selectElementWithMatcher:WebViewContainingText("Unicorn")]
+  [ChromeEarlGrey goBack];
+  [[EarlGrey selectElementWithMatcher:WebViewContainingText(kSubmitButtonLabel)]
       assertWithMatcher:grey_notNil()];
-  [[EarlGrey selectElementWithMatcher:OmniboxText(formURL.GetContent())]
+  [[EarlGrey selectElementWithMatcher:OmniboxText(GetFormUrl().GetContent())]
+      assertWithMatcher:grey_notNil()];
+}
+
+// Tests that a POST followed by a redirect does not show the popup.
+- (void)testRepostFormCancellingAfterRedirect {
+  web::test::SetUpHttpServer(
+      base::MakeUnique<TestFormRedirectResponseProvider>());
+  const GURL destinationURL = GetDestinationUrl();
+
+  [ChromeEarlGrey loadURL:GetRedirectFormUrl()];
+
+  // Submit the form, which redirects before printing the data.
+  chrome_test_util::TapWebViewElementWithId(kSubmitButtonLabel);
+
+  // Check that the redirect changes the POST to a GET.
+  [[EarlGrey selectElementWithMatcher:WebViewContainingText("GET")]
+      assertWithMatcher:grey_notNil()];
+  [[EarlGrey selectElementWithMatcher:OmniboxText(destinationURL.GetContent())]
+      assertWithMatcher:grey_notNil()];
+
+  [ChromeEarlGrey reload];
+
+  // Check that the popup did not show
+  id<GREYMatcher> resendWarning =
+      ButtonWithAccessibilityLabelId(IDS_HTTP_POST_WARNING_RESEND);
+  [[EarlGrey selectElementWithMatcher:resendWarning]
+      assertWithMatcher:grey_nil()];
+  [[EarlGrey selectElementWithMatcher:WebViewContainingText("GET")]
+      assertWithMatcher:grey_notNil()];
+  [[EarlGrey selectElementWithMatcher:OmniboxText(destinationURL.GetContent())]
       assertWithMatcher:grey_notNil()];
 }
 
diff --git a/ios/chrome/test/earl_grey/BUILD.gn b/ios/chrome/test/earl_grey/BUILD.gn
index 3bde644..32cf984 100644
--- a/ios/chrome/test/earl_grey/BUILD.gn
+++ b/ios/chrome/test/earl_grey/BUILD.gn
@@ -162,6 +162,7 @@
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/ui",
     "//ios/chrome/browser/ui/collection_view/cells",
+    "//ios/chrome/browser/ui/commands:commands",
     "//ios/chrome/browser/ui/omnibox:omnibox_internal",
     "//ios/chrome/browser/ui/static_content",
     "//ios/chrome/browser/ui/toolbar",
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey.h b/ios/chrome/test/earl_grey/chrome_earl_grey.h
index 45e4d91..7df9b32 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey.h
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey.h
@@ -48,6 +48,17 @@
 // a timeout.
 + (void)loadURL:(GURL)URL;
 
+// Reloads the page and waits for the loading to complete, or a timeout.
++ (void)reload;
+
+// Navigates back to the previous page and waits for the loading to complete, or
+// a timeout.
++ (void)goBack;
+
+// Navigates forward to the next page and waits for the loading to complete, or
+// a timeout.
++ (void)goForward;
+
 // Waits for the page to finish loading or a timeout.
 + (void)waitForPageToFinishLoading;
 
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey.mm b/ios/chrome/test/earl_grey/chrome_earl_grey.mm
index 8506bb7..9286a2ed 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey.mm
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey.mm
@@ -8,9 +8,12 @@
 #import <WebKit/WebKit.h>
 
 #include "base/mac/foundation_util.h"
+#include "base/mac/scoped_nsobject.h"
 #include "base/strings/sys_string_conversions.h"
 #import "base/test/ios/wait_util.h"
 #include "components/strings/grit/components_strings.h"
+#import "ios/chrome/browser/ui/commands/generic_chrome_command.h"
+#include "ios/chrome/browser/ui/commands/ios_command_ids.h"
 #import "ios/chrome/browser/ui/static_content/static_html_view_controller.h"
 #import "ios/chrome/test/app/chrome_test_util.h"
 #import "ios/chrome/test/app/history_test_util.h"
@@ -112,6 +115,27 @@
     web::WaitUntilWindowIdInjected(webState);
 }
 
++ (void)reload {
+  base::scoped_nsobject<GenericChromeCommand> reloadCommand(
+      [[GenericChromeCommand alloc] initWithTag:IDC_RELOAD]);
+  chrome_test_util::RunCommandWithActiveViewController(reloadCommand);
+  [ChromeEarlGrey waitForPageToFinishLoading];
+}
+
++ (void)goBack {
+  base::scoped_nsobject<GenericChromeCommand> reloadCommand(
+      [[GenericChromeCommand alloc] initWithTag:IDC_BACK]);
+  chrome_test_util::RunCommandWithActiveViewController(reloadCommand);
+  [ChromeEarlGrey waitForPageToFinishLoading];
+}
+
++ (void)goForward {
+  base::scoped_nsobject<GenericChromeCommand> reloadCommand(
+      [[GenericChromeCommand alloc] initWithTag:IDC_FORWARD]);
+  chrome_test_util::RunCommandWithActiveViewController(reloadCommand);
+  [ChromeEarlGrey waitForPageToFinishLoading];
+}
+
 + (void)waitForPageToFinishLoading {
   GREYCondition* condition =
       [GREYCondition conditionWithName:@"Wait for page to complete loading."
diff --git a/ios/testing/BUILD.gn b/ios/testing/BUILD.gn
index 4751fa050..e4b8ef8 100644
--- a/ios/testing/BUILD.gn
+++ b/ios/testing/BUILD.gn
@@ -80,12 +80,12 @@
     "data/http_server_files/multi_field_form.html",
     "data/http_server_files/pony.html",
     "data/http_server_files/redirect_refresh.html",
-    "data/http_server_files/request_desktop_test_page.html",
     "data/http_server_files/single_page_wide.pdf",
     "data/http_server_files/state_operations.html",
     "data/http_server_files/state_operations.js",
     "data/http_server_files/testpage.pdf",
     "data/http_server_files/two_pages.pdf",
+    "data/http_server_files/user_agent_test_page.html",
     "data/http_server_files/window_close.html",
     "data/http_server_files/window_location.html",
     "data/http_server_files/window_location.js",
diff --git a/ios/testing/data/http_server_files/request_desktop_test_page.html b/ios/testing/data/http_server_files/user_agent_test_page.html
similarity index 90%
rename from ios/testing/data/http_server_files/request_desktop_test_page.html
rename to ios/testing/data/http_server_files/user_agent_test_page.html
index 0feb62c..b193cbe 100644
--- a/ios/testing/data/http_server_files/request_desktop_test_page.html
+++ b/ios/testing/data/http_server_files/user_agent_test_page.html
@@ -7,7 +7,7 @@
 <html>
 <head>
 <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
-<title>Test Request Desktop Script Layer</title>
+<title>Test Request Desktop/Mobile Script</title>
 </head>
 <body>
 <script type="text/javascript">
diff --git a/ios/web/public/web_state/ui/crw_web_view_proxy.h b/ios/web/public/web_state/ui/crw_web_view_proxy.h
index 7a5768c7..f5a420f 100644
--- a/ios/web/public/web_state/ui/crw_web_view_proxy.h
+++ b/ios/web/public/web_state/ui/crw_web_view_proxy.h
@@ -62,11 +62,8 @@
 // Returns the currently visible keyboard accessory, or nil.
 - (UIView*)keyboardAccessory;
 
-// Returns the currently visible keyboard input assistant item, or nil. Only
-// valid on iOS 9 or above.
-#if defined(__IPHONE_9_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_9_0
+// Returns the currently visible keyboard input assistant item, or nil.
 - (UITextInputAssistantItem*)inputAssistantItem;
-#endif
 
 // Wrapper around the becomeFirstResponder method of the webview.
 - (BOOL)becomeFirstResponder;
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index 7c10f9f..366ada9 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -943,9 +943,6 @@
 bool WebMediaPlayerImpl::CopyVideoTextureToPlatformTexture(
     gpu::gles2::GLES2Interface* gl,
     unsigned int texture,
-    unsigned internal_format,
-    unsigned format,
-    unsigned type,
     bool premultiply_alpha,
     bool flip_y) {
   DCHECK(main_task_runner_->BelongsToCurrentThread());
@@ -966,8 +963,7 @@
   if (!context_3d_cb_.is_null())
     context_3d = context_3d_cb_.Run();
   return skcanvas_video_renderer_.CopyVideoFrameTexturesToGLTexture(
-      context_3d, gl, video_frame.get(), texture, internal_format, format, type,
-      premultiply_alpha, flip_y);
+      context_3d, gl, video_frame.get(), texture, premultiply_alpha, flip_y);
 }
 
 void WebMediaPlayerImpl::SetContentDecryptionModule(
diff --git a/media/blink/webmediaplayer_impl.h b/media/blink/webmediaplayer_impl.h
index 8e1a16b5..29863b0 100644
--- a/media/blink/webmediaplayer_impl.h
+++ b/media/blink/webmediaplayer_impl.h
@@ -173,9 +173,6 @@
 
   bool CopyVideoTextureToPlatformTexture(gpu::gles2::GLES2Interface* gl,
                                          unsigned int texture,
-                                         unsigned internal_format,
-                                         unsigned format,
-                                         unsigned type,
                                          bool premultiply_alpha,
                                          bool flip_y) override;
 
diff --git a/media/renderers/skcanvas_video_renderer.cc b/media/renderers/skcanvas_video_renderer.cc
index d60e854..1e2e9b2 100644
--- a/media/renderers/skcanvas_video_renderer.cc
+++ b/media/renderers/skcanvas_video_renderer.cc
@@ -160,20 +160,6 @@
   return img;
 }
 
-bool VideoTextureNeedsClipping(const VideoFrame* video_frame) {
-  // There are multiple reasons that the size of the video frame's
-  // visible rectangle may differ from the coded size, including the
-  // encoder rounding up to the size of a macroblock, or use of
-  // non-square pixels.
-  //
-  // Some callers of these APIs (HTMLVideoElement and the 2D canvas
-  // context) already clip to the video frame's visible rectangle.
-  // WebGL on the other hand assumes that only the valid pixels are
-  // contained in the destination texture. This helper function
-  // determines whether this slower path is needed.
-  return video_frame->visible_rect().size() != video_frame->coded_size();
-}
-
 // Creates a SkImage from a |video_frame| backed by native resources.
 // The SkImage will take ownership of the underlying resource.
 sk_sp<SkImage> NewSkImageFromVideoFrameNative(VideoFrame* video_frame,
@@ -198,10 +184,12 @@
     gl->GenTextures(1, &source_texture);
     DCHECK(source_texture);
     gl->BindTexture(GL_TEXTURE_2D, source_texture);
+    const gfx::Size& natural_size = video_frame->natural_size();
+    gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, natural_size.width(),
+                   natural_size.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE,
+                   nullptr);
     SkCanvasVideoRenderer::CopyVideoFrameSingleTextureToGLTexture(
-        gl, video_frame,
-        SkCanvasVideoRenderer::SingleFrameForVideoElementOrCanvas,
-        source_texture, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, true, false);
+        gl, video_frame, source_texture, true, false);
   } else {
     gl->WaitSyncTokenCHROMIUM(mailbox_holder.sync_token.GetConstData());
     source_texture = gl->CreateAndConsumeTextureCHROMIUM(
@@ -765,11 +753,7 @@
 void SkCanvasVideoRenderer::CopyVideoFrameSingleTextureToGLTexture(
     gpu::gles2::GLES2Interface* gl,
     VideoFrame* video_frame,
-    SingleFrameCopyMode copy_mode,
     unsigned int texture,
-    unsigned int internal_format,
-    unsigned int format,
-    unsigned int type,
     bool premultiply_alpha,
     bool flip_y) {
   DCHECK(video_frame);
@@ -792,35 +776,14 @@
   // "flip_y == true" means to reverse the video orientation while
   // "flip_y == false" means to keep the intrinsic orientation.
 
-  if (copy_mode == SingleFrameForVideoElementOrCanvas ||
-      !VideoTextureNeedsClipping(video_frame)) {
-    // No need to clip the source video texture.
-    gl->CopyTextureCHROMIUM(source_texture, 0, GL_TEXTURE_2D, texture, 0,
-                            internal_format, type, flip_y, premultiply_alpha,
-                            false);
-  } else {
-    // Must reallocate the destination texture and copy only a sub-portion.
-    gfx::Rect dest_rect = video_frame->visible_rect();
-#if DCHECK_IS_ON()
-    // The caller should have bound _texture_ to the GL_TEXTURE_2D
-    // binding point already.
-    GLuint current_texture = 0;
-    gl->GetIntegerv(GL_TEXTURE_BINDING_2D,
-                    reinterpret_cast<GLint*>(&current_texture));
-    DCHECK_EQ(current_texture, texture);
-    // There should always be enough data in the source texture to
-    // cover this copy.
-    DCHECK_LE(dest_rect.width(), video_frame->coded_size().width());
-    DCHECK_LE(dest_rect.height(), video_frame->coded_size().height());
-#endif
-    gl->TexImage2D(GL_TEXTURE_2D, 0, internal_format, dest_rect.width(),
-                   dest_rect.height(), 0, format, type, nullptr);
-    gl->CopySubTextureCHROMIUM(source_texture, 0, GL_TEXTURE_2D, texture, 0, 0,
-                               0, dest_rect.x(), dest_rect.y(),
-                               dest_rect.width(), dest_rect.height(), flip_y,
-                               premultiply_alpha, false);
-  }
-
+  // The video's texture might be larger than the natural size because
+  // the encoder might have had to round up to the size of a macroblock.
+  // Make sure to only copy the natural size to avoid putting garbage
+  // into the bottom of the destination texture.
+  const gfx::Size& natural_size = video_frame->natural_size();
+  gl->CopySubTextureCHROMIUM(source_texture, 0, GL_TEXTURE_2D, texture, 0, 0, 0,
+                             0, 0, natural_size.width(), natural_size.height(),
+                             flip_y, premultiply_alpha, false);
   gl->DeleteTextures(1, &source_texture);
   gl->Flush();
 
@@ -833,9 +796,6 @@
     gpu::gles2::GLES2Interface* destination_gl,
     const scoped_refptr<VideoFrame>& video_frame,
     unsigned int texture,
-    unsigned int internal_format,
-    unsigned int format,
-    unsigned int type,
     bool premultiply_alpha,
     bool flip_y) {
   DCHECK(thread_checker_.CalledOnValidThread());
@@ -872,35 +832,15 @@
         destination_gl->CreateAndConsumeTextureCHROMIUM(
             mailbox_holder.texture_target, mailbox_holder.mailbox.name);
 
-    // See whether the source video texture must be clipped.
-    if (VideoTextureNeedsClipping(video_frame.get())) {
-      // Reallocate destination texture and copy only valid region.
-      gfx::Rect dest_rect = video_frame->visible_rect();
-#if DCHECK_IS_ON()
-      // The caller should have bound _texture_ to the GL_TEXTURE_2D
-      // binding point already.
-      GLuint current_texture = 0;
-      destination_gl->GetIntegerv(GL_TEXTURE_BINDING_2D,
-                                  reinterpret_cast<GLint*>(&current_texture));
-      DCHECK_EQ(current_texture, texture);
-      // There should always be enough data in the source texture to
-      // cover this copy.
-      DCHECK_LE(dest_rect.width(), video_frame->coded_size().width());
-      DCHECK_LE(dest_rect.height(), video_frame->coded_size().height());
-#endif
-      destination_gl->TexImage2D(GL_TEXTURE_2D, 0, internal_format,
-                                 dest_rect.width(), dest_rect.height(), 0,
-                                 format, type, nullptr);
-      destination_gl->CopySubTextureCHROMIUM(
-          intermediate_texture, 0, GL_TEXTURE_2D, texture, 0, 0, 0,
-          dest_rect.x(), dest_rect.y(), dest_rect.width(), dest_rect.height(),
-          flip_y, premultiply_alpha, false);
-    } else {
-      destination_gl->CopyTextureCHROMIUM(
-          intermediate_texture, 0, GL_TEXTURE_2D, texture, 0, internal_format,
-          type, flip_y, premultiply_alpha, false);
-    }
-
+    // The video's texture might be larger than the natural size because
+    // the encoder might have had to round up to the size of a macroblock.
+    // Make sure to only copy the natural size to avoid putting garbage
+    // into the bottom of the destination texture.
+    const gfx::Size& natural_size = video_frame->natural_size();
+    destination_gl->CopySubTextureCHROMIUM(
+        intermediate_texture, 0, GL_TEXTURE_2D, texture, 0, 0, 0, 0, 0,
+        natural_size.width(), natural_size.height(), flip_y, premultiply_alpha,
+        false);
     destination_gl->DeleteTextures(1, &intermediate_texture);
 
     // Wait for destination context to consume mailbox before deleting it in
@@ -915,9 +855,8 @@
     SyncTokenClientImpl client(canvas_gl);
     video_frame->UpdateReleaseSyncToken(&client);
   } else {
-    CopyVideoFrameSingleTextureToGLTexture(
-        destination_gl, video_frame.get(), SingleFrameForWebGL, texture,
-        internal_format, format, type, premultiply_alpha, flip_y);
+    CopyVideoFrameSingleTextureToGLTexture(destination_gl, video_frame.get(),
+                                           texture, premultiply_alpha, flip_y);
   }
 
   return true;
diff --git a/media/renderers/skcanvas_video_renderer.h b/media/renderers/skcanvas_video_renderer.h
index 1f3d2f7f..3831139 100644
--- a/media/renderers/skcanvas_video_renderer.h
+++ b/media/renderers/skcanvas_video_renderer.h
@@ -64,25 +64,16 @@
                                            void* rgb_pixels,
                                            size_t row_bytes);
 
-  enum SingleFrameCopyMode {
-    SingleFrameForVideoElementOrCanvas,
-    SingleFrameForWebGL
-  };
-
   // Copy the contents of texture of |video_frame| to texture |texture|.
   // |level|, |internal_format|, |type| specify target texture |texture|.
   // The format of |video_frame| must be VideoFrame::NATIVE_TEXTURE.
-  // |copy_mode| alters how the copy is done, and takes into consideration
-  // whether the caller will clip the texture to the frame's |visible_rect|,
-  // or expects this to be done internally.
+  // Assumes |texture| has already been allocated with the appropriate
+  // size and a compatible format, internal format and type; this is
+  // effectively a "TexSubImage" operation.
   static void CopyVideoFrameSingleTextureToGLTexture(
       gpu::gles2::GLES2Interface* gl,
       VideoFrame* video_frame,
-      SingleFrameCopyMode copy_mode,
       unsigned int texture,
-      unsigned int internal_format,
-      unsigned int format,
-      unsigned int type,
       bool premultiply_alpha,
       bool flip_y);
 
@@ -91,15 +82,15 @@
   // |level|, |internal_format|, |type| specify target texture |texture|.
   // The format of |video_frame| must be VideoFrame::NATIVE_TEXTURE.
   // |context_3d| has a GrContext that may be used during the copy.
+  // Assumes |texture| has already been allocated with the appropriate
+  // size and a compatible format, internal format and type; this is
+  // effectively a "TexSubImage" operation.
   // Returns true on success.
   bool CopyVideoFrameTexturesToGLTexture(
       const Context3D& context_3d,
       gpu::gles2::GLES2Interface* destination_gl,
       const scoped_refptr<VideoFrame>& video_frame,
       unsigned int texture,
-      unsigned int internal_format,
-      unsigned int format,
-      unsigned int type,
       bool premultiply_alpha,
       bool flip_y);
 
diff --git a/mojo/edk/embedder/platform_shared_buffer.cc b/mojo/edk/embedder/platform_shared_buffer.cc
index 58af44d..623e6539 100644
--- a/mojo/edk/embedder/platform_shared_buffer.cc
+++ b/mojo/edk/embedder/platform_shared_buffer.cc
@@ -8,6 +8,7 @@
 
 #include <utility>
 
+#include "base/debug/alias.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/shared_memory.h"
@@ -145,6 +146,10 @@
     base::AutoLock locker(lock_);
     handle = base::SharedMemory::DuplicateHandle(shared_memory_->handle());
   }
+
+  // TODO(crbug.com/706689): Remove this when the bug is sorted out.
+  CHECK(handle != base::SharedMemory::NULLHandle());
+
   if (handle == base::SharedMemory::NULLHandle())
     return nullptr;
 
@@ -310,8 +315,19 @@
   size_t real_offset = offset_ - offset_rounding;
   size_t real_length = length_ + offset_rounding;
 
-  if (!shared_memory_.MapAt(static_cast<off_t>(real_offset), real_length))
-    return false;
+  bool result =
+      shared_memory_.MapAt(static_cast<off_t>(real_offset), real_length);
+
+  // TODO(crbug.com/706689): Remove this when the bug is sorted out.
+  size_t offset = offset_;
+  size_t length = length_;
+  base::debug::Alias(&offset);
+  base::debug::Alias(&length);
+  base::debug::Alias(&page_size);
+  base::debug::Alias(&offset_rounding);
+  base::debug::Alias(&real_offset);
+  base::debug::Alias(&real_length);
+  CHECK(result);
 
   base_ = static_cast<char*>(shared_memory_.memory()) + offset_rounding;
   return true;
diff --git a/mojo/edk/js/tests/js_to_cpp_tests.js b/mojo/edk/js/tests/js_to_cpp_tests.js
index 6b69fca..58592f8 100644
--- a/mojo/edk/js/tests/js_to_cpp_tests.js
+++ b/mojo/edk/js/tests/js_to_cpp_tests.js
@@ -110,16 +110,12 @@
     };
 
     while (!stopSignalled) {
-      dataPipe = core.createDataPipe(DATA_PIPE_PARAMS);
       messagePipe = core.createMessagePipe();
-      writeDataPipe(dataPipe, sampleData);
       writeMessagePipe(messagePipe, sampleMessage);
-      arg.data_handle = dataPipe.consumerHandle;
       arg.message_handle = messagePipe.handle1;
 
       this.cppSide_.bitFlipResponse(createEchoArgsList(arg));
 
-      core.close(dataPipe.producerHandle);
       core.close(messagePipe.handle0);
       iteration += 1;
     }
@@ -150,16 +146,12 @@
     };
 
     while (!stopSignalled) {
-      dataPipe = core.createDataPipe(DATA_PIPE_PARAMS);
       messagePipe = core.createMessagePipe();
-      writeDataPipe(dataPipe, sampleData);
       writeMessagePipe(messagePipe, sampleMessage);
-      arg.data_handle = dataPipe.consumerHandle;
       arg.message_handle = messagePipe.handle1;
 
       this.cppSide_.backPointerResponse(createEchoArgsList(arg));
 
-      core.close(dataPipe.producerHandle);
       core.close(messagePipe.handle0);
       iteration += 1;
     }
diff --git a/mojo/edk/system/data_pipe_consumer_dispatcher.cc b/mojo/edk/system/data_pipe_consumer_dispatcher.cc
index f338732..fe81e9d 100644
--- a/mojo/edk/system/data_pipe_consumer_dispatcher.cc
+++ b/mojo/edk/system/data_pipe_consumer_dispatcher.cc
@@ -322,6 +322,9 @@
 
   ports[0] = control_port_.name();
 
+  // TODO(crbug.com/706689): Remove this when the bug is sorted out.
+  CHECK(shared_ring_buffer_);
+
   buffer_handle_for_transit_ = shared_ring_buffer_->DuplicatePlatformHandle();
   platform_handles[0] = buffer_handle_for_transit_.get();
 
@@ -330,6 +333,10 @@
 
 bool DataPipeConsumerDispatcher::BeginTransit() {
   base::AutoLock lock(lock_);
+
+  // TODO(crbug.com/706689): Remove this when the bug is sorted out.
+  CHECK(shared_ring_buffer_);
+
   if (in_transit_)
     return false;
   in_transit_ = !in_two_phase_read_;
@@ -413,8 +420,15 @@
 void DataPipeConsumerDispatcher::InitializeNoLock() {
   lock_.AssertAcquired();
 
+  // TODO(crbug.com/706689): Remove this when the bug is sorted out.
+  CHECK(shared_ring_buffer_);
+
   if (shared_ring_buffer_) {
     DCHECK(!ring_buffer_mapping_);
+
+    // TODO(crbug.com/706689): Remove this when the bug is sorted out.
+    CHECK(shared_ring_buffer_->IsValidMap(0, options_.capacity_num_bytes));
+
     ring_buffer_mapping_ =
         shared_ring_buffer_->Map(0, options_.capacity_num_bytes);
     if (!ring_buffer_mapping_) {
@@ -423,6 +437,9 @@
     }
   }
 
+  // TODO(crbug.com/706689): Remove this when the bug is sorted out.
+  CHECK(shared_ring_buffer_);
+
   base::AutoUnlock unlock(lock_);
   node_controller_->SetPortObserver(
       control_port_,
diff --git a/remoting/client/ios/host_preferences.mm b/remoting/client/ios/host_preferences.mm
index f229ce25..59ba322 100644
--- a/remoting/client/ios/host_preferences.mm
+++ b/remoting/client/ios/host_preferences.mm
@@ -44,7 +44,7 @@
   NSError* keychainError =
       remoting::ios::WriteHostPreferencesToKeychain(writeData);
 
-  DLOG_IF(ERROR, !keychainError) << "Could not write to keychain.";
+  LOG_IF(ERROR, !keychainError) << "Could not write to keychain.";
 }
 
 + (HostPreferences*)hostForId:(NSString*)hostId {
diff --git a/services/resource_coordinator/memory/coordinator/coordinator_impl.cc b/services/resource_coordinator/memory/coordinator/coordinator_impl.cc
index e3c2551..3f65716f 100644
--- a/services/resource_coordinator/memory/coordinator/coordinator_impl.cc
+++ b/services/resource_coordinator/memory/coordinator/coordinator_impl.cc
@@ -122,7 +122,8 @@
     DCHECK(!queued_memory_dump_requests_.empty());
     OnProcessMemoryDumpResponse(
         process_manager, queued_memory_dump_requests_.front().args.dump_guid,
-        false /* success */);
+        false /* success */,
+        base::Optional<base::trace_event::MemoryDumpCallbackResult>());
   }
 }
 
@@ -138,9 +139,9 @@
   failed_memory_dump_count_ = 0;
   for (const auto& key_value : process_managers_) {
     pending_process_managers_.insert(key_value.first);
-    key_value.second->RequestProcessMemoryDump(
-        args, base::Bind(&CoordinatorImpl::OnProcessMemoryDumpResponse,
-                         base::Unretained(this), key_value.first));
+    auto callback = base::Bind(&CoordinatorImpl::OnProcessMemoryDumpResponse,
+                               base::Unretained(this), key_value.first);
+    key_value.second->RequestProcessMemoryDump(args, callback);
   }
   // Run the callback in case there are no process-local managers.
   FinalizeGlobalMemoryDumpIfAllManagersReplied();
@@ -149,7 +150,8 @@
 void CoordinatorImpl::OnProcessMemoryDumpResponse(
     mojom::ProcessLocalDumpManager* process_manager,
     uint64_t dump_guid,
-    bool success) {
+    bool success,
+    const base::Optional<base::trace_event::MemoryDumpCallbackResult>& result) {
   auto it = pending_process_managers_.find(process_manager);
 
   DCHECK(!queued_memory_dump_requests_.empty());
diff --git a/services/resource_coordinator/memory/coordinator/coordinator_impl.h b/services/resource_coordinator/memory/coordinator/coordinator_impl.h
index b4abc4d..8281c41 100644
--- a/services/resource_coordinator/memory/coordinator/coordinator_impl.h
+++ b/services/resource_coordinator/memory/coordinator/coordinator_impl.h
@@ -66,7 +66,9 @@
   void OnProcessMemoryDumpResponse(
       mojom::ProcessLocalDumpManager* process_manager,
       uint64_t dump_guid,
-      bool success);
+      bool success,
+      const base::Optional<base::trace_event::MemoryDumpCallbackResult>&
+          result);
 
   void PerformNextQueuedGlobalMemoryDump();
   void FinalizeGlobalMemoryDumpIfAllManagersReplied();
diff --git a/services/resource_coordinator/memory/coordinator/coordinator_impl_unittest.cc b/services/resource_coordinator/memory/coordinator/coordinator_impl_unittest.cc
index e19c3c2f3..521f8f2 100644
--- a/services/resource_coordinator/memory/coordinator/coordinator_impl_unittest.cc
+++ b/services/resource_coordinator/memory/coordinator/coordinator_impl_unittest.cc
@@ -82,7 +82,8 @@
       const base::trace_event::MemoryDumpRequestArgs& args,
       const RequestProcessMemoryDumpCallback& callback) override {
     expected_calls_--;
-    callback.Run(args.dump_guid, true);
+    base::trace_event::MemoryDumpCallbackResult result;
+    callback.Run(args.dump_guid, true, result);
   }
 
  private:
diff --git a/services/resource_coordinator/public/cpp/memory/memory_dump_manager_delegate_impl.cc b/services/resource_coordinator/public/cpp/memory/memory_dump_manager_delegate_impl.cc
index b93dd6f..1129bae 100644
--- a/services/resource_coordinator/public/cpp/memory/memory_dump_manager_delegate_impl.cc
+++ b/services/resource_coordinator/public/cpp/memory/memory_dump_manager_delegate_impl.cc
@@ -50,7 +50,7 @@
 
 void MemoryDumpManagerDelegateImpl::RequestGlobalMemoryDump(
     const base::trace_event::MemoryDumpRequestArgs& args,
-    const base::trace_event::MemoryDumpCallback& callback) {
+    const base::trace_event::GlobalMemoryDumpCallback& callback) {
   // Note: This condition is here to match the old behavior. If the delegate is
   // in the browser process, we do not drop parallel requests in the delegate
   // and so they will be queued by the Coordinator service (see
@@ -81,7 +81,7 @@
 }
 
 void MemoryDumpManagerDelegateImpl::MemoryDumpCallbackProxy(
-    const base::trace_event::MemoryDumpCallback& callback,
+    const base::trace_event::GlobalMemoryDumpCallback& callback,
     uint64_t dump_guid,
     bool success) {
   DCHECK_NE(0U, pending_memory_dump_guid_);
diff --git a/services/resource_coordinator/public/cpp/memory/memory_dump_manager_delegate_impl.h b/services/resource_coordinator/public/cpp/memory/memory_dump_manager_delegate_impl.h
index 17b995a..ccff7191 100644
--- a/services/resource_coordinator/public/cpp/memory/memory_dump_manager_delegate_impl.h
+++ b/services/resource_coordinator/public/cpp/memory/memory_dump_manager_delegate_impl.h
@@ -60,7 +60,7 @@
   // The base::trace_event::MemoryDumpManager calls this.
   void RequestGlobalMemoryDump(
       const base::trace_event::MemoryDumpRequestArgs& args,
-      const base::trace_event::MemoryDumpCallback& callback) override;
+      const base::trace_event::GlobalMemoryDumpCallback& callback) override;
 
   Config config() { return config_; }
   void SetAsNonCoordinatorForTesting();
@@ -76,7 +76,7 @@
 
   // A proxy callback for updating |pending_memory_dump_guid_|.
   void MemoryDumpCallbackProxy(
-      const base::trace_event::MemoryDumpCallback& callback,
+      const base::trace_event::GlobalMemoryDumpCallback& callback,
       uint64_t dump_guid,
       bool success);
 
diff --git a/services/resource_coordinator/public/cpp/memory/memory_instrumentation.typemap b/services/resource_coordinator/public/cpp/memory/memory_instrumentation.typemap
index 7dd28cf..32a2e41 100644
--- a/services/resource_coordinator/public/cpp/memory/memory_instrumentation.typemap
+++ b/services/resource_coordinator/public/cpp/memory/memory_instrumentation.typemap
@@ -13,4 +13,7 @@
   "memory_instrumentation.mojom.DumpType=base::trace_event::MemoryDumpType",
   "memory_instrumentation.mojom.LevelOfDetail=base::trace_event::MemoryDumpLevelOfDetail",
   "memory_instrumentation.mojom.RequestArgs=base::trace_event::MemoryDumpRequestArgs",
+  "memory_instrumentation.mojom.ChromeMemDump=base::trace_event::MemoryDumpCallbackResult::ChromeMemDump",
+  "memory_instrumentation.mojom.OSMemDump=base::trace_event::MemoryDumpCallbackResult::OSMemDump",
+  "memory_instrumentation.mojom.MemoryDumpCallbackResult=base::trace_event::MemoryDumpCallbackResult",
 ]
diff --git a/services/resource_coordinator/public/cpp/memory/memory_instrumentation_struct_traits.cc b/services/resource_coordinator/public/cpp/memory/memory_instrumentation_struct_traits.cc
index c1ad690..7bb49b6 100644
--- a/services/resource_coordinator/public/cpp/memory/memory_instrumentation_struct_traits.cc
+++ b/services/resource_coordinator/public/cpp/memory/memory_instrumentation_struct_traits.cc
@@ -104,4 +104,38 @@
   return true;
 }
 
+// static
+bool StructTraits<memory_instrumentation::mojom::ChromeMemDumpDataView,
+                  base::trace_event::MemoryDumpCallbackResult::ChromeMemDump>::
+    Read(memory_instrumentation::mojom::ChromeMemDumpDataView input,
+         base::trace_event::MemoryDumpCallbackResult::ChromeMemDump* out) {
+  out->malloc_total_kb = input.malloc_total_kb();
+  out->partition_alloc_total_kb = input.partition_alloc_total_kb();
+  out->blink_gc_total_kb = input.blink_gc_total_kb();
+  out->v8_total_kb = input.v8_total_kb();
+  return true;
+}
+
+// static
+bool StructTraits<memory_instrumentation::mojom::OSMemDumpDataView,
+                  base::trace_event::MemoryDumpCallbackResult::OSMemDump>::
+    Read(memory_instrumentation::mojom::OSMemDumpDataView input,
+         base::trace_event::MemoryDumpCallbackResult::OSMemDump* out) {
+  out->resident_set_kb = input.resident_set_kb();
+  return true;
+}
+
+// static
+bool StructTraits<
+    memory_instrumentation::mojom::MemoryDumpCallbackResultDataView,
+    base::trace_event::MemoryDumpCallbackResult>::
+    Read(memory_instrumentation::mojom::MemoryDumpCallbackResultDataView input,
+         base::trace_event::MemoryDumpCallbackResult* out) {
+  if (!input.ReadChromeDump(&out->chrome_dump))
+    return false;
+  if (!input.ReadOsDump(&out->os_dump))
+    return false;
+  return true;
+}
+
 }  // namespace mojo
diff --git a/services/resource_coordinator/public/cpp/memory/memory_instrumentation_struct_traits.h b/services/resource_coordinator/public/cpp/memory/memory_instrumentation_struct_traits.h
index c3b2761..c9c6770b78 100644
--- a/services/resource_coordinator/public/cpp/memory/memory_instrumentation_struct_traits.h
+++ b/services/resource_coordinator/public/cpp/memory/memory_instrumentation_struct_traits.h
@@ -47,6 +47,59 @@
                    base::trace_event::MemoryDumpRequestArgs* out);
 };
 
+template <>
+struct StructTraits<
+    memory_instrumentation::mojom::ChromeMemDumpDataView,
+    base::trace_event::MemoryDumpCallbackResult::ChromeMemDump> {
+  static uint32_t malloc_total_kb(
+      const base::trace_event::MemoryDumpCallbackResult::ChromeMemDump& args) {
+    return args.malloc_total_kb;
+  }
+  static uint32_t partition_alloc_total_kb(
+      const base::trace_event::MemoryDumpCallbackResult::ChromeMemDump& args) {
+    return args.partition_alloc_total_kb;
+  }
+  static uint32_t blink_gc_total_kb(
+      const base::trace_event::MemoryDumpCallbackResult::ChromeMemDump& args) {
+    return args.blink_gc_total_kb;
+  }
+  static uint32_t v8_total_kb(
+      const base::trace_event::MemoryDumpCallbackResult::ChromeMemDump& args) {
+    return args.v8_total_kb;
+  }
+  static bool Read(
+      memory_instrumentation::mojom::ChromeMemDumpDataView input,
+      base::trace_event::MemoryDumpCallbackResult::ChromeMemDump* out);
+};
+
+template <>
+struct StructTraits<memory_instrumentation::mojom::OSMemDumpDataView,
+                    base::trace_event::MemoryDumpCallbackResult::OSMemDump> {
+  static uint32_t resident_set_kb(
+      const base::trace_event::MemoryDumpCallbackResult::OSMemDump& args) {
+    return args.resident_set_kb;
+  }
+  static bool Read(memory_instrumentation::mojom::OSMemDumpDataView input,
+                   base::trace_event::MemoryDumpCallbackResult::OSMemDump* out);
+};
+
+template <>
+struct StructTraits<
+    memory_instrumentation::mojom::MemoryDumpCallbackResultDataView,
+    base::trace_event::MemoryDumpCallbackResult> {
+  static base::trace_event::MemoryDumpCallbackResult::OSMemDump os_dump(
+      const base::trace_event::MemoryDumpCallbackResult& args) {
+    return args.os_dump;
+  }
+  static base::trace_event::MemoryDumpCallbackResult::ChromeMemDump chrome_dump(
+      const base::trace_event::MemoryDumpCallbackResult& args) {
+    return args.chrome_dump;
+  }
+  static bool Read(
+      memory_instrumentation::mojom::MemoryDumpCallbackResultDataView input,
+      base::trace_event::MemoryDumpCallbackResult* out);
+};
+
 }  // namespace mojo
 
 #endif  // SERVICES_RESOURCE_COORDINATOR_PUBLIC_CPP_MEMORY_MEMORY_INSTRUMENTATION_STRUCT_TRAITS_H_
diff --git a/services/resource_coordinator/public/interfaces/memory/memory_instrumentation.mojom b/services/resource_coordinator/public/interfaces/memory/memory_instrumentation.mojom
index 3cdba345..12244c6 100644
--- a/services/resource_coordinator/public/interfaces/memory/memory_instrumentation.mojom
+++ b/services/resource_coordinator/public/interfaces/memory/memory_instrumentation.mojom
@@ -22,12 +22,29 @@
   LevelOfDetail level_of_detail;
 };
 
+struct OSMemDump {
+  uint32 resident_set_kb = 0;
+};
+
+struct ChromeMemDump {
+  uint32 malloc_total_kb = 0;
+  uint32 partition_alloc_total_kb = 0;
+  uint32 blink_gc_total_kb = 0;
+  uint32 v8_total_kb = 0;
+};
+
+struct MemoryDumpCallbackResult {
+  OSMemDump os_dump;
+  ChromeMemDump chrome_dump;
+}; 
+
 // There should be at most one implementation of this interface per process.
 interface ProcessLocalDumpManager {
   // When successful, the dump is appended in the process-local trace buffer of
-  // the target process and only an ACK is returned.
+  // the target process and an ACK. A summary of the dump is also returned in
+  // case of success.
   RequestProcessMemoryDump(RequestArgs args) =>
-      (uint64 dump_guid, bool success);
+      (uint64 dump_guid, bool success, MemoryDumpCallbackResult? result);
 };
 
 // Memory Infra service implements this interface. ProcessLocalDumpManagers
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json
index 4cb23ee..1990d48 100644
--- a/testing/buildbot/chromium.gpu.fyi.json
+++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -11989,25 +11989,6 @@
         },
         "test": "video_decode_accelerator_unittest",
         "use_xvfb": false
-      },
-      {
-        "args": [
-          "--use-angle=gl",
-          "--use-test-data-path",
-          "--test_video_data=test-25fps.h264:320:240:250:258:::1"
-        ],
-        "name": "video_decode_accelerator_gl_unittest",
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        },
-        "test": "video_decode_accelerator_unittest",
-        "use_xvfb": false
       }
     ],
     "isolated_scripts": [
@@ -13102,25 +13083,6 @@
         },
         "test": "video_decode_accelerator_unittest",
         "use_xvfb": false
-      },
-      {
-        "args": [
-          "--use-angle=gl",
-          "--use-test-data-path",
-          "--test_video_data=test-25fps.h264:320:240:250:258:::1"
-        ],
-        "name": "video_decode_accelerator_gl_unittest",
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        },
-        "test": "video_decode_accelerator_unittest",
-        "use_xvfb": false
       }
     ],
     "isolated_scripts": [
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2 b/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
index dd900c90..63cf0a6 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
@@ -1421,6 +1421,7 @@
 crbug.com/702805 virtual/threaded/animations/composited-animation-independent-transform-cancel.html [ Crash ]
 crbug.com/702805 virtual/threaded/animations/webkit-filter-default-arg-crash.html [ Crash ]
 crbug.com/702805 svg/dom/filter-reference-in-shadow-tree.html [ Crash ]
+crbug.com/702805 svg/text/removing-id-on-path.html [ Failure ]
 crbug.com/702805 svg/text/selection-style-within-mask-crash.html [ Crash ]
 crbug.com/702805 svg/custom/mask-with-all-units.svg [ Crash ]
 crbug.com/702805 svg/custom/pattern-directly-and-indirectly-referenced.svg [ Crash ]
@@ -1480,6 +1481,7 @@
 crbug.com/702805 compositing/squashing/squash-with-ancestor-reflection.html [ Crash ]
 crbug.com/702805 compositing/squashing/squashing-reflection-disallowed.html [ Crash ]
 crbug.com/702805 compositing/squashing/squash-with-ancestor-filter.html [ Crash ]
+crbug.com/702805 compositing/squashing/remove-from-grouped-mapping-on-reassignment.html [ Crash ]
 crbug.com/702805 compositing/visibility/overlays-persist-on-navigation.html [ Crash ]
 crbug.com/702805 compositing/overflow/reflected-overlay-scrollbars-should-respect-ancestor-clip.html [ Crash ]
 crbug.com/702805 compositing/overflow/reflected-overlay-scrollbars-should-appear-without-compositing.html [ Crash ]
diff --git a/third_party/WebKit/LayoutTests/external/wpt/dom/nodes/attributes-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/dom/nodes/attributes-expected.txt
index da43923..c0004a4 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/dom/nodes/attributes-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/dom/nodes/attributes-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 56 tests; 46 PASS, 10 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 58 tests; 50 PASS, 8 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS When qualifiedName does not match the Name production, an INVALID_CHARACTER_ERR exception is to be thrown. (setAttribute) 
 PASS When qualifiedName does not match the Name production, an INVALID_CHARACTER_ERR exception is to be thrown, even if the attribute is already present. (setAttribute) 
 PASS setAttribute should lowercase its name argument (upper case attribute) 
@@ -31,8 +31,8 @@
 PASS Unset attributes return null 
 PASS First set attribute is returned by getAttribute 
 PASS Style attributes are not normalized 
-FAIL Only lowercase attributes are returned on HTML elements (upper case attribute) assert_equals: expected (object) null but got (string) "left"
-FAIL Only lowercase attributes are returned on HTML elements (mixed case attribute) assert_equals: expected (object) null but got (string) "tasty"
+PASS Only lowercase attributes are returned on HTML elements (upper case attribute) 
+PASS Only lowercase attributes are returned on HTML elements (mixed case attribute) 
 PASS First set attribute is returned with mapped attribute set first 
 PASS First set attribute is returned with mapped attribute set later 
 PASS Non-HTML element with upper-case attribute 
@@ -40,6 +40,8 @@
 PASS Attribute loses its owner when removed 
 PASS Basic functionality of getAttributeNode/getAttributeNodeNS 
 PASS Basic functionality of setAttributeNode 
+PASS setAttributeNode should distinguish attributes with same local name and different namespaces 
+PASS setAttributeNode doesn't have case-insensitivity even with an HTMLElement 
 PASS Basic functionality of setAttributeNodeNS 
 PASS If attr’s element is neither null nor element, throw an InUseAttributeError. 
 PASS Replacing an attr by itself 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/dom/nodes/attributes.html b/third_party/WebKit/LayoutTests/external/wpt/dom/nodes/attributes.html
index 8d98335..c401c2a 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/dom/nodes/attributes.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/dom/nodes/attributes.html
@@ -445,6 +445,30 @@
 }, "Basic functionality of setAttributeNode")
 
 test(function() {
+  var el = document.createElement("div");
+  var attr1 = document.createAttributeNS("ns1", "p1:name");
+  attr1.value = "value1";
+  var attr2 = document.createAttributeNS("ns2", "p2:name");
+  attr2.value = "value2";
+  el.setAttributeNode(attr1);
+  el.setAttributeNode(attr2);
+  assert_equals(el.getAttributeNodeNS("ns1", "name").value, "value1");
+  assert_equals(el.getAttributeNodeNS("ns2", "name").value, "value2");
+}, "setAttributeNode should distinguish attributes with same local name and different namespaces")
+
+test(function() {
+  var el = document.createElement("div");
+  var attr1 = document.createAttributeNS("ns1", "p1:name");
+  attr1.value = "value1";
+  var attr2 = document.createAttributeNS("ns1", "p1:NAME");
+  attr2.value = "VALUE2";
+  el.setAttributeNode(attr1);
+  el.setAttributeNode(attr2);
+  assert_equals(el.getAttributeNodeNS("ns1", "name").value, "value1");
+  assert_equals(el.getAttributeNodeNS("ns1", "NAME").value, "VALUE2");
+}, "setAttributeNode doesn't have case-insensitivity even with an HTMLElement")
+
+test(function() {
   var el = document.createElement("div")
   el.setAttributeNS("x", "foo", "bar")
   var attrNode = el.getAttributeNodeNS("x", "foo");
diff --git a/third_party/WebKit/LayoutTests/external/wpt/media-capabilities/idlharness-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/media-capabilities/idlharness-expected.txt
index 628e903..44200e0 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/media-capabilities/idlharness-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/media-capabilities/idlharness-expected.txt
@@ -17,8 +17,9 @@
 PASS MediaCapabilities interface object name 
 FAIL MediaCapabilities interface: existence and properties of interface prototype object assert_equals: class string of MediaCapabilities.prototype expected "[object MediaCapabilitiesPrototype]" but got "[object MediaCapabilities]"
 PASS MediaCapabilities interface: existence and properties of interface prototype object's "constructor" property 
-FAIL MediaCapabilities interface: operation decodingInfo(MediaConfiguration) assert_throws: calling operation with this = null didn't throw TypeError function "function () {
+FAIL MediaCapabilities interface: operation decodingInfo(MediaDecodingConfiguration) assert_throws: calling operation with this = null didn't throw TypeError function "function () {
             fn.apply(obj, args);
         }" did not throw
+FAIL MediaCapabilities interface: operation encodingInfo(MediaEncodingConfiguration) assert_own_property: interface prototype object missing non-static operation expected property "encodingInfo" missing
 Harness: the test ran to completion.
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/media-capabilities/idlharness.html b/third_party/WebKit/LayoutTests/external/wpt/media-capabilities/idlharness.html
index 52c55565..3efe5a6 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/media-capabilities/idlharness.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/media-capabilities/idlharness.html
@@ -24,13 +24,19 @@
 </pre>
 <pre id='idl'>
 dictionary MediaConfiguration {
-  required MediaConfigurationType type;
-
   VideoConfiguration video;
   AudioConfiguration audio;
 };
 
-enum MediaConfigurationType {
+dictionary MediaDecodingConfiguration : MediaConfiguration {
+  required MediaDecodingType type;
+};
+
+dictionary MediaEncodingConfiguration : MediaConfiguration {
+  required MediaEncodingType type;
+};
+
+enum MediaDecodingType {
   "file",
   "media-source",
 };
@@ -68,7 +74,8 @@
 
 [Exposed=(Window, Worker)]
 interface MediaCapabilities {
-  Promise<MediaCapabilitiesInfo> decodingInfo(MediaConfiguration configuration);
+  Promise<MediaCapabilitiesInfo> decodingInfo(MediaDecodingConfiguration configuration);
+  Promise<MediaCapabilitiesInfo> encodingInfo(MediaEncodingConfiguration configuration);
 };
 </pre>
 <script>
diff --git a/third_party/WebKit/LayoutTests/fast/dom/Element/setAttributeNode-case-insensitivity-expected.txt b/third_party/WebKit/LayoutTests/fast/dom/Element/setAttributeNode-case-insensitivity-expected.txt
deleted file mode 100644
index 8c3e5b5..0000000
--- a/third_party/WebKit/LayoutTests/fast/dom/Element/setAttributeNode-case-insensitivity-expected.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-Test for Bugzilla bug: 90341: createAttribute/setAttributeNode does not properly normalize case.
-This test verifies that the setAttributeNode() API checks for existing attributes case-insensitively. Thus the value of an existing attribute with the same name but in a different case should get replaced by the new value specified via the setAttributeNode() method.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-PASS successfullyParsed is true
-
-TEST COMPLETE
-PASS test.getAttribute('style') is test.getAttribute('STYLE')
-PASS test.getAttributeNode('style').value is test.getAttributeNode('STYLE').value
-PASS resultantAttr is oldStyleAttr
-PASS oldStyleAttr.ownerElement is null
-Verifying that attributes with the same name but different namespaces are treated as unique entities. For the following test two different attribute values should be returned.
-PASS test.getAttributeNodeNS('ns1', 'newattr').value is 'newattr1'
-PASS test.getAttributeNodeNS('ns2', 'newattr').value is 'newattr2'
-Verifying that attributes with same name but different case and having same namespaces are treated as same. In the following test the new attribute should overwrite the value of the existing one.
-PASS test.getAttributeNodeNS('ns1', 'newattr').value is 'newattr3'
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/dom/Element/setAttributeNode-case-insensitivity.html b/third_party/WebKit/LayoutTests/fast/dom/Element/setAttributeNode-case-insensitivity.html
deleted file mode 100644
index 700417eb..0000000
--- a/third_party/WebKit/LayoutTests/fast/dom/Element/setAttributeNode-case-insensitivity.html
+++ /dev/null
@@ -1,61 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-<script src="../../../resources/js-test.js"></script>
-<script>
-var oldStyleAttr;
-var resultantAttr;
-
-function runTest()
-{
-    description("This test verifies that the setAttributeNode() API checks for existing attributes case-insensitively. Thus the value of an existing attribute with the same name but in a different case should get replaced by the new value specified via the setAttributeNode() method.");
-    
-    var test = document.getElementById("test");
-    var newStyleAttr = document.createAttribute("STYLE");
-    newStyleAttr.value = "background-color: green";
-    oldStyleAttr = test.getAttributeNode("style");
-    resultantAttr = test.setAttributeNode(newStyleAttr);
-    if (window.testRunner) {
-        shouldBe("test.getAttribute('style')", "test.getAttribute('STYLE')");
-        shouldBe("test.getAttributeNode('style').value", "test.getAttributeNode('STYLE').value");
-        shouldBe("resultantAttr", "oldStyleAttr");
-        shouldBeNull("oldStyleAttr.ownerElement");
-    }
-
-    debug("Verifying that attributes with the same name but different namespaces are treated as unique entities. For the following test two different attribute values should be returned.");
-    var newAttr1 = document.createAttributeNS("ns1", "newattr");
-    newAttr1.prefix = "prefix1";
-    newAttr1.value = "newattr1";
-    test.setAttributeNode(newAttr1);
-    var newAttr2 = document.createAttributeNS("ns2", "newattr");
-    newAttr2.prefix = "prefix2";
-    newAttr2.value = "newattr2";
-    test.setAttributeNode(newAttr2);
-    if (window.testRunner) {
-        shouldBe("test.getAttributeNodeNS('ns1', 'newattr').value", "'newattr1'");
-        shouldBe("test.getAttributeNodeNS('ns2', 'newattr').value", "'newattr2'");
-    }
-
-    debug("Verifying that attributes with same name but different case and having same namespaces are treated as same. In the following test the new attribute should overwrite the value of the existing one.");
-    var newAttr3 = document.createAttributeNS("ns1", "NEWATTR");
-    newAttr3.prefix = "prefix2";
-    newAttr3.value = "newattr3";
-    test.setAttributeNode(newAttr3);
-
-    if (window.testRunner) {
-        shouldBe("test.getAttributeNodeNS('ns1', 'newattr').value", "'newattr3'");
-    
-        isSuccessfullyParsed();    
-        test.style.display = 'none';
-    }
-    
-}
-</script>
-</head>
-<body onload="runTest()">
-<div>Test for Bugzilla bug:<a href="https://bugs.webkit.org/show_bug.cgi?id=90341"> 90341:</a>  createAttribute/setAttributeNode does not properly normalize case.</div>
-<div id="test" style="background-color: red">&nbsp;</div>
-<div id="description"></div>
-<div id="console"></div>
-</body>
-</html>
diff --git a/third_party/WebKit/Source/core/css/CSSFontSelector.cpp b/third_party/WebKit/Source/core/css/CSSFontSelector.cpp
index 195efd61..35460d3 100644
--- a/third_party/WebKit/Source/core/css/CSSFontSelector.cpp
+++ b/third_party/WebKit/Source/core/css/CSSFontSelector.cpp
@@ -99,7 +99,7 @@
     return FontCache::GetGenericFamilyNameForScript(generic_family_name,
                                                     font_description);
 #else
-  UScriptCode script = font_description.Script();
+  UScriptCode script = font_description.GetScript();
   if (font_description.GenericFamily() == FontDescription::kStandardFamily)
     return settings.Standard(script);
   if (generic_family_name == FontFamilyNames::webkit_serif)
diff --git a/third_party/WebKit/Source/core/dom/AttributeCollection.h b/third_party/WebKit/Source/core/dom/AttributeCollection.h
index 5d68d5e50..f379c0c7 100644
--- a/third_party/WebKit/Source/core/dom/AttributeCollection.h
+++ b/third_party/WebKit/Source/core/dom/AttributeCollection.h
@@ -62,13 +62,12 @@
   bool IsEmpty() const { return !size(); }
 
   iterator Find(const QualifiedName&) const;
-  iterator Find(const AtomicString& name, bool should_ignore_case) const;
-  size_t FindIndex(const QualifiedName&, bool should_ignore_case = false) const;
-  size_t FindIndex(const AtomicString& name, bool should_ignore_case) const;
+  iterator Find(const AtomicString& name) const;
+  size_t FindIndex(const QualifiedName&) const;
+  size_t FindIndex(const AtomicString& name) const;
 
  protected:
-  size_t FindSlowCase(const AtomicString& name,
-                      bool should_ignore_attribute_case) const;
+  size_t FindSlowCase(const AtomicString& name) const;
 
   ContainerMemberType attributes_;
 };
@@ -128,36 +127,29 @@
 inline typename AttributeCollectionGeneric<Container,
                                            ContainerMemberType>::iterator
 AttributeCollectionGeneric<Container, ContainerMemberType>::Find(
-    const AtomicString& name,
-    bool should_ignore_case) const {
-  size_t index = FindIndex(name, should_ignore_case);
+    const AtomicString& name) const {
+  size_t index = FindIndex(name);
   return index != kNotFound ? &at(index) : nullptr;
 }
 
 template <typename Container, typename ContainerMemberType>
 inline size_t
 AttributeCollectionGeneric<Container, ContainerMemberType>::FindIndex(
-    const QualifiedName& name,
-    bool should_ignore_case) const {
+    const QualifiedName& name) const {
   iterator end = this->end();
   unsigned index = 0;
   for (iterator it = begin(); it != end; ++it, ++index) {
-    if (it->GetName().MatchesPossiblyIgnoringASCIICase(name,
-                                                       should_ignore_case))
+    if (it->GetName().Matches(name))
       return index;
   }
   return kNotFound;
 }
 
-// We use a boolean parameter instead of calling shouldIgnoreAttributeCase so
-// that the caller can tune the behavior (hasAttribute is case sensitive whereas
-// getAttribute is not).
 template <typename Container, typename ContainerMemberType>
 inline size_t
 AttributeCollectionGeneric<Container, ContainerMemberType>::FindIndex(
-    const AtomicString& name,
-    bool should_ignore_case) const {
-  bool do_slow_check = should_ignore_case;
+    const AtomicString& name) const {
+  bool do_slow_check = false;
 
   // Optimize for the case where the attribute exists and its name exactly
   // matches.
@@ -175,7 +167,7 @@
   }
 
   if (do_slow_check)
-    return FindSlowCase(name, should_ignore_case);
+    return FindSlowCase(name);
   return kNotFound;
 }
 
@@ -194,25 +186,21 @@
 
 template <typename Container, typename ContainerMemberType>
 size_t AttributeCollectionGeneric<Container, ContainerMemberType>::FindSlowCase(
-    const AtomicString& name,
-    bool should_ignore_attribute_case) const {
+    const AtomicString& name) const {
   // Continue to checking case-insensitively and/or full namespaced names if
   // necessary:
   iterator end = this->end();
   unsigned index = 0;
   for (iterator it = begin(); it != end; ++it, ++index) {
-    // FIXME: Why check the prefix? Namespace is all that should matter
-    // and all HTML/SVG attributes have a null namespace!
     if (!it->GetName().HasPrefix()) {
-      if (should_ignore_attribute_case &&
-          EqualIgnoringASCIICase(name, it->LocalName()))
-        return index;
+      // Skip attributes with no prefixes because they must be checked in
+      // FindIndex(const AtomicString&).
+      DCHECK_NE(name, it->LocalName());
     } else {
       // FIXME: Would be faster to do this comparison without calling ToString,
       // which generates a temporary string by concatenation. But this branch is
       // only reached if the attribute name has a prefix, which is rare in HTML.
-      if (EqualPossiblyIgnoringASCIICase(name, it->GetName().ToString(),
-                                         should_ignore_attribute_case))
+      if (name == it->GetName().ToString())
         return index;
     }
   }
diff --git a/third_party/WebKit/Source/core/dom/Element.cpp b/third_party/WebKit/Source/core/dom/Element.cpp
index 1a77953..93797e20 100644
--- a/third_party/WebKit/Source/core/dom/Element.cpp
+++ b/third_party/WebKit/Source/core/dom/Element.cpp
@@ -393,8 +393,7 @@
   if (!GetElementData())
     return;
   if (GetElementData()->style_attribute_is_dirty_ &&
-      EqualPossiblyIgnoringASCIICase(local_name, styleAttr.LocalName(),
-                                     ShouldIgnoreAttributeCase())) {
+      LowercaseIfNecessary(local_name) == styleAttr.LocalName()) {
     DCHECK(IsStyledElement());
     SynchronizeStyleAttributeInternal();
     return;
@@ -426,8 +425,9 @@
   return g_null_atom;
 }
 
-bool Element::ShouldIgnoreAttributeCase() const {
-  return IsHTMLElement() && GetDocument().IsHTMLDocument();
+AtomicString Element::LowercaseIfNecessary(const AtomicString& name) const {
+  return IsHTMLElement() && GetDocument().IsHTMLDocument() ? name.LowerASCII()
+                                                           : name;
 }
 
 void Element::scrollIntoView(bool align_to_top) {
@@ -1219,8 +1219,8 @@
   if (!GetElementData())
     return g_null_atom;
   SynchronizeAttribute(local_name);
-  if (const Attribute* attribute = GetElementData()->Attributes().Find(
-          local_name, ShouldIgnoreAttributeCase()))
+  if (const Attribute* attribute =
+          GetElementData()->Attributes().Find(LowercaseIfNecessary(local_name)))
     return attribute->Value();
   return g_null_atom;
 }
@@ -1242,8 +1242,7 @@
   }
 
   SynchronizeAttribute(local_name);
-  const AtomicString& case_adjusted_local_name =
-      ShouldIgnoreAttributeCase() ? local_name.LowerASCII() : local_name;
+  AtomicString case_adjusted_local_name = LowercaseIfNecessary(local_name);
 
   if (!GetElementData()) {
     SetAttributeInternal(
@@ -1254,7 +1253,7 @@
   }
 
   AttributeCollection attributes = GetElementData()->Attributes();
-  size_t index = attributes.FindIndex(case_adjusted_local_name, false);
+  size_t index = attributes.FindIndex(case_adjusted_local_name);
   const QualifiedName& q_name =
       index != kNotFound
           ? attributes[index].GetName()
@@ -2473,8 +2472,7 @@
   const UniqueElementData& element_data = EnsureUniqueElementData();
 
   AttributeCollection attributes = element_data.Attributes();
-  size_t index = attributes.FindIndex(attr_node->GetQualifiedName(),
-                                      ShouldIgnoreAttributeCase());
+  size_t index = attributes.FindIndex(attr_node->GetQualifiedName());
   AtomicString local_name;
   if (index != kNotFound) {
     const Attribute& attr = attributes[index];
@@ -2629,9 +2627,8 @@
   if (!GetElementData())
     return;
 
-  AtomicString local_name =
-      ShouldIgnoreAttributeCase() ? name.LowerASCII() : name;
-  size_t index = GetElementData()->Attributes().FindIndex(local_name, false);
+  AtomicString local_name = LowercaseIfNecessary(name);
+  size_t index = GetElementData()->Attributes().FindIndex(local_name);
   if (index == kNotFound) {
     if (UNLIKELY(local_name == styleAttr) &&
         GetElementData()->style_attribute_is_dirty_ && IsStyledElement())
@@ -2651,8 +2648,8 @@
   if (!GetElementData())
     return nullptr;
   SynchronizeAttribute(local_name);
-  const Attribute* attribute = GetElementData()->Attributes().Find(
-      local_name, ShouldIgnoreAttributeCase());
+  const Attribute* attribute =
+      GetElementData()->Attributes().Find(LowercaseIfNecessary(local_name));
   if (!attribute)
     return nullptr;
   return EnsureAttr(attribute->GetName());
@@ -2675,8 +2672,7 @@
     return false;
   SynchronizeAttribute(local_name);
   return GetElementData()->Attributes().FindIndex(
-             ShouldIgnoreAttributeCase() ? local_name.LowerASCII() : local_name,
-             false) != kNotFound;
+             LowercaseIfNecessary(local_name)) != kNotFound;
 }
 
 bool Element::hasAttributeNS(const AtomicString& namespace_uri,
@@ -3885,10 +3881,8 @@
 
 Attr* Element::AttrIfExists(const QualifiedName& name) {
   if (AttrNodeList* attr_node_list = this->GetAttrNodeList()) {
-    bool should_ignore_case = ShouldIgnoreAttributeCase();
     for (const auto& attr : *attr_node_list) {
-      if (attr->GetQualifiedName().MatchesPossiblyIgnoringASCIICase(
-              name, should_ignore_case))
+      if (attr->GetQualifiedName().Matches(name))
         return attr.Get();
     }
   }
diff --git a/third_party/WebKit/Source/core/dom/Element.h b/third_party/WebKit/Source/core/dom/Element.h
index 8f06266..732a418 100644
--- a/third_party/WebKit/Source/core/dom/Element.h
+++ b/third_party/WebKit/Source/core/dom/Element.h
@@ -200,7 +200,11 @@
   const AtomicString& GetNameAttribute() const;
   const AtomicString& GetClassAttribute() const;
 
-  bool ShouldIgnoreAttributeCase() const;
+  // This is an operation defined in the DOM standard like:
+  //   If element is in the HTML namespace and its node document is an HTML
+  //   document, then set qualifiedName to qualifiedName in ASCII lowercase.
+  //   https://dom.spec.whatwg.org/#concept-element-attributes-get-by-name
+  AtomicString LowercaseIfNecessary(const AtomicString&) const;
 
   // Call this to get the value of the id attribute for style resolution
   // purposes.  The value will already be lowercased if the document is in
diff --git a/third_party/WebKit/Source/core/dom/NamedNodeMap.cpp b/third_party/WebKit/Source/core/dom/NamedNodeMap.cpp
index 62092d8a..72cb28de 100644
--- a/third_party/WebKit/Source/core/dom/NamedNodeMap.cpp
+++ b/third_party/WebKit/Source/core/dom/NamedNodeMap.cpp
@@ -45,8 +45,8 @@
 
 Attr* NamedNodeMap::removeNamedItem(const AtomicString& name,
                                     ExceptionState& exception_state) {
-  size_t index = element_->Attributes().FindIndex(
-      name, element_->ShouldIgnoreAttributeCase());
+  size_t index =
+      element_->Attributes().FindIndex(element_->LowercaseIfNecessary(name));
   if (index == kNotFound) {
     exception_state.ThrowDOMException(
         kNotFoundError, "No item with name '" + name + "' was found.");
diff --git a/third_party/WebKit/Source/core/dom/QualifiedName.h b/third_party/WebKit/Source/core/dom/QualifiedName.h
index f289cccf..24658029 100644
--- a/third_party/WebKit/Source/core/dom/QualifiedName.h
+++ b/third_party/WebKit/Source/core/dom/QualifiedName.h
@@ -135,14 +135,6 @@
                                     NamespaceURI() == other.NamespaceURI());
   }
 
-  bool MatchesPossiblyIgnoringASCIICase(const QualifiedName& other,
-                                        bool should_ignore_case) const {
-    return impl_ == other.impl_ ||
-           (EqualPossiblyIgnoringASCIICase(LocalName(), other.LocalName(),
-                                           should_ignore_case) &&
-            NamespaceURI() == other.NamespaceURI());
-  }
-
   bool HasPrefix() const { return impl_->prefix_ != g_null_atom; }
   void SetPrefix(const AtomicString& prefix) {
     *this = QualifiedName(prefix, LocalName(), NamespaceURI());
diff --git a/third_party/WebKit/Source/core/frame/FrameView.cpp b/third_party/WebKit/Source/core/frame/FrameView.cpp
index 06be35c..3a1de81 100644
--- a/third_party/WebKit/Source/core/frame/FrameView.cpp
+++ b/third_party/WebKit/Source/core/frame/FrameView.cpp
@@ -210,10 +210,7 @@
       needs_scrollbars_update_(false),
       suppress_adjust_view_size_(false),
       allows_layout_invalidation_after_layout_clean_(true),
-      main_thread_scrolling_reasons_(0),
-      main_thread_scrolling_reasons_counter_(
-          MainThreadScrollingReason::kMainThreadScrollingReasonCount,
-          0) {
+      main_thread_scrolling_reasons_(0) {
   Init();
 }
 
@@ -5176,6 +5173,8 @@
 
   if (frame.IsMainFrame())
     main_thread_scrolling_reasons_ = reasons;
+  DCHECK(!MainThreadScrollingReason::HasNonCompositedScrollReasons(
+      main_thread_scrolling_reasons_));
 }
 
 MainThreadScrollingReasons FrameView::MainThreadScrollingReasonsPerFrame()
@@ -5234,6 +5233,7 @@
         ToLocalFrame(frame)->View()->MainThreadScrollingReasonsPerFrame();
   }
 
+  DCHECK(!MainThreadScrollingReason::HasNonCompositedScrollReasons(reasons));
   return reasons;
 }
 
@@ -5267,29 +5267,6 @@
   return result;
 }
 
-void FrameView::AdjustStyleRelatedMainThreadScrollingReasons(
-    const uint32_t reason,
-    bool increase) {
-  int index = MainThreadScrollingReason::getReasonIndex(reason);
-  DCHECK_GE(index, 0);
-  main_thread_scrolling_reasons_counter_[index] += increase ? 1 : -1;
-  DCHECK_GE(main_thread_scrolling_reasons_counter_[index], 0);
-}
-
-MainThreadScrollingReasons
-FrameView::GetStyleRelatedMainThreadScrollingReasons() const {
-  MainThreadScrollingReasons reasons =
-      static_cast<MainThreadScrollingReasons>(0);
-  for (uint32_t reason = 0;
-       reason < MainThreadScrollingReason::kMainThreadScrollingReasonCount;
-       ++reason) {
-    if (main_thread_scrolling_reasons_counter_[reason] > 0) {
-      reasons |= 1 << reason;
-    }
-  }
-  return reasons;
-}
-
 void FrameView::SetViewportIntersectionFromParent(
     const IntRect& viewport_intersection) {
   if (remote_viewport_intersection_ != viewport_intersection) {
diff --git a/third_party/WebKit/Source/core/frame/FrameView.h b/third_party/WebKit/Source/core/frame/FrameView.h
index 827969c8..1dd516b9 100644
--- a/third_party/WebKit/Source/core/frame/FrameView.h
+++ b/third_party/WebKit/Source/core/frame/FrameView.h
@@ -803,9 +803,6 @@
   // Main thread scrolling reasons for this object only. For all reasons,
   // see: mainThreadScrollingReasons().
   MainThreadScrollingReasons MainThreadScrollingReasonsPerFrame() const;
-  void AdjustStyleRelatedMainThreadScrollingReasons(const uint32_t reason,
-                                                    bool increase);
-  MainThreadScrollingReasons GetStyleRelatedMainThreadScrollingReasons() const;
 
   bool HasVisibleSlowRepaintViewportConstrainedObjects() const;
 
@@ -1221,11 +1218,6 @@
 
   bool is_storing_composited_layer_debug_info_;
   MainThreadScrollingReasons main_thread_scrolling_reasons_;
-  // For recording main thread scrolling reasons
-  // due to layout object properties. e.g. opacity, transform.
-  // The size of the vector depends on the number of
-  // main thread scrolling reasons.
-  Vector<int> main_thread_scrolling_reasons_counter_;
 
   // TODO(kenrb): Remove these when https://crbug.com/680606 is resolved.
   std::unique_ptr<CompositorAnimationTimeline> animation_timeline_;
diff --git a/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp b/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp
index ce63e6a..d707de7 100644
--- a/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp
@@ -2062,8 +2062,9 @@
     return GetWebMediaPlayer()->CurrentTime();
 
   if (ready_state_ >= kHaveMetadata) {
-    LOG(WARNING) << __func__ << " readyState = " << ready_state_
-                 << " but no webMeidaPlayer to provide currentPlaybackPosition";
+    BLINK_MEDIA_LOG
+        << __func__ << " readyState = " << ready_state_
+        << " but no webMediaPlayer to provide currentPlaybackPosition";
   }
 
   return 0;
diff --git a/third_party/WebKit/Source/core/html/HTMLVideoElement.cpp b/third_party/WebKit/Source/core/html/HTMLVideoElement.cpp
index 2e741f235..3bf2a9d 100644
--- a/third_party/WebKit/Source/core/html/HTMLVideoElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLVideoElement.cpp
@@ -310,16 +310,13 @@
 bool HTMLVideoElement::CopyVideoTextureToPlatformTexture(
     gpu::gles2::GLES2Interface* gl,
     GLuint texture,
-    GLenum internal_format,
-    GLenum format,
-    GLenum type,
     bool premultiply_alpha,
     bool flip_y) {
   if (!GetWebMediaPlayer())
     return false;
 
   return GetWebMediaPlayer()->CopyVideoTextureToPlatformTexture(
-      gl, texture, internal_format, format, type, premultiply_alpha, flip_y);
+      gl, texture, premultiply_alpha, flip_y);
 }
 
 bool HTMLVideoElement::TexImageImpl(
diff --git a/third_party/WebKit/Source/core/html/HTMLVideoElement.h b/third_party/WebKit/Source/core/html/HTMLVideoElement.h
index 6f14c678..bc7e7fa 100644
--- a/third_party/WebKit/Source/core/html/HTMLVideoElement.h
+++ b/third_party/WebKit/Source/core/html/HTMLVideoElement.h
@@ -75,11 +75,9 @@
   void PaintCurrentFrame(PaintCanvas*, const IntRect&, const PaintFlags*) const;
 
   // Used by WebGL to do GPU-GPU textures copy if possible.
+  // The caller is responsible for allocating the destination texture.
   bool CopyVideoTextureToPlatformTexture(gpu::gles2::GLES2Interface*,
                                          GLuint texture,
-                                         GLenum internal_format,
-                                         GLenum format,
-                                         GLenum type,
                                          bool premultiply_alpha,
                                          bool flip_y);
 
diff --git a/third_party/WebKit/Source/core/input/EventHandlerTest.cpp b/third_party/WebKit/Source/core/input/EventHandlerTest.cpp
index d71e2f1..df00778 100644
--- a/third_party/WebKit/Source/core/input/EventHandlerTest.cpp
+++ b/third_party/WebKit/Source/core/input/EventHandlerTest.cpp
@@ -5,6 +5,7 @@
 #include "core/input/EventHandler.h"
 
 #include <memory>
+#include "core/dom/ClientRect.h"
 #include "core/dom/Document.h"
 #include "core/dom/Range.h"
 #include "core/editing/Editor.h"
@@ -12,12 +13,34 @@
 #include "core/frame/FrameView.h"
 #include "core/frame/LocalFrame.h"
 #include "core/frame/Settings.h"
+#include "core/layout/LayoutBox.h"
 #include "core/loader/EmptyClients.h"
 #include "core/page/AutoscrollController.h"
 #include "core/page/Page.h"
+#include "core/paint/PaintLayerScrollableArea.h"
 #include "core/testing/DummyPageHolder.h"
+#include "platform/scroll/MainThreadScrollingReason.h"
+#include "platform/testing/HistogramTester.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+#define EXPECT_WHEEL_BUCKET(reason, count)     \
+  histogram_tester.ExpectBucketCount(          \
+      "Renderer4.MainThreadWheelScrollReason", \
+      GetBucketIndex(MainThreadScrollingReason::reason), count);
+
+#define EXPECT_TOUCH_BUCKET(reason, count)       \
+  histogram_tester.ExpectBucketCount(            \
+      "Renderer4.MainThreadGestureScrollReason", \
+      GetBucketIndex(MainThreadScrollingReason::reason), count);
+
+#define EXPECT_WHEEL_TOTAL(count)                                            \
+  histogram_tester.ExpectTotalCount("Renderer4.MainThreadWheelScrollReason", \
+                                    count);
+
+#define EXPECT_TOUCH_TOTAL(count)                                              \
+  histogram_tester.ExpectTotalCount("Renderer4.MainThreadGestureScrollReason", \
+                                    count);
+
 namespace blink {
 
 class EventHandlerTest : public ::testing::Test {
@@ -36,6 +59,48 @@
   std::unique_ptr<DummyPageHolder> dummy_page_holder_;
 };
 
+class NonCompositedMainThreadScrollingReasonRecordTest
+    : public EventHandlerTest {
+ protected:
+  class ScrollBeginEventBuilder : public WebGestureEvent {
+   public:
+    ScrollBeginEventBuilder(IntPoint position,
+                            FloatPoint delta,
+                            WebGestureDevice device)
+        : WebGestureEvent() {
+      type_ = WebInputEvent::kGestureScrollBegin;
+      x = global_x = position.X();
+      y = global_y = position.Y();
+      data.scroll_begin.delta_y_hint = delta.Y();
+      source_device = device;
+      frame_scale_ = 1;
+    }
+  };
+
+  class ScrollUpdateEventBuilder : public WebGestureEvent {
+   public:
+    ScrollUpdateEventBuilder() : WebGestureEvent() {
+      type_ = WebInputEvent::kGestureScrollUpdate;
+      data.scroll_update.delta_x = 0.0f;
+      data.scroll_update.delta_y = 1.0f;
+      data.scroll_update.velocity_x = 0;
+      data.scroll_update.velocity_y = 1;
+      frame_scale_ = 1;
+    }
+  };
+
+  class ScrollEndEventBuilder : public WebGestureEvent {
+   public:
+    ScrollEndEventBuilder() : WebGestureEvent() {
+      type_ = WebInputEvent::kGestureScrollEnd;
+      frame_scale_ = 1;
+    }
+  };
+
+  int GetBucketIndex(uint32_t reason);
+  void Scroll(Element*, const WebGestureDevice);
+};
+
 class TapEventBuilder : public WebGestureEvent {
  public:
   TapEventBuilder(IntPoint position, int tap_count)
@@ -90,6 +155,34 @@
   GetDocument().View()->UpdateAllLifecyclePhases();
 }
 
+int NonCompositedMainThreadScrollingReasonRecordTest::GetBucketIndex(
+    uint32_t reason) {
+  int index = 1;
+  while (!(reason & 1)) {
+    reason >>= 1;
+    ++index;
+  }
+  DCHECK_EQ(reason, 1u);
+  return index;
+}
+
+void NonCompositedMainThreadScrollingReasonRecordTest::Scroll(
+    Element* element,
+    const WebGestureDevice device) {
+  DCHECK(element);
+  DCHECK(element->getBoundingClientRect());
+  ClientRect* rect = element->getBoundingClientRect();
+  ScrollBeginEventBuilder scroll_begin(
+      IntPoint(rect->left() + rect->width() / 2,
+               rect->top() + rect->height() / 2),
+      FloatPoint(0.f, 1.f), device);
+  ScrollUpdateEventBuilder scroll_update;
+  ScrollEndEventBuilder scroll_end;
+  GetDocument().GetFrame()->GetEventHandler().HandleGestureEvent(scroll_begin);
+  GetDocument().GetFrame()->GetEventHandler().HandleGestureEvent(scroll_update);
+  GetDocument().GetFrame()->GetEventHandler().HandleGestureEvent(scroll_end);
+}
+
 TEST_F(EventHandlerTest, dragSelectionAfterScroll) {
   SetHtmlInnerHTML(
       "<style> body { margin: 0px; } .upper { width: 300px; height: 400px; }"
@@ -548,4 +641,144 @@
   EXPECT_EQ(WTF::String(), LastToolTip());
 }
 
+TEST_F(NonCompositedMainThreadScrollingReasonRecordTest,
+       TouchAndWheelGeneralTest) {
+  SetHtmlInnerHTML(
+      "<style>"
+      " .box { overflow:scroll; width: 100px; height: 100px; }"
+      " .translucent { opacity: 0.5; }"
+      " .spacer { height: 1000px; }"
+      "</style>"
+      "<div id='box' class='translucent box'>"
+      " <div class='spacer'></div>"
+      "</div>");
+
+  GetDocument().View()->UpdateAllLifecyclePhases();
+
+  Element* box = GetDocument().getElementById("box");
+  HistogramTester histogram_tester;
+
+  // Test touch scroll.
+  Scroll(box, kWebGestureDeviceTouchscreen);
+  EXPECT_TOUCH_BUCKET(kHasOpacityAndLCDText, 1);
+  EXPECT_TOUCH_BUCKET(kBackgroundNotOpaqueInRectAndLCDText, 1);
+
+  Scroll(box, kWebGestureDeviceTouchscreen);
+  EXPECT_TOUCH_BUCKET(kHasOpacityAndLCDText, 2);
+  EXPECT_TOUCH_BUCKET(kBackgroundNotOpaqueInRectAndLCDText, 2);
+  EXPECT_TOUCH_TOTAL(4);
+
+  // Test wheel scroll.
+  Scroll(box, kWebGestureDeviceTouchpad);
+  EXPECT_WHEEL_BUCKET(kHasOpacityAndLCDText, 1);
+  EXPECT_WHEEL_BUCKET(kBackgroundNotOpaqueInRectAndLCDText, 1);
+  EXPECT_WHEEL_TOTAL(2);
+}
+
+TEST_F(NonCompositedMainThreadScrollingReasonRecordTest,
+       CompositedScrollableAreaTest) {
+  SetHtmlInnerHTML(
+      "<style>"
+      " .box { overflow:scroll; width: 100px; height: 100px; }"
+      " .translucent { opacity: 0.5; }"
+      " .composited { will-change: transform; }"
+      " .spacer { height: 1000px; }"
+      "</style>"
+      "<div id='box' class='translucent box'>"
+      " <div class='spacer'></div>"
+      "</div>");
+
+  GetPage().GetSettings().SetAcceleratedCompositingEnabled(true);
+  GetDocument().View()->SetParentVisible(true);
+  GetDocument().View()->SetSelfVisible(true);
+  GetDocument().View()->UpdateAllLifecyclePhases();
+
+  Element* box = GetDocument().getElementById("box");
+  HistogramTester histogram_tester;
+
+  Scroll(box, kWebGestureDeviceTouchpad);
+  EXPECT_WHEEL_BUCKET(kHasOpacityAndLCDText, 1);
+  EXPECT_WHEEL_BUCKET(kBackgroundNotOpaqueInRectAndLCDText, 1);
+  EXPECT_WHEEL_TOTAL(2);
+
+  box->setAttribute("class", "composited translucent box");
+  GetDocument().View()->UpdateAllLifecyclePhases();
+  Scroll(box, kWebGestureDeviceTouchpad);
+  EXPECT_FALSE(ToLayoutBox(box->GetLayoutObject())
+                   ->GetScrollableArea()
+                   ->GetNonCompositedMainThreadScrollingReasons());
+  EXPECT_WHEEL_BUCKET(kHasOpacityAndLCDText, 1);
+  EXPECT_WHEEL_BUCKET(kBackgroundNotOpaqueInRectAndLCDText, 1);
+  EXPECT_WHEEL_TOTAL(2);
+}
+
+TEST_F(NonCompositedMainThreadScrollingReasonRecordTest,
+       NotScrollableAreaTest) {
+  SetHtmlInnerHTML(
+      "<style>.box { overflow:scroll; width: 100px; height: 100px; }"
+      " .translucent { opacity: 0.5; }"
+      " .hidden { overflow: hidden; }"
+      " .spacer { height: 1000px; }"
+      "</style>"
+      "<div id='box' class='translucent box'>"
+      " <div class='spacer'></div>"
+      "</div>");
+
+  GetDocument().View()->UpdateAllLifecyclePhases();
+
+  Element* box = GetDocument().getElementById("box");
+  HistogramTester histogram_tester;
+
+  Scroll(box, kWebGestureDeviceTouchpad);
+  EXPECT_WHEEL_BUCKET(kHasOpacityAndLCDText, 1);
+  EXPECT_WHEEL_BUCKET(kBackgroundNotOpaqueInRectAndLCDText, 1);
+  EXPECT_WHEEL_TOTAL(2);
+
+  box->setAttribute("class", "hidden translucent box");
+  GetDocument().View()->UpdateAllLifecyclePhases();
+  Scroll(box, kWebGestureDeviceTouchpad);
+  EXPECT_WHEEL_BUCKET(kHasOpacityAndLCDText, 1);
+  EXPECT_WHEEL_BUCKET(kBackgroundNotOpaqueInRectAndLCDText, 1);
+  EXPECT_WHEEL_TOTAL(2);
+}
+
+TEST_F(NonCompositedMainThreadScrollingReasonRecordTest, NestedScrollersTest) {
+  SetHtmlInnerHTML(
+      "<style>"
+      " .container { overflow:scroll; width: 200px; height: 200px; }"
+      " .box { overflow:scroll; width: 100px; height: 100px; }"
+      " .translucent { opacity: 0.5; }"
+      " .transform { transform: scale(0.8); }"
+      " .with-border-radius { border: 5px solid; border-radius: 5px; }"
+      " .spacer { height: 1000px; }"
+      " .composited { will-change: transform; }"
+      "</style>"
+      "<div id='container' class='container with-border-radius'>"
+      "  <div class='translucent box'>"
+      "    <div id='inner' class='composited transform box'>"
+      "      <div class='spacer'></div>"
+      "    </div>"
+      "  </div>"
+      "  <div class='spacer'></div>"
+      "</div>");
+
+  GetPage().GetSettings().SetAcceleratedCompositingEnabled(true);
+  GetDocument().View()->SetParentVisible(true);
+  GetDocument().View()->SetSelfVisible(true);
+  GetDocument().View()->UpdateAllLifecyclePhases();
+
+  Element* box = GetDocument().getElementById("inner");
+  HistogramTester histogram_tester;
+
+  Scroll(box, kWebGestureDeviceTouchpad);
+  // Scrolling the inner box will gather reasons from the scrolling chain. The
+  // inner box itself has no reason because it's composited. Other scrollable
+  // areas from the chain have corresponding reasons.
+  EXPECT_WHEEL_BUCKET(kHasOpacityAndLCDText, 1);
+  EXPECT_WHEEL_BUCKET(kBackgroundNotOpaqueInRectAndLCDText, 1);
+  EXPECT_WHEEL_BUCKET(kHasBorderRadius, 1);
+  EXPECT_WHEEL_BUCKET(kHasTransformAndLCDText, 0);
+  EXPECT_WHEEL_TOTAL(3);
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/input/ScrollManager.cpp b/third_party/WebKit/Source/core/input/ScrollManager.cpp
index cd8ace4..76a67a5 100644
--- a/third_party/WebKit/Source/core/input/ScrollManager.cpp
+++ b/third_party/WebKit/Source/core/input/ScrollManager.cpp
@@ -22,6 +22,7 @@
 #include "core/page/scrolling/RootScrollerController.h"
 #include "core/page/scrolling/ScrollState.h"
 #include "core/paint/PaintLayer.h"
+#include "platform/Histogram.h"
 #include "platform/RuntimeEnabledFeatures.h"
 #include "wtf/PtrUtil.h"
 
@@ -196,6 +197,67 @@
   scroll_state.distributeToScrollChainDescendant();
 }
 
+uint32_t ScrollManager::ComputeNonCompositedMainThreadScrollingReasons() {
+  // When scrolling on the main thread, the scrollableArea may or may not be
+  // composited. Either way, we have recorded either the reasons stored in
+  // its layer or the reason NonFastScrollableRegion from the compositor
+  // side. Here we record scrolls that occurred on main thread due to a
+  // non-composited scroller.
+  if (!scroll_gesture_handling_node_->GetLayoutObject() || !frame_->View())
+    return 0;
+
+  uint32_t non_composited_main_thread_scrolling_reasons = 0;
+
+  for (auto* cur_box =
+           scroll_gesture_handling_node_->GetLayoutObject()->EnclosingBox();
+       cur_box; cur_box = cur_box->ContainingBlock()) {
+    PaintLayerScrollableArea* scrollable_area = cur_box->GetScrollableArea();
+
+    if (!scrollable_area || !scrollable_area->ScrollsOverflow())
+      continue;
+
+    DCHECK(!scrollable_area->UsesCompositedScrolling() ||
+           !scrollable_area->GetNonCompositedMainThreadScrollingReasons());
+    non_composited_main_thread_scrolling_reasons |=
+        scrollable_area->GetNonCompositedMainThreadScrollingReasons();
+  }
+
+  return non_composited_main_thread_scrolling_reasons;
+}
+
+void ScrollManager::RecordNonCompositedMainThreadScrollingReasons(
+    const WebGestureDevice device) {
+  if (device != kWebGestureDeviceTouchpad &&
+      device != kWebGestureDeviceTouchscreen) {
+    return;
+  }
+
+  uint32_t reasons = ComputeNonCompositedMainThreadScrollingReasons();
+  if (!reasons)
+    return;
+  DCHECK(MainThreadScrollingReason::HasNonCompositedScrollReasons(reasons));
+
+  uint32_t main_thread_scrolling_reason_enum_max =
+      MainThreadScrollingReason::kMainThreadScrollingReasonCount + 1;
+  for (uint32_t i = MainThreadScrollingReason::kNonCompositedReasonsFirst;
+       i <= MainThreadScrollingReason::kNonCompositedReasonsLast; ++i) {
+    unsigned val = 1 << i;
+    if (reasons & val) {
+      if (device == kWebGestureDeviceTouchscreen) {
+        DEFINE_STATIC_LOCAL(EnumerationHistogram, touch_histogram,
+                            ("Renderer4.MainThreadGestureScrollReason",
+                             main_thread_scrolling_reason_enum_max));
+        touch_histogram.Count(i + 1);
+      } else {
+        DEFINE_STATIC_LOCAL(EnumerationHistogram, wheel_histogram,
+                            ("Renderer4.MainThreadWheelScrollReason",
+                             main_thread_scrolling_reason_enum_max));
+        wheel_histogram.Count(i + 1);
+      }
+    }
+  }
+}
+
 WebInputEventResult ScrollManager::HandleGestureScrollBegin(
     const WebGestureEvent& gesture_event) {
   Document* document = frame_->GetDocument();
@@ -221,6 +283,8 @@
   PassScrollGestureEvent(gesture_event,
                          scroll_gesture_handling_node_->GetLayoutObject());
 
+  RecordNonCompositedMainThreadScrollingReasons(gesture_event.source_device);
+
   current_scroll_chain_.clear();
   std::unique_ptr<ScrollStateData> scroll_state_data =
       WTF::MakeUnique<ScrollStateData>();
diff --git a/third_party/WebKit/Source/core/input/ScrollManager.h b/third_party/WebKit/Source/core/input/ScrollManager.h
index 28d492e..bdd7a1e8 100644
--- a/third_party/WebKit/Source/core/input/ScrollManager.h
+++ b/third_party/WebKit/Source/core/input/ScrollManager.h
@@ -111,6 +111,9 @@
   void RecomputeScrollChain(const Node& start_node,
                             std::deque<int>& scroll_chain);
 
+  uint32_t ComputeNonCompositedMainThreadScrollingReasons();
+  void RecordNonCompositedMainThreadScrollingReasons(const WebGestureDevice);
+
   // NOTE: If adding a new field to this class please ensure that it is
   // cleared in |ScrollManager::clear()|.
 
diff --git a/third_party/WebKit/Source/core/inspector/NetworkResourcesData.cpp b/third_party/WebKit/Source/core/inspector/NetworkResourcesData.cpp
index f22d94d6..3a46dac3 100644
--- a/third_party/WebKit/Source/core/inspector/NetworkResourcesData.cpp
+++ b/third_party/WebKit/Source/core/inspector/NetworkResourcesData.cpp
@@ -36,7 +36,7 @@
 
 namespace blink {
 
-static bool IsErrorStatusCode(int status_code) {
+static bool IsHTTPErrorStatusCode(int status_code) {
   return status_code >= 400;
 }
 
@@ -138,7 +138,8 @@
 
   // Mark loaded resources or resources without the buffer as loaded.
   if (cached_resource_->IsLoaded() || !cached_resource_->ResourceBuffer()) {
-    if (!IsErrorStatusCode(cached_resource_->GetResponse().HttpStatusCode())) {
+    if (!IsHTTPErrorStatusCode(
+            cached_resource_->GetResponse().HttpStatusCode())) {
       String content;
       bool base64_encoded;
       if (InspectorPageAgent::CachedResourceContent(cached_resource_, &content,
diff --git a/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp b/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp
index 9278740..4113f099 100644
--- a/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp
+++ b/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp
@@ -160,13 +160,6 @@
          layout_object.IsVideo();
 }
 
-// Get the scrolling coordinator in a way that works inside
-// CompositedLayerMapping's destructor.
-static ScrollingCoordinator* ScrollingCoordinatorFromLayer(PaintLayer& layer) {
-  Page* page = layer.GetLayoutObject().GetFrame()->GetPage();
-  return (!page) ? nullptr : page->GetScrollingCoordinator();
-}
-
 CompositedLayerMapping::CompositedLayerMapping(PaintLayer& layer)
     : owning_layer_(layer),
       content_offset_in_compositing_layer_dirty_(false),
@@ -1604,7 +1597,7 @@
   // Register fixed position layers and their containers with the scrolling
   // coordinator.
   ScrollingCoordinator* scrolling_coordinator =
-      ScrollingCoordinatorFromLayer(owning_layer_);
+      owning_layer_.GetScrollingCoordinator();
   if (!scrolling_coordinator)
     return;
 
@@ -1939,7 +1932,7 @@
   if (PaintLayerScrollableArea* scrollable_area =
           owning_layer_.GetScrollableArea()) {
     if (ScrollingCoordinator* scrolling_coordinator =
-            ScrollingCoordinatorFromLayer(owning_layer_)) {
+            owning_layer_.GetScrollingCoordinator()) {
       if (reason == kCompositingReasonLayerForHorizontalScrollbar)
         scrolling_coordinator->ScrollableAreaScrollbarLayerDidChange(
             scrollable_area, kHorizontalScrollbar);
@@ -2375,7 +2368,7 @@
 bool CompositedLayerMapping::UpdateScrollingLayers(
     bool needs_scrolling_layers) {
   ScrollingCoordinator* scrolling_coordinator =
-      ScrollingCoordinatorFromLayer(owning_layer_);
+      owning_layer_.GetScrollingCoordinator();
 
   bool layer_changed = false;
   if (needs_scrolling_layers) {
@@ -2438,7 +2431,7 @@
 void CompositedLayerMapping::UpdateScrollParent(
     const PaintLayer* scroll_parent) {
   if (ScrollingCoordinator* scrolling_coordinator =
-          ScrollingCoordinatorFromLayer(owning_layer_)) {
+          owning_layer_.GetScrollingCoordinator()) {
     GraphicsLayer* topmost_layer = ChildForSuperlayers();
     UpdateScrollParentForGraphicsLayer(squashing_containment_layer_.get(),
                                        topmost_layer, scroll_parent,
@@ -2481,7 +2474,7 @@
   }
 
   if (ScrollingCoordinator* scrolling_coordinator =
-          ScrollingCoordinatorFromLayer(owning_layer_)) {
+          owning_layer_.GetScrollingCoordinator()) {
     GraphicsLayer* topmost_layer = ChildForSuperlayers();
     UpdateClipParentForGraphicsLayer(squashing_containment_layer_.get(),
                                      topmost_layer, clip_parent,
diff --git a/third_party/WebKit/Source/core/layout/compositing/CompositingLayerAssigner.cpp b/third_party/WebKit/Source/core/layout/compositing/CompositingLayerAssigner.cpp
index 712a6ca..040582d6 100644
--- a/third_party/WebKit/Source/core/layout/compositing/CompositingLayerAssigner.cpp
+++ b/third_party/WebKit/Source/core/layout/compositing/CompositingLayerAssigner.cpp
@@ -273,11 +273,6 @@
   }
 }
 
-static ScrollingCoordinator* ScrollingCoordinatorFromLayer(PaintLayer& layer) {
-  Page* page = layer.GetLayoutObject().GetFrame()->GetPage();
-  return (!page) ? nullptr : page->GetScrollingCoordinator();
-}
-
 void CompositingLayerAssigner::AssignLayersToBackingsInternal(
     PaintLayer* layer,
     SquashingState& squashing_state,
@@ -302,7 +297,7 @@
     layers_needing_paint_invalidation.push_back(layer);
     layers_changed_ = true;
     if (ScrollingCoordinator* scrolling_coordinator =
-            ScrollingCoordinatorFromLayer(*layer)) {
+            layer->GetScrollingCoordinator()) {
       if (layer->GetLayoutObject().Style()->HasViewportConstrainedPosition()) {
         scrolling_coordinator->FrameViewFixedObjectsDidChange(
             layer->GetLayoutObject().View()->GetFrameView());
diff --git a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.cc b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.cc
index 5962834..d78d6b07 100644
--- a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.cc
+++ b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.cc
@@ -56,7 +56,6 @@
     NGConstraintSpace* space,
     NGInlineBreakToken* break_token)
     : NGLayoutAlgorithm(inline_node, space, break_token),
-      container_builder_(NGPhysicalFragment::kFragmentBox, inline_node),
       is_horizontal_writing_mode_(
           blink::IsHorizontalWritingMode(space->WritingMode())),
       disallow_first_line_rules_(false),
diff --git a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.h b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.h
index 326df73..e369232d 100644
--- a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.h
+++ b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.h
@@ -151,7 +151,6 @@
   LayoutUnit last_break_opportunity_position_;
   LayoutUnit content_size_;
   LayoutUnit max_inline_size_;
-  NGFragmentBuilder container_builder_;
   FontBaseline baseline_type_ = FontBaseline::kAlphabeticBaseline;
 
   NGLogicalOffset bfc_offset_;
diff --git a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_node.h b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_node.h
index d6940c12..a2288f0 100644
--- a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_node.h
+++ b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_node.h
@@ -148,7 +148,7 @@
     return bidi_level_ & 1 ? TextDirection::kRtl : TextDirection::kLtr;
   }
   UBiDiLevel BidiLevel() const { return bidi_level_; }
-  UScriptCode Script() const { return script_; }
+  UScriptCode GetScript() const { return script_; }
   const ComputedStyle* Style() const { return style_; }
   LayoutObject* GetLayoutObject() const { return layout_object_; }
 
diff --git a/third_party/WebKit/Source/core/layout/ng/inline/ng_layout_inline_items_builder.cc b/third_party/WebKit/Source/core/layout/ng/inline/ng_layout_inline_items_builder.cc
index 8e0e2a2..70a5a29 100644
--- a/third_party/WebKit/Source/core/layout/ng/inline/ng_layout_inline_items_builder.cc
+++ b/third_party/WebKit/Source/core/layout/ng/inline/ng_layout_inline_items_builder.cc
@@ -31,7 +31,7 @@
 // Unicode East Asian Width
 // http://unicode.org/reports/tr11/
 static bool IsAmbiguosEastAsianWidthWide(const ComputedStyle* style) {
-  UScriptCode script = style->GetFontDescription().Script();
+  UScriptCode script = style->GetFontDescription().GetScript();
   return script == USCRIPT_KATAKANA_OR_HIRAGANA ||
          script == USCRIPT_SIMPLIFIED_HAN || script == USCRIPT_TRADITIONAL_HAN;
 }
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 9b868c9..f7f23b06 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
@@ -88,7 +88,6 @@
                                                NGConstraintSpace* space,
                                                NGBlockBreakToken* break_token)
     : NGLayoutAlgorithm(node, space, break_token),
-      builder_(NGPhysicalFragment::kFragmentBox, node),
       space_builder_(constraint_space_) {}
 
 Optional<MinMaxContentSize> NGBlockLayoutAlgorithm::ComputeMinMaxContentSize()
@@ -136,7 +135,7 @@
   LayoutUnit block_offset = content_size_;
   if (known_fragment_offset) {
     block_offset = known_fragment_offset.value().block_offset -
-                   builder_.BfcOffset().value().block_offset;
+                   ContainerBfcOffset().block_offset;
   }
   return {inline_offset, block_offset};
 }
@@ -167,9 +166,9 @@
   space_builder_.SetAvailableSize(adjusted_size)
       .SetPercentageResolutionSize(adjusted_size);
 
-  builder_.SetDirection(constraint_space_->Direction());
-  builder_.SetWritingMode(constraint_space_->WritingMode());
-  builder_.SetSize(size);
+  container_builder_.SetDirection(constraint_space_->Direction());
+  container_builder_.SetWritingMode(constraint_space_->WritingMode());
+  container_builder_.SetSize(size);
 
   NGBlockChildIterator child_iterator(Node()->FirstChild(), BreakToken());
   NGBlockChildIterator::Entry entry = child_iterator.NextChild();
@@ -189,7 +188,7 @@
   if (border_and_padding_.block_start) {
     curr_bfc_offset_.block_offset += curr_margin_strut_.Sum();
     MaybeUpdateFragmentBfcOffset(ConstraintSpace(), curr_bfc_offset_,
-                                 &builder_);
+                                 &container_builder_);
     curr_margin_strut_ = NGMarginStrut();
   }
 
@@ -197,9 +196,9 @@
   // still {} as the margin strut from the constraint space must also be empty.
   if (ConstraintSpace().IsNewFormattingContext()) {
     MaybeUpdateFragmentBfcOffset(ConstraintSpace(), curr_bfc_offset_,
-                                 &builder_);
+                                 &container_builder_);
     DCHECK_EQ(curr_margin_strut_, NGMarginStrut());
-    DCHECK_EQ(builder_.BfcOffset().value(), NGLogicalOffset());
+    DCHECK_EQ(container_builder_.BfcOffset().value(), NGLogicalOffset());
     curr_bfc_offset_ = {};
   }
 
@@ -213,7 +212,8 @@
         // is no content size yet? See floats-wrap-inside-inline-006.
         NGLogicalOffset offset = {border_and_padding_.inline_start,
                                   content_size_ + curr_margin_strut_.Sum()};
-        builder_.AddOutOfFlowChildCandidate(ToNGBlockNode(child), offset);
+        container_builder_.AddOutOfFlowChildCandidate(ToNGBlockNode(child),
+                                                      offset);
         NGBlockChildIterator::Entry entry = child_iterator.NextChild();
         child = entry.node;
         child_break_token = entry.token;
@@ -251,18 +251,18 @@
   // Recompute the block-axis size now that we know our content size.
   size.block_size =
       ComputeBlockSizeForFragment(ConstraintSpace(), Style(), content_size_);
-  builder_.SetBlockSize(size.block_size);
+  container_builder_.SetBlockSize(size.block_size);
 
   // Layout our absolute and fixed positioned children.
-  NGOutOfFlowLayoutPart(ConstraintSpace(), Style(), &builder_).Run();
+  NGOutOfFlowLayoutPart(ConstraintSpace(), Style(), &container_builder_).Run();
 
   // Non-empty blocks always know their position in space:
   if (size.block_size) {
     curr_bfc_offset_.block_offset += curr_margin_strut_.Sum();
     MaybeUpdateFragmentBfcOffset(ConstraintSpace(), curr_bfc_offset_,
-                                 &builder_);
+                                 &container_builder_);
     PositionPendingFloats(curr_bfc_offset_.block_offset,
-                          MutableConstraintSpace(), &builder_);
+                          MutableConstraintSpace(), &container_builder_);
   }
 
   // Margins collapsing:
@@ -272,14 +272,15 @@
     // TODO(glebl): handle minLogicalHeight, maxLogicalHeight.
     curr_margin_strut_ = NGMarginStrut();
   }
-  builder_.SetEndMarginStrut(curr_margin_strut_);
+  container_builder_.SetEndMarginStrut(curr_margin_strut_);
 
-  builder_.SetOverflowSize(NGLogicalSize(max_inline_size_, content_size_));
+  container_builder_.SetOverflowSize(
+      NGLogicalSize(max_inline_size_, content_size_));
 
   if (ConstraintSpace().HasBlockFragmentation())
     FinalizeForFragmentation();
 
-  return builder_.ToBoxFragment();
+  return container_builder_.ToBoxFragment();
 }
 
 void NGBlockLayoutAlgorithm::PrepareChildLayout(NGLayoutInputNode* child) {
@@ -291,8 +292,9 @@
                  FromPlatformWritingMode(Style().GetWritingMode())));
 
   // Set estimated BFC offset to the next child's constraint space.
-  curr_bfc_offset_ = builder_.BfcOffset() ? builder_.BfcOffset().value()
-                                          : ConstraintSpace().BfcOffset();
+  curr_bfc_offset_ = container_builder_.BfcOffset()
+                         ? container_builder_.BfcOffset().value()
+                         : ConstraintSpace().BfcOffset();
   curr_bfc_offset_.block_offset += content_size_;
   curr_bfc_offset_.inline_offset += border_and_padding_.inline_start;
 
@@ -301,7 +303,8 @@
   bool should_position_pending_floats =
       child->IsBlock() && !is_floating &&
       !IsNewFormattingContextForBlockLevelChild(Style(), *child) &&
-      ClearanceMayAffectLayout(ConstraintSpace(), builder_.UnpositionedFloats(),
+      ClearanceMayAffectLayout(ConstraintSpace(),
+                               container_builder_.UnpositionedFloats(),
                                child->Style());
 
   // Children which may clear a float need to force all the pending floats to
@@ -311,9 +314,10 @@
         curr_bfc_offset_.block_offset + curr_margin_strut_.Sum();
     MaybeUpdateFragmentBfcOffset(
         ConstraintSpace(),
-        {curr_bfc_offset_.inline_offset, origin_point_block_offset}, &builder_);
+        {curr_bfc_offset_.inline_offset, origin_point_block_offset},
+        &container_builder_);
     PositionPendingFloats(origin_point_block_offset, MutableConstraintSpace(),
-                          &builder_);
+                          &container_builder_);
   }
 
   bool is_inflow = child->IsInline() || !is_floating;
@@ -345,9 +349,9 @@
   if (should_collapse_margins) {
     curr_bfc_offset_.block_offset += curr_margin_strut_.Sum();
     MaybeUpdateFragmentBfcOffset(ConstraintSpace(), curr_bfc_offset_,
-                                 &builder_);
+                                 &container_builder_);
     PositionPendingFloats(curr_bfc_offset_.block_offset,
-                          MutableConstraintSpace(), &builder_);
+                          MutableConstraintSpace(), &container_builder_);
     curr_margin_strut_ = {};
   }
 }
@@ -363,7 +367,7 @@
   // Pull out unpositioned floats to the current fragment. This may needed if
   // for example the child fragment could not position its floats because it's
   // empty and therefore couldn't determine its position in space.
-  builder_.MutableUnpositionedFloats().AppendVector(
+  container_builder_.MutableUnpositionedFloats().AppendVector(
       layout_result->UnpositionedFloats());
 
   if (child->IsBlock() && child->Style().IsFloating()) {
@@ -374,16 +378,16 @@
         child_space->AvailableSize(), origin_offset,
         constraint_space_->BfcOffset(), curr_child_margins_,
         layout_result->PhysicalFragment().Get());
-    builder_.AddUnpositionedFloat(floating_object);
+    container_builder_.AddUnpositionedFloat(floating_object);
     // No need to postpone the positioning if we know the correct offset.
-    if (builder_.BfcOffset()) {
+    if (container_builder_.BfcOffset()) {
       NGLogicalOffset origin_point = curr_bfc_offset_;
       // Adjust origin point to the margins of the last child.
       // Example: <div style="margin-bottom: 20px"><float></div>
       //          <div style="margin-bottom: 30px"></div>
       origin_point.block_offset += curr_margin_strut_.Sum();
       PositionPendingFloats(origin_point.block_offset, MutableConstraintSpace(),
-                            &builder_);
+                            &container_builder_);
     }
     return;
   }
@@ -392,17 +396,16 @@
   // content_size_ or known fragment's BFC offset.
   WTF::Optional<NGLogicalOffset> bfc_offset;
   if (child_space->IsNewFormattingContext()) {
-    DCHECK(builder_.BfcOffset());
     bfc_offset = curr_bfc_offset_;
   } else if (fragment.BfcOffset()) {
     // Fragment that knows its offset can be used to set parent's BFC position.
     curr_bfc_offset_.block_offset = fragment.BfcOffset().value().block_offset;
     MaybeUpdateFragmentBfcOffset(ConstraintSpace(), curr_bfc_offset_,
-                                 &builder_);
+                                 &container_builder_);
     PositionPendingFloats(curr_bfc_offset_.block_offset,
-                          MutableConstraintSpace(), &builder_);
+                          MutableConstraintSpace(), &container_builder_);
     bfc_offset = curr_bfc_offset_;
-  } else if (builder_.BfcOffset()) {
+  } else if (container_builder_.BfcOffset()) {
     // Fragment doesn't know its offset but we can still calculate its BFC
     // position because the parent fragment's BFC is known.
     // Example:
@@ -432,7 +435,7 @@
                                      curr_child_margins_.InlineSum() +
                                      border_and_padding_.InlineSum());
 
-  builder_.AddChild(layout_result, logical_offset);
+  container_builder_.AddChild(layout_result, logical_offset);
 }
 
 void NGBlockLayoutAlgorithm::FinalizeForFragmentation() {
@@ -446,33 +449,31 @@
       << "Adding and subtracting the used_block_size shouldn't leave the "
          "block_size for this fragment smaller than zero.";
 
-  DCHECK(builder_.BfcOffset()) << "We must have our BfcOffset by this point "
-                                  "to determine the space left in the flow.";
   LayoutUnit space_left = ConstraintSpace().FragmentainerSpaceAvailable() -
-                          builder_.BfcOffset().value().block_offset;
+                          ContainerBfcOffset().block_offset;
   DCHECK_GE(space_left, LayoutUnit());
 
-  if (builder_.DidBreak()) {
+  if (container_builder_.DidBreak()) {
     // One of our children broke. Even if we fit within the remaining space we
     // need to prepare a break token.
-    builder_.SetUsedBlockSize(std::min(space_left, block_size) +
-                              used_block_size);
-    builder_.SetBlockSize(std::min(space_left, block_size));
-    builder_.SetBlockOverflow(space_left);
+    container_builder_.SetUsedBlockSize(std::min(space_left, block_size) +
+                                        used_block_size);
+    container_builder_.SetBlockSize(std::min(space_left, block_size));
+    container_builder_.SetBlockOverflow(space_left);
     return;
   }
 
   if (block_size > space_left) {
     // Need a break inside this block.
-    builder_.SetUsedBlockSize(space_left + used_block_size);
-    builder_.SetBlockSize(space_left);
-    builder_.SetBlockOverflow(space_left);
+    container_builder_.SetUsedBlockSize(space_left + used_block_size);
+    container_builder_.SetBlockSize(space_left);
+    container_builder_.SetBlockOverflow(space_left);
     return;
   }
 
   // The end of the block fits in the current fragmentainer.
-  builder_.SetBlockSize(block_size);
-  builder_.SetBlockOverflow(content_size_);
+  container_builder_.SetBlockSize(block_size);
+  container_builder_.SetBlockOverflow(content_size_);
 }
 
 NGBoxStrut NGBlockLayoutAlgorithm::CalculateMargins(
@@ -531,7 +532,6 @@
     // position in the formatting context, and are able to adjust the
     // fragmentation line.
     if (is_new_bfc) {
-      DCHECK(builder_.BfcOffset());
       space_available -= curr_bfc_offset_.block_offset;
     }
   }
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.h b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.h
index fd71e88..2e6f19c 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.h
+++ b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.h
@@ -10,7 +10,6 @@
 #include "core/layout/ng/ng_block_break_token.h"
 #include "core/layout/ng/ng_block_node.h"
 #include "core/layout/ng/ng_constraint_space_builder.h"
-#include "core/layout/ng/ng_fragment_builder.h"
 #include "core/layout/ng/ng_layout_algorithm.h"
 #include "platform/wtf/RefPtr.h"
 
@@ -64,7 +63,6 @@
   NGLogicalOffset CalculateLogicalOffset(
       const WTF::Optional<NGLogicalOffset>& known_fragment_offset);
 
-  NGFragmentBuilder builder_;
   NGConstraintSpaceBuilder space_builder_;
 
   NGBoxStrut border_and_padding_;
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm_test.cc b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm_test.cc
index 6e57962e..6f72f77 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm_test.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm_test.cc
@@ -2270,5 +2270,7 @@
   EXPECT_FALSE(iterator.NextChild());
 }
 
+TEST_F(NGBlockLayoutAlgorithmTest, NewFormattingContextBlock) {}
+
 }  // namespace
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_layout_algorithm.h b/third_party/WebKit/Source/core/layout/ng/ng_layout_algorithm.h
index d70e244..2317506 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_layout_algorithm.h
+++ b/third_party/WebKit/Source/core/layout/ng/ng_layout_algorithm.h
@@ -6,6 +6,7 @@
 #define NGLayoutAlgorithm_h
 
 #include "core/CoreExport.h"
+#include "core/layout/ng/ng_fragment_builder.h"
 #include "core/layout/ng/ng_min_max_content_size.h"
 #include "platform/wtf/Allocator.h"
 #include "platform/wtf/Optional.h"
@@ -24,7 +25,10 @@
   NGLayoutAlgorithm(NGInputNodeType* node,
                     NGConstraintSpace* space,
                     NGBreakTokenType* break_token)
-      : node_(node), constraint_space_(space), break_token_(break_token) {}
+      : node_(node),
+        constraint_space_(space),
+        break_token_(break_token),
+        container_builder_(NGPhysicalFragment::kFragmentBox, node) {}
 
   virtual ~NGLayoutAlgorithm() {}
 
@@ -55,6 +59,11 @@
     return node_->Style();
   }
 
+  NGLogicalOffset ContainerBfcOffset() const {
+    DCHECK(container_builder_.BfcOffset().has_value());
+    return container_builder_.BfcOffset().value();
+  }
+
   virtual NGInputNodeType* Node() const { return node_; }
 
   NGBreakTokenType* BreakToken() const { return break_token_; }
@@ -64,6 +73,8 @@
 
   // The break token from which we are currently resuming layout.
   NGBreakTokenType* break_token_;
+
+  NGFragmentBuilder container_builder_;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/paint/InlineTextBoxPainter.cpp b/third_party/WebKit/Source/core/paint/InlineTextBoxPainter.cpp
index 44faeb2..3e4c78fa 100644
--- a/third_party/WebKit/Source/core/paint/InlineTextBoxPainter.cpp
+++ b/third_party/WebKit/Source/core/paint/InlineTextBoxPainter.cpp
@@ -63,7 +63,7 @@
     case kIdeographicBaseline:
       // Compute language-appropriate default underline position.
       // https://drafts.csswg.org/css-text-decor-3/#default-stylesheet
-      UScriptCode script = style.GetFontDescription().Script();
+      UScriptCode script = style.GetFontDescription().GetScript();
       if (script == USCRIPT_KATAKANA_OR_HIRAGANA || script == USCRIPT_HANGUL)
         return ResolvedUnderlinePosition::kOver;
       return ResolvedUnderlinePosition::kUnder;
diff --git a/third_party/WebKit/Source/core/paint/PaintLayer.cpp b/third_party/WebKit/Source/core/paint/PaintLayer.cpp
index 20e3d449c..2736ebb 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayer.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintLayer.cpp
@@ -185,9 +185,8 @@
     }
     rare_data_->resource_info->ClearLayer();
   }
-  if (GetLayoutObject().GetFrame() && GetLayoutObject().GetFrame()->GetPage()) {
-    if (ScrollingCoordinator* scrolling_coordinator =
-            GetLayoutObject().GetFrame()->GetPage()->GetScrollingCoordinator())
+  if (GetLayoutObject().GetFrame()) {
+    if (ScrollingCoordinator* scrolling_coordinator = GetScrollingCoordinator())
       scrolling_coordinator->WillDestroyLayer(this);
   }
 
@@ -2749,6 +2748,11 @@
           GetCompositingState() != kPaintsIntoOwnBacking);
 }
 
+ScrollingCoordinator* PaintLayer::GetScrollingCoordinator() {
+  Page* page = GetLayoutObject().GetFrame()->GetPage();
+  return (!page) ? nullptr : page->GetScrollingCoordinator();
+}
+
 bool PaintLayer::CompositesWithTransform() const {
   return TransformAncestor() || Transform();
 }
diff --git a/third_party/WebKit/Source/core/paint/PaintLayer.h b/third_party/WebKit/Source/core/paint/PaintLayer.h
index 5543048d..c5ef7b9 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayer.h
+++ b/third_party/WebKit/Source/core/paint/PaintLayer.h
@@ -572,6 +572,10 @@
             GetCompositingState() != kPaintsIntoOwnBacking);
   }
 
+  // Returns the ScrollingCoordinator associated with this layer, if
+  // any. Otherwise nullptr.
+  ScrollingCoordinator* GetScrollingCoordinator();
+
   // Returns true if the element or any ancestor is transformed.
   bool CompositesWithTransform() const;
 
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp b/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp
index 638fee9..3eda3a9d 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp
@@ -114,7 +114,7 @@
       scroll_corner_(nullptr),
       resizer_(nullptr),
       scroll_anchor_(this),
-      reasons_(0)
+      non_composited_main_thread_scrolling_reasons_(0)
 #if DCHECK_IS_ON()
       ,
       has_been_disposed_(false)
@@ -152,7 +152,7 @@
     }
   }
 
-  RemoveStyleRelatedMainThreadScrollingReasons();
+  non_composited_main_thread_scrolling_reasons_ = 0;
 
   if (ScrollingCoordinator* scrolling_coordinator = GetScrollingCoordinator())
     scrolling_coordinator->WillDestroyScrollableArea(this);
@@ -1796,6 +1796,7 @@
 bool PaintLayerScrollableArea::ComputeNeedsCompositedScrolling(
     const LCDTextMode mode,
     const PaintLayer* layer) {
+  non_composited_main_thread_scrolling_reasons_ = 0;
   if (!layer->ScrollsOverflow())
     return false;
 
@@ -1810,14 +1811,14 @@
     return false;
 
   bool needs_composited_scrolling = true;
-  uint32_t main_thread_scrolling_reasons = 0;
 
   // TODO(flackr): Allow integer transforms as long as all of the ancestor
   // transforms are also integer.
   bool background_supports_lcd_text =
       RuntimeEnabledFeatures::compositeOpaqueScrollersEnabled() &&
       layer->GetLayoutObject().Style()->IsStackingContext() &&
-      layer->GetBackgroundPaintLocation(&main_thread_scrolling_reasons) &
+      layer->GetBackgroundPaintLocation(
+          &non_composited_main_thread_scrolling_reasons_) &
           kBackgroundPaintInScrollingContents &&
       layer->BackgroundIsKnownToBeOpaqueInRect(
           ToLayoutBox(layer->GetLayoutObject()).PaddingBoxRect()) &&
@@ -1827,16 +1828,16 @@
       !layer->Compositor()->PreferCompositingToLCDTextEnabled() &&
       !background_supports_lcd_text) {
     if (layer->CompositesWithOpacity()) {
-      main_thread_scrolling_reasons |=
+      non_composited_main_thread_scrolling_reasons_ |=
           MainThreadScrollingReason::kHasOpacityAndLCDText;
     }
     if (layer->CompositesWithTransform()) {
-      main_thread_scrolling_reasons |=
+      non_composited_main_thread_scrolling_reasons_ |=
           MainThreadScrollingReason::kHasTransformAndLCDText;
     }
     if (!layer->BackgroundIsKnownToBeOpaqueInRect(
             ToLayoutBox(layer->GetLayoutObject()).PaddingBoxRect())) {
-      main_thread_scrolling_reasons |=
+      non_composited_main_thread_scrolling_reasons_ |=
           MainThreadScrollingReason::kBackgroundNotOpaqueInRectAndLCDText;
     }
 
@@ -1848,68 +1849,24 @@
   // behind dashed borders). Resolve this case, or not, and update this check
   // with the results.
   if (layer->GetLayoutObject().Style()->HasBorderRadius()) {
-    main_thread_scrolling_reasons |=
+    non_composited_main_thread_scrolling_reasons_ |=
         MainThreadScrollingReason::kHasBorderRadius;
     needs_composited_scrolling = false;
   }
   if (layer->GetLayoutObject().HasClip() ||
       layer->HasDescendantWithClipPath() || layer->HasAncestorWithClipPath()) {
-    main_thread_scrolling_reasons |=
+    non_composited_main_thread_scrolling_reasons_ |=
         MainThreadScrollingReason::kHasClipRelatedProperty;
     needs_composited_scrolling = false;
   }
 
-  if (main_thread_scrolling_reasons) {
-    AddStyleRelatedMainThreadScrollingReasons(main_thread_scrolling_reasons);
-  }
-
+  DCHECK(!(non_composited_main_thread_scrolling_reasons_ &
+           ~MainThreadScrollingReason::kNonCompositedReasons));
   return needs_composited_scrolling;
 }
 
-void PaintLayerScrollableArea::AddStyleRelatedMainThreadScrollingReasons(
-    const uint32_t reasons) {
-  LocalFrame* frame = Box().GetFrame();
-  if (!frame)
-    return;
-  FrameView* frame_view = frame->View();
-  if (!frame_view)
-    return;
-
-  for (uint32_t reason = 1;
-       reason < 1 << MainThreadScrollingReason::kMainThreadScrollingReasonCount;
-       reason <<= 1) {
-    if (reasons & reason) {
-      frame_view->AdjustStyleRelatedMainThreadScrollingReasons(reason, true);
-      reasons_ |= reason;
-    }
-  }
-}
-
-void PaintLayerScrollableArea::RemoveStyleRelatedMainThreadScrollingReasons() {
-  LocalFrame* frame = Box().GetFrame();
-  if (!frame)
-    return;
-  FrameView* frame_view = frame->View();
-  if (!frame_view)
-    return;
-
-  // Decrease the number of layers that have any main thread
-  // scrolling reasons stored in FrameView
-  for (uint32_t i = 0;
-       i < MainThreadScrollingReason::kMainThreadScrollingReasonCount; ++i) {
-    uint32_t reason = 1 << i;
-    if (HasMainThreadScrollingReason(reason)) {
-      reasons_ &= ~reason;
-      frame_view->AdjustStyleRelatedMainThreadScrollingReasons(reason, false);
-    }
-  }
-}
-
 void PaintLayerScrollableArea::UpdateNeedsCompositedScrolling(
     LCDTextMode mode) {
-  // Clear all style related main thread scrolling reasons, if any,
-  // before calling computeNeedsCompositedScrolling
-  RemoveStyleRelatedMainThreadScrollingReasons();
   const bool needs_composited_scrolling =
       ComputeNeedsCompositedScrolling(mode, Layer());
 
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.h b/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.h
index 0a348662..8a3e916 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.h
+++ b/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.h
@@ -479,14 +479,12 @@
   StickyConstraintsMap& GetStickyConstraintsMap() {
     return EnsureRareData().sticky_constraints_map_;
   }
+
   void InvalidateAllStickyConstraints();
   void InvalidateStickyConstraintsFor(PaintLayer*,
                                       bool needs_compositing_update = true);
-
-  void RemoveStyleRelatedMainThreadScrollingReasons();
-  void AddStyleRelatedMainThreadScrollingReasons(const uint32_t);
-  bool HasMainThreadScrollingReason(uint32_t reason) const {
-    return reasons_ & reason;
+  uint32_t GetNonCompositedMainThreadScrollingReasons() {
+    return non_composited_main_thread_scrolling_reasons_;
   }
 
   uint64_t Id() const;
@@ -597,7 +595,7 @@
   std::unique_ptr<PaintLayerScrollableAreaRareData> rare_data_;
 
   // MainThreadScrollingReason due to the properties of the LayoutObject
-  uint32_t reasons_;
+  uint32_t non_composited_main_thread_scrolling_reasons_;
 
 #if DCHECK_IS_ON()
   bool has_been_disposed_;
diff --git a/third_party/WebKit/Source/modules/app_banner/BeforeInstallPromptEvent.cpp b/third_party/WebKit/Source/modules/app_banner/BeforeInstallPromptEvent.cpp
index 1b400f0..27911600 100644
--- a/third_party/WebKit/Source/modules/app_banner/BeforeInstallPromptEvent.cpp
+++ b/third_party/WebKit/Source/modules/app_banner/BeforeInstallPromptEvent.cpp
@@ -7,6 +7,7 @@
 #include "core/dom/DOMException.h"
 #include "core/dom/Document.h"
 #include "core/dom/ExceptionCode.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/frame/UseCounter.h"
 #include "modules/app_banner/BeforeInstallPromptEventInit.h"
 
@@ -51,7 +52,7 @@
 }
 
 ScriptPromise BeforeInstallPromptEvent::userChoice(ScriptState* script_state) {
-  UseCounter::Count(script_state->GetExecutionContext(),
+  UseCounter::Count(ExecutionContext::From(script_state),
                     UseCounter::kBeforeInstallPromptEventUserChoice);
   // |m_binding| must be bound to allow the AppBannerService to resolve the
   // userChoice promise.
@@ -74,7 +75,7 @@
                              "following preventDefault()."));
   }
 
-  UseCounter::Count(script_state->GetExecutionContext(),
+  UseCounter::Count(ExecutionContext::From(script_state),
                     UseCounter::kBeforeInstallPromptEventPrompt);
 
   prompt_called_ = true;
diff --git a/third_party/WebKit/Source/modules/battery/BatteryManager.cpp b/third_party/WebKit/Source/modules/battery/BatteryManager.cpp
index 41fbf31..4e50bbae 100644
--- a/third_party/WebKit/Source/modules/battery/BatteryManager.cpp
+++ b/third_party/WebKit/Source/modules/battery/BatteryManager.cpp
@@ -6,6 +6,7 @@
 
 #include "core/dom/DOMException.h"
 #include "core/dom/Document.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/events/Event.h"
 #include "modules/battery/BatteryDispatcher.h"
 #include "platform/wtf/Assertions.h"
@@ -26,8 +27,8 @@
 
 ScriptPromise BatteryManager::StartRequest(ScriptState* script_state) {
   if (!battery_property_) {
-    battery_property_ = new BatteryProperty(script_state->GetExecutionContext(),
-                                            this, BatteryProperty::kReady);
+    battery_property_ = new BatteryProperty(
+        ExecutionContext::From(script_state), this, BatteryProperty::kReady);
 
     // If the context is in a stopped state already, do not start updating.
     if (!GetExecutionContext() || GetExecutionContext()->IsContextDestroyed()) {
diff --git a/third_party/WebKit/Source/modules/battery/NavigatorBattery.cpp b/third_party/WebKit/Source/modules/battery/NavigatorBattery.cpp
index e0a2982..5f6423f 100644
--- a/third_party/WebKit/Source/modules/battery/NavigatorBattery.cpp
+++ b/third_party/WebKit/Source/modules/battery/NavigatorBattery.cpp
@@ -4,6 +4,7 @@
 
 #include "modules/battery/NavigatorBattery.h"
 
+#include "core/dom/ExecutionContext.h"
 #include "core/frame/LocalFrame.h"
 #include "modules/battery/BatteryManager.h"
 
@@ -18,10 +19,10 @@
 }
 
 ScriptPromise NavigatorBattery::getBattery(ScriptState* script_state) {
-  if (!battery_manager_)
+  if (!battery_manager_) {
     battery_manager_ =
-        BatteryManager::Create(script_state->GetExecutionContext());
-
+        BatteryManager::Create(ExecutionContext::From(script_state));
+  }
   return battery_manager_->StartRequest(script_state);
 }
 
diff --git a/third_party/WebKit/Source/modules/beacon/NavigatorBeacon.cpp b/third_party/WebKit/Source/modules/beacon/NavigatorBeacon.cpp
index 8acd932b..972a63f 100644
--- a/third_party/WebKit/Source/modules/beacon/NavigatorBeacon.cpp
+++ b/third_party/WebKit/Source/modules/beacon/NavigatorBeacon.cpp
@@ -106,7 +106,7 @@
     const String& urlstring,
     const ArrayBufferViewOrBlobOrStringOrFormData& data,
     ExceptionState& exception_state) {
-  ExecutionContext* context = script_state->GetExecutionContext();
+  ExecutionContext* context = ExecutionContext::From(script_state);
   KURL url = context->CompleteURL(urlstring);
   if (!CanSendBeacon(context, url, exception_state))
     return false;
diff --git a/third_party/WebKit/Source/modules/bluetooth/Bluetooth.cpp b/third_party/WebKit/Source/modules/bluetooth/Bluetooth.cpp
index 74e90853..6ad5ecd2 100644
--- a/third_party/WebKit/Source/modules/bluetooth/Bluetooth.cpp
+++ b/third_party/WebKit/Source/modules/bluetooth/Bluetooth.cpp
@@ -151,7 +151,7 @@
 ScriptPromise Bluetooth::requestDevice(ScriptState* script_state,
                                        const RequestDeviceOptions& options,
                                        ExceptionState& exception_state) {
-  ExecutionContext* context = script_state->GetExecutionContext();
+  ExecutionContext* context = ExecutionContext::From(script_state);
 
   // If the Relevant settings object is not a secure context, reject promise
   // with a SecurityError and abort these steps.
diff --git a/third_party/WebKit/Source/modules/budget/BudgetService.cpp b/third_party/WebKit/Source/modules/budget/BudgetService.cpp
index 714b52af7..003a72ff 100644
--- a/third_party/WebKit/Source/modules/budget/BudgetService.cpp
+++ b/third_party/WebKit/Source/modules/budget/BudgetService.cpp
@@ -9,6 +9,7 @@
 #include "bindings/core/v8/ScriptState.h"
 #include "core/dom/DOMException.h"
 #include "core/dom/ExceptionCode.h"
+#include "core/dom/ExecutionContext.h"
 #include "modules/budget/BudgetState.h"
 #include "public/platform/InterfaceProvider.h"
 #include "public/platform/Platform.h"
@@ -60,7 +61,7 @@
   DCHECK(service_);
 
   String error_message;
-  if (!script_state->GetExecutionContext()->IsSecureContext(error_message))
+  if (!ExecutionContext::From(script_state)->IsSecureContext(error_message))
     return ScriptPromise::RejectWithDOMException(
         script_state, DOMException::Create(kSecurityError, error_message));
 
@@ -86,7 +87,7 @@
   DCHECK(service_);
 
   String error_message;
-  if (!script_state->GetExecutionContext()->IsSecureContext(error_message))
+  if (!ExecutionContext::From(script_state)->IsSecureContext(error_message))
     return ScriptPromise::RejectWithDOMException(
         script_state, DOMException::Create(kSecurityError, error_message));
 
@@ -95,7 +96,7 @@
 
   // Get the budget from the browser BudgetService.
   RefPtr<SecurityOrigin> origin(
-      script_state->GetExecutionContext()->GetSecurityOrigin());
+      ExecutionContext::From(script_state)->GetSecurityOrigin());
   service_->GetBudget(
       origin, ConvertToBaseCallback(WTF::Bind(&BudgetService::GotBudget,
                                               WrapPersistent(this),
@@ -132,7 +133,7 @@
   DCHECK_NE(type, mojom::blink::BudgetOperationType::INVALID_OPERATION);
 
   String error_message;
-  if (!script_state->GetExecutionContext()->IsSecureContext(error_message))
+  if (!ExecutionContext::From(script_state)->IsSecureContext(error_message))
     return ScriptPromise::RejectWithDOMException(
         script_state, DOMException::Create(kSecurityError, error_message));
 
@@ -141,7 +142,7 @@
 
   // Call to the BudgetService to place the reservation.
   RefPtr<SecurityOrigin> origin(
-      script_state->GetExecutionContext()->GetSecurityOrigin());
+      ExecutionContext::From(script_state)->GetSecurityOrigin());
   service_->Reserve(origin, type,
                     ConvertToBaseCallback(WTF::Bind(
                         &BudgetService::GotReservation, WrapPersistent(this),
diff --git a/third_party/WebKit/Source/modules/cachestorage/CacheStorage.cpp b/third_party/WebKit/Source/modules/cachestorage/CacheStorage.cpp
index 1c51dc5..07113ed 100644
--- a/third_party/WebKit/Source/modules/cachestorage/CacheStorage.cpp
+++ b/third_party/WebKit/Source/modules/cachestorage/CacheStorage.cpp
@@ -10,6 +10,7 @@
 #include "bindings/core/v8/ScriptState.h"
 #include "core/dom/DOMException.h"
 #include "core/dom/ExceptionCode.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/inspector/ConsoleMessage.h"
 #include "modules/cachestorage/CacheStorageError.h"
 #include "modules/fetch/Request.h"
@@ -28,7 +29,7 @@
 }
 
 bool CommonChecks(ScriptState* script_state, ExceptionState& exception_state) {
-  ExecutionContext* execution_context = script_state->GetExecutionContext();
+  ExecutionContext* execution_context = ExecutionContext::From(script_state);
   // FIXME: May be null due to worker termination: http://crbug.com/413518.
   if (!execution_context)
     return false;
diff --git a/third_party/WebKit/Source/modules/cachestorage/CacheTest.cpp b/third_party/WebKit/Source/modules/cachestorage/CacheTest.cpp
index f1cd996..77590693 100644
--- a/third_party/WebKit/Source/modules/cachestorage/CacheTest.cpp
+++ b/third_party/WebKit/Source/modules/cachestorage/CacheTest.cpp
@@ -16,6 +16,7 @@
 #include "bindings/modules/v8/V8Request.h"
 #include "bindings/modules/v8/V8Response.h"
 #include "core/dom/Document.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/frame/Frame.h"
 #include "core/testing/DummyPageHolder.h"
 #include "modules/fetch/BodyStreamBuffer.h"
@@ -244,7 +245,7 @@
     return ToScriptStateForMainWorld(page_->GetDocument().GetFrame());
   }
   ExecutionContext* GetExecutionContext() {
-    return GetScriptState()->GetExecutionContext();
+    return ExecutionContext::From(GetScriptState());
   }
   v8::Isolate* GetIsolate() { return GetScriptState()->GetIsolate(); }
   v8::Local<v8::Context> GetContext() { return GetScriptState()->GetContext(); }
diff --git a/third_party/WebKit/Source/modules/canvas2d/BaseRenderingContext2D.cpp b/third_party/WebKit/Source/modules/canvas2d/BaseRenderingContext2D.cpp
index f9a13995..94fe155 100644
--- a/third_party/WebKit/Source/modules/canvas2d/BaseRenderingContext2D.cpp
+++ b/third_party/WebKit/Source/modules/canvas2d/BaseRenderingContext2D.cpp
@@ -9,6 +9,7 @@
 #include "bindings/core/v8/ScriptState.h"
 #include "core/css/cssom/CSSURLImageValue.h"
 #include "core/css/parser/CSSParser.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/dom/NotShared.h"
 #include "core/frame/ImageBitmap.h"
 #include "core/html/HTMLCanvasElement.h"
@@ -1354,7 +1355,7 @@
   }
 
   if (OriginClean() &&
-      WouldTaintOrigin(image_source, script_state->GetExecutionContext()))
+      WouldTaintOrigin(image_source, ExecutionContext::From(script_state)))
     SetOriginTainted();
 }
 
@@ -1469,7 +1470,7 @@
   DCHECK(image_for_rendering);
 
   bool origin_clean =
-      !WouldTaintOrigin(image_source, script_state->GetExecutionContext());
+      !WouldTaintOrigin(image_source, ExecutionContext::From(script_state));
 
   return CanvasPattern::Create(image_for_rendering.Release(), repeat_mode,
                                origin_clean);
diff --git a/third_party/WebKit/Source/modules/compositorworker/CompositorWorkerGlobalScope.cpp b/third_party/WebKit/Source/modules/compositorworker/CompositorWorkerGlobalScope.cpp
index ffe4cc89..2ffeffd 100644
--- a/third_party/WebKit/Source/modules/compositorworker/CompositorWorkerGlobalScope.cpp
+++ b/third_party/WebKit/Source/modules/compositorworker/CompositorWorkerGlobalScope.cpp
@@ -8,6 +8,7 @@
 #include "bindings/core/v8/ScriptState.h"
 #include "bindings/core/v8/SerializedScriptValue.h"
 #include "core/dom/CompositorWorkerProxyClient.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/workers/InProcessWorkerObjectProxy.h"
 #include "core/workers/WorkerThreadStartupData.h"
 #include "modules/EventTargetModules.h"
@@ -76,7 +77,7 @@
     ExceptionState& exception_state) {
   // Disentangle the port in preparation for sending it to the remote context.
   MessagePortChannelArray channels = MessagePort::DisentanglePorts(
-      script_state->GetExecutionContext(), ports, exception_state);
+      ExecutionContext::From(script_state), ports, exception_state);
   if (exception_state.HadException())
     return;
   WorkerObjectProxy().PostMessageToWorkerObject(std::move(message),
diff --git a/third_party/WebKit/Source/modules/credentialmanager/CredentialsContainer.cpp b/third_party/WebKit/Source/modules/credentialmanager/CredentialsContainer.cpp
index 209a51e..684815b 100644
--- a/third_party/WebKit/Source/modules/credentialmanager/CredentialsContainer.cpp
+++ b/third_party/WebKit/Source/modules/credentialmanager/CredentialsContainer.cpp
@@ -65,7 +65,7 @@
 
   void OnSuccess() override {
     Frame* frame =
-        ToDocument(resolver_->GetScriptState()->GetExecutionContext())
+        ToDocument(ExecutionContext::From(resolver_->GetScriptState()))
             ->GetFrame();
     SECURITY_CHECK(!frame || frame == frame->Tree().Top());
 
@@ -90,7 +90,7 @@
 
   void OnSuccess(std::unique_ptr<WebCredential> web_credential) override {
     Frame* frame =
-        ToDocument(resolver_->GetScriptState()->GetExecutionContext())
+        ToDocument(ExecutionContext::From(resolver_->GetScriptState()))
             ->GetFrame();
     SECURITY_CHECK(!frame || frame == frame->Tree().Top());
 
@@ -103,7 +103,7 @@
 
     ASSERT(credential->IsPasswordCredential() ||
            credential->IsFederatedCredential());
-    UseCounter::Count(resolver_->GetScriptState()->GetExecutionContext(),
+    UseCounter::Count(ExecutionContext::From(resolver_->GetScriptState()),
                       UseCounter::kCredentialManagerGetReturnedCredential);
     if (credential->IsPasswordCredential())
       resolver_->Resolve(PasswordCredential::Create(
@@ -128,8 +128,8 @@
 CredentialsContainer::CredentialsContainer() {}
 
 static bool CheckBoilerplate(ScriptPromiseResolver* resolver) {
-  Frame* frame =
-      ToDocument(resolver->GetScriptState()->GetExecutionContext())->GetFrame();
+  Frame* frame = ToDocument(ExecutionContext::From(resolver->GetScriptState()))
+                     ->GetFrame();
   if (!frame || frame != frame->Tree().Top()) {
     resolver->Reject(DOMException::Create(kSecurityError,
                                           "CredentialContainer methods may "
@@ -139,14 +139,14 @@
   }
 
   String error_message;
-  if (!resolver->GetScriptState()->GetExecutionContext()->IsSecureContext(
-          error_message)) {
+  if (!ExecutionContext::From(resolver->GetScriptState())
+           ->IsSecureContext(error_message)) {
     resolver->Reject(DOMException::Create(kSecurityError, error_message));
     return false;
   }
 
   CredentialManagerClient* client = CredentialManagerClient::From(
-      resolver->GetScriptState()->GetExecutionContext());
+      ExecutionContext::From(resolver->GetScriptState()));
   if (!client) {
     resolver->Reject(DOMException::Create(
         kInvalidStateError,
@@ -181,12 +181,12 @@
     }
   }
 
-  UseCounter::Count(script_state->GetExecutionContext(),
+  UseCounter::Count(ExecutionContext::From(script_state),
                     options.unmediated()
                         ? UseCounter::kCredentialManagerGetWithoutUI
                         : UseCounter::kCredentialManagerGetWithUI);
 
-  CredentialManagerClient::From(script_state->GetExecutionContext())
+  CredentialManagerClient::From(ExecutionContext::From(script_state))
       ->DispatchGet(options.unmediated(), options.password(), providers,
                     new RequestCallbacks(resolver));
   return promise;
@@ -201,7 +201,7 @@
 
   auto web_credential =
       WebCredential::Create(credential->GetPlatformCredential());
-  CredentialManagerClient::From(script_state->GetExecutionContext())
+  CredentialManagerClient::From(ExecutionContext::From(script_state))
       ->DispatchStore(*web_credential, new NotificationCallbacks(resolver));
   return promise;
 }
@@ -213,7 +213,7 @@
   if (!CheckBoilerplate(resolver))
     return promise;
 
-  CredentialManagerClient::From(script_state->GetExecutionContext())
+  CredentialManagerClient::From(ExecutionContext::From(script_state))
       ->DispatchRequireUserMediation(new NotificationCallbacks(resolver));
   return promise;
 }
diff --git a/third_party/WebKit/Source/modules/crypto/CryptoResultImpl.cpp b/third_party/WebKit/Source/modules/crypto/CryptoResultImpl.cpp
index db5abe9..0883422 100644
--- a/third_party/WebKit/Source/modules/crypto/CryptoResultImpl.cpp
+++ b/third_party/WebKit/Source/modules/crypto/CryptoResultImpl.cpp
@@ -123,7 +123,7 @@
     : resolver_(Resolver::Create(script_state, this)),
       cancel_(ResultCancel::Create()) {
   // Sync cancellation state.
-  if (script_state->GetExecutionContext()->IsContextDestroyed())
+  if (ExecutionContext::From(script_state)->IsContextDestroyed())
     cancel_->Cancel();
 }
 
diff --git a/third_party/WebKit/Source/modules/crypto/SubtleCrypto.cpp b/third_party/WebKit/Source/modules/crypto/SubtleCrypto.cpp
index d45a653..64c4e5e 100644
--- a/third_party/WebKit/Source/modules/crypto/SubtleCrypto.cpp
+++ b/third_party/WebKit/Source/modules/crypto/SubtleCrypto.cpp
@@ -61,15 +61,16 @@
 static bool CanAccessWebCrypto(ScriptState* script_state,
                                CryptoResult* result) {
   String error_message;
-  if (!script_state->GetExecutionContext()->IsSecureContext(
-          error_message, ExecutionContext::kWebCryptoSecureContextCheck)) {
+  if (!ExecutionContext::From(script_state)
+           ->IsSecureContext(error_message,
+                             ExecutionContext::kWebCryptoSecureContextCheck)) {
     result->CompleteWithError(kWebCryptoErrorTypeNotSupported, error_message);
     return false;
   }
 
-  if (!script_state->GetExecutionContext()->IsSecureContext()) {
+  if (!ExecutionContext::From(script_state)->IsSecureContext()) {
     Deprecation::CountDeprecation(
-        script_state->GetExecutionContext(),
+        ExecutionContext::From(script_state),
         UseCounter::kSubtleCryptoOnlyStrictSecureContextCheckFailed);
   }
 
@@ -213,7 +214,7 @@
                                   kWebCryptoKeyUsageEncrypt, result))
     return promise;
 
-  HistogramAlgorithmAndKey(script_state->GetExecutionContext(),
+  HistogramAlgorithmAndKey(ExecutionContext::From(script_state),
                            normalized_algorithm, key->Key());
   Platform::Current()->Crypto()->Encrypt(normalized_algorithm, key->Key(),
                                          std::move(data), result->Result());
@@ -254,7 +255,7 @@
                                   kWebCryptoKeyUsageDecrypt, result))
     return promise;
 
-  HistogramAlgorithmAndKey(script_state->GetExecutionContext(),
+  HistogramAlgorithmAndKey(ExecutionContext::From(script_state),
                            normalized_algorithm, key->Key());
   Platform::Current()->Crypto()->Decrypt(normalized_algorithm, key->Key(),
                                          std::move(data), result->Result());
@@ -295,7 +296,7 @@
                                   result))
     return promise;
 
-  HistogramAlgorithmAndKey(script_state->GetExecutionContext(),
+  HistogramAlgorithmAndKey(ExecutionContext::From(script_state),
                            normalized_algorithm, key->Key());
   Platform::Current()->Crypto()->Sign(normalized_algorithm, key->Key(),
                                       std::move(data), result->Result());
@@ -342,7 +343,7 @@
                                   kWebCryptoKeyUsageVerify, result))
     return promise;
 
-  HistogramAlgorithmAndKey(script_state->GetExecutionContext(),
+  HistogramAlgorithmAndKey(ExecutionContext::From(script_state),
                            normalized_algorithm, key->Key());
   Platform::Current()->Crypto()->VerifySignature(
       normalized_algorithm, key->Key(), std::move(signature), std::move(data),
@@ -373,7 +374,8 @@
                       normalized_algorithm, result))
     return promise;
 
-  HistogramAlgorithm(script_state->GetExecutionContext(), normalized_algorithm);
+  HistogramAlgorithm(ExecutionContext::From(script_state),
+                     normalized_algorithm);
   Platform::Current()->Crypto()->Digest(normalized_algorithm, std::move(data),
                                         result->Result());
   return promise;
@@ -409,7 +411,8 @@
   // keys. This normative requirement is enforced by the platform
   // implementation in the call below.
 
-  HistogramAlgorithm(script_state->GetExecutionContext(), normalized_algorithm);
+  HistogramAlgorithm(ExecutionContext::From(script_state),
+                     normalized_algorithm);
   Platform::Current()->Crypto()->GenerateKey(normalized_algorithm, extractable,
                                              key_usages, result->Result());
   return promise;
@@ -495,7 +498,8 @@
                       normalized_algorithm, result))
     return promise;
 
-  HistogramAlgorithm(script_state->GetExecutionContext(), normalized_algorithm);
+  HistogramAlgorithm(ExecutionContext::From(script_state),
+                     normalized_algorithm);
   Platform::Current()->Crypto()->ImportKey(format, std::move(key_data),
                                            normalized_algorithm, extractable,
                                            key_usages, result->Result());
@@ -526,7 +530,7 @@
     return promise;
   }
 
-  HistogramKey(script_state->GetExecutionContext(), key->Key());
+  HistogramKey(ExecutionContext::From(script_state), key->Key());
   Platform::Current()->Crypto()->ExportKey(format, key->Key(),
                                            result->Result());
   return promise;
@@ -585,9 +589,9 @@
     return promise;
   }
 
-  HistogramAlgorithmAndKey(script_state->GetExecutionContext(),
+  HistogramAlgorithmAndKey(ExecutionContext::From(script_state),
                            normalized_algorithm, wrapping_key->Key());
-  HistogramKey(script_state->GetExecutionContext(), key->Key());
+  HistogramKey(ExecutionContext::From(script_state), key->Key());
   Platform::Current()->Crypto()->WrapKey(
       format, key->Key(), wrapping_key->Key(), normalized_algorithm,
       result->Result());
@@ -660,9 +664,9 @@
   // normative requirement is enforced by the platform implementation in the
   // call below.
 
-  HistogramAlgorithmAndKey(script_state->GetExecutionContext(),
+  HistogramAlgorithmAndKey(ExecutionContext::From(script_state),
                            normalized_algorithm, unwrapping_key->Key());
-  HistogramAlgorithm(script_state->GetExecutionContext(),
+  HistogramAlgorithm(ExecutionContext::From(script_state),
                      normalized_key_algorithm);
   Platform::Current()->Crypto()->UnwrapKey(
       format, std::move(wrapped_key), unwrapping_key->Key(),
@@ -702,7 +706,7 @@
                                        kWebCryptoKeyUsageDeriveBits, result))
     return promise;
 
-  HistogramAlgorithmAndKey(script_state->GetExecutionContext(),
+  HistogramAlgorithmAndKey(ExecutionContext::From(script_state),
                            normalized_algorithm, base_key->Key());
   Platform::Current()->Crypto()->DeriveBits(
       normalized_algorithm, base_key->Key(), length_bits, result->Result());
@@ -772,9 +776,9 @@
   // normative requirement is enforced by the platform implementation in the
   // call below.
 
-  HistogramAlgorithmAndKey(script_state->GetExecutionContext(),
+  HistogramAlgorithmAndKey(ExecutionContext::From(script_state),
                            normalized_algorithm, base_key->Key());
-  HistogramAlgorithm(script_state->GetExecutionContext(),
+  HistogramAlgorithm(ExecutionContext::From(script_state),
                      normalized_derived_key_algorithm);
   Platform::Current()->Crypto()->DeriveKey(
       normalized_algorithm, base_key->Key(), normalized_derived_key_algorithm,
diff --git a/third_party/WebKit/Source/modules/csspaint/CSSPaintDefinition.cpp b/third_party/WebKit/Source/modules/csspaint/CSSPaintDefinition.cpp
index 785911fd..9a11dc0c 100644
--- a/third_party/WebKit/Source/modules/csspaint/CSSPaintDefinition.cpp
+++ b/third_party/WebKit/Source/modules/csspaint/CSSPaintDefinition.cpp
@@ -112,7 +112,8 @@
   v8::TryCatch block(isolate);
   block.SetVerbose(true);
 
-  V8ScriptRunner::CallFunction(paint, script_state_->GetExecutionContext(),
+  V8ScriptRunner::CallFunction(paint,
+                               ExecutionContext::From(script_state_.Get()),
                                instance, WTF_ARRAY_LENGTH(argv), argv, isolate);
 
   // The paint function may have produced an error, in which case produce an
diff --git a/third_party/WebKit/Source/modules/encryptedmedia/MediaKeySession.cpp b/third_party/WebKit/Source/modules/encryptedmedia/MediaKeySession.cpp
index 1d2c604..02128d9 100644
--- a/third_party/WebKit/Source/modules/encryptedmedia/MediaKeySession.cpp
+++ b/third_party/WebKit/Source/modules/encryptedmedia/MediaKeySession.cpp
@@ -35,6 +35,7 @@
 #include "core/dom/DOMArrayBuffer.h"
 #include "core/dom/DOMException.h"
 #include "core/dom/ExceptionCode.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/dom/TaskRunnerHelper.h"
 #include "core/events/Event.h"
 #include "core/events/GenericEventQueue.h"
@@ -365,7 +366,7 @@
 MediaKeySession::MediaKeySession(ScriptState* script_state,
                                  MediaKeys* media_keys,
                                  WebEncryptedMediaSessionType session_type)
-    : ContextLifecycleObserver(script_state->GetExecutionContext()),
+    : ContextLifecycleObserver(ExecutionContext::From(script_state)),
       async_event_queue_(GenericEventQueue::Create(this)),
       media_keys_(media_keys),
       session_type_(session_type),
@@ -374,7 +375,7 @@
       is_uninitialized_(true),
       is_callable_(false),
       is_closed_(false),
-      closed_promise_(new ClosedPromise(script_state->GetExecutionContext(),
+      closed_promise_(new ClosedPromise(ExecutionContext::From(script_state),
                                         this,
                                         ClosedPromise::kClosed)),
       action_timer_(
diff --git a/third_party/WebKit/Source/modules/encryptedmedia/NavigatorRequestMediaKeySystemAccess.cpp b/third_party/WebKit/Source/modules/encryptedmedia/NavigatorRequestMediaKeySystemAccess.cpp
index 38fe480..5fceee8b 100644
--- a/third_party/WebKit/Source/modules/encryptedmedia/NavigatorRequestMediaKeySystemAccess.cpp
+++ b/third_party/WebKit/Source/modules/encryptedmedia/NavigatorRequestMediaKeySystemAccess.cpp
@@ -13,6 +13,7 @@
 #include "core/dom/DOMException.h"
 #include "core/dom/Document.h"
 #include "core/dom/ExceptionCode.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/frame/Deprecation.h"
 #include "core/frame/Settings.h"
 #include "core/inspector/ConsoleMessage.h"
@@ -266,7 +267,7 @@
     const HeapVector<MediaKeySystemConfiguration>& supported_configurations) {
   DVLOG(3) << __func__;
 
-  ExecutionContext* execution_context = script_state->GetExecutionContext();
+  ExecutionContext* execution_context = ExecutionContext::From(script_state);
   Document* document = ToDocument(execution_context);
 
   // From https://w3c.github.io/encrypted-media/#common-key-systems
diff --git a/third_party/WebKit/Source/modules/fetch/Body.cpp b/third_party/WebKit/Source/modules/fetch/Body.cpp
index 2eb441bd..e2fb56e4 100644
--- a/third_party/WebKit/Source/modules/fetch/Body.cpp
+++ b/third_party/WebKit/Source/modules/fetch/Body.cpp
@@ -11,6 +11,7 @@
 #include "bindings/core/v8/V8ThrowException.h"
 #include "core/dom/DOMArrayBuffer.h"
 #include "core/dom/DOMTypedArray.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/fileapi/Blob.h"
 #include "modules/fetch/BodyStreamBuffer.h"
 #include "modules/fetch/FetchDataLoader.h"
@@ -119,7 +120,7 @@
   // first check the ExecutionContext and return immediately if it's already
   // gone (which means that the V8::TerminateExecution() signal has been sent
   // to this worker thread).
-  if (!script_state->GetExecutionContext())
+  if (!ExecutionContext::From(script_state))
     return ScriptPromise();
 
   ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
@@ -139,7 +140,7 @@
     return promise;
 
   // See above comment.
-  if (!script_state->GetExecutionContext())
+  if (!ExecutionContext::From(script_state))
     return ScriptPromise();
 
   ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
@@ -163,7 +164,7 @@
     return promise;
 
   // See above comment.
-  if (!script_state->GetExecutionContext())
+  if (!ExecutionContext::From(script_state))
     return ScriptPromise();
 
   ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
@@ -184,7 +185,7 @@
     return promise;
 
   // See above comment.
-  if (!script_state->GetExecutionContext())
+  if (!ExecutionContext::From(script_state))
     return ScriptPromise();
 
   ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
diff --git a/third_party/WebKit/Source/modules/fetch/BodyStreamBuffer.cpp b/third_party/WebKit/Source/modules/fetch/BodyStreamBuffer.cpp
index 340d5b9..14dffef 100644
--- a/third_party/WebKit/Source/modules/fetch/BodyStreamBuffer.cpp
+++ b/third_party/WebKit/Source/modules/fetch/BodyStreamBuffer.cpp
@@ -11,6 +11,7 @@
 #include "core/dom/DOMArrayBuffer.h"
 #include "core/dom/DOMTypedArray.h"
 #include "core/dom/ExceptionCode.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/streams/ReadableStreamController.h"
 #include "core/streams/ReadableStreamOperations.h"
 #include "modules/fetch/Body.h"
@@ -169,9 +170,9 @@
   ASSERT(!loader_);
   ASSERT(script_state_->ContextIsValid());
   loader_ = loader;
-  loader->Start(
-      ReleaseHandle(),
-      new LoaderClient(script_state_->GetExecutionContext(), this, client));
+  loader->Start(ReleaseHandle(),
+                new LoaderClient(ExecutionContext::From(script_state_.Get()),
+                                 this, client));
 }
 
 void BodyStreamBuffer::Tee(BodyStreamBuffer** branch1,
@@ -191,8 +192,8 @@
   }
   BytesConsumer* dest1 = nullptr;
   BytesConsumer* dest2 = nullptr;
-  BytesConsumer::Tee(script_state_->GetExecutionContext(), ReleaseHandle(),
-                     &dest1, &dest2);
+  BytesConsumer::Tee(ExecutionContext::From(script_state_.Get()),
+                     ReleaseHandle(), &dest1, &dest2);
   *branch1 = new BodyStreamBuffer(script_state_.Get(), dest1);
   *branch2 = new BodyStreamBuffer(script_state_.Get(), dest2);
 }
diff --git a/third_party/WebKit/Source/modules/fetch/FetchManager.cpp b/third_party/WebKit/Source/modules/fetch/FetchManager.cpp
index feeb7e99..3fa17aa 100644
--- a/third_party/WebKit/Source/modules/fetch/FetchManager.cpp
+++ b/third_party/WebKit/Source/modules/fetch/FetchManager.cpp
@@ -11,6 +11,7 @@
 #include "bindings/core/v8/V8ThrowException.h"
 #include "core/dom/DOMArrayBuffer.h"
 #include "core/dom/Document.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/fileapi/Blob.h"
 #include "core/frame/Frame.h"
 #include "core/frame/SubresourceIntegrity.h"
@@ -425,7 +426,7 @@
     response_data = FetchResponseData::CreateWithBuffer(new BodyStreamBuffer(
         script_state,
         new BytesConsumerForDataConsumerHandle(
-            script_state->GetExecutionContext(), std::move(handle))));
+            ExecutionContext::From(script_state), std::move(handle))));
   } else {
     sri_consumer = new SRIBytesConsumer();
     response_data = FetchResponseData::CreateWithBuffer(
diff --git a/third_party/WebKit/Source/modules/fetch/FetchRequestData.cpp b/third_party/WebKit/Source/modules/fetch/FetchRequestData.cpp
index 149b951..9b0c1b5 100644
--- a/third_party/WebKit/Source/modules/fetch/FetchRequestData.cpp
+++ b/third_party/WebKit/Source/modules/fetch/FetchRequestData.cpp
@@ -33,10 +33,12 @@
   for (HTTPHeaderMap::const_iterator it = web_request.Headers().begin();
        it != web_request.Headers().end(); ++it)
     request->header_list_->Append(it->key, it->value);
-  if (web_request.GetBlobDataHandle())
+  if (web_request.GetBlobDataHandle()) {
     request->SetBuffer(new BodyStreamBuffer(
-        script_state, new BlobBytesConsumer(script_state->GetExecutionContext(),
-                                            web_request.GetBlobDataHandle())));
+        script_state,
+        new BlobBytesConsumer(ExecutionContext::From(script_state),
+                              web_request.GetBlobDataHandle())));
+  }
   request->SetContext(web_request.GetRequestContext());
   request->SetReferrer(
       Referrer(web_request.ReferrerUrl().GetString(),
diff --git a/third_party/WebKit/Source/modules/fetch/Request.cpp b/third_party/WebKit/Source/modules/fetch/Request.cpp
index 4890e3fe..7df101f 100644
--- a/third_party/WebKit/Source/modules/fetch/Request.cpp
+++ b/third_party/WebKit/Source/modules/fetch/Request.cpp
@@ -35,11 +35,12 @@
   request->SetUnsafeRequestFlag(true);
   // FIXME: Set client.
   DOMWrapperWorld& world = script_state->World();
-  if (world.IsIsolatedWorld())
+  if (world.IsIsolatedWorld()) {
     request->SetOrigin(world.IsolatedWorldSecurityOrigin());
-  else
+  } else {
     request->SetOrigin(
-        script_state->GetExecutionContext()->GetSecurityOrigin());
+        ExecutionContext::From(script_state)->GetSecurityOrigin());
+  }
   // FIXME: Set ForceOriginHeaderFlag.
   request->SetSameOriginDataURLFlag(true);
   request->SetReferrer(original->GetReferrer());
@@ -75,7 +76,7 @@
   // and a new request otherwise."
 
   RefPtr<SecurityOrigin> origin =
-      script_state->GetExecutionContext()->GetSecurityOrigin();
+      ExecutionContext::From(script_state)->GetSecurityOrigin();
 
   // TODO(yhirano): Implement the following steps:
   // - "Let |window| be client."
@@ -109,7 +110,7 @@
   if (!input_request) {
     // "Let |parsedURL| be the result of parsing |input| with |baseURL|."
     KURL parsed_url =
-        script_state->GetExecutionContext()->CompleteURL(input_string);
+        ExecutionContext::From(script_state)->CompleteURL(input_string);
     // "If |parsedURL| is failure, throw a TypeError."
     if (!parsed_url.IsValid()) {
       exception_state.ThrowTypeError("Failed to parse URL from " +
@@ -178,8 +179,8 @@
     } else {
       // "Let |parsedReferrer| be the result of parsing |referrer| with
       // |baseURL|."
-      KURL parsed_referrer = script_state->GetExecutionContext()->CompleteURL(
-          init.referrer.referrer);
+      KURL parsed_referrer = ExecutionContext::From(script_state)
+                                 ->CompleteURL(init.referrer.referrer);
       if (!parsed_referrer.IsValid()) {
         // "If |parsedReferrer| is failure, throw a TypeError."
         exception_state.ThrowTypeError("Referrer '" + init.referrer.referrer +
@@ -452,7 +453,7 @@
                          const String& input,
                          const Dictionary& init,
                          ExceptionState& exception_state) {
-  RequestInit request_init(script_state->GetExecutionContext(), init,
+  RequestInit request_init(ExecutionContext::From(script_state), init,
                            exception_state);
   return CreateRequestWithRequestOrString(script_state, nullptr, input,
                                           request_init, exception_state);
@@ -468,7 +469,7 @@
                          Request* input,
                          const Dictionary& init,
                          ExceptionState& exception_state) {
-  RequestInit request_init(script_state->GetExecutionContext(), init,
+  RequestInit request_init(ExecutionContext::From(script_state), init,
                            exception_state);
   return CreateRequestWithRequestOrString(script_state, input, String(),
                                           request_init, exception_state);
@@ -488,7 +489,7 @@
 Request::Request(ScriptState* script_state,
                  FetchRequestData* request,
                  Headers* headers)
-    : Body(script_state->GetExecutionContext()),
+    : Body(ExecutionContext::From(script_state)),
       request_(request),
       headers_(headers) {
   RefreshBody(script_state);
diff --git a/third_party/WebKit/Source/modules/fetch/Response.cpp b/third_party/WebKit/Source/modules/fetch/Response.cpp
index 1eae69fa..e9a735fd 100644
--- a/third_party/WebKit/Source/modules/fetch/Response.cpp
+++ b/third_party/WebKit/Source/modules/fetch/Response.cpp
@@ -18,6 +18,7 @@
 #include "bindings/modules/v8/ByteStringSequenceSequenceOrByteStringByteStringRecordOrHeaders.h"
 #include "core/dom/DOMArrayBuffer.h"
 #include "core/dom/DOMArrayBufferView.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/dom/URLSearchParams.h"
 #include "core/fileapi/Blob.h"
 #include "core/frame/UseCounter.h"
@@ -64,7 +65,7 @@
   }
 
   response->ReplaceBodyStreamBuffer(new BodyStreamBuffer(
-      script_state, new BlobBytesConsumer(script_state->GetExecutionContext(),
+      script_state, new BlobBytesConsumer(ExecutionContext::From(script_state),
                                           web_response.GetBlobDataHandle())));
 
   // Filter the response according to |webResponse|'s ResponseType.
@@ -134,7 +135,7 @@
                            ExceptionState& exception_state) {
   v8::Local<v8::Value> body = body_value.V8Value();
   v8::Isolate* isolate = script_state->GetIsolate();
-  ExecutionContext* execution_context = script_state->GetExecutionContext();
+  ExecutionContext* execution_context = ExecutionContext::From(script_state);
 
   BodyStreamBuffer* body_buffer = nullptr;
   String content_type;
@@ -214,7 +215,7 @@
 
   // "3. Let |r| be a new Response object, associated with a new response,
   // Headers object, and Body object."
-  Response* r = new Response(script_state->GetExecutionContext());
+  Response* r = new Response(ExecutionContext::From(script_state));
 
   // "4. Set |r|'s response's status to |init|'s status member."
   r->response_->SetStatus(init.status());
@@ -283,14 +284,14 @@
                            const WebServiceWorkerResponse& web_response) {
   FetchResponseData* response_data =
       CreateFetchResponseDataFromWebResponse(script_state, web_response);
-  return new Response(script_state->GetExecutionContext(), response_data);
+  return new Response(ExecutionContext::From(script_state), response_data);
 }
 
 Response* Response::error(ScriptState* script_state) {
   FetchResponseData* response_data =
       FetchResponseData::CreateNetworkErrorResponse();
   Response* r =
-      new Response(script_state->GetExecutionContext(), response_data);
+      new Response(ExecutionContext::From(script_state), response_data);
   r->headers_->SetGuard(Headers::kImmutableGuard);
   return r;
 }
@@ -299,7 +300,7 @@
                              const String& url,
                              unsigned short status,
                              ExceptionState& exception_state) {
-  KURL parsed_url = script_state->GetExecutionContext()->CompleteURL(url);
+  KURL parsed_url = ExecutionContext::From(script_state)->CompleteURL(url);
   if (!parsed_url.IsValid()) {
     exception_state.ThrowTypeError("Failed to parse URL from " + url);
     return nullptr;
@@ -310,7 +311,7 @@
     return nullptr;
   }
 
-  Response* r = new Response(script_state->GetExecutionContext());
+  Response* r = new Response(ExecutionContext::From(script_state));
   r->headers_->SetGuard(Headers::kImmutableGuard);
   r->response_->SetStatus(status);
   r->response_->HeaderList()->Set("Location", parsed_url);
diff --git a/third_party/WebKit/Source/modules/fetch/ResponseTest.cpp b/third_party/WebKit/Source/modules/fetch/ResponseTest.cpp
index 857d974..d99ef016 100644
--- a/third_party/WebKit/Source/modules/fetch/ResponseTest.cpp
+++ b/third_party/WebKit/Source/modules/fetch/ResponseTest.cpp
@@ -9,6 +9,7 @@
 #include "bindings/core/v8/ScriptState.h"
 #include "bindings/core/v8/V8BindingForTesting.h"
 #include "core/dom/Document.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/frame/Frame.h"
 #include "core/testing/DummyPageHolder.h"
 #include "modules/fetch/BodyStreamBuffer.h"
@@ -218,7 +219,7 @@
   using Command = BytesConsumerTestUtil::Command;
   BytesConsumerTestUtil::ReplayingBytesConsumer* src =
       new BytesConsumerTestUtil::ReplayingBytesConsumer(
-          script_state->GetExecutionContext());
+          ExecutionContext::From(script_state));
   src->Add(Command(Command::kData, "Hello, "));
   src->Add(Command(Command::kData, "world"));
   src->Add(Command(Command::kDone));
diff --git a/third_party/WebKit/Source/modules/filesystem/DataTransferItemFileSystem.cpp b/third_party/WebKit/Source/modules/filesystem/DataTransferItemFileSystem.cpp
index 2620c66..9fd4fea 100644
--- a/third_party/WebKit/Source/modules/filesystem/DataTransferItemFileSystem.cpp
+++ b/third_party/WebKit/Source/modules/filesystem/DataTransferItemFileSystem.cpp
@@ -34,6 +34,7 @@
 #include "core/clipboard/DataObject.h"
 #include "core/clipboard/DataTransfer.h"
 #include "core/clipboard/DataTransferItem.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/fileapi/File.h"
 #include "modules/filesystem/DOMFilePath.h"
 #include "modules/filesystem/DOMFileSystem.h"
@@ -62,7 +63,7 @@
   DOMFileSystem* dom_file_system =
       DraggedIsolatedFileSystemImpl::GetDOMFileSystem(
           item.GetDataTransfer()->GetDataObject(),
-          script_state->GetExecutionContext(), *item.GetDataObjectItem());
+          ExecutionContext::From(script_state), *item.GetDataObjectItem());
   if (!dom_file_system) {
     // IsolatedFileSystem may not be enabled.
     return 0;
diff --git a/third_party/WebKit/Source/modules/filesystem/Entry.cpp b/third_party/WebKit/Source/modules/filesystem/Entry.cpp
index 7201b21..53268053 100644
--- a/third_party/WebKit/Source/modules/filesystem/Entry.cpp
+++ b/third_party/WebKit/Source/modules/filesystem/Entry.cpp
@@ -30,6 +30,7 @@
 #include "modules/filesystem/Entry.h"
 
 #include "bindings/core/v8/ScriptState.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/fileapi/FileError.h"
 #include "core/frame/UseCounter.h"
 #include "core/html/VoidCallback.h"
@@ -49,7 +50,7 @@
 DOMFileSystem* Entry::filesystem(ScriptState* script_state) const {
   if (file_system_->GetType() == kFileSystemTypeIsolated)
     UseCounter::Count(
-        script_state->GetExecutionContext(),
+        ExecutionContext::From(script_state),
         UseCounter::kEntry_Filesystem_AttributeGetter_IsolatedFileSystem);
   return filesystem();
 }
@@ -58,7 +59,7 @@
                         MetadataCallback* success_callback,
                         ErrorCallback* error_callback) {
   if (file_system_->GetType() == kFileSystemTypeIsolated)
-    UseCounter::Count(script_state->GetExecutionContext(),
+    UseCounter::Count(ExecutionContext::From(script_state),
                       UseCounter::kEntry_GetMetadata_Method_IsolatedFileSystem);
   file_system_->GetMetadata(this, success_callback,
                             ScriptErrorCallback::Wrap(error_callback));
@@ -70,7 +71,7 @@
                    EntryCallback* success_callback,
                    ErrorCallback* error_callback) const {
   if (file_system_->GetType() == kFileSystemTypeIsolated)
-    UseCounter::Count(script_state->GetExecutionContext(),
+    UseCounter::Count(ExecutionContext::From(script_state),
                       UseCounter::kEntry_MoveTo_Method_IsolatedFileSystem);
   file_system_->Move(this, parent, name, success_callback,
                      ScriptErrorCallback::Wrap(error_callback));
@@ -82,7 +83,7 @@
                    EntryCallback* success_callback,
                    ErrorCallback* error_callback) const {
   if (file_system_->GetType() == kFileSystemTypeIsolated)
-    UseCounter::Count(script_state->GetExecutionContext(),
+    UseCounter::Count(ExecutionContext::From(script_state),
                       UseCounter::kEntry_CopyTo_Method_IsolatedFileSystem);
   file_system_->Copy(this, parent, name, success_callback,
                      ScriptErrorCallback::Wrap(error_callback));
@@ -92,7 +93,7 @@
                    VoidCallback* success_callback,
                    ErrorCallback* error_callback) const {
   if (file_system_->GetType() == kFileSystemTypeIsolated)
-    UseCounter::Count(script_state->GetExecutionContext(),
+    UseCounter::Count(ExecutionContext::From(script_state),
                       UseCounter::kEntry_Remove_Method_IsolatedFileSystem);
   file_system_->Remove(this, success_callback,
                        ScriptErrorCallback::Wrap(error_callback));
@@ -102,7 +103,7 @@
                       EntryCallback* success_callback,
                       ErrorCallback* error_callback) const {
   if (file_system_->GetType() == kFileSystemTypeIsolated)
-    UseCounter::Count(script_state->GetExecutionContext(),
+    UseCounter::Count(ExecutionContext::From(script_state),
                       UseCounter::kEntry_GetParent_Method_IsolatedFileSystem);
   file_system_->GetParent(this, success_callback,
                           ScriptErrorCallback::Wrap(error_callback));
@@ -110,7 +111,7 @@
 
 String Entry::toURL(ScriptState* script_state) const {
   if (file_system_->GetType() == kFileSystemTypeIsolated)
-    UseCounter::Count(script_state->GetExecutionContext(),
+    UseCounter::Count(ExecutionContext::From(script_state),
                       UseCounter::kEntry_ToURL_Method_IsolatedFileSystem);
   return static_cast<const EntryBase*>(this)->toURL();
 }
diff --git a/third_party/WebKit/Source/modules/filesystem/HTMLInputElementFileSystem.cpp b/third_party/WebKit/Source/modules/filesystem/HTMLInputElementFileSystem.cpp
index 25c59c3c..3ff1aa8 100644
--- a/third_party/WebKit/Source/modules/filesystem/HTMLInputElementFileSystem.cpp
+++ b/third_party/WebKit/Source/modules/filesystem/HTMLInputElementFileSystem.cpp
@@ -31,6 +31,7 @@
 #include "modules/filesystem/HTMLInputElementFileSystem.h"
 
 #include "bindings/core/v8/ScriptState.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/fileapi/FileList.h"
 #include "core/html/HTMLInputElement.h"
 #include "modules/filesystem/DOMFilePath.h"
@@ -54,7 +55,7 @@
     return entries;
 
   DOMFileSystem* filesystem = DOMFileSystem::CreateIsolatedFileSystem(
-      script_state->GetExecutionContext(), input.DroppedFileSystemId());
+      ExecutionContext::From(script_state), input.DroppedFileSystemId());
   if (!filesystem) {
     // Drag-drop isolated filesystem is not available.
     return entries;
diff --git a/third_party/WebKit/Source/modules/indexeddb/IDBFactory.cpp b/third_party/WebKit/Source/modules/indexeddb/IDBFactory.cpp
index b237d15..1782a240 100644
--- a/third_party/WebKit/Source/modules/indexeddb/IDBFactory.cpp
+++ b/third_party/WebKit/Source/modules/indexeddb/IDBFactory.cpp
@@ -28,11 +28,13 @@
 
 #include "modules/indexeddb/IDBFactory.h"
 
+#include <memory>
 #include "bindings/core/v8/ExceptionState.h"
 #include "bindings/modules/v8/V8BindingForModules.h"
 #include "core/dom/DOMException.h"
 #include "core/dom/Document.h"
 #include "core/dom/ExceptionCode.h"
+#include "core/dom/ExecutionContext.h"
 #include "modules/indexeddb/IDBDatabase.h"
 #include "modules/indexeddb/IDBDatabaseCallbacks.h"
 #include "modules/indexeddb/IDBKey.h"
@@ -44,7 +46,6 @@
 #include "public/platform/WebSecurityOrigin.h"
 #include "public/platform/modules/indexeddb/WebIDBDatabaseCallbacks.h"
 #include "public/platform/modules/indexeddb/WebIDBFactory.h"
-#include <memory>
 
 namespace blink {
 
@@ -65,9 +66,9 @@
 IDBRequest* IDBFactory::getDatabaseNames(ScriptState* script_state,
                                          ExceptionState& exception_state) {
   IDB_TRACE("IDBFactory::getDatabaseNames");
-  if (!IsContextValid(script_state->GetExecutionContext()))
+  if (!IsContextValid(ExecutionContext::From(script_state)))
     return nullptr;
-  if (!script_state->GetExecutionContext()
+  if (!ExecutionContext::From(script_state)
            ->GetSecurityOrigin()
            ->CanAccessDatabase()) {
     exception_state.ThrowSecurityError(
@@ -78,8 +79,8 @@
   IDBRequest* request =
       IDBRequest::Create(script_state, IDBAny::CreateNull(), nullptr);
 
-  if (!IndexedDBClient::From(script_state->GetExecutionContext())
-           ->AllowIndexedDB(script_state->GetExecutionContext(),
+  if (!IndexedDBClient::From(ExecutionContext::From(script_state))
+           ->AllowIndexedDB(ExecutionContext::From(script_state),
                             "Database Listing")) {
     request->OnError(
         DOMException::Create(kUnknownError, kPermissionDeniedErrorMessage));
@@ -89,7 +90,7 @@
   Platform::Current()->IdbFactory()->GetDatabaseNames(
       request->CreateWebCallbacks().release(),
       WebSecurityOrigin(
-          script_state->GetExecutionContext()->GetSecurityOrigin()));
+          ExecutionContext::From(script_state)->GetSecurityOrigin()));
   return request;
 }
 
@@ -111,9 +112,9 @@
                                            ExceptionState& exception_state) {
   IDBDatabase::RecordApiCallsHistogram(kIDBOpenCall);
   DCHECK(version >= 1 || version == IDBDatabaseMetadata::kNoVersion);
-  if (!IsContextValid(script_state->GetExecutionContext()))
+  if (!IsContextValid(ExecutionContext::From(script_state)))
     return nullptr;
-  if (!script_state->GetExecutionContext()
+  if (!ExecutionContext::From(script_state)
            ->GetSecurityOrigin()
            ->CanAccessDatabase()) {
     exception_state.ThrowSecurityError(
@@ -126,8 +127,8 @@
   IDBOpenDBRequest* request = IDBOpenDBRequest::Create(
       script_state, database_callbacks, transaction_id, version);
 
-  if (!IndexedDBClient::From(script_state->GetExecutionContext())
-           ->AllowIndexedDB(script_state->GetExecutionContext(), name)) {
+  if (!IndexedDBClient::From(ExecutionContext::From(script_state))
+           ->AllowIndexedDB(ExecutionContext::From(script_state), name)) {
     request->OnError(
         DOMException::Create(kUnknownError, kPermissionDeniedErrorMessage));
     return request;
@@ -137,7 +138,7 @@
       name, version, transaction_id, request->CreateWebCallbacks().release(),
       database_callbacks->CreateWebCallbacks().release(),
       WebSecurityOrigin(
-          script_state->GetExecutionContext()->GetSecurityOrigin()));
+          ExecutionContext::From(script_state)->GetSecurityOrigin()));
   return request;
 }
 
@@ -171,9 +172,9 @@
     bool force_close) {
   IDB_TRACE("IDBFactory::deleteDatabase");
   IDBDatabase::RecordApiCallsHistogram(kIDBDeleteDatabaseCall);
-  if (!IsContextValid(script_state->GetExecutionContext()))
+  if (!IsContextValid(ExecutionContext::From(script_state)))
     return nullptr;
-  if (!script_state->GetExecutionContext()
+  if (!ExecutionContext::From(script_state)
            ->GetSecurityOrigin()
            ->CanAccessDatabase()) {
     exception_state.ThrowSecurityError(
@@ -184,8 +185,8 @@
   IDBOpenDBRequest* request = IDBOpenDBRequest::Create(
       script_state, nullptr, 0, IDBDatabaseMetadata::kDefaultVersion);
 
-  if (!IndexedDBClient::From(script_state->GetExecutionContext())
-           ->AllowIndexedDB(script_state->GetExecutionContext(), name)) {
+  if (!IndexedDBClient::From(ExecutionContext::From(script_state))
+           ->AllowIndexedDB(ExecutionContext::From(script_state), name)) {
     request->OnError(
         DOMException::Create(kUnknownError, kPermissionDeniedErrorMessage));
     return request;
@@ -194,7 +195,7 @@
   Platform::Current()->IdbFactory()->DeleteDatabase(
       name, request->CreateWebCallbacks().release(),
       WebSecurityOrigin(
-          script_state->GetExecutionContext()->GetSecurityOrigin()),
+          ExecutionContext::From(script_state)->GetSecurityOrigin()),
       force_close);
   return request;
 }
diff --git a/third_party/WebKit/Source/modules/indexeddb/IDBIndex.cpp b/third_party/WebKit/Source/modules/indexeddb/IDBIndex.cpp
index 69b5cee..d80e150e 100644
--- a/third_party/WebKit/Source/modules/indexeddb/IDBIndex.cpp
+++ b/third_party/WebKit/Source/modules/indexeddb/IDBIndex.cpp
@@ -143,7 +143,7 @@
   WebIDBCursorDirection direction =
       IDBCursor::StringToDirection(direction_string);
   IDBKeyRange* key_range = IDBKeyRange::FromScriptValue(
-      script_state->GetExecutionContext(), range, exception_state);
+      ExecutionContext::From(script_state), range, exception_state);
   if (exception_state.HadException())
     return nullptr;
 
@@ -191,7 +191,7 @@
   }
 
   IDBKeyRange* key_range = IDBKeyRange::FromScriptValue(
-      script_state->GetExecutionContext(), range, exception_state);
+      ExecutionContext::From(script_state), range, exception_state);
   if (exception_state.HadException())
     return nullptr;
 
@@ -233,7 +233,7 @@
   WebIDBCursorDirection direction =
       IDBCursor::StringToDirection(direction_string);
   IDBKeyRange* key_range = IDBKeyRange::FromScriptValue(
-      script_state->GetExecutionContext(), range, exception_state);
+      ExecutionContext::From(script_state), range, exception_state);
   if (exception_state.HadException())
     return nullptr;
   if (!BackendDB()) {
@@ -319,7 +319,7 @@
   }
 
   IDBKeyRange* key_range = IDBKeyRange::FromScriptValue(
-      script_state->GetExecutionContext(), key, exception_state);
+      ExecutionContext::From(script_state), key, exception_state);
   if (exception_state.HadException())
     return nullptr;
   if (!key_range) {
@@ -367,7 +367,7 @@
   }
 
   IDBKeyRange* key_range = IDBKeyRange::FromScriptValue(
-      script_state->GetExecutionContext(), range, exception_state);
+      ExecutionContext::From(script_state), range, exception_state);
   if (exception_state.HadException())
     return nullptr;
   if (!BackendDB()) {
diff --git a/third_party/WebKit/Source/modules/indexeddb/IDBKeyRange.cpp b/third_party/WebKit/Source/modules/indexeddb/IDBKeyRange.cpp
index 6aad5e2..3027290 100644
--- a/third_party/WebKit/Source/modules/indexeddb/IDBKeyRange.cpp
+++ b/third_party/WebKit/Source/modules/indexeddb/IDBKeyRange.cpp
@@ -30,6 +30,7 @@
 #include "bindings/modules/v8/ToV8ForModules.h"
 #include "bindings/modules/v8/V8BindingForModules.h"
 #include "core/dom/ExceptionCode.h"
+#include "core/dom/ExecutionContext.h"
 #include "modules/indexeddb/IDBDatabase.h"
 
 namespace blink {
@@ -94,7 +95,7 @@
                                const ScriptValue& key_value,
                                ExceptionState& exception_state) {
   IDBKey* key =
-      ScriptValue::To<IDBKey*>(ToIsolate(script_state->GetExecutionContext()),
+      ScriptValue::To<IDBKey*>(ToIsolate(ExecutionContext::From(script_state)),
                                key_value, exception_state);
   if (exception_state.HadException())
     return nullptr;
@@ -112,7 +113,7 @@
                                      bool open,
                                      ExceptionState& exception_state) {
   IDBKey* bound =
-      ScriptValue::To<IDBKey*>(ToIsolate(script_state->GetExecutionContext()),
+      ScriptValue::To<IDBKey*>(ToIsolate(ExecutionContext::From(script_state)),
                                bound_value, exception_state);
   if (exception_state.HadException())
     return nullptr;
@@ -132,7 +133,7 @@
                                      bool open,
                                      ExceptionState& exception_state) {
   IDBKey* bound =
-      ScriptValue::To<IDBKey*>(ToIsolate(script_state->GetExecutionContext()),
+      ScriptValue::To<IDBKey*>(ToIsolate(ExecutionContext::From(script_state)),
                                bound_value, exception_state);
   if (exception_state.HadException())
     return nullptr;
@@ -153,7 +154,7 @@
                                 bool upper_open,
                                 ExceptionState& exception_state) {
   IDBKey* lower =
-      ScriptValue::To<IDBKey*>(ToIsolate(script_state->GetExecutionContext()),
+      ScriptValue::To<IDBKey*>(ToIsolate(ExecutionContext::From(script_state)),
                                lower_value, exception_state);
   if (exception_state.HadException())
     return nullptr;
@@ -164,7 +165,7 @@
   }
 
   IDBKey* upper =
-      ScriptValue::To<IDBKey*>(ToIsolate(script_state->GetExecutionContext()),
+      ScriptValue::To<IDBKey*>(ToIsolate(ExecutionContext::From(script_state)),
                                upper_value, exception_state);
   if (exception_state.HadException())
     return nullptr;
@@ -195,7 +196,7 @@
                            const ScriptValue& key_value,
                            ExceptionState& exception_state) {
   IDBKey* key =
-      ScriptValue::To<IDBKey*>(ToIsolate(script_state->GetExecutionContext()),
+      ScriptValue::To<IDBKey*>(ToIsolate(ExecutionContext::From(script_state)),
                                key_value, exception_state);
   if (exception_state.HadException())
     return false;
diff --git a/third_party/WebKit/Source/modules/indexeddb/IDBObjectStore.cpp b/third_party/WebKit/Source/modules/indexeddb/IDBObjectStore.cpp
index 65aff652..48e4aa7 100644
--- a/third_party/WebKit/Source/modules/indexeddb/IDBObjectStore.cpp
+++ b/third_party/WebKit/Source/modules/indexeddb/IDBObjectStore.cpp
@@ -153,7 +153,7 @@
     return nullptr;
   }
   IDBKeyRange* key_range = IDBKeyRange::FromScriptValue(
-      script_state->GetExecutionContext(), key, exception_state);
+      ExecutionContext::From(script_state), key, exception_state);
   if (exception_state.HadException())
     return nullptr;
   if (!key_range) {
@@ -197,7 +197,7 @@
     return nullptr;
   }
   IDBKeyRange* key_range = IDBKeyRange::FromScriptValue(
-      script_state->GetExecutionContext(), key, exception_state);
+      ExecutionContext::From(script_state), key, exception_state);
   if (exception_state.HadException())
     return nullptr;
   if (!key_range) {
@@ -252,7 +252,7 @@
     return nullptr;
   }
   IDBKeyRange* range = IDBKeyRange::FromScriptValue(
-      script_state->GetExecutionContext(), key_range, exception_state);
+      ExecutionContext::From(script_state), key_range, exception_state);
   if (exception_state.HadException())
     return nullptr;
   if (!BackendDB()) {
@@ -302,7 +302,7 @@
     return nullptr;
   }
   IDBKeyRange* range = IDBKeyRange::FromScriptValue(
-      script_state->GetExecutionContext(), key_range, exception_state);
+      ExecutionContext::From(script_state), key_range, exception_state);
   if (exception_state.HadException())
     return nullptr;
   if (!BackendDB()) {
@@ -422,7 +422,7 @@
   SerializedScriptValue::SerializeOptions options;
   options.blob_info = &blob_info;
   options.write_wasm_to_stream =
-      script_state->GetExecutionContext()->IsSecureContext();
+      ExecutionContext::From(script_state)->IsSecureContext();
   RefPtr<SerializedScriptValue> serialized_value =
       SerializedScriptValue::Serialize(isolate, value.V8Value(), options,
                                        exception_state);
@@ -583,7 +583,7 @@
   }
 
   IDBKeyRange* key_range = IDBKeyRange::FromScriptValue(
-      script_state->GetExecutionContext(), key, exception_state);
+      ExecutionContext::From(script_state), key, exception_state);
   if (exception_state.HadException())
     return nullptr;
   if (!key_range) {
@@ -689,7 +689,7 @@
     if (!script_state_->ContextIsValid())
       return;
 
-    DCHECK_EQ(script_state_->GetExecutionContext(), execution_context);
+    DCHECK_EQ(ExecutionContext::From(script_state_.Get()), execution_context);
     DCHECK_EQ(event->type(), EventTypeNames::success);
     EventTarget* target = event->target();
     IDBRequest* request = static_cast<IDBRequest*>(target);
@@ -930,7 +930,7 @@
   WebIDBCursorDirection direction =
       IDBCursor::StringToDirection(direction_string);
   IDBKeyRange* key_range = IDBKeyRange::FromScriptValue(
-      script_state->GetExecutionContext(), range, exception_state);
+      ExecutionContext::From(script_state), range, exception_state);
   if (exception_state.HadException())
     return nullptr;
 
@@ -983,7 +983,7 @@
   WebIDBCursorDirection direction =
       IDBCursor::StringToDirection(direction_string);
   IDBKeyRange* key_range = IDBKeyRange::FromScriptValue(
-      script_state->GetExecutionContext(), range, exception_state);
+      ExecutionContext::From(script_state), range, exception_state);
   if (exception_state.HadException())
     return nullptr;
 
@@ -1027,7 +1027,7 @@
   }
 
   IDBKeyRange* key_range = IDBKeyRange::FromScriptValue(
-      script_state->GetExecutionContext(), range, exception_state);
+      ExecutionContext::From(script_state), range, exception_state);
   if (exception_state.HadException())
     return nullptr;
 
diff --git a/third_party/WebKit/Source/modules/indexeddb/IDBRequest.cpp b/third_party/WebKit/Source/modules/indexeddb/IDBRequest.cpp
index 89472ec..b62eeb02 100644
--- a/third_party/WebKit/Source/modules/indexeddb/IDBRequest.cpp
+++ b/third_party/WebKit/Source/modules/indexeddb/IDBRequest.cpp
@@ -66,7 +66,7 @@
 IDBRequest::IDBRequest(ScriptState* script_state,
                        IDBAny* source,
                        IDBTransaction* transaction)
-    : SuspendableObject(script_state->GetExecutionContext()),
+    : SuspendableObject(ExecutionContext::From(script_state)),
       transaction_(transaction),
       isolate_(script_state->GetIsolate()),
       source_(source) {}
diff --git a/third_party/WebKit/Source/modules/indexeddb/IDBTransaction.cpp b/third_party/WebKit/Source/modules/indexeddb/IDBTransaction.cpp
index 3c9e699..40af722 100644
--- a/third_party/WebKit/Source/modules/indexeddb/IDBTransaction.cpp
+++ b/third_party/WebKit/Source/modules/indexeddb/IDBTransaction.cpp
@@ -125,7 +125,7 @@
                                const HashSet<String>& scope,
                                WebIDBTransactionMode mode,
                                IDBDatabase* db)
-    : ContextLifecycleObserver(script_state->GetExecutionContext()),
+    : ContextLifecycleObserver(ExecutionContext::From(script_state)),
       id_(id),
       database_(db),
       mode_(mode),
diff --git a/third_party/WebKit/Source/modules/indexeddb/InspectorIndexedDBAgent.cpp b/third_party/WebKit/Source/modules/indexeddb/InspectorIndexedDBAgent.cpp
index f7dd18d..880b93e 100644
--- a/third_party/WebKit/Source/modules/indexeddb/InspectorIndexedDBAgent.cpp
+++ b/third_party/WebKit/Source/modules/indexeddb/InspectorIndexedDBAgent.cpp
@@ -37,6 +37,7 @@
 #include "bindings/core/v8/V8PerIsolateData.h"
 #include "core/dom/DOMStringList.h"
 #include "core/dom/Document.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/events/EventListener.h"
 #include "core/frame/LocalFrame.h"
 #include "core/inspector/InspectedFrames.h"
@@ -218,7 +219,7 @@
   virtual void Execute(IDBDatabase*) = 0;
   virtual RequestCallback* GetRequestCallback() = 0;
   ExecutionContext* Context() const {
-    return script_state_->GetExecutionContext();
+    return ExecutionContext::From(script_state_.Get());
   }
   ScriptState* GetScriptState() const { return script_state_.Get(); }
 
@@ -572,7 +573,8 @@
       return;
     }
 
-    Document* document = ToDocument(script_state_->GetExecutionContext());
+    Document* document =
+        ToDocument(ExecutionContext::From(script_state_.Get()));
     if (!document)
       return;
     ScriptState* script_state = script_state_.Get();
diff --git a/third_party/WebKit/Source/modules/installedapp/NavigatorInstalledApp.cpp b/third_party/WebKit/Source/modules/installedapp/NavigatorInstalledApp.cpp
index a666b5c..dfcea97 100644
--- a/third_party/WebKit/Source/modules/installedapp/NavigatorInstalledApp.cpp
+++ b/third_party/WebKit/Source/modules/installedapp/NavigatorInstalledApp.cpp
@@ -11,6 +11,7 @@
 #include "core/dom/DOMException.h"
 #include "core/dom/Document.h"
 #include "core/dom/ExceptionCode.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/frame/LocalDOMWindow.h"
 #include "core/frame/LocalFrame.h"
 #include "core/frame/Navigator.h"
@@ -45,7 +46,7 @@
     ScriptState* script_state,
     Navigator& navigator) {
   // [SecureContext] from the IDL ensures this.
-  DCHECK(script_state->GetExecutionContext()->IsSecureContext());
+  DCHECK(ExecutionContext::From(script_state)->IsSecureContext());
   return NavigatorInstalledApp::From(navigator).getInstalledRelatedApps(
       script_state);
 }
diff --git a/third_party/WebKit/Source/modules/media_capabilities/MediaCapabilities.cpp b/third_party/WebKit/Source/modules/media_capabilities/MediaCapabilities.cpp
index 61926a8..1858f56 100644
--- a/third_party/WebKit/Source/modules/media_capabilities/MediaCapabilities.cpp
+++ b/third_party/WebKit/Source/modules/media_capabilities/MediaCapabilities.cpp
@@ -9,7 +9,7 @@
 #include "bindings/core/v8/ScriptPromiseResolver.h"
 #include "bindings/core/v8/ScriptState.h"
 #include "modules/media_capabilities/MediaCapabilitiesInfo.h"
-#include "modules/media_capabilities/MediaConfiguration.h"
+#include "modules/media_capabilities/MediaDecodingConfiguration.h"
 #include "public/platform/Platform.h"
 #include "public/platform/modules/media_capabilities/WebMediaCapabilitiesClient.h"
 #include "public/platform/modules/media_capabilities/WebMediaCapabilitiesInfo.h"
@@ -23,11 +23,11 @@
     const AudioConfiguration& configuration) {
   WebAudioConfiguration web_configuration;
 
-  // contentType is mandatory.
+  // |contentType| is mandatory.
   DCHECK(configuration.hasContentType());
   web_configuration.content_type = configuration.contentType();
 
-  // channels is optional and will be set to a null WebString if not present.
+  // |channels| is optional and will be set to a null WebString if not present.
   web_configuration.channels = configuration.hasChannels()
                                    ? WebString(configuration.channels())
                                    : WebString();
@@ -65,10 +65,10 @@
 }
 
 WebMediaConfiguration ToWebMediaConfiguration(
-    const MediaConfiguration& configuration) {
+    const MediaDecodingConfiguration& configuration) {
   WebMediaConfiguration web_configuration;
 
-  // type is mandatory.
+  // |type| is mandatory.
   DCHECK(configuration.hasType());
 
   if (configuration.hasAudio()) {
@@ -90,7 +90,7 @@
 
 ScriptPromise MediaCapabilities::decodingInfo(
     ScriptState* script_state,
-    const MediaConfiguration& configuration) {
+    const MediaDecodingConfiguration& configuration) {
   ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
   ScriptPromise promise = resolver->Promise();
 
diff --git a/third_party/WebKit/Source/modules/media_capabilities/MediaCapabilities.h b/third_party/WebKit/Source/modules/media_capabilities/MediaCapabilities.h
index e751cdd0..35d0781 100644
--- a/third_party/WebKit/Source/modules/media_capabilities/MediaCapabilities.h
+++ b/third_party/WebKit/Source/modules/media_capabilities/MediaCapabilities.h
@@ -10,7 +10,7 @@
 
 namespace blink {
 
-class MediaConfiguration;
+class MediaDecodingConfiguration;
 class ScriptPromise;
 class ScriptState;
 
@@ -22,7 +22,7 @@
  public:
   MediaCapabilities();
 
-  ScriptPromise decodingInfo(ScriptState*, const MediaConfiguration&);
+  ScriptPromise decodingInfo(ScriptState*, const MediaDecodingConfiguration&);
 
   DECLARE_VIRTUAL_TRACE();
 };
diff --git a/third_party/WebKit/Source/modules/media_capabilities/MediaCapabilities.idl b/third_party/WebKit/Source/modules/media_capabilities/MediaCapabilities.idl
index bd92ba8..75e767e4 100644
--- a/third_party/WebKit/Source/modules/media_capabilities/MediaCapabilities.idl
+++ b/third_party/WebKit/Source/modules/media_capabilities/MediaCapabilities.idl
@@ -8,5 +8,7 @@
     Exposed=Window,
     RuntimeEnabled=MediaCapabilities
 ] interface MediaCapabilities {
-    [CallWith=ScriptState] Promise<MediaCapabilitiesInfo> decodingInfo(MediaConfiguration configuration);
+    [CallWith=ScriptState] Promise<MediaCapabilitiesInfo> decodingInfo(MediaDecodingConfiguration configuration);
+
+    // TODO(mcasas): Implement encodingInfo(), https://crbug.com/709181
 };
diff --git a/third_party/WebKit/Source/modules/media_capabilities/MediaConfiguration.idl b/third_party/WebKit/Source/modules/media_capabilities/MediaConfiguration.idl
index aa3da1d..afaa0f0e 100644
--- a/third_party/WebKit/Source/modules/media_capabilities/MediaConfiguration.idl
+++ b/third_party/WebKit/Source/modules/media_capabilities/MediaConfiguration.idl
@@ -4,14 +4,7 @@
 
 // https://wicg.github.io/media-capabilities/#dictdef-mediaconfiguration
 
-enum MediaConfigurationType {
-    "file",
-    "media-source",
-};
-
 dictionary MediaConfiguration {
-    required MediaConfigurationType type;
-
     VideoConfiguration video;
     AudioConfiguration audio;
 };
diff --git a/third_party/WebKit/Source/modules/media_capabilities/MediaDecodingConfiguration.idl b/third_party/WebKit/Source/modules/media_capabilities/MediaDecodingConfiguration.idl
new file mode 100644
index 0000000..b794fd6
--- /dev/null
+++ b/third_party/WebKit/Source/modules/media_capabilities/MediaDecodingConfiguration.idl
@@ -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.
+
+// https://wicg.github.io/media-capabilities/#enumdef-mediadecodingtype
+
+enum MediaDecodingType {
+    "file",
+    "media-source",
+};
+
+// https://wicg.github.io/media-capabilities/#dictdef-mediadecodingconfiguration
+
+dictionary MediaDecodingConfiguration : MediaConfiguration {
+    required MediaDecodingType type;
+};
diff --git a/third_party/WebKit/Source/modules/mediasession/MediaMetadata.cpp b/third_party/WebKit/Source/modules/mediasession/MediaMetadata.cpp
index 0b4f151..ca8750ad 100644
--- a/third_party/WebKit/Source/modules/mediasession/MediaMetadata.cpp
+++ b/third_party/WebKit/Source/modules/mediasession/MediaMetadata.cpp
@@ -7,6 +7,7 @@
 #include "bindings/core/v8/ExceptionState.h"
 #include "bindings/core/v8/ScriptState.h"
 #include "bindings/core/v8/ToV8.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/dom/TaskRunnerHelper.h"
 #include "modules/mediasession/MediaImage.h"
 #include "modules/mediasession/MediaMetadataInit.h"
@@ -106,7 +107,7 @@
   HeapVector<MediaImage> processed_artwork(artwork);
 
   for (MediaImage& image : processed_artwork) {
-    KURL url = script_state->GetExecutionContext()->CompleteURL(image.src());
+    KURL url = ExecutionContext::From(script_state)->CompleteURL(image.src());
     if (!url.IsValid()) {
       exception_state.ThrowTypeError("'" + image.src() +
                                      "' can't be resolved to a valid URL.");
diff --git a/third_party/WebKit/Source/modules/mediasession/NavigatorMediaSession.cpp b/third_party/WebKit/Source/modules/mediasession/NavigatorMediaSession.cpp
index e87d64f48..955558b 100644
--- a/third_party/WebKit/Source/modules/mediasession/NavigatorMediaSession.cpp
+++ b/third_party/WebKit/Source/modules/mediasession/NavigatorMediaSession.cpp
@@ -5,6 +5,7 @@
 #include "modules/mediasession/NavigatorMediaSession.h"
 
 #include "bindings/core/v8/ScriptState.h"
+#include "core/dom/ExecutionContext.h"
 #include "modules/mediasession/MediaSession.h"
 #include "platform/Supplementable.h"
 
@@ -36,7 +37,7 @@
                                                   Navigator& navigator) {
   NavigatorMediaSession& self = NavigatorMediaSession::From(navigator);
   if (!self.session_)
-    self.session_ = MediaSession::Create(script_state->GetExecutionContext());
+    self.session_ = MediaSession::Create(ExecutionContext::From(script_state));
   return self.session_.Get();
 }
 
diff --git a/third_party/WebKit/Source/modules/mediasource/URLMediaSource.cpp b/third_party/WebKit/Source/modules/mediasource/URLMediaSource.cpp
index 1885723..03063305 100644
--- a/third_party/WebKit/Source/modules/mediasource/URLMediaSource.cpp
+++ b/third_party/WebKit/Source/modules/mediasource/URLMediaSource.cpp
@@ -32,6 +32,7 @@
 
 #include "bindings/core/v8/ScriptState.h"
 #include "core/dom/DOMURL.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/frame/UseCounter.h"
 #include "modules/mediasource/MediaSource.h"
 
@@ -42,7 +43,7 @@
   // Since WebWorkers cannot obtain MediaSource objects, we should be on the
   // main thread.
   DCHECK(IsMainThread());
-  ExecutionContext* execution_context = script_state->GetExecutionContext();
+  ExecutionContext* execution_context = ExecutionContext::From(script_state);
   DCHECK(execution_context);
   DCHECK(source);
 
diff --git a/third_party/WebKit/Source/modules/mediastream/MediaDevices.cpp b/third_party/WebKit/Source/modules/mediastream/MediaDevices.cpp
index 328a379..e6f8231c 100644
--- a/third_party/WebKit/Source/modules/mediastream/MediaDevices.cpp
+++ b/third_party/WebKit/Source/modules/mediastream/MediaDevices.cpp
@@ -10,6 +10,7 @@
 #include "core/dom/DOMException.h"
 #include "core/dom/Document.h"
 #include "core/dom/ExceptionCode.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/events/Event.h"
 #include "modules/mediastream/MediaErrorState.h"
 #include "modules/mediastream/MediaStream.h"
@@ -78,7 +79,7 @@
 MediaDevices::~MediaDevices() {}
 
 ScriptPromise MediaDevices::enumerateDevices(ScriptState* script_state) {
-  Document* document = ToDocument(script_state->GetExecutionContext());
+  Document* document = ToDocument(ExecutionContext::From(script_state));
   UserMediaController* user_media =
       UserMediaController::From(document->GetFrame());
   if (!user_media)
@@ -103,7 +104,7 @@
   NavigatorUserMediaErrorCallback* error_callback =
       new PromiseErrorCallback(resolver);
 
-  Document* document = ToDocument(script_state->GetExecutionContext());
+  Document* document = ToDocument(ExecutionContext::From(script_state));
   UserMediaController* user_media =
       UserMediaController::From(document->GetFrame());
   if (!user_media)
diff --git a/third_party/WebKit/Source/modules/mediastream/MediaStream.cpp b/third_party/WebKit/Source/modules/mediastream/MediaStream.cpp
index 8ca5d1a8..2b52b45 100644
--- a/third_party/WebKit/Source/modules/mediastream/MediaStream.cpp
+++ b/third_party/WebKit/Source/modules/mediastream/MediaStream.cpp
@@ -278,7 +278,7 @@
 
 MediaStream* MediaStream::clone(ScriptState* script_state) {
   MediaStreamTrackVector tracks;
-  ExecutionContext* context = script_state->GetExecutionContext();
+  ExecutionContext* context = ExecutionContext::From(script_state);
   for (MediaStreamTrackVector::iterator iter = audio_tracks_.begin();
        iter != audio_tracks_.end(); ++iter)
     tracks.push_back((*iter)->clone(script_state));
diff --git a/third_party/WebKit/Source/modules/mediastream/MediaStreamTrack.cpp b/third_party/WebKit/Source/modules/mediastream/MediaStreamTrack.cpp
index 6b119c4..8f276012 100644
--- a/third_party/WebKit/Source/modules/mediastream/MediaStreamTrack.cpp
+++ b/third_party/WebKit/Source/modules/mediastream/MediaStreamTrack.cpp
@@ -211,7 +211,7 @@
   // tracks.
   MediaStreamComponent* cloned_component = Component()->Clone();
   MediaStreamTrack* cloned_track = MediaStreamTrack::Create(
-      script_state->GetExecutionContext(), cloned_component);
+      ExecutionContext::From(script_state), cloned_component);
   MediaStreamCenter::Instance().DidCloneMediaStreamTrack(Component(),
                                                          cloned_component);
   return cloned_track;
diff --git a/third_party/WebKit/Source/modules/mediastream/URLMediaStream.cpp b/third_party/WebKit/Source/modules/mediastream/URLMediaStream.cpp
index f6e5ef4..ff0a346 100644
--- a/third_party/WebKit/Source/modules/mediastream/URLMediaStream.cpp
+++ b/third_party/WebKit/Source/modules/mediastream/URLMediaStream.cpp
@@ -32,6 +32,7 @@
 
 #include "bindings/core/v8/ScriptState.h"
 #include "core/dom/DOMURL.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/frame/UseCounter.h"
 #include "modules/mediastream/MediaStream.h"
 
@@ -42,7 +43,7 @@
   // Since WebWorkers cannot obtain Stream objects, we should be on the main
   // thread.
   DCHECK(IsMainThread());
-  ExecutionContext* execution_context = script_state->GetExecutionContext();
+  ExecutionContext* execution_context = ExecutionContext::From(script_state);
   DCHECK(execution_context);
   DCHECK(stream);
 
diff --git a/third_party/WebKit/Source/modules/modules_idl_files.gni b/third_party/WebKit/Source/modules/modules_idl_files.gni
index 7af8a57..15cfb772 100644
--- a/third_party/WebKit/Source/modules/modules_idl_files.gni
+++ b/third_party/WebKit/Source/modules/modules_idl_files.gni
@@ -447,6 +447,7 @@
                     "indexeddb/IDBVersionChangeEventInit.idl",
                     "media_capabilities/AudioConfiguration.idl",
                     "media_capabilities/MediaConfiguration.idl",
+                    "media_capabilities/MediaDecodingConfiguration.idl",
                     "media_capabilities/VideoConfiguration.idl",
                     "mediarecorder/BlobEventInit.idl",
                     "mediarecorder/MediaRecorderOptions.idl",
@@ -795,6 +796,8 @@
   "$blink_modules_output_dir/media_capabilities/AudioConfiguration.h",
   "$blink_modules_output_dir/media_capabilities/MediaConfiguration.cpp",
   "$blink_modules_output_dir/media_capabilities/MediaConfiguration.h",
+  "$blink_modules_output_dir/media_capabilities/MediaDecodingConfiguration.cpp",
+  "$blink_modules_output_dir/media_capabilities/MediaDecodingConfiguration.h",
   "$blink_modules_output_dir/media_capabilities/VideoConfiguration.cpp",
   "$blink_modules_output_dir/media_capabilities/VideoConfiguration.h",
   "$blink_modules_output_dir/mediarecorder/BlobEventInit.cpp",
diff --git a/third_party/WebKit/Source/modules/netinfo/WorkerNavigatorNetworkInformation.cpp b/third_party/WebKit/Source/modules/netinfo/WorkerNavigatorNetworkInformation.cpp
index f206037..52d27bcb 100644
--- a/third_party/WebKit/Source/modules/netinfo/WorkerNavigatorNetworkInformation.cpp
+++ b/third_party/WebKit/Source/modules/netinfo/WorkerNavigatorNetworkInformation.cpp
@@ -5,6 +5,7 @@
 #include "modules/netinfo/WorkerNavigatorNetworkInformation.h"
 
 #include "bindings/core/v8/ScriptState.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/workers/WorkerNavigator.h"
 #include "modules/netinfo/NetworkInformation.h"
 
@@ -42,7 +43,7 @@
 NetworkInformation* WorkerNavigatorNetworkInformation::connection(
     ScriptState* script_state,
     WorkerNavigator& navigator) {
-  ExecutionContext* context = script_state->GetExecutionContext();
+  ExecutionContext* context = ExecutionContext::From(script_state);
   return WorkerNavigatorNetworkInformation::From(navigator, context)
       .connection(context);
 }
diff --git a/third_party/WebKit/Source/modules/nfc/NFC.cpp b/third_party/WebKit/Source/modules/nfc/NFC.cpp
index 52a9ccd..9e21af2d 100644
--- a/third_party/WebKit/Source/modules/nfc/NFC.cpp
+++ b/third_party/WebKit/Source/modules/nfc/NFC.cpp
@@ -11,6 +11,7 @@
 #include "core/dom/DOMException.h"
 #include "core/dom/Document.h"
 #include "core/dom/ExceptionCode.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/frame/LocalDOMWindow.h"
 #include "modules/nfc/NFCError.h"
 #include "modules/nfc/NFCMessage.h"
@@ -624,7 +625,7 @@
         script_state, DOMException::Create(kSyntaxError));
 
   if (!SetURL(
-          script_state->GetExecutionContext()->GetSecurityOrigin()->ToString(),
+          ExecutionContext::From(script_state)->GetSecurityOrigin()->ToString(),
           message))
     return ScriptPromise::RejectWithDOMException(
         script_state, DOMException::Create(kSyntaxError));
@@ -775,7 +776,7 @@
 
 ScriptPromise NFC::RejectIfNotSupported(ScriptState* script_state) {
   String error_message;
-  ExecutionContext* context = script_state->GetExecutionContext();
+  ExecutionContext* context = ExecutionContext::From(script_state);
   if (!context->IsSecureContext(error_message)) {
     return ScriptPromise::RejectWithDOMException(
         script_state, DOMException::Create(kSecurityError, error_message));
diff --git a/third_party/WebKit/Source/modules/notifications/Notification.cpp b/third_party/WebKit/Source/modules/notifications/Notification.cpp
index 0b801f6..230146f 100644
--- a/third_party/WebKit/Source/modules/notifications/Notification.cpp
+++ b/third_party/WebKit/Source/modules/notifications/Notification.cpp
@@ -355,7 +355,7 @@
 }
 
 String Notification::permission(ScriptState* script_state) {
-  ExecutionContext* context = script_state->GetExecutionContext();
+  ExecutionContext* context = ExecutionContext::From(script_state);
   return PermissionString(
       NotificationManager::From(context)->GetPermissionStatus(context));
 }
@@ -363,7 +363,7 @@
 ScriptPromise Notification::requestPermission(
     ScriptState* script_state,
     NotificationPermissionCallback* deprecated_callback) {
-  ExecutionContext* context = script_state->GetExecutionContext();
+  ExecutionContext* context = ExecutionContext::From(script_state);
   if (!context->IsSecureContext()) {
     Deprecation::CountDeprecation(
         context, UseCounter::kNotificationPermissionRequestedInsecureOrigin);
diff --git a/third_party/WebKit/Source/modules/notifications/NotificationManager.cpp b/third_party/WebKit/Source/modules/notifications/NotificationManager.cpp
index 2a585106..fa84d72 100644
--- a/third_party/WebKit/Source/modules/notifications/NotificationManager.cpp
+++ b/third_party/WebKit/Source/modules/notifications/NotificationManager.cpp
@@ -63,7 +63,7 @@
 ScriptPromise NotificationManager::RequestPermission(
     ScriptState* script_state,
     NotificationPermissionCallback* deprecated_callback) {
-  ExecutionContext* context = script_state->GetExecutionContext();
+  ExecutionContext* context = ExecutionContext::From(script_state);
 
   if (!permission_service_) {
     ConnectToPermissionService(context,
diff --git a/third_party/WebKit/Source/modules/notifications/ServiceWorkerRegistrationNotifications.cpp b/third_party/WebKit/Source/modules/notifications/ServiceWorkerRegistrationNotifications.cpp
index f9cf60c..c8cdd80 100644
--- a/third_party/WebKit/Source/modules/notifications/ServiceWorkerRegistrationNotifications.cpp
+++ b/third_party/WebKit/Source/modules/notifications/ServiceWorkerRegistrationNotifications.cpp
@@ -66,7 +66,7 @@
     const String& title,
     const NotificationOptions& options,
     ExceptionState& exception_state) {
-  ExecutionContext* execution_context = script_state->GetExecutionContext();
+  ExecutionContext* execution_context = ExecutionContext::From(script_state);
 
   // If context object's active worker is null, reject the promise with a
   // TypeError exception.
diff --git a/third_party/WebKit/Source/modules/offscreencanvas/OffscreenCanvasModules.cpp b/third_party/WebKit/Source/modules/offscreencanvas/OffscreenCanvasModules.cpp
index 9b20958..92e8b6d 100644
--- a/third_party/WebKit/Source/modules/offscreencanvas/OffscreenCanvasModules.cpp
+++ b/third_party/WebKit/Source/modules/offscreencanvas/OffscreenCanvasModules.cpp
@@ -4,6 +4,7 @@
 
 #include "modules/offscreencanvas/OffscreenCanvasModules.h"
 
+#include "core/dom/ExecutionContext.h"
 #include "core/html/canvas/CanvasContextCreationAttributes.h"
 #include "core/offscreencanvas/OffscreenCanvas.h"
 #include "modules/offscreencanvas2d/OffscreenCanvasRenderingContext2D.h"
@@ -25,7 +26,7 @@
 
   // OffscreenCanvas cannot be transferred after getContext, so this execution
   // context will always be the right one from here on.
-  offscreen_canvas.SetExecutionContext(script_state->GetExecutionContext());
+  offscreen_canvas.SetExecutionContext(ExecutionContext::From(script_state));
   CanvasRenderingContext* context =
       offscreen_canvas.GetCanvasRenderingContext(script_state, id, attributes);
   if (context)
diff --git a/third_party/WebKit/Source/modules/offscreencanvas2d/OffscreenCanvasRenderingContext2D.cpp b/third_party/WebKit/Source/modules/offscreencanvas2d/OffscreenCanvasRenderingContext2D.cpp
index 1f40dd1..a4b1297 100644
--- a/third_party/WebKit/Source/modules/offscreencanvas2d/OffscreenCanvasRenderingContext2D.cpp
+++ b/third_party/WebKit/Source/modules/offscreencanvas2d/OffscreenCanvasRenderingContext2D.cpp
@@ -5,6 +5,7 @@
 #include "modules/offscreencanvas2d/OffscreenCanvasRenderingContext2D.h"
 
 #include "bindings/modules/v8/OffscreenCanvasRenderingContext2DOrWebGLRenderingContextOrWebGL2RenderingContext.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/frame/ImageBitmap.h"
 #include "core/frame/Settings.h"
 #include "core/workers/WorkerGlobalScope.h"
@@ -26,7 +27,7 @@
     OffscreenCanvas* canvas,
     const CanvasContextCreationAttributes& attrs)
     : CanvasRenderingContext(nullptr, canvas, attrs) {
-  ExecutionContext* execution_context = script_state->GetExecutionContext();
+  ExecutionContext* execution_context = ExecutionContext::From(script_state);
   if (execution_context->IsDocument()) {
     if (ToDocument(execution_context)
             ->GetSettings()
@@ -50,7 +51,7 @@
     ScriptState* script_state,
     ExceptionState& exception_state) {
   UseCounter::Feature feature = UseCounter::kOffscreenCanvasCommit2D;
-  UseCounter::Count(script_state->GetExecutionContext(), feature);
+  UseCounter::Count(ExecutionContext::From(script_state), feature);
   if (!offscreenCanvas()->HasPlaceholderCanvas()) {
     // If an OffscreenCanvas has no associated canvas Id, it indicates that
     // it is not an OffscreenCanvas created by transfering control from html
@@ -157,7 +158,7 @@
     ScriptState* script_state) {
   UseCounter::Feature feature =
       UseCounter::kOffscreenCanvasTransferToImageBitmap2D;
-  UseCounter::Count(script_state->GetExecutionContext(), feature);
+  UseCounter::Count(ExecutionContext::From(script_state), feature);
   RefPtr<StaticBitmapImage> image = TransferToStaticBitmapImage();
   if (!image)
     return nullptr;
diff --git a/third_party/WebKit/Source/modules/peerconnection/RTCPeerConnection.cpp b/third_party/WebKit/Source/modules/peerconnection/RTCPeerConnection.cpp
index 569b12a..dd52150 100644
--- a/third_party/WebKit/Source/modules/peerconnection/RTCPeerConnection.cpp
+++ b/third_party/WebKit/Source/modules/peerconnection/RTCPeerConnection.cpp
@@ -400,7 +400,7 @@
 
   ~WebRTCStatsReportCallbackResolver() override {
     DCHECK(
-        resolver_->GetScriptState()->GetExecutionContext()->IsContextThread());
+        ExecutionContext::From(resolver_->GetScriptState())->IsContextThread());
   }
 
  private:
@@ -409,7 +409,7 @@
 
   void OnStatsDelivered(std::unique_ptr<WebRTCStatsReport> report) override {
     DCHECK(
-        resolver_->GetScriptState()->GetExecutionContext()->IsContextThread());
+        ExecutionContext::From(resolver_->GetScriptState())->IsContextThread());
     resolver_->Resolve(new RTCStatsReport(std::move(report)));
   }
 
@@ -564,7 +564,7 @@
   RTCSessionDescriptionRequest* request =
       RTCSessionDescriptionRequestPromiseImpl::Create(this, resolver);
   if (options.hasOfferToReceiveAudio() || options.hasOfferToReceiveVideo()) {
-    ExecutionContext* context = script_state->GetExecutionContext();
+    ExecutionContext* context = ExecutionContext::From(script_state);
     UseCounter::Count(
         context,
         UseCounter::kRTCPeerConnectionCreateOfferOptionsOfferToReceive);
@@ -581,7 +581,7 @@
     ExceptionState& exception_state) {
   DCHECK(success_callback);
   DCHECK(error_callback);
-  ExecutionContext* context = script_state->GetExecutionContext();
+  ExecutionContext* context = ExecutionContext::From(script_state);
   UseCounter::Count(
       context, UseCounter::kRTCPeerConnectionCreateOfferLegacyFailureCallback);
   if (CallErrorCallbackIfSignalingStateClosed(signaling_state_, error_callback))
@@ -654,7 +654,7 @@
     const Dictionary& media_constraints) {
   DCHECK(success_callback);
   DCHECK(error_callback);
-  ExecutionContext* context = script_state->GetExecutionContext();
+  ExecutionContext* context = ExecutionContext::From(script_state);
   UseCounter::Count(
       context, UseCounter::kRTCPeerConnectionCreateAnswerLegacyFailureCallback);
   if (media_constraints.IsObject())
@@ -709,7 +709,7 @@
     const RTCSessionDescriptionInit& session_description_init,
     VoidCallback* success_callback,
     RTCPeerConnectionErrorCallback* error_callback) {
-  ExecutionContext* context = script_state->GetExecutionContext();
+  ExecutionContext* context = ExecutionContext::From(script_state);
   if (success_callback && error_callback) {
     UseCounter::Count(
         context,
@@ -769,7 +769,7 @@
     const RTCSessionDescriptionInit& session_description_init,
     VoidCallback* success_callback,
     RTCPeerConnectionErrorCallback* error_callback) {
-  ExecutionContext* context = script_state->GetExecutionContext();
+  ExecutionContext* context = ExecutionContext::From(script_state);
   if (success_callback && error_callback) {
     UseCounter::Count(
         context,
@@ -815,7 +815,7 @@
     return;
 
   WebRTCConfiguration configuration = ParseConfiguration(
-      script_state->GetExecutionContext(), rtc_configuration, exception_state);
+      ExecutionContext::From(script_state), rtc_configuration, exception_state);
 
   if (exception_state.HadException())
     return;
@@ -982,7 +982,7 @@
   ScriptPromise promise = resolver->Promise();
   RTCVoidRequest* request = RTCVoidRequestPromiseImpl::Create(this, resolver);
   WebRTCICECandidate web_candidate = ConvertToWebRTCIceCandidate(
-      script_state->GetExecutionContext(), candidate);
+      ExecutionContext::From(script_state), candidate);
   bool implemented = peer_handler_->AddICECandidate(request, web_candidate);
   if (!implemented)
     resolver->Reject(DOMException::Create(
@@ -1012,7 +1012,7 @@
   RTCVoidRequest* request = RTCVoidRequestImpl::Create(
       GetExecutionContext(), this, success_callback, error_callback);
   WebRTCICECandidate web_candidate = ConvertToWebRTCIceCandidate(
-      script_state->GetExecutionContext(), candidate);
+      ExecutionContext::From(script_state), candidate);
   bool implemented = peer_handler_->AddICECandidate(request, web_candidate);
   if (!implemented)
     AsyncCallErrorCallback(
@@ -1098,7 +1098,7 @@
 
   MediaErrorState media_error_state;
   WebMediaConstraints constraints =
-      MediaConstraintsImpl::Create(script_state->GetExecutionContext(),
+      MediaConstraintsImpl::Create(ExecutionContext::From(script_state),
                                    media_constraints, media_error_state);
   if (media_error_state.HadException()) {
     media_error_state.RaiseException(exception_state);
@@ -1161,7 +1161,7 @@
 ScriptPromise RTCPeerConnection::getStats(ScriptState* script_state,
                                           RTCStatsCallback* success_callback,
                                           MediaStreamTrack* selector) {
-  ExecutionContext* context = script_state->GetExecutionContext();
+  ExecutionContext* context = ExecutionContext::From(script_state);
   ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
   ScriptPromise promise = resolver->Promise();
 
@@ -1177,7 +1177,7 @@
 }
 
 ScriptPromise RTCPeerConnection::getStats(ScriptState* script_state) {
-  ExecutionContext* context = script_state->GetExecutionContext();
+  ExecutionContext* context = ExecutionContext::From(script_state);
   UseCounter::Count(context, UseCounter::kRTCPeerConnectionGetStats);
 
   ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
@@ -1224,7 +1224,7 @@
   DictionaryHelper::Get(options, "negotiated", init.negotiated);
 
   unsigned short value = 0;
-  ExecutionContext* context = script_state->GetExecutionContext();
+  ExecutionContext* context = ExecutionContext::From(script_state);
   if (DictionaryHelper::Get(options, "id", value))
     init.id = value;
   if (DictionaryHelper::Get(options, "maxRetransmits", value)) {
diff --git a/third_party/WebKit/Source/modules/permissions/Permissions.cpp b/third_party/WebKit/Source/modules/permissions/Permissions.cpp
index 33e84e59..f9a49940 100644
--- a/third_party/WebKit/Source/modules/permissions/Permissions.cpp
+++ b/third_party/WebKit/Source/modules/permissions/Permissions.cpp
@@ -15,6 +15,7 @@
 #include "core/dom/DOMException.h"
 #include "core/dom/Document.h"
 #include "core/dom/ExceptionCode.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/frame/LocalFrame.h"
 #include "modules/permissions/PermissionDescriptor.h"
 #include "modules/permissions/PermissionStatus.h"
@@ -108,7 +109,7 @@
 
   // This must be called after `parsePermission` because the website might
   // be able to run code.
-  PermissionService* service = GetService(script_state->GetExecutionContext());
+  PermissionService* service = GetService(ExecutionContext::From(script_state));
   if (!service)
     return ScriptPromise::RejectWithDOMException(
         script_state,
@@ -126,7 +127,7 @@
   PermissionDescriptorPtr descriptor_copy = descriptor->Clone();
   service->HasPermission(
       std::move(descriptor),
-      script_state->GetExecutionContext()->GetSecurityOrigin(),
+      ExecutionContext::From(script_state)->GetSecurityOrigin(),
       ConvertToBaseCallback(WTF::Bind(
           &Permissions::TaskComplete, WrapPersistent(this),
           WrapPersistent(resolver), WTF::Passed(std::move(descriptor_copy)))));
@@ -145,7 +146,7 @@
 
   // This must be called after `parsePermission` because the website might
   // be able to run code.
-  PermissionService* service = GetService(script_state->GetExecutionContext());
+  PermissionService* service = GetService(ExecutionContext::From(script_state));
   if (!service)
     return ScriptPromise::RejectWithDOMException(
         script_state, DOMException::Create(kInvalidStateError,
@@ -158,7 +159,7 @@
   PermissionDescriptorPtr descriptor_copy = descriptor->Clone();
   service->RequestPermission(
       std::move(descriptor),
-      script_state->GetExecutionContext()->GetSecurityOrigin(),
+      ExecutionContext::From(script_state)->GetSecurityOrigin(),
       UserGestureIndicator::ProcessingUserGestureThreadSafe(),
       ConvertToBaseCallback(WTF::Bind(
           &Permissions::TaskComplete, WrapPersistent(this),
@@ -178,7 +179,7 @@
 
   // This must be called after `parsePermission` because the website might
   // be able to run code.
-  PermissionService* service = GetService(script_state->GetExecutionContext());
+  PermissionService* service = GetService(ExecutionContext::From(script_state));
   if (!service)
     return ScriptPromise::RejectWithDOMException(
         script_state, DOMException::Create(kInvalidStateError,
@@ -191,7 +192,7 @@
   PermissionDescriptorPtr descriptor_copy = descriptor->Clone();
   service->RevokePermission(
       std::move(descriptor),
-      script_state->GetExecutionContext()->GetSecurityOrigin(),
+      ExecutionContext::From(script_state)->GetSecurityOrigin(),
       ConvertToBaseCallback(WTF::Bind(
           &Permissions::TaskComplete, WrapPersistent(this),
           WrapPersistent(resolver), WTF::Passed(std::move(descriptor_copy)))));
@@ -232,7 +233,7 @@
 
   // This must be called after `parsePermission` because the website might
   // be able to run code.
-  PermissionService* service = GetService(script_state->GetExecutionContext());
+  PermissionService* service = GetService(ExecutionContext::From(script_state));
   if (!service)
     return ScriptPromise::RejectWithDOMException(
         script_state, DOMException::Create(kInvalidStateError,
@@ -249,7 +250,7 @@
 
   service->RequestPermissions(
       std::move(internal_permissions),
-      script_state->GetExecutionContext()->GetSecurityOrigin(),
+      ExecutionContext::From(script_state)->GetSecurityOrigin(),
       UserGestureIndicator::ProcessingUserGestureThreadSafe(),
       ConvertToBaseCallback(
           WTF::Bind(&Permissions::BatchTaskComplete, WrapPersistent(this),
diff --git a/third_party/WebKit/Source/modules/presentation/PresentationReceiver.cpp b/third_party/WebKit/Source/modules/presentation/PresentationReceiver.cpp
index 064ca77..a0df1f3 100644
--- a/third_party/WebKit/Source/modules/presentation/PresentationReceiver.cpp
+++ b/third_party/WebKit/Source/modules/presentation/PresentationReceiver.cpp
@@ -9,6 +9,7 @@
 #include "core/dom/DOMException.h"
 #include "core/dom/Document.h"
 #include "core/dom/ExceptionCode.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/frame/LocalDOMWindow.h"
 #include "core/frame/LocalFrame.h"
 #include "core/frame/Navigator.h"
@@ -46,7 +47,7 @@
 ScriptPromise PresentationReceiver::connectionList(ScriptState* script_state) {
   if (!connection_list_property_)
     connection_list_property_ =
-        new ConnectionListProperty(script_state->GetExecutionContext(), this,
+        new ConnectionListProperty(ExecutionContext::From(script_state), this,
                                    ConnectionListProperty::kReady);
 
   if (!connection_list_->IsEmpty() && connection_list_property_->GetState() ==
diff --git a/third_party/WebKit/Source/modules/presentation/PresentationRequest.cpp b/third_party/WebKit/Source/modules/presentation/PresentationRequest.cpp
index 054cd2d..58eb8dfe 100644
--- a/third_party/WebKit/Source/modules/presentation/PresentationRequest.cpp
+++ b/third_party/WebKit/Source/modules/presentation/PresentationRequest.cpp
@@ -198,7 +198,7 @@
 
   if (!availability_property_) {
     availability_property_ = new PresentationAvailabilityProperty(
-        script_state->GetExecutionContext(), this,
+        ExecutionContext::From(script_state), this,
         PresentationAvailabilityProperty::kReady);
 
     client->GetAvailability(urls_,
diff --git a/third_party/WebKit/Source/modules/push_messaging/PushManager.cpp b/third_party/WebKit/Source/modules/push_messaging/PushManager.cpp
index 0add47fc..454a57fa 100644
--- a/third_party/WebKit/Source/modules/push_messaging/PushManager.cpp
+++ b/third_party/WebKit/Source/modules/push_messaging/PushManager.cpp
@@ -62,8 +62,8 @@
   // The document context is the only reasonable context from which to ask the
   // user for permission to use the Push API. The embedder should persist the
   // permission so that later calls in different contexts can succeed.
-  if (script_state->GetExecutionContext()->IsDocument()) {
-    Document* document = ToDocument(script_state->GetExecutionContext());
+  if (ExecutionContext::From(script_state)->IsDocument()) {
+    Document* document = ToDocument(ExecutionContext::From(script_state));
     if (!document->domWindow() || !document->GetFrame())
       return ScriptPromise::RejectWithDOMException(
           script_state,
@@ -96,8 +96,8 @@
     ScriptState* script_state,
     const PushSubscriptionOptionsInit& options,
     ExceptionState& exception_state) {
-  if (script_state->GetExecutionContext()->IsDocument()) {
-    Document* document = ToDocument(script_state->GetExecutionContext());
+  if (ExecutionContext::From(script_state)->IsDocument()) {
+    Document* document = ToDocument(ExecutionContext::From(script_state));
     if (!document->domWindow() || !document->GetFrame())
       return ScriptPromise::RejectWithDOMException(
           script_state,
diff --git a/third_party/WebKit/Source/modules/quota/DeprecatedStorageQuota.cpp b/third_party/WebKit/Source/modules/quota/DeprecatedStorageQuota.cpp
index ed0f0e4f..fd66c2e 100644
--- a/third_party/WebKit/Source/modules/quota/DeprecatedStorageQuota.cpp
+++ b/third_party/WebKit/Source/modules/quota/DeprecatedStorageQuota.cpp
@@ -55,7 +55,7 @@
     ScriptState* script_state,
     StorageUsageCallback* success_callback,
     StorageErrorCallback* error_callback) {
-  ExecutionContext* execution_context = script_state->GetExecutionContext();
+  ExecutionContext* execution_context = ExecutionContext::From(script_state);
   DCHECK(execution_context);
 
   WebStorageQuotaType storage_type = static_cast<WebStorageQuotaType>(type_);
@@ -89,7 +89,7 @@
     unsigned long long new_quota_in_bytes,
     StorageQuotaCallback* success_callback,
     StorageErrorCallback* error_callback) {
-  ExecutionContext* execution_context = script_state->GetExecutionContext();
+  ExecutionContext* execution_context = ExecutionContext::From(script_state);
   DCHECK(execution_context);
 
   WebStorageQuotaType storage_type = static_cast<WebStorageQuotaType>(type_);
diff --git a/third_party/WebKit/Source/modules/quota/StorageManager.cpp b/third_party/WebKit/Source/modules/quota/StorageManager.cpp
index 507ad4a..03c84ae1e 100644
--- a/third_party/WebKit/Source/modules/quota/StorageManager.cpp
+++ b/third_party/WebKit/Source/modules/quota/StorageManager.cpp
@@ -9,6 +9,7 @@
 #include "core/dom/DOMException.h"
 #include "core/dom/Document.h"
 #include "core/dom/ExceptionCode.h"
+#include "core/dom/ExecutionContext.h"
 #include "modules/permissions/PermissionUtils.h"
 #include "modules/quota/StorageEstimate.h"
 #include "platform/StorageQuotaCallbacks.h"
@@ -63,7 +64,7 @@
 ScriptPromise StorageManager::persist(ScriptState* script_state) {
   ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
   ScriptPromise promise = resolver->Promise();
-  ExecutionContext* execution_context = script_state->GetExecutionContext();
+  ExecutionContext* execution_context = ExecutionContext::From(script_state);
   DCHECK(execution_context->IsSecureContext());  // [SecureContext] in IDL
   SecurityOrigin* security_origin = execution_context->GetSecurityOrigin();
   if (security_origin->IsUnique()) {
@@ -74,7 +75,7 @@
 
   DCHECK(execution_context->IsDocument());
   PermissionService* permission_service =
-      GetPermissionService(script_state->GetExecutionContext());
+      GetPermissionService(ExecutionContext::From(script_state));
   if (!permission_service) {
     resolver->Reject(DOMException::Create(
         kInvalidStateError,
@@ -83,7 +84,7 @@
   }
   permission_service->RequestPermission(
       CreatePermissionDescriptor(PermissionName::DURABLE_STORAGE),
-      script_state->GetExecutionContext()->GetSecurityOrigin(),
+      ExecutionContext::From(script_state)->GetSecurityOrigin(),
       UserGestureIndicator::ProcessingUserGesture(),
       ConvertToBaseCallback(
           WTF::Bind(&StorageManager::PermissionRequestComplete,
@@ -95,7 +96,7 @@
 ScriptPromise StorageManager::persisted(ScriptState* script_state) {
   ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
   ScriptPromise promise = resolver->Promise();
-  ExecutionContext* execution_context = script_state->GetExecutionContext();
+  ExecutionContext* execution_context = ExecutionContext::From(script_state);
   DCHECK(execution_context->IsSecureContext());  // [SecureContext] in IDL
   SecurityOrigin* security_origin = execution_context->GetSecurityOrigin();
   if (security_origin->IsUnique()) {
@@ -105,7 +106,7 @@
   }
 
   PermissionService* permission_service =
-      GetPermissionService(script_state->GetExecutionContext());
+      GetPermissionService(ExecutionContext::From(script_state));
   if (!permission_service) {
     resolver->Reject(DOMException::Create(
         kInvalidStateError,
@@ -114,7 +115,7 @@
   }
   permission_service->HasPermission(
       CreatePermissionDescriptor(PermissionName::DURABLE_STORAGE),
-      script_state->GetExecutionContext()->GetSecurityOrigin(),
+      ExecutionContext::From(script_state)->GetSecurityOrigin(),
       ConvertToBaseCallback(
           WTF::Bind(&StorageManager::PermissionRequestComplete,
                     WrapPersistent(this), WrapPersistent(resolver))));
@@ -124,7 +125,7 @@
 ScriptPromise StorageManager::estimate(ScriptState* script_state) {
   ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
   ScriptPromise promise = resolver->Promise();
-  ExecutionContext* execution_context = script_state->GetExecutionContext();
+  ExecutionContext* execution_context = ExecutionContext::From(script_state);
   DCHECK(execution_context->IsSecureContext());  // [SecureContext] in IDL
   SecurityOrigin* security_origin = execution_context->GetSecurityOrigin();
   if (security_origin->IsUnique()) {
diff --git a/third_party/WebKit/Source/modules/serviceworkers/FetchEvent.cpp b/third_party/WebKit/Source/modules/serviceworkers/FetchEvent.cpp
index d714e9e..04f19ac 100644
--- a/third_party/WebKit/Source/modules/serviceworkers/FetchEvent.cpp
+++ b/third_party/WebKit/Source/modules/serviceworkers/FetchEvent.cpp
@@ -7,6 +7,7 @@
 #include "bindings/core/v8/ScriptState.h"
 #include "bindings/core/v8/ToV8.h"
 #include "bindings/core/v8/V8PrivateProperty.h"
+#include "core/dom/ExecutionContext.h"
 #include "modules/fetch/BytesConsumerForDataConsumerHandle.h"
 #include "modules/fetch/Request.h"
 #include "modules/fetch/Response.h"
@@ -75,7 +76,7 @@
     : ExtendableEvent(type, initializer, wait_until_observer),
       observer_(respond_with_observer),
       preload_response_property_(new PreloadResponseProperty(
-          script_state->GetExecutionContext(),
+          ExecutionContext::From(script_state),
           this,
           PreloadResponseProperty::kPreloadResponse)) {
   if (!navigation_preload_sent)
@@ -118,7 +119,7 @@
       data_consume_handle
           ? FetchResponseData::CreateWithBuffer(new BodyStreamBuffer(
                 script_state, new BytesConsumerForDataConsumerHandle(
-                                  script_state->GetExecutionContext(),
+                                  ExecutionContext::From(script_state),
                                   std::move(data_consume_handle))))
           : FetchResponseData::Create();
   Vector<KURL> url_list(1);
@@ -137,7 +138,7 @@
           ? response_data->CreateOpaqueRedirectFilteredResponse()
           : response_data->CreateBasicFilteredResponse();
   preload_response_property_->Resolve(
-      Response::Create(script_state->GetExecutionContext(), tainted_response));
+      Response::Create(ExecutionContext::From(script_state), tainted_response));
 }
 
 void FetchEvent::OnNavigationPreloadError(
diff --git a/third_party/WebKit/Source/modules/serviceworkers/InstallEvent.cpp b/third_party/WebKit/Source/modules/serviceworkers/InstallEvent.cpp
index e91c724..c09c82e 100644
--- a/third_party/WebKit/Source/modules/serviceworkers/InstallEvent.cpp
+++ b/third_party/WebKit/Source/modules/serviceworkers/InstallEvent.cpp
@@ -6,6 +6,7 @@
 
 #include "bindings/core/v8/ScriptState.h"
 #include "core/dom/ExceptionCode.h"
+#include "core/dom/ExecutionContext.h"
 #include "modules/serviceworkers/ServiceWorkerGlobalScopeClient.h"
 #include "public/platform/WebSecurityOrigin.h"
 
@@ -56,7 +57,7 @@
     }
   }
 
-  ExecutionContext* execution_context = script_state->GetExecutionContext();
+  ExecutionContext* execution_context = ExecutionContext::From(script_state);
   ServiceWorkerGlobalScopeClient* client =
       ServiceWorkerGlobalScopeClient::From(execution_context);
 
diff --git a/third_party/WebKit/Source/modules/serviceworkers/NavigatorServiceWorker.cpp b/third_party/WebKit/Source/modules/serviceworkers/NavigatorServiceWorker.cpp
index 0f1f3e2..7efc1f2 100644
--- a/third_party/WebKit/Source/modules/serviceworkers/NavigatorServiceWorker.cpp
+++ b/third_party/WebKit/Source/modules/serviceworkers/NavigatorServiceWorker.cpp
@@ -6,6 +6,7 @@
 
 #include "bindings/core/v8/ScriptState.h"
 #include "core/dom/Document.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/frame/LocalDOMWindow.h"
 #include "core/frame/LocalFrame.h"
 #include "core/frame/Navigator.h"
@@ -54,7 +55,7 @@
     ScriptState* script_state,
     Navigator& navigator,
     ExceptionState& exception_state) {
-  ExecutionContext* execution_context = script_state->GetExecutionContext();
+  ExecutionContext* execution_context = ExecutionContext::From(script_state);
   DCHECK(!navigator.GetFrame() ||
          execution_context->GetSecurityOrigin()->CanAccess(
              navigator.GetFrame()->GetSecurityContext()->GetSecurityOrigin()));
@@ -66,7 +67,7 @@
     ScriptState* script_state,
     Navigator& navigator,
     String& error_message) {
-  ExecutionContext* execution_context = script_state->GetExecutionContext();
+  ExecutionContext* execution_context = ExecutionContext::From(script_state);
   DCHECK(!navigator.GetFrame() ||
          execution_context->GetSecurityOrigin()->CanAccess(
              navigator.GetFrame()->GetSecurityContext()->GetSecurityOrigin()));
diff --git a/third_party/WebKit/Source/modules/serviceworkers/ServiceWorker.cpp b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorker.cpp
index b264136..24e284ff 100644
--- a/third_party/WebKit/Source/modules/serviceworkers/ServiceWorker.cpp
+++ b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorker.cpp
@@ -34,6 +34,7 @@
 #include "bindings/core/v8/ExceptionState.h"
 #include "bindings/core/v8/ScriptState.h"
 #include "core/dom/ExceptionCode.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/dom/MessagePort.h"
 #include "core/events/Event.h"
 #include "modules/EventTargetModules.h"
@@ -64,7 +65,7 @@
 
   // Disentangle the port in preparation for sending it to the remote context.
   MessagePortChannelArray channels = MessagePort::DisentanglePorts(
-      script_state->GetExecutionContext(), ports, exception_state);
+      ExecutionContext::From(script_state), ports, exception_state);
   if (exception_state.HadException())
     return;
   if (handle_->ServiceWorker()->GetState() == kWebServiceWorkerStateRedundant) {
diff --git a/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerClient.cpp b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerClient.cpp
index eecb7b5..595a92c 100644
--- a/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerClient.cpp
+++ b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerClient.cpp
@@ -10,6 +10,7 @@
 #include "bindings/core/v8/ExceptionState.h"
 #include "bindings/core/v8/ScriptState.h"
 #include "bindings/core/v8/SerializedScriptValue.h"
+#include "core/dom/ExecutionContext.h"
 #include "modules/serviceworkers/ServiceWorkerGlobalScopeClient.h"
 #include "platform/wtf/RefPtr.h"
 #include "public/platform/WebString.h"
@@ -68,7 +69,7 @@
                                       PassRefPtr<SerializedScriptValue> message,
                                       const MessagePortArray& ports,
                                       ExceptionState& exception_state) {
-  ExecutionContext* context = script_state->GetExecutionContext();
+  ExecutionContext* context = ExecutionContext::From(script_state);
   // Disentangle the port in preparation for sending it to the remote context.
   MessagePortChannelArray channels =
       MessagePort::DisentanglePorts(context, ports, exception_state);
diff --git a/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerClients.cpp b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerClients.cpp
index 760b26cf..b2d60e8 100644
--- a/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerClients.cpp
+++ b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerClients.cpp
@@ -10,6 +10,7 @@
 #include "bindings/core/v8/ScriptPromiseResolver.h"
 #include "core/dom/DOMException.h"
 #include "core/dom/ExceptionCode.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/workers/WorkerGlobalScope.h"
 #include "core/workers/WorkerLocation.h"
 #include "modules/serviceworkers/ServiceWorkerError.h"
@@ -103,7 +104,7 @@
 
 ScriptPromise ServiceWorkerClients::get(ScriptState* script_state,
                                         const String& id) {
-  ExecutionContext* execution_context = script_state->GetExecutionContext();
+  ExecutionContext* execution_context = ExecutionContext::From(script_state);
   // TODO(jungkees): May be null due to worker termination:
   // http://crbug.com/413518.
   if (!execution_context)
@@ -120,7 +121,7 @@
 ScriptPromise ServiceWorkerClients::matchAll(
     ScriptState* script_state,
     const ClientQueryOptions& options) {
-  ExecutionContext* execution_context = script_state->GetExecutionContext();
+  ExecutionContext* execution_context = ExecutionContext::From(script_state);
   // FIXME: May be null due to worker termination: http://crbug.com/413518.
   if (!execution_context)
     return ScriptPromise();
@@ -140,7 +141,7 @@
 }
 
 ScriptPromise ServiceWorkerClients::claim(ScriptState* script_state) {
-  ExecutionContext* execution_context = script_state->GetExecutionContext();
+  ExecutionContext* execution_context = ExecutionContext::From(script_state);
 
   // FIXME: May be null due to worker termination: http://crbug.com/413518.
   if (!execution_context)
@@ -161,7 +162,7 @@
                                                const String& url) {
   ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
   ScriptPromise promise = resolver->Promise();
-  ExecutionContext* context = script_state->GetExecutionContext();
+  ExecutionContext* context = ExecutionContext::From(script_state);
 
   KURL parsed_url = KURL(ToWorkerGlobalScope(context)->location()->Url(), url);
   if (!parsed_url.IsValid()) {
diff --git a/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerContainer.cpp b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerContainer.cpp
index 87ceca0..2b93e2c 100644
--- a/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerContainer.cpp
+++ b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerContainer.cpp
@@ -279,7 +279,7 @@
     return promise;
   }
 
-  ExecutionContext* execution_context = script_state->GetExecutionContext();
+  ExecutionContext* execution_context = ExecutionContext::From(script_state);
   // FIXME: May be null due to worker termination: http://crbug.com/413518.
   if (!execution_context)
     return ScriptPromise();
@@ -316,7 +316,7 @@
     return promise;
   }
 
-  ExecutionContext* execution_context = script_state->GetExecutionContext();
+  ExecutionContext* execution_context = ExecutionContext::From(script_state);
   // FIXME: May be null due to worker termination: http://crbug.com/413518.
   if (!execution_context)
     return ScriptPromise();
@@ -373,7 +373,7 @@
     return promise;
   }
 
-  ExecutionContext* execution_context = script_state->GetExecutionContext();
+  ExecutionContext* execution_context = ExecutionContext::From(script_state);
   RefPtr<SecurityOrigin> document_origin =
       execution_context->GetSecurityOrigin();
   String error_message;
diff --git a/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerGlobalScope.cpp b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerGlobalScope.cpp
index a6db17ab..4bcf3d7 100644
--- a/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerGlobalScope.cpp
+++ b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerGlobalScope.cpp
@@ -39,6 +39,7 @@
 #include "bindings/core/v8/SourceLocation.h"
 #include "bindings/core/v8/V8ThrowException.h"
 #include "core/dom/ExceptionCode.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/events/Event.h"
 #include "core/inspector/ConsoleMessage.h"
 #include "core/inspector/WorkerInspectorController.h"
@@ -157,7 +158,7 @@
 }
 
 ScriptPromise ServiceWorkerGlobalScope::skipWaiting(ScriptState* script_state) {
-  ExecutionContext* execution_context = script_state->GetExecutionContext();
+  ExecutionContext* execution_context = ExecutionContext::From(script_state);
   // FIXME: short-term fix, see details at:
   // https://codereview.chromium.org/535193002/.
   if (!execution_context)
diff --git a/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerWindowClient.cpp b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerWindowClient.cpp
index a9b3218c..ec1a10d0 100644
--- a/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerWindowClient.cpp
+++ b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerWindowClient.cpp
@@ -9,6 +9,7 @@
 #include "bindings/core/v8/ScriptPromiseResolver.h"
 #include "core/dom/DOMException.h"
 #include "core/dom/ExceptionCode.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/page/PageVisibilityState.h"
 #include "core/workers/WorkerGlobalScope.h"
 #include "core/workers/WorkerLocation.h"
@@ -48,14 +49,14 @@
   ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
   ScriptPromise promise = resolver->Promise();
 
-  if (!script_state->GetExecutionContext()->IsWindowInteractionAllowed()) {
+  if (!ExecutionContext::From(script_state)->IsWindowInteractionAllowed()) {
     resolver->Reject(DOMException::Create(kInvalidAccessError,
                                           "Not allowed to focus a window."));
     return promise;
   }
-  script_state->GetExecutionContext()->ConsumeWindowInteraction();
+  ExecutionContext::From(script_state)->ConsumeWindowInteraction();
 
-  ServiceWorkerGlobalScopeClient::From(script_state->GetExecutionContext())
+  ServiceWorkerGlobalScopeClient::From(ExecutionContext::From(script_state))
       ->Focus(Uuid(),
               WTF::MakeUnique<CallbackPromiseAdapter<ServiceWorkerWindowClient,
                                                      ServiceWorkerError>>(
@@ -67,7 +68,7 @@
                                                   const String& url) {
   ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
   ScriptPromise promise = resolver->Promise();
-  ExecutionContext* context = script_state->GetExecutionContext();
+  ExecutionContext* context = ExecutionContext::From(script_state);
 
   KURL parsed_url = KURL(ToWorkerGlobalScope(context)->location()->Url(), url);
   if (!parsed_url.IsValid() || parsed_url.ProtocolIsAbout()) {
diff --git a/third_party/WebKit/Source/modules/shapedetection/ShapeDetector.cpp b/third_party/WebKit/Source/modules/shapedetection/ShapeDetector.cpp
index e54dd68ed..3287f40 100644
--- a/third_party/WebKit/Source/modules/shapedetection/ShapeDetector.cpp
+++ b/third_party/WebKit/Source/modules/shapedetection/ShapeDetector.cpp
@@ -6,6 +6,7 @@
 
 #include "core/dom/DOMException.h"
 #include "core/dom/Document.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/frame/ImageBitmap.h"
 #include "core/frame/LocalFrame.h"
 #include "core/geometry/DOMRect.h"
@@ -77,7 +78,7 @@
   }
 
   if (canvas_image_source->WouldTaintOrigin(
-          script_state->GetExecutionContext()->GetSecurityOrigin())) {
+          ExecutionContext::From(script_state)->GetSecurityOrigin())) {
     resolver->Reject(
         DOMException::Create(kSecurityError, "Source would taint origin."));
     return promise;
diff --git a/third_party/WebKit/Source/modules/speech/DOMWindowSpeechSynthesis.cpp b/third_party/WebKit/Source/modules/speech/DOMWindowSpeechSynthesis.cpp
index f0f337c4..ced3d516 100644
--- a/third_party/WebKit/Source/modules/speech/DOMWindowSpeechSynthesis.cpp
+++ b/third_party/WebKit/Source/modules/speech/DOMWindowSpeechSynthesis.cpp
@@ -31,6 +31,7 @@
 #include "modules/speech/DOMWindowSpeechSynthesis.h"
 
 #include "bindings/core/v8/ScriptState.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/frame/LocalDOMWindow.h"
 #include "core/frame/LocalFrame.h"
 #include "platform/wtf/PassRefPtr.h"
@@ -67,7 +68,7 @@
     ScriptState* script_state) {
   if (!speech_synthesis_) {
     speech_synthesis_ =
-        SpeechSynthesis::Create(script_state->GetExecutionContext());
+        SpeechSynthesis::Create(ExecutionContext::From(script_state));
   }
   return speech_synthesis_;
 }
diff --git a/third_party/WebKit/Source/modules/speech/SpeechGrammar.cpp b/third_party/WebKit/Source/modules/speech/SpeechGrammar.cpp
index c24dbc2..681ceb70 100644
--- a/third_party/WebKit/Source/modules/speech/SpeechGrammar.cpp
+++ b/third_party/WebKit/Source/modules/speech/SpeechGrammar.cpp
@@ -26,6 +26,7 @@
 #include "modules/speech/SpeechGrammar.h"
 
 #include "core/dom/Document.h"
+#include "core/dom/ExecutionContext.h"
 
 namespace blink {
 
@@ -38,7 +39,7 @@
 }
 
 void SpeechGrammar::setSrc(ScriptState* script_state, const String& src) {
-  Document* document = ToDocument(script_state->GetExecutionContext());
+  Document* document = ToDocument(ExecutionContext::From(script_state));
   src_ = document->CompleteURL(src);
 }
 
diff --git a/third_party/WebKit/Source/modules/speech/SpeechGrammarList.cpp b/third_party/WebKit/Source/modules/speech/SpeechGrammarList.cpp
index ae5af549..eadbca85 100644
--- a/third_party/WebKit/Source/modules/speech/SpeechGrammarList.cpp
+++ b/third_party/WebKit/Source/modules/speech/SpeechGrammarList.cpp
@@ -27,6 +27,7 @@
 
 #include "bindings/core/v8/ScriptState.h"
 #include "core/dom/Document.h"
+#include "core/dom/ExecutionContext.h"
 
 namespace blink {
 
@@ -44,7 +45,7 @@
 void SpeechGrammarList::addFromUri(ScriptState* script_state,
                                    const String& src,
                                    double weight) {
-  Document* document = ToDocument(script_state->GetExecutionContext());
+  Document* document = ToDocument(ExecutionContext::From(script_state));
   grammars_.push_back(
       SpeechGrammar::Create(document->CompleteURL(src), weight));
 }
diff --git a/third_party/WebKit/Source/modules/vr/NavigatorVR.cpp b/third_party/WebKit/Source/modules/vr/NavigatorVR.cpp
index 2dc43c9..13273bb 100644
--- a/third_party/WebKit/Source/modules/vr/NavigatorVR.cpp
+++ b/third_party/WebKit/Source/modules/vr/NavigatorVR.cpp
@@ -8,6 +8,7 @@
 #include "core/dom/DOMException.h"
 #include "core/dom/Document.h"
 #include "core/dom/ExceptionCode.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/dom/Fullscreen.h"
 #include "core/frame/LocalDOMWindow.h"
 #include "core/frame/LocalFrame.h"
@@ -73,7 +74,7 @@
   }
 
   UseCounter::Count(*GetDocument(), UseCounter::kVRGetDisplays);
-  ExecutionContext* execution_context = script_state->GetExecutionContext();
+  ExecutionContext* execution_context = ExecutionContext::From(script_state);
   if (!execution_context->IsSecureContext())
     UseCounter::Count(*GetDocument(), UseCounter::kVRGetDisplaysInsecureOrigin);
 
diff --git a/third_party/WebKit/Source/modules/vr/VRDisplay.cpp b/third_party/WebKit/Source/modules/vr/VRDisplay.cpp
index bc08afd..29548126 100644
--- a/third_party/WebKit/Source/modules/vr/VRDisplay.cpp
+++ b/third_party/WebKit/Source/modules/vr/VRDisplay.cpp
@@ -36,6 +36,7 @@
 #include "public/platform/Platform.h"
 
 #include <array>
+#include "core/dom/ExecutionContext.h"
 
 namespace blink {
 
@@ -177,7 +178,7 @@
 
 ScriptPromise VRDisplay::requestPresent(ScriptState* script_state,
                                         const HeapVector<VRLayer>& layers) {
-  ExecutionContext* execution_context = script_state->GetExecutionContext();
+  ExecutionContext* execution_context = ExecutionContext::From(script_state);
   UseCounter::Count(execution_context, UseCounter::kVRRequestPresent);
   if (!execution_context->IsSecureContext()) {
     UseCounter::Count(execution_context,
@@ -278,7 +279,7 @@
     pending_present_resolvers_.push_back(resolver);
   } else if (first_present) {
     bool secure_context =
-        script_state->GetExecutionContext()->IsSecureContext();
+        ExecutionContext::From(script_state)->IsSecureContext();
     if (!display_) {
       ForceExitPresent();
       DOMException* exception = DOMException::Create(
diff --git a/third_party/WebKit/Source/modules/webaudio/AudioWorkletGlobalScope.cpp b/third_party/WebKit/Source/modules/webaudio/AudioWorkletGlobalScope.cpp
index 820c947..4c1e7ec8 100644
--- a/third_party/WebKit/Source/modules/webaudio/AudioWorkletGlobalScope.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/AudioWorkletGlobalScope.cpp
@@ -174,7 +174,7 @@
   // V8 operation happens here to make the AudioWorkletProcessor class a thin
   // wrapper of v8::Object instance.
   V8ScriptRunner::CallFunction(
-      definition->ProcessLocal(isolate), script_state->GetExecutionContext(),
+      definition->ProcessLocal(isolate), ExecutionContext::From(script_state),
       processor->InstanceLocal(isolate), WTF_ARRAY_LENGTH(argv), argv, isolate);
 
   return !block.HasCaught();
diff --git a/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp b/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp
index a62f158..25e6283 100644
--- a/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp
+++ b/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp
@@ -34,6 +34,7 @@
 #include "bindings/modules/v8/WebGLAny.h"
 #include "core/dom/DOMArrayBuffer.h"
 #include "core/dom/DOMTypedArray.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/dom/FlexibleArrayBufferView.h"
 #include "core/dom/TaskRunnerHelper.h"
 #include "core/frame/ImageBitmap.h"
@@ -647,7 +648,7 @@
   DCHECK(!canvas || IsMainThread());
 
   auto execution_context = canvas ? canvas->GetDocument().GetExecutionContext()
-                                  : script_state->GetExecutionContext();
+                                  : ExecutionContext::From(script_state);
   Platform::ContextAttributes context_attributes = ToPlatformContextAttributes(
       attributes, web_gl_version,
       SupportOwnOffscreenSurface(execution_context));
@@ -655,7 +656,7 @@
   Platform::GraphicsInfo gl_info;
   std::unique_ptr<WebGraphicsContext3DProvider> context_provider;
   const auto& url = canvas ? canvas->GetDocument().TopDocument().Url()
-                           : script_state->GetExecutionContext()->Url();
+                           : ExecutionContext::From(script_state)->Url();
   if (IsMainThread()) {
     context_provider = WTF::WrapUnique(
         Platform::Current()->CreateOffscreenGraphicsContext3DProvider(
@@ -736,7 +737,7 @@
     ScriptState* script_state) {
   UseCounter::Feature feature =
       UseCounter::kOffscreenCanvasTransferToImageBitmapWebGL;
-  UseCounter::Count(script_state->GetExecutionContext(), feature);
+  UseCounter::Count(ExecutionContext::From(script_state), feature);
   if (!GetDrawingBuffer())
     return nullptr;
   return ImageBitmap::Create(GetDrawingBuffer()->TransferToStaticBitmapImage());
@@ -746,7 +747,7 @@
     ScriptState* script_state,
     ExceptionState& exception_state) {
   UseCounter::Feature feature = UseCounter::kOffscreenCanvasCommitWebGL;
-  UseCounter::Count(script_state->GetExecutionContext(), feature);
+  UseCounter::Count(ExecutionContext::From(script_state), feature);
   if (!offscreenCanvas()) {
     exception_state.ThrowDOMException(kInvalidStateError,
                                       "Commit() was called on a rendering "
@@ -4914,8 +4915,7 @@
                                  SentinelEmptyRect(), 1, 0, exception_state);
 }
 
-bool WebGLRenderingContextBase::CanUseTexImageByGPU(GLenum format,
-                                                    GLenum type) {
+bool WebGLRenderingContextBase::CanUseTexImageByGPU(GLenum type) {
 #if OS(MACOSX)
   // RGB5_A1 is not color-renderable on NVIDIA Mac, see crbug.com/676209.
   // Though, glCopyTextureCHROMIUM can handle RGB5_A1 internalformat by doing a
@@ -4925,14 +4925,6 @@
   if (type == GL_UNSIGNED_SHORT_5_5_5_1)
     return false;
 #endif
-  // TODO(kbr): bugs were observed when using CopyTextureCHROMIUM to
-  // copy hardware-accelerated video textures to red-channel textures.
-  // These bugs were seen on macOS but may indicate more general
-  // problems. Investigate the root cause of this and fix it.
-  // crbug.com/710673
-  if (format == GL_RED || format == GL_RED_INTEGER)
-    return false;
-
   // OES_texture_half_float doesn't support HALF_FLOAT_OES type for
   // CopyTexImage/CopyTexSubImage. And OES_texture_half_float doesn't require
   // HALF_FLOAT_OES type texture to be renderable. So, HALF_FLOAT_OES type
@@ -5124,7 +5116,7 @@
     // float/integer/sRGB internal format.
     // TODO(crbug.com/622958): relax the constrains if copyTextureCHROMIUM is
     // upgraded to handle more formats.
-    if (!canvas->IsAccelerated() || !CanUseTexImageByGPU(format, type)) {
+    if (!canvas->IsAccelerated() || !CanUseTexImageByGPU(type)) {
       // 2D canvas has only FrontBuffer.
       TexImageImpl(function_id, target, level, internalformat, xoffset, yoffset,
                    zoffset, format, type,
@@ -5236,10 +5228,9 @@
       source_image_rect == SentinelEmptyRect() ||
       source_image_rect ==
           IntRect(0, 0, video->videoWidth(), video->videoHeight());
-  const bool use_copyTextureCHROMIUM = function_id == kTexImage2D &&
-                                       source_image_rect_is_default &&
-                                       depth == 1 && GL_TEXTURE_2D == target &&
-                                       CanUseTexImageByGPU(format, type);
+  const bool use_copyTextureCHROMIUM =
+      function_id == kTexImage2D && source_image_rect_is_default &&
+      depth == 1 && GL_TEXTURE_2D == target && CanUseTexImageByGPU(type);
   // Format of source video may be 16-bit format, e.g. Y16 format.
   // glCopyTextureCHROMIUM requires the source texture to be in 8-bit format.
   // Converting 16-bits formated source texture to 8-bits formated texture will
@@ -5253,9 +5244,16 @@
     // to system memory if possible.  Otherwise, it will fall back to the normal
     // SW path.
 
-    if (video->CopyVideoTextureToPlatformTexture(
-            ContextGL(), texture->Object(), internalformat, format, type,
-            unpack_premultiply_alpha_, unpack_flip_y_)) {
+    // Note that neither
+    // HTMLVideoElement::copyVideoTextureToPlatformTexture nor
+    // ImageBuffer::copyToPlatformTexture allocate the destination texture
+    // any more.
+    TexImage2DBase(target, level, internalformat, video->videoWidth(),
+                   video->videoHeight(), 0, format, type, nullptr);
+
+    if (video->CopyVideoTextureToPlatformTexture(ContextGL(), texture->Object(),
+                                                 unpack_premultiply_alpha_,
+                                                 unpack_flip_y_)) {
       texture->UpdateLastUploadedVideo(video->GetWebMediaPlayer());
       return;
     }
@@ -5299,11 +5297,6 @@
         // This is a straight GPU-GPU copy, any necessary color space conversion
         // was handled in the paintCurrentFrameInContext() call.
 
-        // Note that copyToPlatformTexture no longer allocates the destination
-        // texture.
-        TexImage2DBase(target, level, internalformat, video->videoWidth(),
-                       video->videoHeight(), 0, format, type, nullptr);
-
         if (image_buffer->CopyToPlatformTexture(
                 FunctionIDToSnapshotReason(function_id), ContextGL(), target,
                 texture->Object(), unpack_premultiply_alpha_, unpack_flip_y_,
@@ -5400,7 +5393,7 @@
 
   // TODO(kbr): make this work for sub-rectangles of ImageBitmaps.
   if (function_id != kTexSubImage3D && function_id != kTexImage3D &&
-      bitmap->IsAccelerated() && CanUseTexImageByGPU(format, type) &&
+      bitmap->IsAccelerated() && CanUseTexImageByGPU(type) &&
       !selecting_sub_rectangle) {
     if (function_id == kTexImage2D) {
       TexImage2DBase(target, level, internalformat, width, height, 0, format,
diff --git a/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.h b/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.h
index ee5d953..3f92cf2 100644
--- a/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.h
+++ b/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.h
@@ -1111,7 +1111,7 @@
                      GLint zoffset,
                      CanvasImageSource*,
                      const IntRect& source_sub_rectangle);
-  bool CanUseTexImageByGPU(GLenum format, GLenum type);
+  virtual bool CanUseTexImageByGPU(GLenum type);
 
   virtual WebGLImageConversion::PixelStoreParams GetPackPixelStoreParams();
   virtual WebGLImageConversion::PixelStoreParams GetUnpackPixelStoreParams(
diff --git a/third_party/WebKit/Source/modules/webmidi/MIDIAccessInitializer.cpp b/third_party/WebKit/Source/modules/webmidi/MIDIAccessInitializer.cpp
index e98b4c7..dcd1256 100644
--- a/third_party/WebKit/Source/modules/webmidi/MIDIAccessInitializer.cpp
+++ b/third_party/WebKit/Source/modules/webmidi/MIDIAccessInitializer.cpp
@@ -9,6 +9,7 @@
 #include "core/dom/DOMException.h"
 #include "core/dom/Document.h"
 #include "core/dom/ExceptionCode.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/frame/LocalFrame.h"
 #include "core/frame/Navigator.h"
 #include "modules/permissions/PermissionUtils.h"
@@ -107,7 +108,7 @@
 }
 
 ExecutionContext* MIDIAccessInitializer::GetExecutionContext() const {
-  return GetScriptState()->GetExecutionContext();
+  return ExecutionContext::From(GetScriptState());
 }
 
 void MIDIAccessInitializer::OnPermissionsUpdated(PermissionStatus status) {
diff --git a/third_party/WebKit/Source/modules/webmidi/NavigatorWebMIDI.cpp b/third_party/WebKit/Source/modules/webmidi/NavigatorWebMIDI.cpp
index 2a6c69c9..2065ee4 100644
--- a/third_party/WebKit/Source/modules/webmidi/NavigatorWebMIDI.cpp
+++ b/third_party/WebKit/Source/modules/webmidi/NavigatorWebMIDI.cpp
@@ -34,6 +34,7 @@
 #include "bindings/core/v8/ScriptPromiseResolver.h"
 #include "core/dom/DOMException.h"
 #include "core/dom/Document.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/frame/LocalFrame.h"
 #include "core/frame/Navigator.h"
 #include "core/frame/UseCounter.h"
@@ -79,7 +80,7 @@
   }
 
   UseCounter::CountCrossOriginIframe(
-      *ToDocument(script_state->GetExecutionContext()),
+      *ToDocument(ExecutionContext::From(script_state)),
       UseCounter::kRequestMIDIAccessIframe);
   return MIDIAccessInitializer::Start(script_state, options);
 }
diff --git a/third_party/WebKit/Source/modules/webshare/NavigatorShare.cpp b/third_party/WebKit/Source/modules/webshare/NavigatorShare.cpp
index 861905a9..34cbe23 100644
--- a/third_party/WebKit/Source/modules/webshare/NavigatorShare.cpp
+++ b/third_party/WebKit/Source/modules/webshare/NavigatorShare.cpp
@@ -7,6 +7,7 @@
 #include "core/dom/DOMException.h"
 #include "core/dom/Document.h"
 #include "core/dom/ExceptionCode.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/frame/LocalFrame.h"
 #include "core/frame/Navigator.h"
 #include "modules/webshare/ShareData.h"
@@ -104,7 +105,7 @@
 ScriptPromise NavigatorShare::share(ScriptState* script_state,
                                     const ShareData& share_data) {
   String error_message;
-  if (!script_state->GetExecutionContext()->IsSecureContext(error_message)) {
+  if (!ExecutionContext::From(script_state)->IsSecureContext(error_message)) {
     DOMException* error = DOMException::Create(kSecurityError, error_message);
     return ScriptPromise::RejectWithDOMException(script_state, error);
   }
@@ -115,7 +116,7 @@
     return ScriptPromise::RejectWithDOMException(script_state, error);
   }
 
-  Document* doc = ToDocument(script_state->GetExecutionContext());
+  Document* doc = ToDocument(ExecutionContext::From(script_state));
   DCHECK(doc);
   if (!service_) {
     LocalFrame* frame = doc->GetFrame();
diff --git a/third_party/WebKit/Source/platform/LayoutLocale.cpp b/third_party/WebKit/Source/platform/LayoutLocale.cpp
index cf1df56..67b13c50 100644
--- a/third_party/WebKit/Source/platform/LayoutLocale.cpp
+++ b/third_party/WebKit/Source/platform/LayoutLocale.cpp
@@ -69,7 +69,7 @@
   DCHECK(IsUnambiguousHanScript(script_for_han_));
 }
 
-UScriptCode LayoutLocale::ScriptForHan() const {
+UScriptCode LayoutLocale::GetScriptForHan() const {
   if (script_for_han_ == USCRIPT_COMMON)
     ComputeScriptForHan();
   return script_for_han_;
@@ -103,7 +103,7 @@
 }
 
 const char* LayoutLocale::LocaleForHanForSkFontMgr() const {
-  const char* locale = ToSkFontMgrLocale(ScriptForHan());
+  const char* locale = ToSkFontMgrLocale(GetScriptForHan());
   DCHECK(locale);
   return locale;
 }
diff --git a/third_party/WebKit/Source/platform/LayoutLocale.h b/third_party/WebKit/Source/platform/LayoutLocale.h
index abd287de..952f13492 100644
--- a/third_party/WebKit/Source/platform/LayoutLocale.h
+++ b/third_party/WebKit/Source/platform/LayoutLocale.h
@@ -48,10 +48,10 @@
     return harfbuzz_language_;
   }
   const char* LocaleForSkFontMgr() const;
-  UScriptCode Script() const { return script_; }
+  UScriptCode GetScript() const { return script_; }
 
   // Disambiguation of the Unified Han Ideographs.
-  UScriptCode ScriptForHan() const;
+  UScriptCode GetScriptForHan() const;
   bool HasScriptForHan() const;
   static const LayoutLocale* LocaleForHan(const LayoutLocale*);
   static void InvalidateLocaleForHan() { default_for_han_computed_ = false; }
diff --git a/third_party/WebKit/Source/platform/LayoutLocaleTest.cpp b/third_party/WebKit/Source/platform/LayoutLocaleTest.cpp
index 0751eab..791f8a2 100644
--- a/third_party/WebKit/Source/platform/LayoutLocaleTest.cpp
+++ b/third_party/WebKit/Source/platform/LayoutLocaleTest.cpp
@@ -101,15 +101,17 @@
 
   for (const auto& test : tests) {
     RefPtr<LayoutLocale> locale = LayoutLocale::CreateForTesting(test.locale);
-    EXPECT_EQ(test.script, locale->Script()) << test.locale;
+    EXPECT_EQ(test.script, locale->GetScript()) << test.locale;
     EXPECT_EQ(test.has_script_for_han, locale->HasScriptForHan())
         << test.locale;
-    if (!test.has_script_for_han)
-      EXPECT_EQ(USCRIPT_SIMPLIFIED_HAN, locale->ScriptForHan()) << test.locale;
-    else if (test.script_for_han)
-      EXPECT_EQ(test.script_for_han, locale->ScriptForHan()) << test.locale;
-    else
-      EXPECT_EQ(test.script, locale->ScriptForHan()) << test.locale;
+    if (!test.has_script_for_han) {
+      EXPECT_EQ(USCRIPT_SIMPLIFIED_HAN, locale->GetScriptForHan())
+          << test.locale;
+    } else if (test.script_for_han) {
+      EXPECT_EQ(test.script_for_han, locale->GetScriptForHan()) << test.locale;
+    } else {
+      EXPECT_EQ(test.script, locale->GetScriptForHan()) << test.locale;
+    }
   }
 }
 
diff --git a/third_party/WebKit/Source/platform/fonts/AcceptLanguagesResolverTest.cpp b/third_party/WebKit/Source/platform/fonts/AcceptLanguagesResolverTest.cpp
index c72f132..af4987c 100644
--- a/third_party/WebKit/Source/platform/fonts/AcceptLanguagesResolverTest.cpp
+++ b/third_party/WebKit/Source/platform/fonts/AcceptLanguagesResolverTest.cpp
@@ -56,7 +56,7 @@
     }
 
     ASSERT_NE(nullptr, locale) << test.accept_languages;
-    EXPECT_EQ(test.script, locale->ScriptForHan()) << test.accept_languages;
+    EXPECT_EQ(test.script, locale->GetScriptForHan()) << test.accept_languages;
     EXPECT_STRCASEEQ(test.locale, locale->LocaleForHanForSkFontMgr())
         << test.accept_languages;
   }
diff --git a/third_party/WebKit/Source/platform/fonts/FontDescription.h b/third_party/WebKit/Source/platform/fonts/FontDescription.h
index 604b2bce..871a913 100644
--- a/third_party/WebKit/Source/platform/fonts/FontDescription.h
+++ b/third_party/WebKit/Source/platform/fonts/FontDescription.h
@@ -193,7 +193,7 @@
   const LayoutLocale& LocaleOrDefault() const {
     return LayoutLocale::ValueOrDefault(locale_.Get());
   }
-  UScriptCode Script() const { return LocaleOrDefault().Script(); }
+  UScriptCode GetScript() const { return LocaleOrDefault().GetScript(); }
   bool IsSyntheticBold() const { return fields_.synthetic_bold_; }
   bool IsSyntheticItalic() const { return fields_.synthetic_italic_; }
   bool UseSubpixelPositioning() const {
diff --git a/third_party/WebKit/Source/platform/fonts/android/FontCacheAndroid.cpp b/third_party/WebKit/Source/platform/fonts/android/FontCacheAndroid.cpp
index ded79fe..370603eb 100644
--- a/third_party/WebKit/Source/platform/fonts/android/FontCacheAndroid.cpp
+++ b/third_party/WebKit/Source/platform/fonts/android/FontCacheAndroid.cpp
@@ -113,7 +113,7 @@
   // or locale. We need an API that honors both to find appropriate
   // fonts. crbug.com/642340
   UChar32 exampler_char;
-  switch (content_locale->Script()) {
+  switch (content_locale->GetScript()) {
     case USCRIPT_SIMPLIFIED_HAN:
     case USCRIPT_TRADITIONAL_HAN:
     case USCRIPT_KATAKANA_OR_HIRAGANA:
diff --git a/third_party/WebKit/Source/platform/fonts/android/FontCacheAndroidTest.cpp b/third_party/WebKit/Source/platform/fonts/android/FontCacheAndroidTest.cpp
index 893dffd..cb8d209 100644
--- a/third_party/WebKit/Source/platform/fonts/android/FontCacheAndroidTest.cpp
+++ b/third_party/WebKit/Source/platform/fonts/android/FontCacheAndroidTest.cpp
@@ -16,7 +16,7 @@
 
   FontDescription font_description;
   font_description.SetLocale(LayoutLocale::Get("zh"));
-  ASSERT_EQ(USCRIPT_SIMPLIFIED_HAN, font_description.Script());
+  ASSERT_EQ(USCRIPT_SIMPLIFIED_HAN, font_description.GetScript());
   font_description.SetGenericFamily(FontDescription::kStandardFamily);
 
   FontCache* font_cache = FontCache::GetFontCache();
diff --git a/third_party/WebKit/Source/platform/fonts/shaping/CachingWordShaperTest.cpp b/third_party/WebKit/Source/platform/fonts/shaping/CachingWordShaperTest.cpp
index 5831322..1de4021 100644
--- a/third_party/WebKit/Source/platform/fonts/shaping/CachingWordShaperTest.cpp
+++ b/third_party/WebKit/Source/platform/fonts/shaping/CachingWordShaperTest.cpp
@@ -19,7 +19,7 @@
   void SetUp() override {
     font_description.SetComputedSize(12.0);
     font_description.SetLocale(LayoutLocale::Get("en"));
-    ASSERT_EQ(USCRIPT_LATIN, font_description.Script());
+    ASSERT_EQ(USCRIPT_LATIN, font_description.GetScript());
     font_description.SetGenericFamily(FontDescription::kStandardFamily);
 
     font = Font(font_description);
diff --git a/third_party/WebKit/Source/platform/fonts/win/FontFallbackWin.cpp b/third_party/WebKit/Source/platform/fonts/win/FontFallbackWin.cpp
index 3874415..1e54e49 100644
--- a/third_party/WebKit/Source/platform/fonts/win/FontFallbackWin.cpp
+++ b/third_party/WebKit/Source/platform/fonts/win/FontFallbackWin.cpp
@@ -298,7 +298,7 @@
   }
 
   // Initialize the locale-dependent mapping from system locale.
-  UScriptCode han_script = LayoutLocale::GetSystem().ScriptForHan();
+  UScriptCode han_script = LayoutLocale::GetSystem().GetScriptForHan();
   DCHECK(han_script != USCRIPT_HAN);
   if (script_font_map[han_script].candidate_family_names) {
     script_font_map[USCRIPT_HAN].candidate_family_names =
@@ -505,7 +505,7 @@
   if (script == USCRIPT_HAN) {
     if (const LayoutLocale* locale_for_han =
             LayoutLocale::LocaleForHan(content_locale))
-      script = locale_for_han->ScriptForHan();
+      script = locale_for_han->GetScriptForHan();
     // If still unknown, USCRIPT_HAN uses UI locale.
     // See initializeScriptFontMap().
   }
diff --git a/third_party/WebKit/Source/platform/wtf/text/WTFString.h b/third_party/WebKit/Source/platform/wtf/text/WTFString.h
index d1271e1..608e2b4 100644
--- a/third_party/WebKit/Source/platform/wtf/text/WTFString.h
+++ b/third_party/WebKit/Source/platform/wtf/text/WTFString.h
@@ -475,12 +475,6 @@
   return !(a == b);
 }
 
-inline bool EqualPossiblyIgnoringASCIICase(const String& a,
-                                           const String& b,
-                                           bool ignore_case) {
-  return ignore_case ? EqualIgnoringASCIICase(a, b) : (a == b);
-}
-
 inline bool EqualIgnoringNullity(const String& a, const String& b) {
   return EqualIgnoringNullity(a.Impl(), b.Impl());
 }
diff --git a/third_party/WebKit/Source/web/tests/ScrollingCoordinatorTest.cpp b/third_party/WebKit/Source/web/tests/ScrollingCoordinatorTest.cpp
index 66b0374..74162c9 100644
--- a/third_party/WebKit/Source/web/tests/ScrollingCoordinatorTest.cpp
+++ b/third_party/WebKit/Source/web/tests/ScrollingCoordinatorTest.cpp
@@ -1047,7 +1047,7 @@
   }
 }
 
-class StyleRelatedMainThreadScrollingReasonTest
+class NonCompositedMainThreadScrollingReasonTest
     : public ScrollingCoordinatorTest {
   static const uint32_t kLCDTextRelatedReasons =
       MainThreadScrollingReason::kHasOpacityAndLCDText |
@@ -1055,132 +1055,188 @@
       MainThreadScrollingReason::kBackgroundNotOpaqueInRectAndLCDText;
 
  protected:
-  StyleRelatedMainThreadScrollingReasonTest() {
+  NonCompositedMainThreadScrollingReasonTest() {
     RegisterMockedHttpURLLoad("two_scrollable_area.html");
     NavigateTo(base_url_ + "two_scrollable_area.html");
   }
-  void TestStyle(const std::string& target, const uint32_t reason) {
+  void TestNonCompositedReasons(const std::string& target,
+                                const uint32_t reason) {
     GetWebViewImpl()->GetSettings()->SetPreferCompositingToLCDTextEnabled(
         false);
     Document* document = GetFrame()->GetDocument();
     Element* container = document->GetElementById("scroller1");
     container->setAttribute("class", target.c_str(), ASSERT_NO_EXCEPTION);
-    container = document->GetElementById("scroller2");
-    container->setAttribute("class", target.c_str(), ASSERT_NO_EXCEPTION);
     ForceFullCompositingUpdate();
 
+    PaintLayerScrollableArea* scrollable_area =
+        ToLayoutBoxModelObject(container->GetLayoutObject())
+            ->GetScrollableArea();
+    ASSERT_TRUE(scrollable_area);
+    EXPECT_TRUE(scrollable_area->GetNonCompositedMainThreadScrollingReasons() &
+                reason);
+
+    Element* container2 = document->GetElementById("scroller2");
+    PaintLayerScrollableArea* scrollable_area2 =
+        ToLayoutBoxModelObject(container2->GetLayoutObject())
+            ->GetScrollableArea();
+    ASSERT_TRUE(scrollable_area2);
+    // Different scrollable area should remain unaffected.
+    EXPECT_FALSE(
+        scrollable_area2->GetNonCompositedMainThreadScrollingReasons() &
+        reason);
+
     FrameView* frame_view = GetFrame()->View();
     ASSERT_TRUE(frame_view);
-    ASSERT_TRUE(frame_view->GetMainThreadScrollingReasons() & reason);
+    EXPECT_FALSE(frame_view->GetMainThreadScrollingReasons() & reason);
 
-    // Remove the target attribute from one of the scrollers.
-    // Still need to scroll on main thread.
-    container = document->GetElementById("scroller1");
-    DCHECK(container);
-
+    // Remove attribute from the scroller 1 would lead to scroll on impl.
     container->removeAttribute("class");
     ForceFullCompositingUpdate();
 
-    ASSERT_TRUE(frame_view->GetMainThreadScrollingReasons() & reason);
-
-    // Remove attribute from the other scroller would lead to
-    // scroll on impl.
-    container = document->GetElementById("scroller2");
-    DCHECK(container);
-
-    container->removeAttribute("class");
-    ForceFullCompositingUpdate();
-
-    ASSERT_FALSE(frame_view->GetMainThreadScrollingReasons() & reason);
+    EXPECT_FALSE(scrollable_area->GetNonCompositedMainThreadScrollingReasons() &
+                 reason);
+    EXPECT_FALSE(frame_view->GetMainThreadScrollingReasons() & reason);
 
     // Add target attribute would again lead to scroll on main thread
     container->setAttribute("class", target.c_str(), ASSERT_NO_EXCEPTION);
     ForceFullCompositingUpdate();
 
-    ASSERT_TRUE(frame_view->GetMainThreadScrollingReasons() & reason);
+    EXPECT_TRUE(scrollable_area->GetNonCompositedMainThreadScrollingReasons() &
+                reason);
+    EXPECT_FALSE(frame_view->GetMainThreadScrollingReasons() & reason);
 
     if ((reason & kLCDTextRelatedReasons) &&
         !(reason & ~kLCDTextRelatedReasons)) {
       GetWebViewImpl()->GetSettings()->SetPreferCompositingToLCDTextEnabled(
           true);
       ForceFullCompositingUpdate();
-      ASSERT_FALSE(frame_view->GetMainThreadScrollingReasons());
+      EXPECT_FALSE(
+          scrollable_area->GetNonCompositedMainThreadScrollingReasons());
+      EXPECT_FALSE(frame_view->GetMainThreadScrollingReasons());
     }
   }
 };
 
 INSTANTIATE_TEST_CASE_P(All,
-                        StyleRelatedMainThreadScrollingReasonTest,
+                        NonCompositedMainThreadScrollingReasonTest,
                         ::testing::Bool());
 
-// TODO(yigu): This test and all other style realted main thread scrolling
-// reason tests below have been disabled due to https://crbug.com/701355.
-TEST_P(StyleRelatedMainThreadScrollingReasonTest, DISABLED_TransparentTest) {
-  TestStyle("transparent", MainThreadScrollingReason::kHasOpacityAndLCDText);
+TEST_P(NonCompositedMainThreadScrollingReasonTest, TransparentTest) {
+  TestNonCompositedReasons("transparent",
+                           MainThreadScrollingReason::kHasOpacityAndLCDText);
 }
 
-TEST_P(StyleRelatedMainThreadScrollingReasonTest, DISABLED_TransformTest) {
-  TestStyle("transform", MainThreadScrollingReason::kHasTransformAndLCDText);
+TEST_P(NonCompositedMainThreadScrollingReasonTest, TransformTest) {
+  TestNonCompositedReasons("transform",
+                           MainThreadScrollingReason::kHasTransformAndLCDText);
 }
 
-TEST_P(StyleRelatedMainThreadScrollingReasonTest,
-       DISABLED_BackgroundNotOpaqueTest) {
-  TestStyle("background-not-opaque",
-            MainThreadScrollingReason::kBackgroundNotOpaqueInRectAndLCDText);
+TEST_P(NonCompositedMainThreadScrollingReasonTest, BackgroundNotOpaqueTest) {
+  TestNonCompositedReasons(
+      "background-not-opaque",
+      MainThreadScrollingReason::kBackgroundNotOpaqueInRectAndLCDText);
 }
 
-TEST_P(StyleRelatedMainThreadScrollingReasonTest, DISABLED_BorderRadiusTest) {
-  TestStyle("border-radius", MainThreadScrollingReason::kHasBorderRadius);
+TEST_P(NonCompositedMainThreadScrollingReasonTest, BorderRadiusTest) {
+  TestNonCompositedReasons("border-radius",
+                           MainThreadScrollingReason::kHasBorderRadius);
 }
 
-TEST_P(StyleRelatedMainThreadScrollingReasonTest, DISABLED_ClipTest) {
-  TestStyle("clip", MainThreadScrollingReason::kHasClipRelatedProperty);
+TEST_P(NonCompositedMainThreadScrollingReasonTest, ClipTest) {
+  TestNonCompositedReasons("clip",
+                           MainThreadScrollingReason::kHasClipRelatedProperty);
 }
 
-TEST_P(StyleRelatedMainThreadScrollingReasonTest, DISABLED_ClipPathTest) {
-  uint32_t reason = MainThreadScrollingReason::kHasClipRelatedProperty;
+TEST_P(NonCompositedMainThreadScrollingReasonTest, ClipPathTest) {
+  uint32_t clip_reason = MainThreadScrollingReason::kHasClipRelatedProperty;
   GetWebViewImpl()->GetSettings()->SetPreferCompositingToLCDTextEnabled(false);
   Document* document = GetFrame()->GetDocument();
   // Test ancestor with ClipPath
   Element* element = document->body();
-  DCHECK(element);
+  ASSERT_TRUE(element);
   element->setAttribute(HTMLNames::styleAttr,
                         "clip-path:circle(115px at 20px 20px);");
+  Element* container = document->getElementById("scroller1");
+  ASSERT_TRUE(container);
   ForceFullCompositingUpdate();
 
+  PaintLayerScrollableArea* scrollable_area =
+      ToLayoutBoxModelObject(container->GetLayoutObject())->GetScrollableArea();
+  ASSERT_TRUE(scrollable_area);
+  EXPECT_TRUE(scrollable_area->GetNonCompositedMainThreadScrollingReasons() &
+              clip_reason);
+
   FrameView* frame_view = GetFrame()->View();
   ASSERT_TRUE(frame_view);
-  ASSERT_TRUE(frame_view->GetMainThreadScrollingReasons() & reason);
+  EXPECT_FALSE(frame_view->GetMainThreadScrollingReasons() & clip_reason);
 
   // Remove clip path from ancestor.
   element->removeAttribute(HTMLNames::styleAttr);
   ForceFullCompositingUpdate();
 
-  ASSERT_FALSE(frame_view->GetMainThreadScrollingReasons() & reason);
+  EXPECT_FALSE(scrollable_area->GetNonCompositedMainThreadScrollingReasons() &
+               clip_reason);
+  EXPECT_FALSE(frame_view->GetMainThreadScrollingReasons() & clip_reason);
 
   // Test descendant with ClipPath
   element = document->GetElementById("content1");
-  DCHECK(element);
+  ASSERT_TRUE(element);
   element->setAttribute(HTMLNames::styleAttr,
                         "clip-path:circle(115px at 20px 20px);");
   ForceFullCompositingUpdate();
-  ASSERT_TRUE(frame_view->GetMainThreadScrollingReasons() & reason);
+  EXPECT_TRUE(scrollable_area->GetNonCompositedMainThreadScrollingReasons() &
+              clip_reason);
+  EXPECT_FALSE(frame_view->GetMainThreadScrollingReasons() & clip_reason);
 
   // Remove clip path from descendant.
   element->removeAttribute(HTMLNames::styleAttr);
   ForceFullCompositingUpdate();
-  ASSERT_FALSE(frame_view->GetMainThreadScrollingReasons() & reason);
+  EXPECT_FALSE(scrollable_area->GetNonCompositedMainThreadScrollingReasons() &
+               clip_reason);
+  EXPECT_FALSE(frame_view->GetMainThreadScrollingReasons() & clip_reason);
 }
 
-TEST_P(StyleRelatedMainThreadScrollingReasonTest, DISABLED_LCDTextEnabledTest) {
-  TestStyle("transparent border-radius",
-            MainThreadScrollingReason::kHasOpacityAndLCDText |
-                MainThreadScrollingReason::kHasBorderRadius);
+TEST_P(NonCompositedMainThreadScrollingReasonTest, LCDTextEnabledTest) {
+  TestNonCompositedReasons("transparent border-radius",
+                           MainThreadScrollingReason::kHasOpacityAndLCDText |
+                               MainThreadScrollingReason::kHasBorderRadius);
 }
 
-TEST_P(StyleRelatedMainThreadScrollingReasonTest, DISABLED_BoxShadowTest) {
-  TestStyle("box-shadow",
-            MainThreadScrollingReason::kHasBoxShadowFromNonRootLayer);
+TEST_P(NonCompositedMainThreadScrollingReasonTest, BoxShadowTest) {
+  TestNonCompositedReasons(
+      "box-shadow", MainThreadScrollingReason::kHasBoxShadowFromNonRootLayer);
+}
+
+TEST_P(NonCompositedMainThreadScrollingReasonTest,
+       CompositedWithLCDTextRelatedReasonsTest) {
+  // With "will-change:transform" we composite elements with
+  // LCDTextRelatedReasons only. For elements with other
+  // NonCompositedReasons, we don't create scrollingLayer for their
+  // CompositedLayerMapping therefore they don't get composited.
+  GetWebViewImpl()->GetSettings()->SetPreferCompositingToLCDTextEnabled(false);
+  Document* document = GetFrame()->GetDocument();
+  Element* container = document->getElementById("scroller1");
+  ASSERT_TRUE(container);
+  container->setAttribute("class", "composited transparent",
+                          ASSERT_NO_EXCEPTION);
+  ForceFullCompositingUpdate();
+
+  PaintLayerScrollableArea* scrollable_area =
+      ToLayoutBoxModelObject(container->GetLayoutObject())->GetScrollableArea();
+  ASSERT_TRUE(scrollable_area);
+  EXPECT_FALSE(scrollable_area->GetNonCompositedMainThreadScrollingReasons());
+
+  Element* container2 = document->getElementById("scroller2");
+  ASSERT_TRUE(container2);
+  container2->setAttribute("class", "composited border-radius",
+                           ASSERT_NO_EXCEPTION);
+  ForceFullCompositingUpdate();
+  PaintLayerScrollableArea* scrollable_area2 =
+      ToLayoutBoxModelObject(container2->GetLayoutObject())
+          ->GetScrollableArea();
+  ASSERT_TRUE(scrollable_area2);
+  EXPECT_TRUE(scrollable_area2->GetNonCompositedMainThreadScrollingReasons() &
+              MainThreadScrollingReason::kHasBorderRadius);
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/web/tests/data/two_scrollable_area.html b/third_party/WebKit/Source/web/tests/data/two_scrollable_area.html
index 9de7288..81e1e37 100644
--- a/third_party/WebKit/Source/web/tests/data/two_scrollable_area.html
+++ b/third_party/WebKit/Source/web/tests/data/two_scrollable_area.html
@@ -32,6 +32,10 @@
   will-change:transform; /*This reason is not recorded for root layer*/
 }
 
+.composited {
+  will-change:transform;
+}
+
 .content {
   height: 500px;
 }
diff --git a/third_party/WebKit/public/platform/WebMediaPlayer.h b/third_party/WebKit/public/platform/WebMediaPlayer.h
index 4f17e2e..959631b 100644
--- a/third_party/WebKit/public/platform/WebMediaPlayer.h
+++ b/third_party/WebKit/public/platform/WebMediaPlayer.h
@@ -177,20 +177,19 @@
 
   // TODO(kbr): remove non-|target| version. crbug.com/349871
   //
-  // Do a GPU-GPU texture copy of the current video frame to |texture|,
-  // reallocating |texture| at the appropriate size with given internal
-  // format, format, and type if necessary. If the copy is impossible
-  // or fails, it returns false.
+  // Do a GPU-GPU texture copy of the natural size of the current
+  // video frame to |texture|. Caller is responsible for allocating
+  // |texture| with the appropriate size. If the copy is impossible or
+  // fails, it returns false.
   virtual bool CopyVideoTextureToPlatformTexture(gpu::gles2::GLES2Interface*,
                                                  unsigned texture,
-                                                 unsigned internal_format,
-                                                 unsigned format,
-                                                 unsigned type,
                                                  bool premultiply_alpha,
                                                  bool flip_y) {
     return false;
   }
 
+  // TODO(kbr): when updating calling code to use this, remove the
+  // |internalFormat| and |type| parameters. crbug.com/349871
   // Do a GPU-GPU textures copy. If the copy is impossible or fails, it returns
   // false.
   virtual bool CopyVideoTextureToPlatformTexture(gpu::gles2::GLES2Interface*,
diff --git a/third_party/crashpad/README.chromium b/third_party/crashpad/README.chromium
index c90b2b72..dc71b92 100644
--- a/third_party/crashpad/README.chromium
+++ b/third_party/crashpad/README.chromium
@@ -2,7 +2,7 @@
 Short Name: crashpad
 URL: https://crashpad.chromium.org/
 Version: unknown
-Revision: b4095401639ebe2ad33169e5c1d994065cbff1b8
+Revision: 1f28a123a4c9449e3d7ddad4ff00dacd366d5216
 License: Apache 2.0
 License File: crashpad/LICENSE
 Security Critical: yes
diff --git a/third_party/crashpad/crashpad/build/run_tests.py b/third_party/crashpad/crashpad/build/run_tests.py
index 00b40f6..c5cbb03 100755
--- a/third_party/crashpad/crashpad/build/run_tests.py
+++ b/third_party/crashpad/crashpad/build/run_tests.py
@@ -38,6 +38,11 @@
       'crashpad_test_test',
       'crashpad_util_test',
   ]
+
+  if sys.platform == 'win32':
+    tests.append('crashpad_handler_test')
+    tests = sorted(tests)
+
   for test in tests:
     print '-' * 80
     print test
diff --git a/third_party/crashpad/crashpad/client/crashpad_client_win_test.cc b/third_party/crashpad/crashpad/client/crashpad_client_win_test.cc
index 743e930d..7e5093f2 100644
--- a/third_party/crashpad/crashpad/client/crashpad_client_win_test.cc
+++ b/third_party/crashpad/crashpad/client/crashpad_client_win_test.cc
@@ -14,8 +14,6 @@
 
 #include "client/crashpad_client.h"
 
-#include <tlhelp32.h>
-
 #include <vector>
 
 #include "base/files/file_path.h"
@@ -23,11 +21,10 @@
 #include "base/memory/ptr_util.h"
 #include "base/logging.h"
 #include "gtest/gtest.h"
-#include "test/errors.h"
-#include "test/scoped_temp_dir.h"
 #include "test/test_paths.h"
+#include "test/scoped_temp_dir.h"
 #include "test/win/win_multiprocess.h"
-#include "util/win/process_info.h"
+#include "test/win/win_multiprocess_with_temp_dir.h"
 #include "util/win/scoped_handle.h"
 #include "util/win/termination_codes.h"
 
@@ -35,159 +32,6 @@
 namespace test {
 namespace {
 
-class ScopedEnvironmentVariable {
- public:
-  explicit ScopedEnvironmentVariable(const wchar_t* name);
-  ~ScopedEnvironmentVariable();
-
-  std::wstring GetValue() const;
-
-  // Sets this environment variable to |new_value|. If |new_value| is nullptr
-  // this environment variable will be undefined.
-  void SetValue(const wchar_t* new_value) const;
-
- private:
-  std::wstring GetValueImpl(bool* is_defined) const;
-
-  std::wstring original_value_;
-  const wchar_t* name_;
-  bool was_defined_;
-
-  DISALLOW_COPY_AND_ASSIGN(ScopedEnvironmentVariable);
-};
-
-ScopedEnvironmentVariable::ScopedEnvironmentVariable(const wchar_t* name)
-    : name_(name) {
-  original_value_ = GetValueImpl(&was_defined_);
-}
-
-ScopedEnvironmentVariable::~ScopedEnvironmentVariable() {
-  if (was_defined_)
-    SetValue(original_value_.data());
-  else
-    SetValue(nullptr);
-}
-
-std::wstring ScopedEnvironmentVariable::GetValue() const {
-  bool dummy;
-  return GetValueImpl(&dummy);
-}
-
-std::wstring ScopedEnvironmentVariable::GetValueImpl(bool* is_defined) const {
-  // The length returned is inclusive of the terminating zero, except
-  // if the variable doesn't exist, in which case the return value is zero.
-  DWORD len = GetEnvironmentVariable(name_, nullptr, 0);
-  if (len == 0) {
-    *is_defined = false;
-    return L"";
-  }
-
-  *is_defined = true;
-
-  std::wstring ret;
-  ret.resize(len);
-  // The length returned on success is exclusive of the terminating zero.
-  len = GetEnvironmentVariable(name_, &ret[0], len);
-  ret.resize(len);
-
-  return ret;
-}
-
-void ScopedEnvironmentVariable::SetValue(const wchar_t* new_value) const {
-  SetEnvironmentVariable(name_, new_value);
-}
-
-// Returns the process IDs of all processes that have |parent_pid| as
-// parent process ID.
-std::vector<pid_t> GetPotentialChildProcessesOf(pid_t parent_pid) {
-  ScopedFileHANDLE snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0));
-  if (!snapshot.is_valid()) {
-    ADD_FAILURE() << ErrorMessage("CreateToolhelp32Snapshot");
-    return std::vector<pid_t>();
-  }
-
-  PROCESSENTRY32 entry = {sizeof(entry)};
-  if (!Process32First(snapshot.get(), &entry)) {
-    ADD_FAILURE() << ErrorMessage("Process32First");
-    return std::vector<pid_t>();
-  }
-
-  std::vector<pid_t> child_pids;
-  do {
-    if (entry.th32ParentProcessID == parent_pid)
-      child_pids.push_back(entry.th32ProcessID);
-  } while (Process32Next(snapshot.get(), &entry));
-
-  return child_pids;
-}
-
-ULARGE_INTEGER GetProcessCreationTime(HANDLE process) {
-  ULARGE_INTEGER ret = {};
-  FILETIME creation_time;
-  FILETIME dummy;
-  if (GetProcessTimes(process, &creation_time, &dummy, &dummy, &dummy)) {
-    ret.LowPart = creation_time.dwLowDateTime;
-    ret.HighPart = creation_time.dwHighDateTime;
-  } else {
-    ADD_FAILURE() << ErrorMessage("GetProcessTimes");
-  }
-
-  return ret;
-}
-
-// Waits for the processes directly created by |parent| - and specifically not
-// their offspring. For this to work without race, |parent| has to be suspended
-// or have exited.
-void WaitForAllChildProcessesOf(HANDLE parent) {
-  pid_t parent_pid = GetProcessId(parent);
-  std::vector<pid_t> child_pids = GetPotentialChildProcessesOf(parent_pid);
-
-  ULARGE_INTEGER parent_creationtime = GetProcessCreationTime(parent);
-  for (pid_t child_pid : child_pids) {
-    // Try and open the process. This may fail for reasons such as:
-    // 1. The process isn't |parent|'s child process, but rather a
-    //    higher-privilege sub-process of an earlier process that had
-    //    |parent|'s PID.
-    // 2. The process no longer exists, e.g. it exited after enumeration.
-    ScopedKernelHANDLE child_process(
-        OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION | SYNCHRONIZE,
-                    false,
-                    child_pid));
-    if (!child_process.is_valid())
-      continue;
-
-    // Check that the child now has the right parent PID, as its PID may have
-    // been reused after the enumeration above.
-    ProcessInfo child_info;
-    if (!child_info.Initialize(child_process.get())) {
-      // This can happen if child_process has exited after the handle is opened.
-      LOG(ERROR) << "ProcessInfo::Initialize, pid: " << child_pid;
-      continue;
-    }
-
-    if (parent_pid != child_info.ParentProcessID()) {
-      // The child's process ID was reused after enumeration.
-      continue;
-    }
-
-    // We successfully opened |child_process| and it has |parent|'s PID for
-    // parent process ID. However, this could still be a sub-process of another
-    // process that earlier had |parent|'s PID. To make sure, check that
-    // |child_process| was created after |parent_process|.
-    ULARGE_INTEGER process_creationtime =
-        GetProcessCreationTime(child_process.get());
-    if (process_creationtime.QuadPart < parent_creationtime.QuadPart)
-      continue;
-
-    DWORD err = WaitForSingleObject(child_process.get(), INFINITE);
-    if (err == WAIT_FAILED) {
-      ADD_FAILURE() << ErrorMessage("WaitForSingleObject");
-    } else if (err != WAIT_OBJECT_0) {
-      ADD_FAILURE() << "WaitForSingleObject returned " << err;
-    }
-  }
-}
-
 void StartAndUseHandler(const base::FilePath& temp_dir) {
   base::FilePath handler_path = TestPaths::Executable().DirName().Append(
       FILE_PATH_LITERAL("crashpad_handler.com"));
@@ -204,32 +48,6 @@
   ASSERT_TRUE(client.WaitForHandlerStart(INFINITE));
 }
 
-// Name of the environment variable used to communicate the name of the
-// temp directory from parent to child process.
-constexpr wchar_t kTempDirEnvName[] = L"CRASHPAD_TEST_TEMP_DIR";
-
-class WinMultiprocessWithTempDir : public WinMultiprocess {
- public:
-  WinMultiprocessWithTempDir()
-      : WinMultiprocess(), temp_dir_env_(kTempDirEnvName) {}
-
-  void WinMultiprocessParentBeforeChild() override {
-    temp_dir_ = base::WrapUnique(new ScopedTempDir);
-    temp_dir_env_.SetValue(temp_dir_->path().value().c_str());
-  }
-
-  void WinMultiprocessParentAfterChild(HANDLE child) override {
-    WaitForAllChildProcessesOf(child);
-    temp_dir_.reset();
-  }
-
- protected:
-  std::unique_ptr<ScopedTempDir> temp_dir_;
-  ScopedEnvironmentVariable temp_dir_env_;
-
-  DISALLOW_COPY_AND_ASSIGN(WinMultiprocessWithTempDir);
-};
-
 class StartWithInvalidHandles final : public WinMultiprocessWithTempDir {
  public:
   StartWithInvalidHandles() : WinMultiprocessWithTempDir() {}
@@ -244,7 +62,7 @@
     SetStdHandle(STD_OUTPUT_HANDLE, INVALID_HANDLE_VALUE);
     SetStdHandle(STD_ERROR_HANDLE, INVALID_HANDLE_VALUE);
 
-    StartAndUseHandler(base::FilePath(temp_dir_env_.GetValue()));
+    StartAndUseHandler(GetTempDirPath());
 
     SetStdHandle(STD_OUTPUT_HANDLE, original_stdout);
     SetStdHandle(STD_ERROR_HANDLE, original_stderr);
@@ -252,7 +70,7 @@
 };
 
 TEST(CrashpadClient, StartWithInvalidHandles) {
-  WinMultiprocess::Run<StartWithInvalidHandles>();
+  WinMultiprocessWithTempDir::Run<StartWithInvalidHandles>();
 }
 
 class StartWithSameStdoutStderr final : public WinMultiprocessWithTempDir {
@@ -268,14 +86,14 @@
     HANDLE original_stderr = GetStdHandle(STD_ERROR_HANDLE);
     SetStdHandle(STD_OUTPUT_HANDLE, original_stderr);
 
-    StartAndUseHandler(base::FilePath(temp_dir_env_.GetValue()));
+    StartAndUseHandler(GetTempDirPath());
 
     SetStdHandle(STD_OUTPUT_HANDLE, original_stdout);
   }
 };
 
 TEST(CrashpadClient, StartWithSameStdoutStderr) {
-  WinMultiprocess::Run<StartWithSameStdoutStderr>();
+  WinMultiprocessWithTempDir::Run<StartWithSameStdoutStderr>();
 }
 
 void StartAndUseBrokenHandler(CrashpadClient* client) {
diff --git a/third_party/crashpad/crashpad/compat/android/linux/prctl.h b/third_party/crashpad/crashpad/compat/android/linux/prctl.h
new file mode 100644
index 0000000..046f900
--- /dev/null
+++ b/third_party/crashpad/crashpad/compat/android/linux/prctl.h
@@ -0,0 +1,25 @@
+// Copyright 2017 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_COMPAT_ANDROID_LINUX_PRCTL_H_
+#define CRASHPAD_COMPAT_ANDROID_LINUX_PRCTL_H_
+
+#include_next <linux/prctl.h>
+
+// Android 5.0.0 (API 21) NDK
+#if !defined(PR_SET_PTRACER)
+#define PR_SET_PTRACER 0x59616d61
+#endif
+
+#endif  // CRASHPAD_COMPAT_ANDROID_LINUX_PRCTL_H_
diff --git a/third_party/crashpad/crashpad/crashpad.gyp b/third_party/crashpad/crashpad/crashpad.gyp
index 42fe0a2..f30a9df 100644
--- a/third_party/crashpad/crashpad/crashpad.gyp
+++ b/third_party/crashpad/crashpad/crashpad.gyp
@@ -22,6 +22,7 @@
         'client/client_test.gyp:*',
         'compat/compat.gyp:*',
         'handler/handler.gyp:*',
+        'handler/handler_test.gyp:*',
         'minidump/minidump.gyp:*',
         'minidump/minidump_test.gyp:*',
         'snapshot/snapshot.gyp:*',
diff --git a/third_party/crashpad/crashpad/doc/overview_design.md b/third_party/crashpad/crashpad/doc/overview_design.md
index ad871c18..07a7d65 100644
--- a/third_party/crashpad/crashpad/doc/overview_design.md
+++ b/third_party/crashpad/crashpad/doc/overview_design.md
@@ -452,6 +452,8 @@
 
 ### Extensibility
 
+#### Client Extensibility
+
 Clients are able to extend the generated crash reports in two ways, by
 manipulating their CrashpadInfo structure.
 The two extensibility points are:
@@ -461,6 +463,18 @@
 In both cases the CrashpadInfo structure has to be updated before a crash
 occurs.
 
+##### Embedder Extensibility
+
+Additionally, embedders of the handler can provide "user stream data source"
+instances to the handler's main function. Any time a minidump is written, these
+instances get called.
+
+Each data source may contribute a custom stream to the minidump, which can be
+computed from e.g. system or application state relevant to the crash.
+
+As a case in point, it can be handy to know whether the system was under memory
+or other resource duress at the time of crash.
+
 ### Dependencies
 
 Aside from system headers and APIs, when used outside of Chromium, Crashpad has
diff --git a/third_party/crashpad/crashpad/handler/crashpad_handler_test.cc b/third_party/crashpad/crashpad/handler/crashpad_handler_test.cc
new file mode 100644
index 0000000..7237e8c
--- /dev/null
+++ b/third_party/crashpad/crashpad/handler/crashpad_handler_test.cc
@@ -0,0 +1,132 @@
+// Copyright 2017 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <windows.h>
+#include <dbghelp.h>
+#include <string.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "client/crash_report_database.h"
+#include "client/crashpad_client.h"
+#include "gtest/gtest.h"
+#include "minidump/test/minidump_file_writer_test_util.h"
+#include "test/test_paths.h"
+#include "test/win/win_multiprocess_with_temp_dir.h"
+#include "util/file/file_reader.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+void StartAndCrashWithExtendedHandler(const base::FilePath& temp_dir) {
+  base::FilePath handler_path = TestPaths::Executable().DirName().Append(
+      FILE_PATH_LITERAL("crashpad_handler_test_extended_handler.exe"));
+
+  CrashpadClient client;
+  ASSERT_TRUE(client.StartHandler(handler_path,
+                                  temp_dir,
+                                  base::FilePath(),
+                                  "",
+                                  std::map<std::string, std::string>(),
+                                  std::vector<std::string>(),
+                                  false,
+                                  false));
+
+  __debugbreak();
+}
+
+class CrashWithExtendedHandler final : public WinMultiprocessWithTempDir {
+ public:
+  CrashWithExtendedHandler() : WinMultiprocessWithTempDir() {}
+  ~CrashWithExtendedHandler() {}
+
+ private:
+  void ValidateGeneratedDump();
+
+  void WinMultiprocessParent() override {
+    SetExpectedChildExitCode(EXCEPTION_BREAKPOINT);
+  }
+
+  void WinMultiprocessChild() override {
+    StartAndCrashWithExtendedHandler(GetTempDirPath());
+  }
+
+  void WinMultiprocessParentAfterChild(HANDLE child) override {
+    // At this point the child has exited, which means the crash report should
+    // have been written.
+    ValidateGeneratedDump();
+
+    // Delegate the cleanup to the superclass.
+    WinMultiprocessWithTempDir::WinMultiprocessParentAfterChild(child);
+  }
+};
+
+void CrashWithExtendedHandler::ValidateGeneratedDump() {
+  // Open the database and find the sole dump that should have been created.
+  std::unique_ptr<CrashReportDatabase> database(
+      CrashReportDatabase::Initialize(GetTempDirPath()));
+  ASSERT_TRUE(database);
+
+  std::vector<CrashReportDatabase::Report> reports;
+  ASSERT_EQ(database->GetCompletedReports(&reports),
+            CrashReportDatabase::kNoError);
+  ASSERT_EQ(reports.size(), 1u);
+
+  // Open the dump and validate that it has the extension stream with the
+  // expected contents.
+  FileReader reader;
+  ASSERT_TRUE(reader.Open(reports[0].file_path));
+
+  // Read the header.
+  MINIDUMP_HEADER header = {};
+  ASSERT_TRUE(reader.ReadExactly(&header, sizeof(header)));
+
+  // Read the directory.
+  std::vector<MINIDUMP_DIRECTORY> directory(header.NumberOfStreams);
+  ASSERT_TRUE(reader.SeekSet(header.StreamDirectoryRva));
+  ASSERT_TRUE(reader.ReadExactly(directory.data(),
+                                 directory.size() * sizeof(directory[0])));
+
+  // Search for the extension stream.
+  size_t found_extension_streams = 0;
+  for (const auto& entry : directory) {
+    if (entry.StreamType == 0xCAFEBABE) {
+      ++found_extension_streams;
+
+      ASSERT_TRUE(reader.SeekSet(entry.Location.Rva));
+
+      std::vector<char> data;
+      data.resize(entry.Location.DataSize);
+
+      ASSERT_TRUE(reader.ReadExactly(data.data(), data.size()));
+
+      static const char kExpectedData[] = "Injected extension stream!";
+      EXPECT_EQ(memcmp(kExpectedData, data.data(), sizeof(kExpectedData)), 0);
+    }
+  }
+
+  EXPECT_EQ(found_extension_streams, 1u);
+}
+
+TEST(CrashpadHandler, ExtensibilityCalloutsWork) {
+  WinMultiprocessWithTempDir::Run<CrashWithExtendedHandler>();
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace crashpad
diff --git a/third_party/crashpad/crashpad/handler/crashpad_handler_test_extended_handler.cc b/third_party/crashpad/crashpad/handler/crashpad_handler_test_extended_handler.cc
new file mode 100644
index 0000000..ede0f6e7
--- /dev/null
+++ b/third_party/crashpad/crashpad/handler/crashpad_handler_test_extended_handler.cc
@@ -0,0 +1,70 @@
+// Copyright 2017 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "build/build_config.h"
+#include "handler/handler_main.h"
+#include "minidump/test/minidump_user_extension_stream_util.h"
+#include "tools/tool_support.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
+namespace {
+
+class TestUserStreamDataSource : public crashpad::UserStreamDataSource {
+ public:
+  TestUserStreamDataSource() {}
+
+  std::unique_ptr<crashpad::MinidumpUserExtensionStreamDataSource>
+  ProduceStreamData(crashpad::ProcessSnapshot* process_snapshot) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestUserStreamDataSource);
+};
+
+std::unique_ptr<crashpad::MinidumpUserExtensionStreamDataSource>
+TestUserStreamDataSource::ProduceStreamData(
+    crashpad::ProcessSnapshot* process_snapshot) {
+  static const char kTestData[] = "Injected extension stream!";
+
+  return base::WrapUnique(new crashpad::test::BufferExtensionStreamDataSource(
+      0xCAFEBABE, kTestData, sizeof(kTestData)));
+}
+
+int ExtendedHandlerMain(int argc, char* argv[]) {
+  crashpad::UserStreamDataSources user_stream_data_sources;
+  user_stream_data_sources.push_back(
+      base::WrapUnique(new TestUserStreamDataSource()));
+
+  return crashpad::HandlerMain(argc, argv, &user_stream_data_sources);
+}
+
+}  // namespace
+
+#if defined(OS_MACOSX)
+
+int main(int argc, char* argv[]) {
+  return ExtendedHandlerMain(argc, argv);
+}
+
+#elif defined(OS_WIN)
+
+int wmain(int argc, wchar_t* argv[]) {
+  return crashpad::ToolSupport::Wmain(argc, argv, &ExtendedHandlerMain);
+}
+
+#endif  // OS_MACOSX
diff --git a/third_party/crashpad/crashpad/handler/handler.gyp b/third_party/crashpad/crashpad/handler/handler.gyp
index 6a51222c..d6e4c27 100644
--- a/third_party/crashpad/crashpad/handler/handler.gyp
+++ b/third_party/crashpad/crashpad/handler/handler.gyp
@@ -45,6 +45,8 @@
         'mac/exception_handler_server.h',
         'prune_crash_reports_thread.cc',
         'prune_crash_reports_thread.h',
+        'user_stream_data_source.cc',
+        'user_stream_data_source.h',
         'win/crash_report_exception_handler.cc',
         'win/crash_report_exception_handler.h',
       ],
diff --git a/third_party/crashpad/crashpad/handler/handler_main.cc b/third_party/crashpad/crashpad/handler/handler_main.cc
index f58f1ba..641bf0f6 100644
--- a/third_party/crashpad/crashpad/handler/handler_main.cc
+++ b/third_party/crashpad/crashpad/handler/handler_main.cc
@@ -388,7 +388,9 @@
 
 }  // namespace
 
-int HandlerMain(int argc, char* argv[]) {
+int HandlerMain(int argc,
+                char* argv[],
+                const UserStreamDataSources* user_stream_sources) {
   InstallCrashHandler();
   CallMetricsRecordNormalExit metrics_record_normal_exit;
 
@@ -727,8 +729,10 @@
                                       PruneCondition::GetDefault());
   prune_thread.Start();
 
-  CrashReportExceptionHandler exception_handler(
-      database.get(), &upload_thread, &options.annotations);
+  CrashReportExceptionHandler exception_handler(database.get(),
+                                                &upload_thread,
+                                                &options.annotations,
+                                                user_stream_sources);
 
 #if defined(OS_WIN)
   if (options.initial_client_data.IsValid()) {
diff --git a/third_party/crashpad/crashpad/handler/handler_main.h b/third_party/crashpad/crashpad/handler/handler_main.h
index 5b5568e..1e78aa5 100644
--- a/third_party/crashpad/crashpad/handler/handler_main.h
+++ b/third_party/crashpad/crashpad/handler/handler_main.h
@@ -15,13 +15,22 @@
 #ifndef CRASHPAD_HANDLER_HANDLER_MAIN_H_
 #define CRASHPAD_HANDLER_HANDLER_MAIN_H_
 
+#include "handler/user_stream_data_source.h"
+
 namespace crashpad {
 
 //! \brief The `main()` of the `crashpad_handler` binary.
 //!
 //! This is exposed so that `crashpad_handler` can be embedded into another
 //! binary, but called and used as if it were a standalone executable.
-int HandlerMain(int argc, char* argv[]);
+//!
+//! \param[in] user_stream_sources An optional vector containing the
+//!     extensibility data sources to call on crash. Each time a minidump is
+//!     created, the sources are called in turn. Any streams returned are added
+//!     to the minidump.
+int HandlerMain(int argc,
+                char* argv[],
+                const UserStreamDataSources* user_stream_sources);
 
 }  // namespace crashpad
 
diff --git a/third_party/crashpad/crashpad/handler/handler_test.gyp b/third_party/crashpad/crashpad/handler/handler_test.gyp
new file mode 100644
index 0000000..e526418
--- /dev/null
+++ b/third_party/crashpad/crashpad/handler/handler_test.gyp
@@ -0,0 +1,64 @@
+# Copyright 2017 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+  'includes': [
+    '../build/crashpad.gypi',
+  ],
+  'targets': [
+    {
+      'target_name': 'crashpad_handler_test_extended_handler',
+      'type': 'executable',
+      'dependencies': [
+        '../compat/compat.gyp:crashpad_compat',
+        '../minidump/minidump_test.gyp:crashpad_minidump_test_lib',
+        '../third_party/mini_chromium/mini_chromium.gyp:base',
+        '../tools/tools.gyp:crashpad_tool_support',
+        'handler.gyp:crashpad_handler_lib',
+      ],
+      'include_dirs': [
+        '..',
+      ],
+      'sources': [
+        'crashpad_handler_test_extended_handler.cc',
+      ],
+    },
+  ],
+  'conditions': [
+    ['OS=="win"', {
+      'targets': [{
+        # The handler is only tested on Windows for now.
+        'target_name': 'crashpad_handler_test',
+        'type': 'executable',
+        'dependencies': [
+          'crashpad_handler_test_extended_handler',
+          'handler.gyp:crashpad_handler_lib',
+          '../client/client.gyp:crashpad_client',
+          '../compat/compat.gyp:crashpad_compat',
+          '../test/test.gyp:crashpad_gtest_main',
+          '../test/test.gyp:crashpad_test',
+          '../third_party/gtest/gtest.gyp:gtest',
+          '../third_party/mini_chromium/mini_chromium.gyp:base',
+          '../util/util.gyp:crashpad_util',
+        ],
+        'include_dirs': [
+          '..',
+        ],
+        'sources': [
+          'crashpad_handler_test.cc',
+        ],
+      }],
+    }],
+  ],
+}
diff --git a/third_party/crashpad/crashpad/handler/mac/crash_report_exception_handler.cc b/third_party/crashpad/crashpad/handler/mac/crash_report_exception_handler.cc
index 7784afe..a96131c 100644
--- a/third_party/crashpad/crashpad/handler/mac/crash_report_exception_handler.cc
+++ b/third_party/crashpad/crashpad/handler/mac/crash_report_exception_handler.cc
@@ -22,6 +22,7 @@
 #include "base/strings/stringprintf.h"
 #include "client/settings.h"
 #include "minidump/minidump_file_writer.h"
+#include "minidump/minidump_user_extension_stream_data_source.h"
 #include "snapshot/crashpad_info_client_options.h"
 #include "snapshot/mac/process_snapshot_mac.h"
 #include "util/file/file_writer.h"
@@ -41,11 +42,12 @@
 CrashReportExceptionHandler::CrashReportExceptionHandler(
     CrashReportDatabase* database,
     CrashReportUploadThread* upload_thread,
-    const std::map<std::string, std::string>* process_annotations)
+    const std::map<std::string, std::string>* process_annotations,
+    const UserStreamDataSources* user_stream_data_sources)
     : database_(database),
       upload_thread_(upload_thread),
-      process_annotations_(process_annotations) {
-}
+      process_annotations_(process_annotations),
+      user_stream_data_sources_(user_stream_data_sources) {}
 
 CrashReportExceptionHandler::~CrashReportExceptionHandler() {
 }
@@ -169,6 +171,9 @@
 
     MinidumpFileWriter minidump;
     minidump.InitializeFromSnapshot(&process_snapshot);
+    AddUserExtensionStreams(
+        user_stream_data_sources_, &process_snapshot, &minidump);
+
     if (!minidump.WriteEverything(&file_writer)) {
       Metrics::ExceptionCaptureResult(
           Metrics::CaptureResult::kMinidumpWriteFailed);
diff --git a/third_party/crashpad/crashpad/handler/mac/crash_report_exception_handler.h b/third_party/crashpad/crashpad/handler/mac/crash_report_exception_handler.h
index 63d9bef..b878cfe4 100644
--- a/third_party/crashpad/crashpad/handler/mac/crash_report_exception_handler.h
+++ b/third_party/crashpad/crashpad/handler/mac/crash_report_exception_handler.h
@@ -23,6 +23,7 @@
 #include "base/macros.h"
 #include "client/crash_report_database.h"
 #include "handler/crash_report_upload_thread.h"
+#include "handler/user_stream_data_source.h"
 #include "util/mach/exc_server_variants.h"
 
 namespace crashpad {
@@ -49,7 +50,8 @@
   CrashReportExceptionHandler(
       CrashReportDatabase* database,
       CrashReportUploadThread* upload_thread,
-      const std::map<std::string, std::string>* process_annotations);
+      const std::map<std::string, std::string>* process_annotations,
+      const UserStreamDataSources* user_stream_data_sources);
 
   ~CrashReportExceptionHandler();
 
@@ -77,6 +79,7 @@
   CrashReportDatabase* database_;  // weak
   CrashReportUploadThread* upload_thread_;  // weak
   const std::map<std::string, std::string>* process_annotations_;  // weak
+  const UserStreamDataSources* user_stream_data_sources_;  // weak
 
   DISALLOW_COPY_AND_ASSIGN(CrashReportExceptionHandler);
 };
diff --git a/third_party/crashpad/crashpad/handler/main.cc b/third_party/crashpad/crashpad/handler/main.cc
index 7a169e2d..bc4ab99b 100644
--- a/third_party/crashpad/crashpad/handler/main.cc
+++ b/third_party/crashpad/crashpad/handler/main.cc
@@ -23,10 +23,18 @@
 
 #if defined(OS_MACOSX)
 int main(int argc, char* argv[]) {
-  return crashpad::HandlerMain(argc, argv);
+  return crashpad::HandlerMain(argc, argv, nullptr);
 }
 #elif defined(OS_WIN)
+namespace {
+
+int HandlerMainAdaptor(int argc, char* argv[]) {
+  return crashpad::HandlerMain(argc, argv, nullptr);
+}
+
+}  // namespace
+
 int APIENTRY wWinMain(HINSTANCE, HINSTANCE, wchar_t*, int) {
-  return crashpad::ToolSupport::Wmain(__argc, __wargv, crashpad::HandlerMain);
+  return crashpad::ToolSupport::Wmain(__argc, __wargv, HandlerMainAdaptor);
 }
 #endif  // OS_MACOSX
diff --git a/third_party/crashpad/crashpad/handler/user_stream_data_source.cc b/third_party/crashpad/crashpad/handler/user_stream_data_source.cc
new file mode 100644
index 0000000..7e30fa8c
--- /dev/null
+++ b/third_party/crashpad/crashpad/handler/user_stream_data_source.cc
@@ -0,0 +1,43 @@
+// Copyright 2017 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "handler/user_stream_data_source.h"
+
+#include "base/logging.h"
+#include "minidump/minidump_file_writer.h"
+#include "minidump/minidump_user_extension_stream_data_source.h"
+#include "snapshot/process_snapshot.h"
+
+namespace crashpad {
+
+void AddUserExtensionStreams(
+    const UserStreamDataSources* user_stream_data_sources,
+    ProcessSnapshot* process_snapshot,
+    MinidumpFileWriter* minidump_file_writer) {
+  if (!user_stream_data_sources)
+    return;
+  for (const auto& source : *user_stream_data_sources) {
+    std::unique_ptr<MinidumpUserExtensionStreamDataSource> data_source(
+        source->ProduceStreamData(process_snapshot));
+    if (data_source &&
+        !minidump_file_writer->AddUserExtensionStream(std::move(data_source))) {
+      // This should only happen if multiple user stream sources yield the
+      // same stream type. It's the user's responsibility to make sure
+      // sources don't collide on the same stream type.
+      LOG(ERROR) << "AddUserExtensionStream failed";
+    }
+  }
+}
+
+}  // namespace crashpad
diff --git a/third_party/crashpad/crashpad/handler/user_stream_data_source.h b/third_party/crashpad/crashpad/handler/user_stream_data_source.h
new file mode 100644
index 0000000..11bb9c3
--- /dev/null
+++ b/third_party/crashpad/crashpad/handler/user_stream_data_source.h
@@ -0,0 +1,68 @@
+// Copyright 2017 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_HANDLER_USER_STREAM_DATA_SOURCE_H_
+#define CRASHPAD_HANDLER_USER_STREAM_DATA_SOURCE_H_
+
+#include <memory>
+#include <vector>
+
+namespace crashpad {
+
+class MinidumpFileWriter;
+class MinidumpUserExtensionStreamDataSource;
+class ProcessSnapshot;
+
+//! \brief Extensibility interface for embedders who wish to add custom streams
+//!     to minidumps.
+class UserStreamDataSource {
+ public:
+  virtual ~UserStreamDataSource() {}
+
+  //! \brief Produce the contents for an extension stream for a crashed program.
+  //!
+  //! Called after \a process_snapshot has been initialized for the crashed
+  //! process to (optionally) produce the contents of a user extension stream
+  //! that will be attached to the minidump.
+  //!
+  //! \param[in] process_snapshot An initialized snapshot for the crashed
+  //!     process.
+  //!
+  //! \return A new data source for the stream to add to the minidump or
+  //!      `nullptr` on failure or to opt out of adding a stream.
+  virtual std::unique_ptr<MinidumpUserExtensionStreamDataSource>
+  ProduceStreamData(ProcessSnapshot* process_snapshot) = 0;
+};
+
+using UserStreamDataSources =
+    std::vector<std::unique_ptr<UserStreamDataSource>>;
+
+//! \brief Adds user extension streams to a minidump.
+//!
+//! Dispatches to each source in \a user_stream_data_sources and adds returned
+//! extension streams to \a minidump_file_writer.
+//!
+//! \param[in] user_stream_data_sources A pointer to the data sources, or
+//!     `nullptr`.
+//! \param[in] process_snapshot An initialized snapshot to the crashing process.
+//! \param[in] minidump_file_writer Any extension streams will be added to this
+//!     minidump.
+void AddUserExtensionStreams(
+    const UserStreamDataSources* user_stream_data_sources,
+    ProcessSnapshot* process_snapshot,
+    MinidumpFileWriter* minidump_file_writer);
+
+}  // namespace crashpad
+
+#endif  // CRASHPAD_HANDLER_USER_STREAM_DATA_SOURCE_H_
diff --git a/third_party/crashpad/crashpad/handler/win/crash_report_exception_handler.cc b/third_party/crashpad/crashpad/handler/win/crash_report_exception_handler.cc
index 180e6cb..7828aac6 100644
--- a/third_party/crashpad/crashpad/handler/win/crash_report_exception_handler.cc
+++ b/third_party/crashpad/crashpad/handler/win/crash_report_exception_handler.cc
@@ -20,6 +20,7 @@
 #include "client/settings.h"
 #include "handler/crash_report_upload_thread.h"
 #include "minidump/minidump_file_writer.h"
+#include "minidump/minidump_user_extension_stream_data_source.h"
 #include "snapshot/win/process_snapshot_win.h"
 #include "util/file/file_writer.h"
 #include "util/misc/metrics.h"
@@ -32,11 +33,12 @@
 CrashReportExceptionHandler::CrashReportExceptionHandler(
     CrashReportDatabase* database,
     CrashReportUploadThread* upload_thread,
-    const std::map<std::string, std::string>* process_annotations)
+    const std::map<std::string, std::string>* process_annotations,
+    const UserStreamDataSources* user_stream_data_sources)
     : database_(database),
       upload_thread_(upload_thread),
-      process_annotations_(process_annotations) {
-}
+      process_annotations_(process_annotations),
+      user_stream_data_sources_(user_stream_data_sources) {}
 
 CrashReportExceptionHandler::~CrashReportExceptionHandler() {
 }
@@ -107,6 +109,9 @@
 
     MinidumpFileWriter minidump;
     minidump.InitializeFromSnapshot(&process_snapshot);
+    AddUserExtensionStreams(
+        user_stream_data_sources_, &process_snapshot, &minidump);
+
     if (!minidump.WriteEverything(&file_writer)) {
       LOG(ERROR) << "WriteEverything failed";
       Metrics::ExceptionCaptureResult(
diff --git a/third_party/crashpad/crashpad/handler/win/crash_report_exception_handler.h b/third_party/crashpad/crashpad/handler/win/crash_report_exception_handler.h
index 11b440c4..521ccd3 100644
--- a/third_party/crashpad/crashpad/handler/win/crash_report_exception_handler.h
+++ b/third_party/crashpad/crashpad/handler/win/crash_report_exception_handler.h
@@ -21,6 +21,7 @@
 #include <string>
 
 #include "base/macros.h"
+#include "handler/user_stream_data_source.h"
 #include "util/win/exception_handler_server.h"
 
 namespace crashpad {
@@ -50,7 +51,8 @@
   CrashReportExceptionHandler(
       CrashReportDatabase* database,
       CrashReportUploadThread* upload_thread,
-      const std::map<std::string, std::string>* process_annotations);
+      const std::map<std::string, std::string>* process_annotations,
+      const UserStreamDataSources* user_stream_data_sources);
 
   ~CrashReportExceptionHandler() override;
 
@@ -68,6 +70,7 @@
   CrashReportDatabase* database_;  // weak
   CrashReportUploadThread* upload_thread_;  // weak
   const std::map<std::string, std::string>* process_annotations_;  // weak
+  const UserStreamDataSources* user_stream_data_sources_;  // weak
 
   DISALLOW_COPY_AND_ASSIGN(CrashReportExceptionHandler);
 };
diff --git a/third_party/crashpad/crashpad/minidump/minidump_file_writer.cc b/third_party/crashpad/crashpad/minidump/minidump_file_writer.cc
index 3e99d2a3..5a34f59 100644
--- a/third_party/crashpad/crashpad/minidump/minidump_file_writer.cc
+++ b/third_party/crashpad/crashpad/minidump/minidump_file_writer.cc
@@ -206,9 +206,8 @@
   DCHECK_EQ(state(), kStateMutable);
 
   auto user_stream = base::WrapUnique(new MinidumpUserStreamWriter());
-  user_stream->InitializeFromBuffer(user_extension_stream_data->stream_type(),
-                                    user_extension_stream_data->buffer(),
-                                    user_extension_stream_data->buffer_size());
+  user_stream->InitializeFromUserExtensionStream(
+      std::move(user_extension_stream_data));
 
   return AddStream(std::move(user_stream));
 }
diff --git a/third_party/crashpad/crashpad/minidump/minidump_file_writer_test.cc b/third_party/crashpad/crashpad/minidump/minidump_file_writer_test.cc
index 4429c11..63f2ade8 100644
--- a/third_party/crashpad/crashpad/minidump/minidump_file_writer_test.cc
+++ b/third_party/crashpad/crashpad/minidump/minidump_file_writer_test.cc
@@ -28,6 +28,7 @@
 #include "minidump/minidump_user_extension_stream_data_source.h"
 #include "minidump/minidump_writable.h"
 #include "minidump/test/minidump_file_writer_test_util.h"
+#include "minidump/test/minidump_user_extension_stream_util.h"
 #include "minidump/test/minidump_writable_test_util.h"
 #include "snapshot/test/test_cpu_context.h"
 #include "snapshot/test/test_exception_snapshot.h"
@@ -137,14 +138,14 @@
   const size_t kStreamSize = arraysize(kStreamData);
   const MinidumpStreamType kStreamType = static_cast<MinidumpStreamType>(0x4d);
 
-  auto stream = base::WrapUnique(new MinidumpUserExtensionStreamDataSource(
+  auto data_source = base::WrapUnique(new test::BufferExtensionStreamDataSource(
       kStreamType, kStreamData, kStreamSize));
-  ASSERT_TRUE(minidump_file.AddUserExtensionStream(std::move(stream)));
+  ASSERT_TRUE(minidump_file.AddUserExtensionStream(std::move(data_source)));
 
   // Adding the same stream type a second time should fail.
-  stream = base::WrapUnique(new MinidumpUserExtensionStreamDataSource(
+  data_source = base::WrapUnique(new test::BufferExtensionStreamDataSource(
       kStreamType, kStreamData, kStreamSize));
-  ASSERT_FALSE(minidump_file.AddUserExtensionStream(std::move(stream)));
+  ASSERT_FALSE(minidump_file.AddUserExtensionStream(std::move(data_source)));
 
   StringFile string_file;
   ASSERT_TRUE(minidump_file.WriteEverything(&string_file));
@@ -172,6 +173,37 @@
   EXPECT_EQ(memcmp(stream_data, kStreamData, kStreamSize), 0);
 }
 
+TEST(MinidumpFileWriter, AddEmptyUserExtensionStream) {
+  MinidumpFileWriter minidump_file;
+  const time_t kTimestamp = 0x155d2fb8;
+  minidump_file.SetTimestamp(kTimestamp);
+
+  const MinidumpStreamType kStreamType = static_cast<MinidumpStreamType>(0x4d);
+
+  auto data_source = base::WrapUnique(
+      new test::BufferExtensionStreamDataSource(kStreamType, nullptr, 0));
+  ASSERT_TRUE(minidump_file.AddUserExtensionStream(std::move(data_source)));
+
+  StringFile string_file;
+  ASSERT_TRUE(minidump_file.WriteEverything(&string_file));
+
+  const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER);
+  const size_t kStreamOffset = kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY);
+  const size_t kFileSize = kStreamOffset;
+
+  ASSERT_EQ(string_file.string().size(), kFileSize);
+
+  const MINIDUMP_DIRECTORY* directory;
+  const MINIDUMP_HEADER* header =
+      MinidumpHeaderAtStart(string_file.string(), &directory);
+  ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, kTimestamp));
+  ASSERT_TRUE(directory);
+
+  EXPECT_EQ(directory[0].StreamType, kStreamType);
+  EXPECT_EQ(directory[0].Location.DataSize, 0u);
+  EXPECT_EQ(directory[0].Location.Rva, kStreamOffset);
+}
+
 TEST(MinidumpFileWriter, ThreeStreams) {
   MinidumpFileWriter minidump_file;
   const time_t kTimestamp = 0x155d2fb8;
diff --git a/third_party/crashpad/crashpad/minidump/minidump_test.gyp b/third_party/crashpad/crashpad/minidump/minidump_test.gyp
index acf4d63..80f803db 100644
--- a/third_party/crashpad/crashpad/minidump/minidump_test.gyp
+++ b/third_party/crashpad/crashpad/minidump/minidump_test.gyp
@@ -18,9 +18,38 @@
   ],
   'targets': [
     {
+      'target_name': 'crashpad_minidump_test_lib',
+      'type': 'static_library',
+      'dependencies': [
+        'minidump.gyp:crashpad_minidump',
+        '../third_party/gtest/gtest.gyp:gtest',
+        '../third_party/mini_chromium/mini_chromium.gyp:base',
+      ],
+      'include_dirs': [
+        '..',
+      ],
+      'sources': [
+        'test/minidump_context_test_util.cc',
+        'test/minidump_context_test_util.h',
+        'test/minidump_file_writer_test_util.cc',
+        'test/minidump_file_writer_test_util.h',
+        'test/minidump_memory_writer_test_util.cc',
+        'test/minidump_memory_writer_test_util.h',
+        'test/minidump_rva_list_test_util.cc',
+        'test/minidump_rva_list_test_util.h',
+        'test/minidump_string_writer_test_util.cc',
+        'test/minidump_string_writer_test_util.h',
+        'test/minidump_user_extension_stream_util.cc',
+        'test/minidump_user_extension_stream_util.h',
+        'test/minidump_writable_test_util.cc',
+        'test/minidump_writable_test_util.h',
+      ],
+    },
+    {
       'target_name': 'crashpad_minidump_test',
       'type': 'executable',
       'dependencies': [
+        'crashpad_minidump_test_lib',
         'minidump.gyp:crashpad_minidump',
         '../snapshot/snapshot_test.gyp:crashpad_snapshot_test_lib',
         '../test/test.gyp:crashpad_gtest_main',
@@ -36,8 +65,8 @@
         'minidump_context_writer_test.cc',
         'minidump_crashpad_info_writer_test.cc',
         'minidump_exception_writer_test.cc',
-        'minidump_handle_writer_test.cc',
         'minidump_file_writer_test.cc',
+        'minidump_handle_writer_test.cc',
         'minidump_memory_info_writer_test.cc',
         'minidump_memory_writer_test.cc',
         'minidump_misc_info_writer_test.cc',
@@ -52,18 +81,6 @@
         'minidump_unloaded_module_writer_test.cc',
         'minidump_user_stream_writer_test.cc',
         'minidump_writable_test.cc',
-        'test/minidump_context_test_util.cc',
-        'test/minidump_context_test_util.h',
-        'test/minidump_file_writer_test_util.cc',
-        'test/minidump_file_writer_test_util.h',
-        'test/minidump_memory_writer_test_util.cc',
-        'test/minidump_memory_writer_test_util.h',
-        'test/minidump_rva_list_test_util.cc',
-        'test/minidump_rva_list_test_util.h',
-        'test/minidump_string_writer_test_util.cc',
-        'test/minidump_string_writer_test_util.h',
-        'test/minidump_writable_test_util.cc',
-        'test/minidump_writable_test_util.h',
       ],
     },
   ],
diff --git a/third_party/crashpad/crashpad/minidump/minidump_user_extension_stream_data_source.cc b/third_party/crashpad/crashpad/minidump/minidump_user_extension_stream_data_source.cc
index 8056011..884bf8591 100644
--- a/third_party/crashpad/crashpad/minidump/minidump_user_extension_stream_data_source.cc
+++ b/third_party/crashpad/crashpad/minidump/minidump_user_extension_stream_data_source.cc
@@ -17,12 +17,8 @@
 namespace crashpad {
 
 MinidumpUserExtensionStreamDataSource::MinidumpUserExtensionStreamDataSource(
-    uint32_t stream_type,
-    const void* buffer,
-    size_t buffer_size)
-    : stream_type_(static_cast<MinidumpStreamType>(stream_type)),
-      buffer_(buffer),
-      buffer_size_(buffer_size) {}
+    uint32_t stream_type)
+    : stream_type_(static_cast<MinidumpStreamType>(stream_type)) {}
 
 MinidumpUserExtensionStreamDataSource::
     ~MinidumpUserExtensionStreamDataSource() {}
diff --git a/third_party/crashpad/crashpad/minidump/minidump_user_extension_stream_data_source.h b/third_party/crashpad/crashpad/minidump/minidump_user_extension_stream_data_source.h
index 1afb166..3eb0c87 100644
--- a/third_party/crashpad/crashpad/minidump/minidump_user_extension_stream_data_source.h
+++ b/third_party/crashpad/crashpad/minidump/minidump_user_extension_stream_data_source.h
@@ -27,25 +27,54 @@
 //! \brief Describes a user extension data stream in a minidump.
 class MinidumpUserExtensionStreamDataSource {
  public:
+  //! \brief An interface implemented by readers of
+  //!     MinidumpUserExtensionStreamDataSource.
+  class Delegate {
+   public:
+    //! \brief Called by  MinidumpUserExtensionStreamDataSource::Read() to
+    //!     provide data requested by a call to that method.
+    //!
+    //! \param[in] data A pointer to the data that was read. The callee does not
+    //!     take ownership of this data. This data is only valid for the
+    //!     duration of the call to this method. This parameter may be `nullptr`
+    //!     if \a size is `0`.
+    //! \param[in] size The size of the data that was read.
+    //!
+    //! \return `true` on success, `false` on failure.
+    //!     MinidumpUserExtensionStreamDataSource::ReadStreamData() will use
+    //!     this as its own return value.
+    virtual bool ExtensionStreamDataSourceRead(const void* data,
+                                               size_t size) = 0;
+
+   protected:
+    ~Delegate() {}
+  };
+
   //! \brief Constructs a MinidumpUserExtensionStreamDataSource.
   //!
   //! \param[in] stream_type The type of the user extension stream.
-  //! \param[in] buffer Points to the data for this stream. \a buffer is not
-  //!     owned, and must outlive the use of this object.
-  //! \param[in] buffer_size The length of data in \a buffer.
-  MinidumpUserExtensionStreamDataSource(uint32_t stream_type,
-                                        const void* buffer,
-                                        size_t buffer_size);
-  ~MinidumpUserExtensionStreamDataSource();
+  explicit MinidumpUserExtensionStreamDataSource(uint32_t stream_type);
+  virtual ~MinidumpUserExtensionStreamDataSource();
 
   MinidumpStreamType stream_type() const { return stream_type_; }
-  const void* buffer() const { return buffer_; }
-  size_t buffer_size() const { return buffer_size_; }
+
+  //! \brief The size of this data stream.
+  virtual size_t StreamDataSize() = 0;
+
+  //! \brief Calls Delegate::UserStreamDataSourceRead(), providing it with
+  //!     the stream data.
+  //!
+  //! Implementations do not necessarily compute the stream data prior to
+  //! this method being called. The stream data may be computed or loaded
+  //! lazily and may be discarded after being passed to the delegate.
+  //!
+  //! \return `false` on failure, otherwise, the return value of
+  //!     Delegate::ExtensionStreamDataSourceRead(), which should be `true` on
+  //!     success and `false` on failure.
+  virtual bool ReadStreamData(Delegate* delegate) = 0;
 
  private:
   MinidumpStreamType stream_type_;
-  const void* buffer_;  // weak
-  size_t buffer_size_;
 
   DISALLOW_COPY_AND_ASSIGN(MinidumpUserExtensionStreamDataSource);
 };
diff --git a/third_party/crashpad/crashpad/minidump/minidump_user_stream_writer.cc b/third_party/crashpad/crashpad/minidump/minidump_user_stream_writer.cc
index 6520f0f..2cd5035 100644
--- a/third_party/crashpad/crashpad/minidump/minidump_user_stream_writer.cc
+++ b/third_party/crashpad/crashpad/minidump/minidump_user_stream_writer.cc
@@ -56,22 +56,32 @@
   DISALLOW_COPY_AND_ASSIGN(SnapshotContentsWriter);
 };
 
-class MinidumpUserStreamWriter::BufferContentsWriter final
-    : public MinidumpUserStreamWriter::ContentsWriter {
+class MinidumpUserStreamWriter::ExtensionStreamContentsWriter final
+    : public MinidumpUserStreamWriter::ContentsWriter,
+      public MinidumpUserExtensionStreamDataSource::Delegate {
  public:
-  BufferContentsWriter(const void* buffer, size_t buffer_size)
-      : buffer_(buffer), buffer_size_(buffer_size) {}
+  explicit ExtensionStreamContentsWriter(
+      std::unique_ptr<MinidumpUserExtensionStreamDataSource> data_source)
+      : data_source_(std::move(data_source)), writer_(nullptr) {}
 
   bool WriteContents(FileWriterInterface* writer) override {
-    return writer->Write(buffer_, buffer_size_);
+    DCHECK(!writer_);
+
+    writer_ = writer;
+    return data_source_->ReadStreamData(this);
   }
-  size_t GetSize() const override { return buffer_size_; }
+
+  size_t GetSize() const override { return data_source_->StreamDataSize(); }
+
+  bool ExtensionStreamDataSourceRead(const void* data, size_t size) override {
+    return writer_->Write(data, size);
+  }
 
  private:
-  const void* buffer_;
-  size_t buffer_size_;
+  std::unique_ptr<MinidumpUserExtensionStreamDataSource> data_source_;
+  FileWriterInterface* writer_;
 
-  DISALLOW_COPY_AND_ASSIGN(BufferContentsWriter);
+  DISALLOW_COPY_AND_ASSIGN(ExtensionStreamContentsWriter);
 };
 
 MinidumpUserStreamWriter::MinidumpUserStreamWriter() : stream_type_() {}
@@ -89,16 +99,14 @@
       base::WrapUnique(new SnapshotContentsWriter(stream->memory()));
 }
 
-void MinidumpUserStreamWriter::InitializeFromBuffer(
-    MinidumpStreamType stream_type,
-    const void* buffer,
-    size_t buffer_size) {
+void MinidumpUserStreamWriter::InitializeFromUserExtensionStream(
+    std::unique_ptr<MinidumpUserExtensionStreamDataSource> data_source) {
   DCHECK_EQ(state(), kStateMutable);
   DCHECK(!contents_writer_.get());
 
-  stream_type_ = stream_type;
-  contents_writer_ =
-      base::WrapUnique(new BufferContentsWriter(buffer, buffer_size));
+  stream_type_ = data_source->stream_type();
+  contents_writer_ = base::WrapUnique(
+      new ExtensionStreamContentsWriter(std::move(data_source)));
 }
 
 bool MinidumpUserStreamWriter::Freeze() {
diff --git a/third_party/crashpad/crashpad/minidump/minidump_user_stream_writer.h b/third_party/crashpad/crashpad/minidump/minidump_user_stream_writer.h
index 838ed0de..48698fd 100644
--- a/third_party/crashpad/crashpad/minidump/minidump_user_stream_writer.h
+++ b/third_party/crashpad/crashpad/minidump/minidump_user_stream_writer.h
@@ -25,6 +25,7 @@
 #include "minidump/minidump_extensions.h"
 #include "minidump/minidump_stream_writer.h"
 #include "minidump/minidump_writable.h"
+#include "minidump/minidump_user_extension_stream_data_source.h"
 #include "snapshot/module_snapshot.h"
 
 namespace crashpad {
@@ -42,17 +43,13 @@
   //! \note Valid in #kStateMutable.
   void InitializeFromSnapshot(const UserMinidumpStream* stream);
 
-  //! \brief Initializes a MINIDUMP_USER_STREAM based on \a stream_type,
-  //!     \a buffer and \a buffer_size.
+  //! \brief Initializes a MINIDUMP_USER_STREAM based on \a data_source.
   //!
-  //! \param[in] stream_type The type of the stream.
-  //! \param[in] buffer The data for the stream.
-  //! \param[in] buffer_size The length of \a buffer, and the resulting stream.
+  //! \param[in] data_source The content and type of the stream.
   //!
   //! \note Valid in #kStateMutable.
-  void InitializeFromBuffer(MinidumpStreamType stream_type,
-                            const void* buffer,
-                            size_t buffer_size);
+  void InitializeFromUserExtensionStream(
+      std::unique_ptr<MinidumpUserExtensionStreamDataSource> data_source);
 
  protected:
   // MinidumpWritable:
@@ -67,7 +64,7 @@
  private:
   class ContentsWriter;
   class SnapshotContentsWriter;
-  class BufferContentsWriter;
+  class ExtensionStreamContentsWriter;
 
   std::unique_ptr<ContentsWriter> contents_writer_;
 
diff --git a/third_party/crashpad/crashpad/minidump/minidump_user_stream_writer_test.cc b/third_party/crashpad/crashpad/minidump/minidump_user_stream_writer_test.cc
index 45089ca..e6627e6 100644
--- a/third_party/crashpad/crashpad/minidump/minidump_user_stream_writer_test.cc
+++ b/third_party/crashpad/crashpad/minidump/minidump_user_stream_writer_test.cc
@@ -21,6 +21,7 @@
 #include "gtest/gtest.h"
 #include "minidump/minidump_file_writer.h"
 #include "minidump/test/minidump_file_writer_test_util.h"
+#include "minidump/test/minidump_user_extension_stream_util.h"
 #include "minidump/test/minidump_writable_test_util.h"
 #include "snapshot/test/test_memory_snapshot.h"
 #include "util/file/string_file.h"
@@ -74,10 +75,12 @@
       string_file.string(), &user_stream_location, kTestStreamId, 0u));
 }
 
-TEST(MinidumpUserStreamWriter, InitializeFromBufferNoData) {
+TEST(MinidumpUserStreamWriter, InitializeFromUserExtensionStreamNoData) {
   MinidumpFileWriter minidump_file_writer;
+  auto data_source = base::WrapUnique(
+      new test::BufferExtensionStreamDataSource(kTestStreamId, nullptr, 0));
   auto user_stream_writer = base::WrapUnique(new MinidumpUserStreamWriter());
-  user_stream_writer->InitializeFromBuffer(kTestStreamId, nullptr, 0);
+  user_stream_writer->InitializeFromUserExtensionStream(std::move(data_source));
   minidump_file_writer.AddStream(std::move(user_stream_writer));
 
   StringFile string_file;
@@ -121,12 +124,13 @@
 
 TEST(MinidumpUserStreamWriter, InitializeFromBufferOneStream) {
   MinidumpFileWriter minidump_file_writer;
-  auto user_stream_writer = base::WrapUnique(new MinidumpUserStreamWriter());
 
   const size_t kStreamSize = 128;
   std::vector<uint8_t> data(kStreamSize, 'c');
-  user_stream_writer->InitializeFromBuffer(
-      kTestStreamId, &data[0], data.size());
+  auto data_source = base::WrapUnique(new test::BufferExtensionStreamDataSource(
+      kTestStreamId, &data[0], data.size()));
+  auto user_stream_writer = base::WrapUnique(new MinidumpUserStreamWriter());
+  user_stream_writer->InitializeFromUserExtensionStream(std::move(data_source));
   minidump_file_writer.AddStream(std::move(user_stream_writer));
 
   StringFile string_file;
diff --git a/third_party/crashpad/crashpad/minidump/test/minidump_user_extension_stream_util.cc b/third_party/crashpad/crashpad/minidump/test/minidump_user_extension_stream_util.cc
new file mode 100644
index 0000000..5010082
--- /dev/null
+++ b/third_party/crashpad/crashpad/minidump/test/minidump_user_extension_stream_util.cc
@@ -0,0 +1,43 @@
+// Copyright 2017 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "minidump/test/minidump_user_extension_stream_util.h"
+
+#include <string.h>
+
+namespace crashpad {
+namespace test {
+
+BufferExtensionStreamDataSource::BufferExtensionStreamDataSource(
+    uint32_t stream_type,
+    const void* data,
+    size_t data_size)
+    : MinidumpUserExtensionStreamDataSource(stream_type) {
+  data_.resize(data_size);
+
+  if (data_size)
+    memcpy(data_.data(), data, data_size);
+}
+
+size_t BufferExtensionStreamDataSource::StreamDataSize() {
+  return data_.size();
+}
+
+bool BufferExtensionStreamDataSource::ReadStreamData(Delegate* delegate) {
+  return delegate->ExtensionStreamDataSourceRead(
+      data_.size() ? data_.data() : nullptr, data_.size());
+}
+
+}  // namespace test
+}  // namespace crashpad
diff --git a/third_party/crashpad/crashpad/minidump/test/minidump_user_extension_stream_util.h b/third_party/crashpad/crashpad/minidump/test/minidump_user_extension_stream_util.h
new file mode 100644
index 0000000..3a31b948
--- /dev/null
+++ b/third_party/crashpad/crashpad/minidump/test/minidump_user_extension_stream_util.h
@@ -0,0 +1,53 @@
+// Copyright 2017 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_MINIDUMP_TEST_MINIDUMP_USER_EXTENSION_STREAM_UTIL_H_
+#define CRASHPAD_MINIDUMP_TEST_MINIDUMP_USER_EXTENSION_STREAM_UTIL_H_
+
+#include "minidump/minidump_user_extension_stream_data_source.h"
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <vector>
+
+namespace crashpad {
+namespace test {
+
+//! \brief A user extension data source that wraps a buffer.
+class BufferExtensionStreamDataSource final
+    : public MinidumpUserExtensionStreamDataSource {
+ public:
+  //! \brief Creates a data source with \a stream_type.
+  //!
+  //! param[in] stream_type The type of the stream.
+  //! param[in] data The data of the stream.
+  //! param[in] data_size The length of \a data.
+  BufferExtensionStreamDataSource(uint32_t stream_type,
+                                  const void* data,
+                                  size_t data_size);
+
+  size_t StreamDataSize() override;
+  bool ReadStreamData(Delegate* delegate) override;
+
+ private:
+  std::vector<uint8_t> data_;
+
+  DISALLOW_COPY_AND_ASSIGN(BufferExtensionStreamDataSource);
+};
+
+}  // namespace test
+}  // namespace crashpad
+
+#endif  // CRASHPAD_MINIDUMP_TEST_MINIDUMP_USER_EXTENSION_STREAM_UTIL_H_
diff --git a/third_party/crashpad/crashpad/snapshot/win/process_reader_win.cc b/third_party/crashpad/crashpad/snapshot/win/process_reader_win.cc
index b7bae6a..f8a2f92 100644
--- a/third_party/crashpad/crashpad/snapshot/win/process_reader_win.cc
+++ b/third_party/crashpad/crashpad/snapshot/win/process_reader_win.cc
@@ -57,6 +57,7 @@
     HANDLE process_handle,
     std::unique_ptr<uint8_t[]>* buffer) {
   ULONG buffer_size = 16384;
+  ULONG actual_size;
   buffer->reset(new uint8_t[buffer_size]);
   NTSTATUS status;
   // This must be in retry loop, as we're racing with process creation on the
@@ -66,13 +67,19 @@
         SystemProcessInformation,
         reinterpret_cast<void*>(buffer->get()),
         buffer_size,
-        &buffer_size);
+        &actual_size);
     if (status == STATUS_BUFFER_TOO_SMALL ||
         status == STATUS_INFO_LENGTH_MISMATCH) {
+      DCHECK_GT(actual_size, buffer_size);
+
       // Add a little extra to try to avoid an additional loop iteration. We're
       // racing with system-wide process creation between here and the next call
       // to NtQuerySystemInformation().
-      buffer_size += 4096;
+      buffer_size = actual_size + 4096;
+
+      // Free the old buffer before attempting to allocate a new one.
+      buffer->reset();
+
       buffer->reset(new uint8_t[buffer_size]);
     } else {
       break;
@@ -84,6 +91,8 @@
     return nullptr;
   }
 
+  DCHECK_LE(actual_size, buffer_size);
+
   process_types::SYSTEM_PROCESS_INFORMATION<Traits>* process =
       reinterpret_cast<process_types::SYSTEM_PROCESS_INFORMATION<Traits>*>(
           buffer->get());
diff --git a/third_party/crashpad/crashpad/test/scoped_temp_dir_test.cc b/third_party/crashpad/crashpad/test/scoped_temp_dir_test.cc
index cb09732..9ad801f 100644
--- a/third_party/crashpad/crashpad/test/scoped_temp_dir_test.cc
+++ b/third_party/crashpad/crashpad/test/scoped_temp_dir_test.cc
@@ -38,8 +38,10 @@
 #if defined(OS_POSIX)
   int fd = HANDLE_EINTR(creat(path.value().c_str(), 0644));
   ASSERT_GE(fd, 0) << ErrnoMessage("creat") << " " << path.value();
-  ASSERT_EQ(IGNORE_EINTR(close(fd)), 0) << ErrnoMessage("close") << " "
-                                        << path.value();
+
+  // gcc refuses to compile ASSERT_EQ(IGNORE_EINTR(close(fd)), 0).
+  int close_rv = IGNORE_EINTR(close(fd));
+  ASSERT_EQ(close_rv, 0) << ErrnoMessage("close") << " " << path.value();
 #elif defined(OS_WIN)
   int fd = _wcreat(path.value().c_str(), _S_IREAD | _S_IWRITE);
   ASSERT_GE(fd, 0) << ErrnoMessage("_wcreat") << " " << path.value();
diff --git a/third_party/crashpad/crashpad/test/test.gyp b/third_party/crashpad/crashpad/test/test.gyp
index f1adf9c..94b198c 100644
--- a/third_party/crashpad/crashpad/test/test.gyp
+++ b/third_party/crashpad/crashpad/test/test.gyp
@@ -61,6 +61,8 @@
         'win/win_child_process.h',
         'win/win_multiprocess.cc',
         'win/win_multiprocess.h',
+        'win/win_multiprocess_with_temp_dir.cc',
+        'win/win_multiprocess_with_temp_dir.h',
       ],
       'direct_dependent_settings': {
         'include_dirs': [
diff --git a/third_party/crashpad/crashpad/test/win/win_multiprocess_with_temp_dir.cc b/third_party/crashpad/crashpad/test/win/win_multiprocess_with_temp_dir.cc
new file mode 100644
index 0000000..13e38a5
--- /dev/null
+++ b/third_party/crashpad/crashpad/test/win/win_multiprocess_with_temp_dir.cc
@@ -0,0 +1,188 @@
+// Copyright 2017 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "test/win/win_multiprocess_with_temp_dir.h"
+
+#include <tlhelp32.h>
+
+#include "base/memory/ptr_util.h"
+#include "test/errors.h"
+#include "util/win/process_info.h"
+
+namespace crashpad {
+namespace test {
+
+namespace {
+
+constexpr wchar_t kTempDirEnvName[] = L"CRASHPAD_TEST_TEMP_DIR";
+
+// Returns the process IDs of all processes that have |parent_pid| as
+// parent process ID.
+std::vector<pid_t> GetPotentialChildProcessesOf(pid_t parent_pid) {
+  ScopedFileHANDLE snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0));
+  if (!snapshot.is_valid()) {
+    ADD_FAILURE() << ErrorMessage("CreateToolhelp32Snapshot");
+    return std::vector<pid_t>();
+  }
+
+  PROCESSENTRY32 entry = {sizeof(entry)};
+  if (!Process32First(snapshot.get(), &entry)) {
+    ADD_FAILURE() << ErrorMessage("Process32First");
+    return std::vector<pid_t>();
+  }
+
+  std::vector<pid_t> child_pids;
+  do {
+    if (entry.th32ParentProcessID == parent_pid)
+      child_pids.push_back(entry.th32ProcessID);
+  } while (Process32Next(snapshot.get(), &entry));
+
+  return child_pids;
+}
+
+ULARGE_INTEGER GetProcessCreationTime(HANDLE process) {
+  ULARGE_INTEGER ret = {};
+  FILETIME creation_time;
+  FILETIME dummy;
+  if (GetProcessTimes(process, &creation_time, &dummy, &dummy, &dummy)) {
+    ret.LowPart = creation_time.dwLowDateTime;
+    ret.HighPart = creation_time.dwHighDateTime;
+  } else {
+    ADD_FAILURE() << ErrorMessage("GetProcessTimes");
+  }
+
+  return ret;
+}
+
+// Waits for the processes directly created by |parent| - and specifically
+// not their offspring. For this to work without race, |parent| has to be
+// suspended or have exited.
+void WaitForAllChildProcessesOf(HANDLE parent) {
+  pid_t parent_pid = GetProcessId(parent);
+  std::vector<pid_t> child_pids = GetPotentialChildProcessesOf(parent_pid);
+
+  ULARGE_INTEGER parent_creationtime = GetProcessCreationTime(parent);
+  for (pid_t child_pid : child_pids) {
+    // Try and open the process. This may fail for reasons such as:
+    // 1. The process isn't |parent|'s child process, but rather a
+    //    higher-privilege sub-process of an earlier process that had
+    //    |parent|'s PID.
+    // 2. The process no longer exists, e.g. it exited after enumeration.
+    ScopedKernelHANDLE child_process(
+        OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION | SYNCHRONIZE,
+                    false,
+                    child_pid));
+    if (!child_process.is_valid())
+      continue;
+
+    // Check that the child now has the right parent PID, as its PID may have
+    // been reused after the enumeration above.
+    ProcessInfo child_info;
+    if (!child_info.Initialize(child_process.get())) {
+      // This can happen if child_process has exited after the handle is opened.
+      LOG(ERROR) << "ProcessInfo::Initialize, pid: " << child_pid;
+      continue;
+    }
+
+    if (parent_pid != child_info.ParentProcessID()) {
+      // The child's process ID was reused after enumeration.
+      continue;
+    }
+
+    // We successfully opened |child_process| and it has |parent|'s PID for
+    // parent process ID. However, this could still be a sub-process of another
+    // process that earlier had |parent|'s PID. To make sure, check that
+    // |child_process| was created after |parent_process|.
+    ULARGE_INTEGER process_creationtime =
+        GetProcessCreationTime(child_process.get());
+    if (process_creationtime.QuadPart < parent_creationtime.QuadPart)
+      continue;
+
+    DWORD err = WaitForSingleObject(child_process.get(), INFINITE);
+    if (err == WAIT_FAILED) {
+      ADD_FAILURE() << ErrorMessage("WaitForSingleObject");
+    } else if (err != WAIT_OBJECT_0) {
+      ADD_FAILURE() << "WaitForSingleObject returned " << err;
+    }
+  }
+}
+
+}  // namespace
+
+WinMultiprocessWithTempDir::WinMultiprocessWithTempDir()
+    : WinMultiprocess(), temp_dir_env_(kTempDirEnvName) {}
+
+void WinMultiprocessWithTempDir::WinMultiprocessParentBeforeChild() {
+  temp_dir_ = base::WrapUnique(new ScopedTempDir);
+  temp_dir_env_.SetValue(temp_dir_->path().value().c_str());
+}
+
+void WinMultiprocessWithTempDir::WinMultiprocessParentAfterChild(HANDLE child) {
+  WaitForAllChildProcessesOf(child);
+  temp_dir_.reset();
+}
+
+base::FilePath WinMultiprocessWithTempDir::GetTempDirPath() const {
+  return base::FilePath(temp_dir_env_.GetValue());
+}
+
+WinMultiprocessWithTempDir::ScopedEnvironmentVariable::
+    ScopedEnvironmentVariable(const wchar_t* name)
+    : name_(name) {
+  original_value_ = GetValueImpl(&was_defined_);
+}
+
+WinMultiprocessWithTempDir::ScopedEnvironmentVariable::
+    ~ScopedEnvironmentVariable() {
+  if (was_defined_)
+    SetValue(original_value_.data());
+  else
+    SetValue(nullptr);
+}
+
+std::wstring WinMultiprocessWithTempDir::ScopedEnvironmentVariable::GetValue()
+    const {
+  bool dummy;
+  return GetValueImpl(&dummy);
+}
+
+std::wstring
+WinMultiprocessWithTempDir::ScopedEnvironmentVariable::GetValueImpl(
+    bool* is_defined) const {
+  // The length returned is inclusive of the terminating zero, except
+  // if the variable doesn't exist, in which case the return value is zero.
+  DWORD len = GetEnvironmentVariable(name_, nullptr, 0);
+  if (len == 0) {
+    *is_defined = false;
+    return L"";
+  }
+
+  *is_defined = true;
+
+  std::wstring ret;
+  ret.resize(len);
+  // The length returned on success is exclusive of the terminating zero.
+  len = GetEnvironmentVariable(name_, &ret[0], len);
+  ret.resize(len);
+
+  return ret;
+}
+
+void WinMultiprocessWithTempDir::ScopedEnvironmentVariable::SetValue(
+    const wchar_t* new_value) const {
+  SetEnvironmentVariable(name_, new_value);
+}
+
+}  // namespace test
+}  // namespace crashpad
diff --git a/third_party/crashpad/crashpad/test/win/win_multiprocess_with_temp_dir.h b/third_party/crashpad/crashpad/test/win/win_multiprocess_with_temp_dir.h
new file mode 100644
index 0000000..c9b48607
--- /dev/null
+++ b/third_party/crashpad/crashpad/test/win/win_multiprocess_with_temp_dir.h
@@ -0,0 +1,80 @@
+// Copyright 2017 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_TEST_WIN_WIN_MULTIPROCESS_WITH_TEMPDIR_H_
+#define CRASHPAD_TEST_WIN_WIN_MULTIPROCESS_WITH_TEMPDIR_H_
+
+#include <wchar.h>
+#include <windows.h>
+
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "test/scoped_temp_dir.h"
+#include "test/win/win_multiprocess.h"
+
+namespace crashpad {
+namespace test {
+
+//! \brief Manages a multiprocess test on Windows with a parent-created
+//!     temporary directory.
+//!
+//! This class creates a temp directory in the parent process for the use of
+//! the subprocess and its children. To ensure a raceless rundown, it waits on
+//! the child process and any processes directly created by the child before
+//! deleting the temporary directory.
+class WinMultiprocessWithTempDir : public WinMultiprocess {
+ public:
+  WinMultiprocessWithTempDir();
+
+ protected:
+  void WinMultiprocessParentBeforeChild() override;
+  void WinMultiprocessParentAfterChild(HANDLE child) override;
+
+  //! \brief Returns the path of the temp directory.
+  base::FilePath GetTempDirPath() const;
+
+ private:
+  class ScopedEnvironmentVariable {
+   public:
+    explicit ScopedEnvironmentVariable(const wchar_t* name);
+    ~ScopedEnvironmentVariable();
+
+    std::wstring GetValue() const;
+
+    // Sets this environment variable to |new_value|. If |new_value| is nullptr
+    // this environment variable will be undefined.
+    void SetValue(const wchar_t* new_value) const;
+
+   private:
+    std::wstring GetValueImpl(bool* is_defined) const;
+
+    std::wstring original_value_;
+    const wchar_t* name_;
+    bool was_defined_;
+
+    DISALLOW_COPY_AND_ASSIGN(ScopedEnvironmentVariable);
+  };
+
+  std::unique_ptr<ScopedTempDir> temp_dir_;
+  ScopedEnvironmentVariable temp_dir_env_;
+
+  DISALLOW_COPY_AND_ASSIGN(WinMultiprocessWithTempDir);
+};
+
+}  // namespace test
+}  // namespace crashpad
+
+#endif  // CRASHPAD_TEST_WIN_WIN_MULTIPROCESS_WITH_TEMPDIR_H_
diff --git a/third_party/crashpad/crashpad/util/linux/process_memory.cc b/third_party/crashpad/crashpad/util/linux/process_memory.cc
index 72b05bfdd..8f3290a 100644
--- a/third_party/crashpad/crashpad/util/linux/process_memory.cc
+++ b/third_party/crashpad/crashpad/util/linux/process_memory.cc
@@ -50,9 +50,9 @@
   char* buffer_c = static_cast<char*>(buffer);
   while (size > 0) {
     ssize_t bytes_read =
-        HANDLE_EINTR(pread(mem_fd_.get(), buffer_c, size, address));
+        HANDLE_EINTR(pread64(mem_fd_.get(), buffer_c, size, address));
     if (bytes_read < 0) {
-      PLOG(ERROR) << "pread";
+      PLOG(ERROR) << "pread64";
       return false;
     }
     if (bytes_read == 0) {
@@ -95,9 +95,10 @@
       read_size = sizeof(buffer);
     }
     ssize_t bytes_read;
-    bytes_read = HANDLE_EINTR(pread(mem_fd_.get(), buffer, read_size, address));
+    bytes_read =
+        HANDLE_EINTR(pread64(mem_fd_.get(), buffer, read_size, address));
     if (bytes_read < 0) {
-      PLOG(ERROR) << "pread";
+      PLOG(ERROR) << "pread64";
       return false;
     }
     if (bytes_read == 0) {
diff --git a/third_party/crashpad/crashpad/util/linux/scoped_ptrace_attach.cc b/third_party/crashpad/crashpad/util/linux/scoped_ptrace_attach.cc
new file mode 100644
index 0000000..09e3fba
--- /dev/null
+++ b/third_party/crashpad/crashpad/util/linux/scoped_ptrace_attach.cc
@@ -0,0 +1,62 @@
+// Copyright 2017 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/linux/scoped_ptrace_attach.h"
+
+#include <sys/ptrace.h>
+#include <sys/wait.h>
+
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+
+namespace crashpad {
+
+ScopedPtraceAttach::ScopedPtraceAttach()
+    : pid_(-1) {}
+
+ScopedPtraceAttach::~ScopedPtraceAttach() {
+  Reset();
+}
+
+bool ScopedPtraceAttach::Reset() {
+  if (pid_ >= 0 && ptrace(PTRACE_DETACH, pid_, nullptr, nullptr) != 0) {
+    PLOG(ERROR) << "ptrace";
+    return false;
+  }
+  pid_ = -1;
+  return true;
+}
+
+bool ScopedPtraceAttach::ResetAttach(pid_t pid) {
+  Reset();
+
+  if (ptrace(PTRACE_ATTACH, pid, nullptr, nullptr) != 0) {
+    PLOG(ERROR) << "ptrace";
+    return false;
+  }
+  pid_ = pid;
+
+  int status;
+  if (HANDLE_EINTR(waitpid(pid_, &status, __WALL)) < 0) {
+    PLOG(ERROR) << "waitpid";
+    return false;
+  }
+  if (!WIFSTOPPED(status)) {
+    LOG(ERROR) << "process not stopped";
+    return false;
+  }
+  return true;
+}
+
+}  // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/linux/scoped_ptrace_attach.h b/third_party/crashpad/crashpad/util/linux/scoped_ptrace_attach.h
new file mode 100644
index 0000000..a3d9d69
--- /dev/null
+++ b/third_party/crashpad/crashpad/util/linux/scoped_ptrace_attach.h
@@ -0,0 +1,52 @@
+// Copyright 2017 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_LINUX_SCOPED_PTRACE_ATTACH_H_
+#define CRASHPAD_UTIL_LINUX_SCOPED_PTRACE_ATTACH_H_
+
+#include <sys/types.h>
+
+#include "base/macros.h"
+
+namespace crashpad {
+
+//! \brief Maintains a `ptrace()` attachment to a process.
+//!
+//! On destruction, the process will be detached.
+class ScopedPtraceAttach {
+ public:
+  ScopedPtraceAttach();
+  ~ScopedPtraceAttach();
+
+  //! \brief Detaches from the process by calling `ptrace()`.
+  //!
+  //! \return `true` on success. `false` on failure, with a message logged.
+  bool Reset();
+
+  //! \brief Detaches from any previously attached process, attaches to the
+  //!      process with process ID \a pid, and blocks until the target process
+  //!      has stopped by calling `waitpid()`.
+  //!
+  //! \return `true` on success. `false` on failure, with a message logged.
+  bool ResetAttach(pid_t pid);
+
+ private:
+  pid_t pid_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedPtraceAttach);
+};
+
+}  // namespace crashpad
+
+#endif  // CRASHPAD_UTIL_LINUX_SCOPED_PTRACE_ATTACH_H_
diff --git a/third_party/crashpad/crashpad/util/linux/scoped_ptrace_attach_test.cc b/third_party/crashpad/crashpad/util/linux/scoped_ptrace_attach_test.cc
new file mode 100644
index 0000000..99072e4c
--- /dev/null
+++ b/third_party/crashpad/crashpad/util/linux/scoped_ptrace_attach_test.cc
@@ -0,0 +1,204 @@
+// Copyright 2017 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/linux/scoped_ptrace_attach.h"
+
+#include <errno.h>
+#include <sys/prctl.h>
+#include <sys/ptrace.h>
+#include <unistd.h>
+
+#include "gtest/gtest.h"
+#include "test/errors.h"
+#include "test/multiprocess.h"
+#include "util/file/file_io.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+class ScopedPrSetPtracer {
+ public:
+  explicit ScopedPrSetPtracer(pid_t pid) {
+    // PR_SET_PTRACER is only supported if the Yama Linux security module (LSM)
+    // is enabled. Otherwise, this prctl() call fails with EINVAL. See
+    // linux-4.9.20/security/yama/yama_lsm.c yama_task_prctl() and
+    // linux-4.9.20/kernel/sys.c [sys_]prctl().
+    //
+    // If Yama is not enabled, the default ptrace restrictions should be
+    // sufficient for these tests.
+    //
+    // If Yama is enabled, then /proc/sys/kernel/yama/ptrace_scope must be 0
+    // (YAMA_SCOPE_DISABLED, in which case this prctl() is not necessary) or 1
+    // (YAMA_SCOPE_RELATIONAL) for these tests to succeed. If it is 2
+    // (YAMA_SCOPE_CAPABILITY) then the test requires CAP_SYS_PTRACE, and if it
+    // is 3 (YAMA_SCOPE_NO_ATTACH), these tests will fail.
+    success_ = prctl(PR_SET_PTRACER, pid, 0, 0, 0) == 0;
+    if (!success_) {
+      EXPECT_EQ(errno, EINVAL) << ErrnoMessage("prctl");
+    }
+  }
+
+  ~ScopedPrSetPtracer() {
+    if (success_) {
+      EXPECT_EQ(prctl(PR_SET_PTRACER, 0, 0, 0, 0), 0) << ErrnoMessage("prctl");
+    }
+  }
+
+ private:
+  bool success_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedPrSetPtracer);
+};
+
+class AttachTest : public Multiprocess {
+ public:
+  AttachTest() : Multiprocess() {}
+  ~AttachTest() {}
+
+ protected:
+  const long kWord = 42;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AttachTest);
+};
+
+class AttachToChildTest : public AttachTest {
+ public:
+  AttachToChildTest() : AttachTest() {}
+  ~AttachToChildTest() {}
+
+ private:
+  void MultiprocessParent() override {
+    // Wait for the child to set the parent as its ptracer.
+    char c;
+    CheckedReadFileExactly(ReadPipeHandle(), &c, sizeof(c));
+
+    pid_t pid = ChildPID();
+
+    ASSERT_EQ(ptrace(PTRACE_PEEKDATA, pid, &kWord, nullptr), -1);
+    EXPECT_EQ(errno, ESRCH) << ErrnoMessage("ptrace");
+
+    ScopedPtraceAttach attachment;
+    ASSERT_EQ(attachment.ResetAttach(pid), true);
+    EXPECT_EQ(ptrace(PTRACE_PEEKDATA, pid, &kWord, nullptr), kWord)
+        << ErrnoMessage("ptrace");
+    attachment.Reset();
+
+    ASSERT_EQ(ptrace(PTRACE_PEEKDATA, pid, &kWord, nullptr), -1);
+    EXPECT_EQ(errno, ESRCH) << ErrnoMessage("ptrace");
+  }
+
+  void MultiprocessChild() override {
+    ScopedPrSetPtracer set_ptracer(getppid());
+
+    char c = '\0';
+    CheckedWriteFile(WritePipeHandle(), &c, sizeof(c));
+
+    CheckedReadFileAtEOF(ReadPipeHandle());
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(AttachToChildTest);
+};
+
+TEST(ScopedPtraceAttach, AttachChild) {
+  AttachToChildTest test;
+  test.Run();
+}
+
+class AttachToParentResetTest : public AttachTest {
+ public:
+  AttachToParentResetTest() : AttachTest() {}
+  ~AttachToParentResetTest() {}
+
+ private:
+  void MultiprocessParent() override {
+    ScopedPrSetPtracer set_ptracer(ChildPID());
+    char c = '\0';
+    CheckedWriteFile(WritePipeHandle(), &c, sizeof(c));
+
+    CheckedReadFileAtEOF(ReadPipeHandle());
+  }
+
+  void MultiprocessChild() override {
+    // Wait for the parent to set the child as its ptracer.
+    char c;
+    CheckedReadFileExactly(ReadPipeHandle(), &c, sizeof(c));
+
+    pid_t pid = getppid();
+
+    ASSERT_EQ(ptrace(PTRACE_PEEKDATA, pid, &kWord, nullptr), -1);
+    EXPECT_EQ(errno, ESRCH) << ErrnoMessage("ptrace");
+
+    ScopedPtraceAttach attachment;
+    ASSERT_EQ(attachment.ResetAttach(pid), true);
+    EXPECT_EQ(ptrace(PTRACE_PEEKDATA, pid, &kWord, nullptr), kWord)
+        << ErrnoMessage("ptrace");
+    attachment.Reset();
+
+    ASSERT_EQ(ptrace(PTRACE_PEEKDATA, pid, &kWord, nullptr), -1);
+    EXPECT_EQ(errno, ESRCH) << ErrnoMessage("ptrace");
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(AttachToParentResetTest);
+};
+
+TEST(ScopedPtraceAttach, AttachParentReset) {
+  AttachToParentResetTest test;
+  test.Run();
+}
+
+class AttachToParentDestructorTest : public AttachTest {
+ public:
+  AttachToParentDestructorTest() : AttachTest() {}
+  ~AttachToParentDestructorTest() {}
+
+ private:
+  void MultiprocessParent() override {
+    ScopedPrSetPtracer set_ptracer(ChildPID());
+    char c = '\0';
+    CheckedWriteFile(WritePipeHandle(), &c, sizeof(c));
+
+    CheckedReadFileAtEOF(ReadPipeHandle());
+  }
+
+  void MultiprocessChild() override {
+    // Wait for the parent to set the child as its ptracer.
+    char c;
+    CheckedReadFileExactly(ReadPipeHandle(), &c, sizeof(c));
+
+    pid_t pid = getppid();
+    ASSERT_EQ(ptrace(PTRACE_PEEKDATA, pid, &kWord, nullptr), -1);
+    EXPECT_EQ(errno, ESRCH) << ErrnoMessage("ptrace");
+    {
+      ScopedPtraceAttach attachment;
+      ASSERT_EQ(attachment.ResetAttach(pid), true);
+      EXPECT_EQ(ptrace(PTRACE_PEEKDATA, pid, &kWord, nullptr), kWord)
+          << ErrnoMessage("ptrace");
+    }
+    ASSERT_EQ(ptrace(PTRACE_PEEKDATA, pid, &kWord, nullptr), -1);
+    EXPECT_EQ(errno, ESRCH) << ErrnoMessage("ptrace");
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(AttachToParentDestructorTest);
+};
+
+TEST(ScopedPtraceAttach, AttachParentDestructor) {
+  AttachToParentDestructorTest test;
+  test.Run();
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/posix/close_multiple.cc b/third_party/crashpad/crashpad/util/posix/close_multiple.cc
index 3749191b..908febf 100644
--- a/third_party/crashpad/crashpad/util/posix/close_multiple.cc
+++ b/third_party/crashpad/crashpad/util/posix/close_multiple.cc
@@ -24,7 +24,6 @@
 #include <unistd.h>
 
 #include <algorithm>
-#include <memory>
 
 #include "base/files/scoped_file.h"
 #include "base/logging.h"
@@ -32,6 +31,7 @@
 #include "build/build_config.h"
 #include "util/misc/implicit_cast.h"
 #include "util/numeric/safe_assignment.h"
+#include "util/posix/scoped_dir.h"
 
 #if defined(OS_MACOSX)
 #include <sys/sysctl.h>
@@ -69,18 +69,6 @@
   }
 }
 
-struct ScopedDIRCloser {
-  void operator()(DIR* dir) const {
-    if (dir) {
-      if (closedir(dir) < 0) {
-        PLOG(ERROR) << "closedir";
-      }
-    }
-  }
-};
-
-using ScopedDIR = std::unique_ptr<DIR, ScopedDIRCloser>;
-
 // This function implements CloseMultipleNowOrOnExec() using an operating
 // system-specific FD directory to determine which file descriptors are open.
 // This is an advantage over looping over all possible file descriptors, because
diff --git a/third_party/crashpad/crashpad/util/posix/process_info_linux.cc b/third_party/crashpad/crashpad/util/posix/process_info_linux.cc
index bd4202a..13b15a0 100644
--- a/third_party/crashpad/crashpad/util/posix/process_info_linux.cc
+++ b/third_party/crashpad/crashpad/util/posix/process_info_linux.cc
@@ -21,7 +21,6 @@
 #include <sys/ptrace.h>
 #include <sys/uio.h>
 #include <sys/user.h>
-#include <sys/wait.h>
 #include <time.h>
 #include <unistd.h>
 
@@ -33,6 +32,7 @@
 #include "base/strings/string_piece.h"
 #include "util/file/delimited_file_reader.h"
 #include "util/file/file_reader.h"
+#include "util/linux/scoped_ptrace_attach.h"
 
 namespace crashpad {
 
@@ -107,21 +107,6 @@
   tv->tv_usec = ts.tv_nsec / 1000;
 }
 
-class ScopedPtraceDetach {
- public:
-  explicit ScopedPtraceDetach(pid_t pid) : pid_(pid) {}
-  ~ScopedPtraceDetach() {
-    if (ptrace(PTRACE_DETACH, pid_, nullptr, nullptr) != 0) {
-      PLOG(ERROR) << "ptrace";
-    }
-  }
-
- private:
-  pid_t pid_;
-
-  DISALLOW_COPY_AND_ASSIGN(ScopedPtraceDetach);
-};
-
 }  // namespace
 
 ProcessInfo::ProcessInfo()
@@ -330,15 +315,8 @@
     if (pid_ == getpid()) {
       is_64_bit_ = am_64_bit;
     } else {
-      if (ptrace(PTRACE_ATTACH, pid_, nullptr, nullptr) != 0) {
-        PLOG(ERROR) << "ptrace";
-        return false;
-      }
-
-      ScopedPtraceDetach ptrace_detach(pid_);
-
-      if (HANDLE_EINTR(waitpid(pid_, nullptr, __WALL)) < 0) {
-        PLOG(ERROR) << "waitpid";
+      ScopedPtraceAttach ptrace_attach;
+      if (!ptrace_attach.ResetAttach(pid_)) {
         return false;
       }
 
diff --git a/third_party/crashpad/crashpad/util/posix/scoped_dir.cc b/third_party/crashpad/crashpad/util/posix/scoped_dir.cc
new file mode 100644
index 0000000..555900a
--- /dev/null
+++ b/third_party/crashpad/crashpad/util/posix/scoped_dir.cc
@@ -0,0 +1,30 @@
+// Copyright 2017 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/posix/scoped_dir.h"
+
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+
+namespace crashpad {
+namespace internal {
+
+void ScopedDIRCloser::operator()(DIR* dir) const {
+  if (dir && IGNORE_EINTR(closedir(dir)) != 0) {
+    PLOG(ERROR) << "closedir";
+  }
+}
+
+}  // namespace internal
+}  // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/posix/scoped_dir.h b/third_party/crashpad/crashpad/util/posix/scoped_dir.h
new file mode 100644
index 0000000..2eade40e
--- /dev/null
+++ b/third_party/crashpad/crashpad/util/posix/scoped_dir.h
@@ -0,0 +1,38 @@
+// Copyright 2017 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_POSIX_SCOPED_DIR_H_
+#define CRASHPAD_UTIL_POSIX_SCOPED_DIR_H_
+
+#include <dirent.h>
+
+#include <memory>
+
+namespace crashpad {
+namespace internal {
+
+struct ScopedDIRCloser {
+  void operator()(DIR* dir) const;
+};
+
+}  // namespace internal
+
+//! \brief Maintains a directory opened by `opendir`.
+//!
+//! On destruction, the directory will be closed by calling `closedir`.
+using ScopedDIR = std::unique_ptr<DIR, internal::ScopedDIRCloser>;
+
+}  // namespace crashpad
+
+#endif  // CRASHPAD_UTIL_POSIX_SCOPED_DIR_H_
diff --git a/third_party/crashpad/crashpad/util/util.gyp b/third_party/crashpad/crashpad/util/util.gyp
index aeb26e5..7ef7a0c 100644
--- a/third_party/crashpad/crashpad/util/util.gyp
+++ b/third_party/crashpad/crashpad/util/util.gyp
@@ -47,6 +47,8 @@
         'linux/address_types.h',
         'linux/process_memory.cc',
         'linux/process_memory.h',
+        'linux/scoped_ptrace_attach.cc',
+        'linux/scoped_ptrace_attach.h',
         'mac/checked_mach_address_range.h',
         'mac/launchd.h',
         'mac/launchd.mm',
@@ -146,6 +148,8 @@
         'posix/process_info.h',
         'posix/process_info_linux.cc',
         'posix/process_info_mac.cc',
+        'posix/scoped_dir.cc',
+        'posix/scoped_dir.h',
         'posix/scoped_mmap.cc',
         'posix/scoped_mmap.h',
         'posix/signals.cc',
diff --git a/third_party/crashpad/crashpad/util/util_test.gyp b/third_party/crashpad/crashpad/util/util_test.gyp
index fa204974..31323c0 100644
--- a/third_party/crashpad/crashpad/util/util_test.gyp
+++ b/third_party/crashpad/crashpad/util/util_test.gyp
@@ -40,6 +40,7 @@
         'file/file_reader_test.cc',
         'file/string_file_test.cc',
         'linux/process_memory_test.cc',
+        'linux/scoped_ptrace_attach_test.cc',
         'mac/launchd_test.mm',
         'mac/mac_util_test.mm',
         'mac/service_management_test.mm',
diff --git a/third_party/crashpad/crashpad/util/win/process_info.cc b/third_party/crashpad/crashpad/util/win/process_info.cc
index cb2051c..f0e455db 100644
--- a/third_party/crashpad/crashpad/util/win/process_info.cc
+++ b/third_party/crashpad/crashpad/util/win/process_info.cc
@@ -157,6 +157,10 @@
   if (status == STATUS_INFO_LENGTH_MISMATCH) {
     DCHECK_GT(return_length, size);
     size = return_length;
+
+    // Free the old buffer before attempting to allocate a new one.
+    buffer.reset();
+
     buffer.reset(new uint8_t[size]);
     status = crashpad::NtQueryObject(
         handle, object_information_class, buffer.get(), size, &return_length);
@@ -167,6 +171,7 @@
     return nullptr;
   }
 
+  DCHECK_LE(return_length, size);
   DCHECK_GE(return_length, minimum_size);
   return buffer;
 }
diff --git a/third_party/libaddressinput/chromium/chrome_address_validator.cc b/third_party/libaddressinput/chromium/chrome_address_validator.cc
index 320451b..b0bd189 100644
--- a/third_party/libaddressinput/chromium/chrome_address_validator.cc
+++ b/third_party/libaddressinput/chromium/chrome_address_validator.cc
@@ -17,7 +17,6 @@
 #include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_normalizer.h"
 #include "third_party/libaddressinput/src/cpp/include/libaddressinput/source.h"
 #include "third_party/libaddressinput/src/cpp/include/libaddressinput/storage.h"
-#include "third_party/libaddressinput/src/cpp/src/rule.h"
 
 namespace autofill {
 namespace {
@@ -28,7 +27,6 @@
 using ::i18n::addressinput::BuildCallback;
 using ::i18n::addressinput::FieldProblemMap;
 using ::i18n::addressinput::PreloadSupplier;
-using ::i18n::addressinput::Rule;
 using ::i18n::addressinput::Source;
 using ::i18n::addressinput::Storage;
 
@@ -60,20 +58,6 @@
   supplier_->LoadRules(region_code, *rules_loaded_);
 }
 
-std::vector<std::string> AddressValidator::GetRegionSubKeys(
-    const std::string& region_code) {
-  if (!AreRulesLoadedForRegion(region_code))
-    return std::vector<std::string>();
-
-  auto rules = supplier_->GetRulesForRegion(region_code);
-  auto rule_iterator = rules.find("data/" + region_code);
-
-  if (rule_iterator == rules.end() || !rule_iterator->second)
-    return std::vector<std::string>();
-
-  return rule_iterator->second->GetSubKeys();
-}
-
 AddressValidator::Status AddressValidator::ValidateAddress(
     const AddressData& address,
     const FieldProblemMap* filter,
@@ -157,7 +141,7 @@
                                    const std::string& region_code,
                                    int) {
   if (load_rules_listener_)
-    load_rules_listener_->OnAddressRulesLoaded(region_code, success);
+    load_rules_listener_->OnAddressValidationRulesLoaded(region_code, success);
 
   // Count the first failed attempt to load rules as well.
   if (success || attempts_number_[region_code] + 1 >= kMaxAttemptsNumber)
diff --git a/third_party/libaddressinput/chromium/chrome_address_validator.h b/third_party/libaddressinput/chromium/chrome_address_validator.h
index f956bd0..43a4a40 100644
--- a/third_party/libaddressinput/chromium/chrome_address_validator.h
+++ b/third_party/libaddressinput/chromium/chrome_address_validator.h
@@ -46,8 +46,8 @@
   // then these are also loaded.
   //
   // The |success| parameter is true when the rules were loaded successfully.
-  virtual void OnAddressRulesLoaded(const std::string& region_code,
-                                    bool success) = 0;
+  virtual void OnAddressValidationRulesLoaded(const std::string& region_code,
+                                              bool success) = 0;
 };
 
 // Interface to the libaddressinput AddressValidator for Chromium Autofill. The
@@ -94,13 +94,6 @@
   // Invokes |load_rules_listener| when the loading has finished.
   virtual void LoadRules(const std::string& region_code);
 
-  // Returns the list of sub-regions (recorded as sub-keys) of the region
-  // (recorded as rule) indicated by |region_code|. So, if the |region_code| is
-  // a country code, sub-region means the country's admin area.
-  // This function should be called when the rules are loaded.
-  virtual std::vector<std::string> GetRegionSubKeys(
-      const std::string& region_code);
-
   // Validates the |address| and populates |problems| with the validation
   // problems, filtered according to the |filter| parameter.
   //
diff --git a/third_party/libaddressinput/chromium/chrome_address_validator_unittest.cc b/third_party/libaddressinput/chromium/chrome_address_validator_unittest.cc
index 17cae79..13b5a8b 100644
--- a/third_party/libaddressinput/chromium/chrome_address_validator_unittest.cc
+++ b/third_party/libaddressinput/chromium/chrome_address_validator_unittest.cc
@@ -5,7 +5,6 @@
 #include "third_party/libaddressinput/chromium/chrome_address_validator.h"
 
 #include <stddef.h>
-#include <set>
 #include <string>
 #include <utility>
 #include <vector>
@@ -68,8 +67,8 @@
 
  private:
   // LoadRulesListener implementation.
-  void OnAddressRulesLoaded(const std::string& country_code,
-                            bool success) override {
+  virtual void OnAddressValidationRulesLoaded(const std::string& country_code,
+                                              bool success) override {
     AddressData address_data;
     address_data.region_code = country_code;
     FieldProblemMap dummy;
@@ -111,26 +110,6 @@
 
 AddressValidator* LargeAddressValidatorTest::validator_ = NULL;
 
-TEST_F(AddressValidatorTest, SubKeysLoaded) {
-  const std::string country_code = "US";
-  const std::string first_state = "AL";
-
-  validator_->LoadRules(country_code);
-  std::vector<std::string> sub_keys =
-      validator_->GetRegionSubKeys(country_code);
-  ASSERT_FALSE(sub_keys.empty());
-  ASSERT_EQ(sub_keys[0], first_state);
-}
-
-TEST_F(AddressValidatorTest, SubKeysNotLoaded) {
-  const std::string country_code = "ZZ";
-
-  validator_->LoadRules(country_code);
-  std::vector<std::string> sub_keys =
-      validator_->GetRegionSubKeys(country_code);
-  ASSERT_TRUE(sub_keys.empty());
-}
-
 TEST_F(AddressValidatorTest, RegionHasRules) {
   const std::vector<std::string>& region_codes = GetRegionCodes();
   AddressData address;
@@ -779,7 +758,7 @@
     virtual ~TestAddressValidator() {}
 
    protected:
-    base::TimeDelta GetBaseRetryPeriod() const override {
+    virtual base::TimeDelta GetBaseRetryPeriod() const override {
       return base::TimeDelta::FromSeconds(0);
     }
 
@@ -791,7 +770,7 @@
   // data.
   class FailingSource : public Source {
    public:
-    FailingSource()
+    explicit FailingSource()
         : failures_number_(0), attempts_number_(0), actual_source_(true) {}
     virtual ~FailingSource() {}
 
@@ -802,7 +781,8 @@
 
     // Source implementation.
     // Always fails for the first |failures_number| times.
-    void Get(const std::string& url, const Callback& callback) const override {
+    virtual void Get(const std::string& url,
+                     const Callback& callback) const override {
       ++attempts_number_;
       // |callback| takes ownership of the |new std::string|.
       if (failures_number_-- > 0)
@@ -843,7 +823,8 @@
 
  private:
   // LoadRulesListener implementation.
-  void OnAddressRulesLoaded(const std::string&, bool success) override {
+  virtual void OnAddressValidationRulesLoaded(const std::string&,
+                                              bool success) override {
     load_rules_success_ = success;
   }
 
diff --git a/tools/json_comment_eater/json_comment_eater.py b/tools/json_comment_eater/json_comment_eater.py
index d61ece2..17a1525 100755
--- a/tools/json_comment_eater/json_comment_eater.py
+++ b/tools/json_comment_eater/json_comment_eater.py
@@ -21,13 +21,12 @@
   '''Finds the next token in |tokens| that occurs in |string| from |start|.
   Returns a tuple (index, token key).
   '''
-  min_index, min_key = (-1, None)
-  for k in tokens:
-    index = string.find(k, start)
-    if index != -1 and (min_index == -1 or index < min_index):
-      min_index, min_key = (index, k)
-  return (min_index, min_key)
+  for index, item in enumerate(string, start):
+    for k in tokens:
+      if (string[index:index + len(k)] == k):
+        return (index, k)
 
+  return (-1, None)
 
 def _ReadString(input, start, output):
   output.append('"')
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 29585d80..dadb938 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -569,6 +569,7 @@
       'linux_chromium_headless_rel': 'headless_linux_release_trybot',
       'linux_chromium_ozone_compile_only_ng': 'ozone_linux_release_trybot',
       'linux_chromium_ozone_ng': 'ozone_linux_release_trybot',
+      'linux_layout_tests_slimming_paint_v2': 'release_trybot',
 
       # This is intentionally a release_bot and not a release_trybot;
       # enabling DCHECKs seems to cause flaky failures that don't show up
@@ -583,7 +584,6 @@
       # 'release_trybot' includes 'dcheck_always_on', which might cause
       # some layout test results to be different than for normal release
       # builds (and we only store baselines for release builds).
-      'linux_layout_tests_slimming_paint_v2': 'release_bot',
       'linux_layout_tests_layout_ng': 'release_bot',
 
       'linux_nacl_sdk_build': 'release_bot',
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index c867b90c..2fccfaa 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -105445,6 +105445,8 @@
   <int value="6" label="Already exists"/>
   <int value="7" label="Skipped"/>
   <int value="8" label="Security certificate error"/>
+  <int value="9" label="Error page detected"/>
+  <int value="10" label="Interstitial page detected"/>
 </enum>
 
 <enum name="OfflinePagesSharedPageWasOffline" type="int">
diff --git a/tools/perf/measurements/smoothness.py b/tools/perf/measurements/smoothness.py
index 77301a6..90e58db0 100644
--- a/tools/perf/measurements/smoothness.py
+++ b/tools/perf/measurements/smoothness.py
@@ -34,6 +34,7 @@
     super(Smoothness, self).__init__(needs_browser_restart_after_each_page)
     self._results_wrapper = _CustomResultsWrapper()
     self._tbm = None
+    self._results = None
 
   @classmethod
   def CustomizeBrowserOptions(cls, options):
@@ -58,11 +59,12 @@
     self._tbm.WillRunStory(tab.browser.platform)
 
   def ValidateAndMeasurePage(self, _, tab, results):
+    self._results = results
     self._tbm.Measure(tab.browser.platform, results)
 
   def DidRunPage(self, platform):
     if self._tbm:
-      self._tbm.DidRunStory(platform)
+      self._tbm.DidRunStory(platform, self._results)
 
 
 class Repaint(Smoothness):
diff --git a/ui/events/blink/input_handler_proxy.cc b/ui/events/blink/input_handler_proxy.cc
index 1a2e313..911147a 100644
--- a/ui/events/blink/input_handler_proxy.cc
+++ b/ui/events/blink/input_handler_proxy.cc
@@ -557,6 +557,10 @@
     return;
   }
 
+  // NonCompositedScrollReasons should only be set on the main thread.
+  DCHECK(
+      !cc::MainThreadScrollingReason::HasNonCompositedScrollReasons(reasons));
+
   // UMA_HISTOGRAM_ENUMERATION requires that the enum_max must be strictly
   // greater than the sample value. kMainThreadScrollingReasonCount doesn't
   // include the NotScrollingOnMain enum but the histograms do so adding