diff --git a/DEPS b/DEPS
index c6af233..3ad96f6 100644
--- a/DEPS
+++ b/DEPS
@@ -40,11 +40,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '70e3e9adc57d765cbd1e86d8f54145e1b4a564f4',
+  'skia_revision': 'ed50200682e0de72c3abecaa4d5324ebcd1ed9f9',
   # 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': 'd3230cfb7f2df6997c76111740cc9f9bf7289423',
+  'v8_revision': '23203004176173339487767d9a9ff0968f5641e5',
   # 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.
@@ -52,7 +52,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '579d8c7dfcfaeb44fc7a650cd151539d87eedcd2',
+  'angle_revision': '0dc97810e5e4ed49d4a6c31d071550e618e9e25e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling build tools
   # 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': 'd261b065d09046bbcc6594e48220cff44a371732',
+  'pdfium_revision': 'c0c39cbb638dd24b07b9cdcf7e9179e88b162ad3',
   # 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': '520dd376a20eb6ab41cd88a5dbfd56ee3d156d2f',
+  'catapult_revision': '45d20f29a9ac09db8e197637ff975b0c9fbe01b3',
   # 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/test/embedded_test_server/BUILD.gn b/android_webview/test/embedded_test_server/BUILD.gn
index f9d6dbf9..e9b2f3af 100644
--- a/android_webview/test/embedded_test_server/BUILD.gn
+++ b/android_webview/test/embedded_test_server/BUILD.gn
@@ -57,8 +57,6 @@
     ":aw_java_test_native_support",
     "//net:test_support",
   ]
-  configs -= [ "//build/config/android:hide_all_but_jni_onload" ]
-  configs += [ "//build/config/android:hide_all_but_jni" ]
 }
 
 android_apk("aw_net_test_support_apk") {
diff --git a/ash/accelerators/accelerator_controller.cc b/ash/accelerators/accelerator_controller.cc
index cff2bbf..b5145db4 100644
--- a/ash/accelerators/accelerator_controller.cc
+++ b/ash/accelerators/accelerator_controller.cc
@@ -12,6 +12,7 @@
 #include "ash/accessibility_delegate.h"
 #include "ash/accessibility_types.h"
 #include "ash/focus_cycler.h"
+#include "ash/ime/ime_controller.h"
 #include "ash/ime/ime_switch_type.h"
 #include "ash/media_controller.h"
 #include "ash/multi_profile_uma.h"
@@ -259,14 +260,7 @@
 }
 
 bool CanCycleInputMethod() {
-  InputMethodManager* manager = InputMethodManager::Get();
-  DCHECK(manager);
-  if (!manager->GetActiveIMEState()) {
-    LOG(WARNING) << "Cannot cycle through input methods as they are not "
-                    "initialized yet.";
-    return false;
-  }
-  return manager->GetActiveIMEState()->CanCycleInputMethod();
+  return Shell::Get()->ime_controller()->CanSwitchIme();
 }
 
 bool CanHandleCycleMru(const ui::Accelerator& accelerator) {
@@ -284,7 +278,7 @@
 void HandleNextIme() {
   base::RecordAction(UserMetricsAction("Accel_Next_Ime"));
   RecordImeSwitchByAccelerator();
-  InputMethodManager::Get()->GetActiveIMEState()->SwitchToNextInputMethod();
+  Shell::Get()->ime_controller()->SwitchToNextIme();
 }
 
 void HandleOpenFeedbackPage() {
@@ -296,9 +290,7 @@
   base::RecordAction(UserMetricsAction("Accel_Previous_Ime"));
   if (accelerator.key_state() == ui::Accelerator::KeyState::PRESSED) {
     RecordImeSwitchByAccelerator();
-    InputMethodManager::Get()
-        ->GetActiveIMEState()
-        ->SwitchToPreviousInputMethod();
+    Shell::Get()->ime_controller()->SwitchToPreviousIme();
   }
   // Else: consume the Ctrl+Space ET_KEY_RELEASED event but do not do anything.
 }
@@ -368,21 +360,14 @@
 }
 
 bool CanHandleSwitchIme(const ui::Accelerator& accelerator) {
-  InputMethodManager* manager = InputMethodManager::Get();
-  DCHECK(manager);
-  if (!manager->GetActiveIMEState()) {
-    LOG(WARNING) << "Cannot switch input methods as they are not "
-                    "initialized yet.";
-    return false;
-  }
-  return manager->GetActiveIMEState()->CanSwitchInputMethod(accelerator);
+  return Shell::Get()->ime_controller()->CanSwitchImeWithAccelerator(
+      accelerator);
 }
 
 void HandleSwitchIme(const ui::Accelerator& accelerator) {
   base::RecordAction(UserMetricsAction("Accel_Switch_Ime"));
   RecordImeSwitchByAccelerator();
-  InputMethodManager::Get()->GetActiveIMEState()->SwitchInputMethod(
-      accelerator);
+  Shell::Get()->ime_controller()->SwitchImeWithAccelerator(accelerator);
 }
 
 bool CanHandleToggleAppList(const ui::Accelerator& accelerator,
diff --git a/ash/accelerators/accelerator_controller_unittest.cc b/ash/accelerators/accelerator_controller_unittest.cc
index d5120d95..84034f9 100644
--- a/ash/accelerators/accelerator_controller_unittest.cc
+++ b/ash/accelerators/accelerator_controller_unittest.cc
@@ -8,8 +8,10 @@
 #include "ash/accessibility_delegate.h"
 #include "ash/accessibility_types.h"
 #include "ash/ash_switches.h"
+#include "ash/ime/ime_controller.h"
 #include "ash/public/cpp/config.h"
 #include "ash/public/cpp/shell_window_ids.h"
+#include "ash/public/interfaces/ime_info.mojom.h"
 #include "ash/session/session_controller.h"
 #include "ash/shell.h"
 #include "ash/shell_port.h"
@@ -58,6 +60,18 @@
 
 namespace {
 
+void AddTestImes() {
+  mojom::ImeInfoPtr ime1 = mojom::ImeInfo::New();
+  ime1->id = "id1";
+  mojom::ImeInfoPtr ime2 = mojom::ImeInfo::New();
+  ime2->id = "id2";
+  std::vector<mojom::ImeInfoPtr> available_imes;
+  available_imes.push_back(std::move(ime1));
+  available_imes.push_back(std::move(ime2));
+  Shell::Get()->ime_controller()->RefreshIme(
+      "id1", std::move(available_imes), std::vector<mojom::ImeMenuItemPtr>());
+}
+
 class TestTarget : public ui::AcceleratorTarget {
  public:
   TestTarget() : accelerator_pressed_count_(0), accelerator_repeat_count_(0) {}
@@ -124,54 +138,18 @@
   DISALLOW_COPY_AND_ASSIGN(DummyBrightnessControlDelegate);
 };
 
-class TestInputMethodManagerState
-    : public chromeos::input_method::MockInputMethodManager::State {
- public:
-  TestInputMethodManagerState() = default;
-
-  // InputMethodManager::State:
-  bool CanCycleInputMethod() override { return can_change_input_method_; }
-  void SwitchToNextInputMethod() override { next_ime_count_++; }
-  void SwitchToPreviousInputMethod() override { previous_ime_count_++; }
-  bool CanSwitchInputMethod(const ui::Accelerator& accelerator) override {
-    return can_change_input_method_;
-  }
-  void SwitchInputMethod(const ui::Accelerator& accelerator) override {
-    switch_ime_count_++;
-  }
-
-  bool can_change_input_method_ = true;
-  int next_ime_count_ = 0;
-  int previous_ime_count_ = 0;
-  int switch_ime_count_ = 0;
-
- private:
-  // Base class is ref-counted.
-  ~TestInputMethodManagerState() override = default;
-
-  DISALLOW_COPY_AND_ASSIGN(TestInputMethodManagerState);
-};
-
 class TestInputMethodManager
     : public chromeos::input_method::MockInputMethodManager {
  public:
-  TestInputMethodManager() : state_(new TestInputMethodManagerState) {}
+  TestInputMethodManager() = default;
   ~TestInputMethodManager() override = default;
 
-  void SetCanChangeInputMethod(bool can_change) {
-    state_->can_change_input_method_ = can_change;
-  }
-
   // MockInputMethodManager:
   chromeos::input_method::ImeKeyboard* GetImeKeyboard() override {
     return &keyboard_;
   }
-  scoped_refptr<InputMethodManager::State> GetActiveIMEState() override {
-    return state_;
-  }
 
   chromeos::input_method::FakeImeKeyboard keyboard_;
-  scoped_refptr<TestInputMethodManagerState> state_;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(TestInputMethodManager);
@@ -887,45 +865,23 @@
 }
 
 TEST_F(AcceleratorControllerTest, ImeGlobalAccelerators) {
-  TestInputMethodManagerState* test_state =
-      test_input_method_manager_->state_.get();
+  ASSERT_EQ(0u, Shell::Get()->ime_controller()->available_imes().size());
 
-  // Test IME shortcuts when cycling IME is blocked.
-  test_state->can_change_input_method_ = false;
+  // Cycling IME is blocked because there is nothing to switch to.
   ui::Accelerator control_space_down(ui::VKEY_SPACE, ui::EF_CONTROL_DOWN);
   ui::Accelerator control_space_up(ui::VKEY_SPACE, ui::EF_CONTROL_DOWN);
   control_space_up.set_key_state(ui::Accelerator::KeyState::RELEASED);
-  const ui::Accelerator convert(ui::VKEY_CONVERT, ui::EF_NONE);
-  const ui::Accelerator non_convert(ui::VKEY_NONCONVERT, ui::EF_NONE);
-  const ui::Accelerator wide_half_1(ui::VKEY_DBE_SBCSCHAR, ui::EF_NONE);
-  const ui::Accelerator wide_half_2(ui::VKEY_DBE_DBCSCHAR, ui::EF_NONE);
-  const ui::Accelerator hangul(ui::VKEY_HANGUL, ui::EF_NONE);
+  ui::Accelerator control_shift_space(ui::VKEY_SPACE,
+                                      ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN);
   EXPECT_FALSE(ProcessInController(control_space_down));
   EXPECT_FALSE(ProcessInController(control_space_up));
-  EXPECT_FALSE(ProcessInController(convert));
-  EXPECT_FALSE(ProcessInController(non_convert));
-  EXPECT_FALSE(ProcessInController(wide_half_1));
-  EXPECT_FALSE(ProcessInController(wide_half_2));
-  EXPECT_FALSE(ProcessInController(hangul));
+  EXPECT_FALSE(ProcessInController(control_shift_space));
 
-  // Test IME shortcuts when cycling IME is allowed.
-  test_state->can_change_input_method_ = true;
-  EXPECT_EQ(0, test_state->previous_ime_count_);
+  // Cycling IME works when there are IMEs available.
+  AddTestImes();
   EXPECT_TRUE(ProcessInController(control_space_down));
-  EXPECT_EQ(1, test_state->previous_ime_count_);
   EXPECT_TRUE(ProcessInController(control_space_up));
-  EXPECT_EQ(1, test_state->previous_ime_count_);
-  EXPECT_EQ(0, test_state->switch_ime_count_);
-  EXPECT_TRUE(ProcessInController(convert));
-  EXPECT_EQ(1, test_state->switch_ime_count_);
-  EXPECT_TRUE(ProcessInController(non_convert));
-  EXPECT_EQ(2, test_state->switch_ime_count_);
-  EXPECT_TRUE(ProcessInController(wide_half_1));
-  EXPECT_EQ(3, test_state->switch_ime_count_);
-  EXPECT_TRUE(ProcessInController(wide_half_2));
-  EXPECT_EQ(4, test_state->switch_ime_count_);
-  EXPECT_TRUE(ProcessInController(hangul));
-  EXPECT_EQ(5, test_state->switch_ime_count_);
+  EXPECT_TRUE(ProcessInController(control_shift_space));
 }
 
 // TODO(nona|mazda): Remove this when crbug.com/139556 in a better way.
@@ -1348,6 +1304,9 @@
        SHOW_IME_MENU_BUBBLE},
   };
 
+  // The NEXT_IME accelerator requires multiple IMEs to be available.
+  AddTestImes();
+
   EXPECT_TRUE(IsMessageCenterEmpty());
 
   for (auto data : kNewAccelerators) {
diff --git a/ash/accelerators/accelerator_table.cc b/ash/accelerators/accelerator_table.cc
index 0139b12c..a4cc748 100644
--- a/ash/accelerators/accelerator_table.cc
+++ b/ash/accelerators/accelerator_table.cc
@@ -20,8 +20,6 @@
     {true, ui::VKEY_NONCONVERT, ui::EF_NONE, SWITCH_IME},
     {true, ui::VKEY_DBE_SBCSCHAR, ui::EF_NONE, SWITCH_IME},
     {true, ui::VKEY_DBE_DBCSCHAR, ui::EF_NONE, SWITCH_IME},
-    // Shortcut for Koren IME.
-    {true, ui::VKEY_HANGUL, ui::EF_NONE, SWITCH_IME},
 
     {true, ui::VKEY_TAB, ui::EF_ALT_DOWN, CYCLE_FORWARD_MRU},
     {true, ui::VKEY_TAB, ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
diff --git a/ash/accelerators/accelerator_table_unittest.cc b/ash/accelerators/accelerator_table_unittest.cc
index 79ae8833..2bee4f5 100644
--- a/ash/accelerators/accelerator_table_unittest.cc
+++ b/ash/accelerators/accelerator_table_unittest.cc
@@ -15,12 +15,12 @@
 
 namespace {
 
-// The number of non-Search-based accelerators as of 2017-04-11.
-constexpr int kNonSearchAcceleratorsNum = 93;
-// The hash of non-Search-based accelerators as of 2017-04-11.
+// The number of non-Search-based accelerators as of 2017-06-26.
+constexpr int kNonSearchAcceleratorsNum = 92;
+// The hash of non-Search-based accelerators as of 2017-06-26.
 // See HashAcceleratorData().
 constexpr char kNonSearchAcceleratorsHash[] =
-    "8230d7ecd3180fdbc10f96bfa170b1ac";
+    "c45246c981cba1fe2c6d0d3c494e1117";
 
 struct Cmp {
   bool operator()(const AcceleratorData& lhs, const AcceleratorData& rhs) {
diff --git a/ash/ash_switches.cc b/ash/ash_switches.cc
index 3db6f13..664c269 100644
--- a/ash/ash_switches.cc
+++ b/ash/ash_switches.cc
@@ -110,6 +110,30 @@
 // enables the IME service (i.e. InputMethodMus) instead.
 const char kUseIMEService[] = "use-ime-service";
 
+// Number of recent accelerometer samples to examine to determine if a power
+// button event was spurious.
+const char kSpuriousPowerButtonWindow[] = "spurious-power-button-window";
+
+// Number of recent acceleration samples that must meet or exceed the threshold
+// in order for a power button event to be considered spurious.
+const char kSpuriousPowerButtonAccelCount[] =
+    "spurious-power-button-accel-count";
+
+// Threshold (in m/s^2, disregarding gravity) that screen acceleration must meet
+// or exceed for a power button event to be considered spurious.
+const char kSpuriousPowerButtonScreenAccel[] =
+    "spurious-power-button-screen-accel";
+
+// Threshold (in m/s^2, disregarding gravity) that keyboard acceleration must
+// meet or exceed for a power button event to be considered spurious.
+const char kSpuriousPowerButtonKeyboardAccel[] =
+    "spurious-power-button-keyboard-accel";
+
+// Change in lid angle (i.e. hinge between keyboard and screen) that must be
+// exceeded for a power button event to be considered spurious.
+const char kSpuriousPowerButtonLidAngleChange[] =
+    "spurious-power-button-lid-angle-change";
+
 // Constrains the pointer movement within a root window on desktop.
 bool ConstrainPointerToRoot() {
   const char kAshConstrainPointerToRoot[] = "ash-constrain-pointer-to-root";
diff --git a/ash/ash_switches.h b/ash/ash_switches.h
index e3e7337..66f8266 100644
--- a/ash/ash_switches.h
+++ b/ash/ash_switches.h
@@ -47,6 +47,11 @@
 ASH_EXPORT extern const char kAshShelfColorSchemeDarkVibrant[];
 ASH_EXPORT extern const char kAshTouchHud[];
 ASH_EXPORT extern const char kAuraLegacyPowerButton[];
+ASH_EXPORT extern const char kSpuriousPowerButtonWindow[];
+ASH_EXPORT extern const char kSpuriousPowerButtonAccelCount[];
+ASH_EXPORT extern const char kSpuriousPowerButtonScreenAccel[];
+ASH_EXPORT extern const char kSpuriousPowerButtonKeyboardAccel[];
+ASH_EXPORT extern const char kSpuriousPowerButtonLidAngleChange[];
 ASH_EXPORT extern const char kUseIMEService[];
 
 // True if the pointer (cursor) position should be kept inside root windows.
diff --git a/ash/ime/ime_controller.cc b/ash/ime/ime_controller.cc
index b73c8bf8..496c1e0 100644
--- a/ash/ime/ime_controller.cc
+++ b/ash/ime/ime_controller.cc
@@ -6,6 +6,8 @@
 
 #include "ash/shell.h"
 #include "ash/system/tray/system_tray_notifier.h"
+#include "ui/base/accelerators/accelerator.h"
+#include "ui/base/ime/chromeos/extension_ime_util.h"
 
 namespace ash {
 
@@ -22,27 +24,50 @@
 }
 
 bool ImeController::CanSwitchIme() const {
-  NOTIMPLEMENTED();
-  return true;
+  // Cannot switch unless there is an active IME.
+  if (current_ime_.id.empty())
+    return false;
+
+  // Do not consume key event if there is only one input method is enabled.
+  // Ctrl+Space or Alt+Shift may be used by other application.
+  return available_imes_.size() > 1;
 }
 
 void ImeController::SwitchToNextIme() {
-  NOTIMPLEMENTED();
+  if (client_)
+    client_->SwitchToNextIme();
 }
 
 void ImeController::SwitchToPreviousIme() {
-  NOTIMPLEMENTED();
+  if (client_)
+    client_->SwitchToPreviousIme();
 }
 
 bool ImeController::CanSwitchImeWithAccelerator(
     const ui::Accelerator& accelerator) const {
-  NOTIMPLEMENTED();
-  return true;
+  // If none of the input methods associated with |accelerator| are active, we
+  // should ignore the accelerator.
+  std::vector<std::string> candidate_ids =
+      GetCandidateImesForAccelerator(accelerator);
+  return !candidate_ids.empty();
 }
 
 void ImeController::SwitchImeWithAccelerator(
     const ui::Accelerator& accelerator) {
-  NOTIMPLEMENTED();
+  if (!client_)
+    return;
+
+  std::vector<std::string> candidate_ids =
+      GetCandidateImesForAccelerator(accelerator);
+  if (candidate_ids.empty())
+    return;
+  auto it =
+      std::find(candidate_ids.begin(), candidate_ids.end(), current_ime_.id);
+  if (it != candidate_ids.end())
+    ++it;
+  if (it == candidate_ids.end())
+    it = candidate_ids.begin();
+  client_->SwitchImeById(*it);
 }
 
 // mojom::ImeController:
@@ -85,4 +110,46 @@
   Shell::Get()->system_tray_notifier()->NotifyRefreshIMEMenu(show);
 }
 
+void ImeController::FlushMojoForTesting() {
+  client_.FlushForTesting();
+}
+
+std::vector<std::string> ImeController::GetCandidateImesForAccelerator(
+    const ui::Accelerator& accelerator) const {
+  std::vector<std::string> candidate_ids;
+
+  using chromeos::extension_ime_util::GetInputMethodIDByEngineID;
+  std::vector<std::string> input_method_ids_to_switch;
+  switch (accelerator.key_code()) {
+    case ui::VKEY_CONVERT:  // Henkan key on JP106 keyboard
+      input_method_ids_to_switch.push_back(
+          GetInputMethodIDByEngineID("nacl_mozc_jp"));
+      break;
+    case ui::VKEY_NONCONVERT:  // Muhenkan key on JP106 keyboard
+      input_method_ids_to_switch.push_back(
+          GetInputMethodIDByEngineID("xkb:jp::jpn"));
+      break;
+    case ui::VKEY_DBE_SBCSCHAR:  // ZenkakuHankaku key on JP106 keyboard
+    case ui::VKEY_DBE_DBCSCHAR:
+      input_method_ids_to_switch.push_back(
+          GetInputMethodIDByEngineID("nacl_mozc_jp"));
+      input_method_ids_to_switch.push_back(
+          GetInputMethodIDByEngineID("xkb:jp::jpn"));
+      break;
+    default:
+      break;
+  }
+  if (input_method_ids_to_switch.empty()) {
+    DVLOG(1) << "Unexpected VKEY: " << accelerator.key_code();
+    return std::vector<std::string>();
+  }
+
+  // Obtain the intersection of input_method_ids_to_switch and available_imes_.
+  for (const mojom::ImeInfo& ime : available_imes_) {
+    if (base::ContainsValue(input_method_ids_to_switch, ime.id))
+      candidate_ids.push_back(ime.id);
+  }
+  return candidate_ids;
+}
+
 }  // namespace ash
diff --git a/ash/ime/ime_controller.h b/ash/ime/ime_controller.h
index ce967bbc..a9652b8 100644
--- a/ash/ime/ime_controller.h
+++ b/ash/ime/ime_controller.h
@@ -42,11 +42,14 @@
   // Binds the mojo interface to this object.
   void BindRequest(mojom::ImeControllerRequest request);
 
-  // TODO(jamescook): Implement these. http://crbug.com/724305
   bool CanSwitchIme() const;
   void SwitchToNextIme();
   void SwitchToPreviousIme();
+
+  // Returns true if the switch is allowed and the keystroke should be
+  // consumed.
   bool CanSwitchImeWithAccelerator(const ui::Accelerator& accelerator) const;
+
   void SwitchImeWithAccelerator(const ui::Accelerator& accelerator);
 
   // mojom::ImeController:
@@ -57,7 +60,15 @@
   void SetImesManagedByPolicy(bool managed) override;
   void ShowImeMenuOnShelf(bool show) override;
 
+  void FlushMojoForTesting();
+
  private:
+  // Returns the IDs of the subset of input methods which are active and are
+  // associated with |accelerator|. For example, two Japanese IMEs can be
+  // returned for ui::VKEY_DBE_SBCSCHAR if both are active.
+  std::vector<std::string> GetCandidateImesForAccelerator(
+      const ui::Accelerator& accelerator) const;
+
   // Bindings for users of the mojo interface.
   mojo::BindingSet<mojom::ImeController> bindings_;
 
diff --git a/ash/ime/ime_controller_unittest.cc b/ash/ime/ime_controller_unittest.cc
index d6716cf..46a93cf 100644
--- a/ash/ime/ime_controller_unittest.cc
+++ b/ash/ime/ime_controller_unittest.cc
@@ -11,10 +11,34 @@
 #include "ash/system/ime/ime_observer.h"
 #include "ash/system/tray/system_tray_notifier.h"
 #include "ash/test/ash_test_base.h"
+#include "ui/base/accelerators/accelerator.h"
+#include "ui/base/ime/chromeos/extension_ime_util.h"
+#include "ui/events/keycodes/keyboard_codes.h"
 
 namespace ash {
 namespace {
 
+// Refreshes the IME list with fake IMEs and fake menu items.
+void RefreshImes(const std::string& current_ime_id,
+                 const std::vector<std::string>& ime_ids,
+                 const std::vector<std::string>& menu_item_keys =
+                     std::vector<std::string>()) {
+  std::vector<mojom::ImeInfoPtr> available_imes;
+  for (const std::string& ime_id : ime_ids) {
+    mojom::ImeInfoPtr ime = mojom::ImeInfo::New();
+    ime->id = ime_id;
+    available_imes.push_back(std::move(ime));
+  }
+  std::vector<mojom::ImeMenuItemPtr> menu_items;
+  for (const std::string& menu_item_key : menu_item_keys) {
+    mojom::ImeMenuItemPtr item = mojom::ImeMenuItem::New();
+    item->key = menu_item_key;
+    menu_items.push_back(std::move(item));
+  }
+  Shell::Get()->ime_controller()->RefreshIme(
+      current_ime_id, std::move(available_imes), std::move(menu_items));
+}
+
 class TestImeObserver : public IMEObserver {
  public:
   TestImeObserver() = default;
@@ -30,6 +54,37 @@
   bool ime_menu_active_ = false;
 };
 
+class TestImeControllerClient : public mojom::ImeControllerClient {
+ public:
+  TestImeControllerClient() : binding_(this) {}
+  ~TestImeControllerClient() override = default;
+
+  mojom::ImeControllerClientPtr CreateInterfacePtr() {
+    mojom::ImeControllerClientPtr ptr;
+    binding_.Bind(mojo::MakeRequest(&ptr));
+    return ptr;
+  }
+
+  // mojom::ImeControllerClient:
+  void SwitchToNextIme() override { ++next_ime_count_; }
+  void SwitchToPreviousIme() override { ++previous_ime_count_; }
+  void SwitchImeById(const std::string& id) override {
+    ++switch_ime_count_;
+    last_switch_ime_id_ = id;
+  }
+  void ActivateImeProperty(const std::string& key) override {}
+
+  int next_ime_count_ = 0;
+  int previous_ime_count_ = 0;
+  int switch_ime_count_ = 0;
+  std::string last_switch_ime_id_;
+
+ private:
+  mojo::Binding<mojom::ImeControllerClient> binding_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestImeControllerClient);
+};
+
 using ImeControllerTest = test::AshTestBase;
 
 TEST_F(ImeControllerTest, RefreshIme) {
@@ -37,23 +92,7 @@
   TestImeObserver observer;
   Shell::Get()->system_tray_notifier()->AddIMEObserver(&observer);
 
-  mojom::ImeInfoPtr ime1 = mojom::ImeInfo::New();
-  ime1->id = "ime1";
-  mojom::ImeInfoPtr ime2 = mojom::ImeInfo::New();
-  ime2->id = "ime2";
-
-  std::vector<mojom::ImeInfoPtr> available_imes;
-  available_imes.push_back(ime1.Clone());
-  available_imes.push_back(ime2.Clone());
-
-  mojom::ImeMenuItemPtr item1 = mojom::ImeMenuItem::New();
-  item1->key = "key1";
-
-  std::vector<mojom::ImeMenuItemPtr> menu_items;
-  menu_items.push_back(item1.Clone());
-
-  controller->RefreshIme("ime1", std::move(available_imes),
-                         std::move(menu_items));
+  RefreshImes("ime1", {"ime1", "ime2"}, {"menu1"});
 
   // Cached data was updated.
   EXPECT_EQ("ime1", controller->current_ime().id);
@@ -61,7 +100,7 @@
   EXPECT_EQ("ime1", controller->available_imes()[0].id);
   EXPECT_EQ("ime2", controller->available_imes()[1].id);
   ASSERT_EQ(1u, controller->current_ime_menu_items().size());
-  EXPECT_EQ("key1", controller->current_ime_menu_items()[0].key);
+  EXPECT_EQ("menu1", controller->current_ime_menu_items()[0].key);
 
   // Observer was notified.
   EXPECT_EQ(1, observer.refresh_count_);
@@ -71,20 +110,12 @@
   ImeController* controller = Shell::Get()->ime_controller();
 
   // Set up a single IME.
-  mojom::ImeInfoPtr ime1 = mojom::ImeInfo::New();
-  ime1->id = "ime1";
-  std::vector<mojom::ImeInfoPtr> available_imes1;
-  available_imes1.push_back(ime1.Clone());
-  controller->RefreshIme("ime1", std::move(available_imes1),
-                         std::vector<mojom::ImeMenuItemPtr>());
+  RefreshImes("ime1", {"ime1"});
   EXPECT_EQ("ime1", controller->current_ime().id);
 
   // When there is no current IME the cached current IME is empty.
-  std::vector<mojom::ImeInfoPtr> available_imes2;
-  available_imes2.push_back(ime1.Clone());
   const std::string empty_ime_id;
-  controller->RefreshIme(empty_ime_id, std::move(available_imes2),
-                         std::vector<mojom::ImeMenuItemPtr>());
+  RefreshImes(empty_ime_id, {"ime1"});
   EXPECT_TRUE(controller->current_ime().id.empty());
 }
 
@@ -111,5 +142,105 @@
   EXPECT_TRUE(observer.ime_menu_active_);
 }
 
+TEST_F(ImeControllerTest, CanSwitchIme) {
+  ImeController* controller = Shell::Get()->ime_controller();
+
+  // Can't switch IMEs when none are available.
+  ASSERT_EQ(0u, controller->available_imes().size());
+  EXPECT_FALSE(controller->CanSwitchIme());
+
+  // Can't switch with only 1 IME.
+  RefreshImes("ime1", {"ime1"});
+  EXPECT_FALSE(controller->CanSwitchIme());
+
+  // Can switch with more than 1 IME.
+  RefreshImes("ime1", {"ime1", "ime2"});
+  EXPECT_TRUE(controller->CanSwitchIme());
+}
+
+TEST_F(ImeControllerTest, SwitchIme) {
+  ImeController* controller = Shell::Get()->ime_controller();
+  TestImeControllerClient client;
+
+  // Can't switch IME before the client is set.
+  controller->SwitchToNextIme();
+  EXPECT_EQ(0, client.next_ime_count_);
+
+  controller->SwitchToPreviousIme();
+  EXPECT_EQ(0, client.previous_ime_count_);
+
+  // After setting the client the requests are forwarded.
+  controller->SetClient(client.CreateInterfacePtr());
+  controller->SwitchToNextIme();
+  controller->FlushMojoForTesting();
+  EXPECT_EQ(1, client.next_ime_count_);
+
+  controller->SwitchToPreviousIme();
+  controller->FlushMojoForTesting();
+  EXPECT_EQ(1, client.previous_ime_count_);
+}
+
+TEST_F(ImeControllerTest, SwitchImeWithAccelerator) {
+  ImeController* controller = Shell::Get()->ime_controller();
+  TestImeControllerClient client;
+  controller->SetClient(client.CreateInterfacePtr());
+
+  const ui::Accelerator convert(ui::VKEY_CONVERT, ui::EF_NONE);
+  const ui::Accelerator non_convert(ui::VKEY_NONCONVERT, ui::EF_NONE);
+  const ui::Accelerator wide_half_1(ui::VKEY_DBE_SBCSCHAR, ui::EF_NONE);
+  const ui::Accelerator wide_half_2(ui::VKEY_DBE_DBCSCHAR, ui::EF_NONE);
+
+  // When there are no IMEs available switching by accelerator does not work.
+  ASSERT_EQ(0u, controller->available_imes().size());
+  EXPECT_FALSE(controller->CanSwitchImeWithAccelerator(convert));
+  EXPECT_FALSE(controller->CanSwitchImeWithAccelerator(non_convert));
+  EXPECT_FALSE(controller->CanSwitchImeWithAccelerator(wide_half_1));
+  EXPECT_FALSE(controller->CanSwitchImeWithAccelerator(wide_half_2));
+
+  // With only test IMEs (and no Japanese IMEs) the special keys do not work.
+  RefreshImes("ime1", {"ime1", "ime2"});
+  EXPECT_FALSE(controller->CanSwitchImeWithAccelerator(convert));
+  EXPECT_FALSE(controller->CanSwitchImeWithAccelerator(non_convert));
+  EXPECT_FALSE(controller->CanSwitchImeWithAccelerator(wide_half_1));
+  EXPECT_FALSE(controller->CanSwitchImeWithAccelerator(wide_half_2));
+
+  // Install both a test IME and Japanese IMEs.
+  using chromeos::extension_ime_util::GetInputMethodIDByEngineID;
+  const std::string nacl_mozc_jp = GetInputMethodIDByEngineID("nacl_mozc_jp");
+  const std::string xkb_jp_jpn = GetInputMethodIDByEngineID("xkb:jp::jpn");
+  RefreshImes("ime1", {"ime1", nacl_mozc_jp, xkb_jp_jpn});
+
+  // Accelerator based switching now works.
+  EXPECT_TRUE(controller->CanSwitchImeWithAccelerator(convert));
+  EXPECT_TRUE(controller->CanSwitchImeWithAccelerator(non_convert));
+  EXPECT_TRUE(controller->CanSwitchImeWithAccelerator(wide_half_1));
+  EXPECT_TRUE(controller->CanSwitchImeWithAccelerator(wide_half_2));
+
+  // Convert keys jump directly to the requested IME.
+  controller->SwitchImeWithAccelerator(convert);
+  controller->FlushMojoForTesting();
+  EXPECT_EQ(1, client.switch_ime_count_);
+  EXPECT_EQ(nacl_mozc_jp, client.last_switch_ime_id_);
+
+  controller->SwitchImeWithAccelerator(non_convert);
+  controller->FlushMojoForTesting();
+  EXPECT_EQ(2, client.switch_ime_count_);
+  EXPECT_EQ(xkb_jp_jpn, client.last_switch_ime_id_);
+
+  // Switch from nacl_mozc_jp to xkb_jp_jpn.
+  RefreshImes(nacl_mozc_jp, {"ime1", nacl_mozc_jp, xkb_jp_jpn});
+  controller->SwitchImeWithAccelerator(wide_half_1);
+  controller->FlushMojoForTesting();
+  EXPECT_EQ(3, client.switch_ime_count_);
+  EXPECT_EQ(xkb_jp_jpn, client.last_switch_ime_id_);
+
+  // Switch from xkb_jp_jpn to nacl_mozc_jp.
+  RefreshImes(xkb_jp_jpn, {"ime1", nacl_mozc_jp, xkb_jp_jpn});
+  controller->SwitchImeWithAccelerator(wide_half_2);
+  controller->FlushMojoForTesting();
+  EXPECT_EQ(4, client.switch_ime_count_);
+  EXPECT_EQ(nacl_mozc_jp, client.last_switch_ime_id_);
+}
+
 }  // namespace
 }  // namespace ash
diff --git a/ash/public/interfaces/ime_controller.mojom b/ash/public/interfaces/ime_controller.mojom
index 549e5966..d66f415 100644
--- a/ash/public/interfaces/ime_controller.mojom
+++ b/ash/public/interfaces/ime_controller.mojom
@@ -38,8 +38,10 @@
   // method is installed.
   SwitchToPreviousIme();
 
-  // Switches to an input method by |id| (for example, "xkb:jp::jpn"). Does
-  // nothing if the input method is not installed.
+  // Switches to an input method by |id|. Does nothing if the input method is
+  // not installed. The ID is usually the output of a call like
+  // chromeos::extension_ime_util::GetInputMethodIDByEngineID("xkb:jp::jpn"),
+  // see that function for details.
   SwitchImeById(string id);
 
   // Activates an input method property. These are settings that are provided by
diff --git a/ash/system/power/tablet_power_button_controller.cc b/ash/system/power/tablet_power_button_controller.cc
index 0f8dea77..fafda54 100644
--- a/ash/system/power/tablet_power_button_controller.cc
+++ b/ash/system/power/tablet_power_button_controller.cc
@@ -5,13 +5,19 @@
 #include "ash/system/power/tablet_power_button_controller.h"
 
 #include "ash/accessibility_delegate.h"
+#include "ash/ash_switches.h"
 #include "ash/session/session_controller.h"
 #include "ash/shell.h"
 #include "ash/shell_delegate.h"
 #include "ash/wm/lock_state_controller.h"
 #include "ash/wm/maximize_mode/maximize_mode_controller.h"
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
 #include "base/time/default_tick_clock.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
+#include "ui/chromeos/accelerometer/accelerometer_util.h"
 #include "ui/events/devices/input_device_manager.h"
 #include "ui/events/devices/stylus_state.h"
 #include "ui/events/event.h"
@@ -55,6 +61,36 @@
          maximize_mode_controller->IsMaximizeModeWindowManagerEnabled();
 }
 
+// Returns the value for the command-line switch identified by |name|. Returns 0
+// if the switch was unset or contained a non-float value.
+double GetNumSwitch(const base::CommandLine& command_line,
+                    const std::string& name) {
+  std::string str = command_line.GetSwitchValueASCII(name);
+  if (str.empty())
+    return 0.0;
+
+  double value = 0.0;
+  if (!base::StringToDouble(str, &value)) {
+    LOG(WARNING) << "Failed to parse value \"" << str << "\" from switch "
+                 << "--" << name << " as float";
+    return 0.0;
+  }
+  return value;
+}
+
+// Computes the lid angle based on readings from accelerometers in the screen
+// and keyboard panels.
+float GetLidAngle(const gfx::Vector3dF& screen,
+                  const gfx::Vector3dF& keyboard) {
+  constexpr gfx::Vector3dF kHingeVector(1.0f, 0.0f, 0.0f);
+  float lid_angle = 180.0f - gfx::ClockwiseAngleBetweenVectorsInDegrees(
+                                 keyboard, screen, kHingeVector);
+  if (lid_angle < 0.0f)
+    lid_angle += 360.0f;
+
+  return lid_angle;
+}
+
 }  // namespace
 
 TabletPowerButtonController::TestApi::TestApi(
@@ -73,10 +109,26 @@
   controller_->shutdown_timer_.Stop();
 }
 
+bool TabletPowerButtonController::TestApi::IsObservingAccelerometerReader(
+    chromeos::AccelerometerReader* reader) const {
+  DCHECK(reader);
+  return controller_->accelerometer_scoped_observer_.IsObserving(reader);
+}
+
+void TabletPowerButtonController::TestApi::ParseSpuriousPowerButtonSwitches(
+    const base::CommandLine& command_line) {
+  controller_->ParseSpuriousPowerButtonSwitches(command_line);
+}
+
+bool TabletPowerButtonController::TestApi::IsSpuriousPowerButtonEvent() const {
+  return controller_->IsSpuriousPowerButtonEvent();
+}
+
 TabletPowerButtonController::TabletPowerButtonController(
     LockStateController* controller)
     : tick_clock_(new base::DefaultTickClock()),
       controller_(controller),
+      accelerometer_scoped_observer_(this),
       weak_ptr_factory_(this) {
   chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver(
       this);
@@ -87,6 +139,7 @@
     ui::InputDeviceManager::GetInstance()->AddObserver(this);
   Shell::Get()->PrependPreTargetHandler(this);
 
+  ParseSpuriousPowerButtonSwitches(*base::CommandLine::ForCurrentProcess());
   GetInitialBacklightsForcedOff();
 }
 
@@ -107,6 +160,11 @@
     bool down,
     const base::TimeTicks& timestamp) {
   if (down) {
+    if ((power_button_down_was_spurious_ = IsSpuriousPowerButtonEvent())) {
+      LOG(WARNING) << "Ignoring spurious power button down event";
+      return;
+    }
+
     force_off_on_button_up_ = true;
     // When the system resumes in response to the power button being pressed,
     // Chrome receives powerd's SuspendDone signal and notification that the
@@ -121,6 +179,10 @@
     SetDisplayForcedOff(false);
     StartShutdownTimer();
   } else {
+    // Don't process the up event if we previously ignored the down event.
+    if (power_button_down_was_spurious_)
+      return;
+
     // When power button is released, cancel shutdown animation whenever it is
     // still cancellable.
     if (controller_->CanCancelShutdownAnimation())
@@ -145,6 +207,25 @@
   }
 }
 
+void TabletPowerButtonController::OnAccelerometerUpdated(
+    scoped_refptr<const chromeos::AccelerometerUpdate> update) {
+  const gfx::Vector3dF screen = ui::ConvertAccelerometerReadingToVector3dF(
+      update->get(chromeos::ACCELEROMETER_SOURCE_SCREEN));
+  const gfx::Vector3dF keyboard = ui::ConvertAccelerometerReadingToVector3dF(
+      update->get(chromeos::ACCELEROMETER_SOURCE_ATTACHED_KEYBOARD));
+
+  DCHECK_GT(max_accelerometer_samples_, 0u);
+  if (accelerometer_samples_.size() < max_accelerometer_samples_) {
+    accelerometer_samples_.push_back(std::make_pair(screen, keyboard));
+    last_accelerometer_sample_index_ = accelerometer_samples_.size() - 1;
+  } else {
+    last_accelerometer_sample_index_ =
+        (last_accelerometer_sample_index_ + 1) % max_accelerometer_samples_;
+    accelerometer_samples_[last_accelerometer_sample_index_] =
+        std::make_pair(screen, keyboard);
+  }
+}
+
 void TabletPowerButtonController::PowerManagerRestarted() {
   chromeos::DBusThreadManager::Get()
       ->GetPowerManagerClient()
@@ -213,6 +294,114 @@
   tick_clock_ = std::move(tick_clock);
 }
 
+void TabletPowerButtonController::ParseSpuriousPowerButtonSwitches(
+    const base::CommandLine& command_line) {
+  // Support being called multiple times from tests.
+  max_accelerometer_samples_ = 0;
+  accelerometer_samples_.clear();
+  accelerometer_scoped_observer_.RemoveAll();
+
+  int window = static_cast<int>(
+      GetNumSwitch(command_line, switches::kSpuriousPowerButtonWindow));
+  if (window <= 0)
+    return;
+
+  max_accelerometer_samples_ = static_cast<size_t>(window);
+  accelerometer_samples_.reserve(max_accelerometer_samples_);
+  accelerometer_scoped_observer_.Add(
+      chromeos::AccelerometerReader::GetInstance());
+
+  spurious_accel_count_ = static_cast<size_t>(
+      GetNumSwitch(command_line, switches::kSpuriousPowerButtonAccelCount));
+  spurious_screen_accel_ =
+      GetNumSwitch(command_line, switches::kSpuriousPowerButtonScreenAccel);
+  spurious_keyboard_accel_ =
+      GetNumSwitch(command_line, switches::kSpuriousPowerButtonKeyboardAccel);
+  spurious_lid_angle_change_ =
+      GetNumSwitch(command_line, switches::kSpuriousPowerButtonLidAngleChange);
+}
+
+bool TabletPowerButtonController::IsSpuriousPowerButtonEvent() const {
+  if (max_accelerometer_samples_ <= 0)
+    return false;
+
+  // Number of screen and keyboard accelerations exceeding the threshold.
+  size_t num_big_screen_accels = 0;
+  size_t num_big_keyboard_accels = 0;
+
+  // The last lid angle that we saw.
+  float last_angle = 0.0f;
+  // The current distance (in degrees) that we've traveled from the starting
+  // angle and the max distance that we've seen so far.
+  float cur_angle_dist = 0.0f, max_angle_dist = 0.0f;
+
+  std::string screen_debug, keyboard_debug, angle_debug;
+
+  // Get the index of the oldest pair of samples.
+  const size_t start =
+      accelerometer_samples_.size() == max_accelerometer_samples_
+          ? (last_accelerometer_sample_index_ + 1) % max_accelerometer_samples_
+          : 0;
+  for (size_t i = 0; i < accelerometer_samples_.size(); ++i) {
+    const auto& pair =
+        accelerometer_samples_[(start + i) % max_accelerometer_samples_];
+    const gfx::Vector3dF& screen = pair.first;
+    const gfx::Vector3dF& keyboard = pair.second;
+
+    const float screen_accel = std::abs(screen.Length() - kGravity);
+    if (spurious_screen_accel_ > 0 && screen_accel >= spurious_screen_accel_)
+      num_big_screen_accels++;
+
+    const float keyboard_accel = std::abs(keyboard.Length() - kGravity);
+    if (spurious_keyboard_accel_ > 0 &&
+        keyboard_accel >= spurious_keyboard_accel_) {
+      num_big_keyboard_accels++;
+    }
+
+    float angle = GetLidAngle(screen, keyboard);
+    if (i > 0u) {
+      // Lid angle readings are computed based on the screen and keyboard
+      // acceleration vectors and can be noisy. Compute the minimum angle
+      // difference between the previous reading and the current one and use it
+      // to maintain a running total of how far the lid has traveled, also
+      // keeping track of the max distance from the start that we've seen.
+      float min_diff = angle - last_angle;
+      if (min_diff < -180.0f)
+        min_diff += 360.0f;
+      else if (min_diff > 180.0f)
+        min_diff -= 360.0f;
+
+      cur_angle_dist += min_diff;
+      max_angle_dist =
+          std::min(std::max(max_angle_dist, std::abs(cur_angle_dist)), 360.0f);
+    }
+    last_angle = angle;
+
+    if (VLOG_IS_ON(1)) {
+      screen_debug = base::StringPrintf("%0.1f", screen_accel) +
+                     (screen_debug.empty() ? "" : " " + screen_debug);
+      keyboard_debug = base::StringPrintf("%0.1f", keyboard_accel) +
+                       (keyboard_debug.empty() ? "" : " " + keyboard_debug);
+      angle_debug = base::IntToString(static_cast<int>(angle + 0.5)) +
+                    (angle_debug.empty() ? "" : " " + angle_debug);
+    }
+  }
+
+  VLOG(1) << "Screen accelerations (" << num_big_screen_accels << " big): "
+          << "[" << screen_debug << "]";
+  VLOG(1) << "Keyboard accelerations (" << num_big_keyboard_accels << " big): "
+          << "[" << keyboard_debug << "]";
+  VLOG(1) << "Lid angles (" << base::StringPrintf("%0.1f", max_angle_dist)
+          << " degrees): [" << angle_debug << "]";
+
+  return (spurious_screen_accel_ > 0 &&
+          num_big_screen_accels >= spurious_accel_count_) ||
+         (spurious_keyboard_accel_ > 0 &&
+          num_big_keyboard_accels >= spurious_accel_count_) ||
+         (spurious_lid_angle_change_ > 0 &&
+          max_angle_dist >= spurious_lid_angle_change_);
+}
+
 void TabletPowerButtonController::SetDisplayForcedOff(bool forced_off) {
   if (backlights_forced_off_ == forced_off)
     return;
diff --git a/ash/system/power/tablet_power_button_controller.h b/ash/system/power/tablet_power_button_controller.h
index 69d32cc1..ab89f72 100644
--- a/ash/system/power/tablet_power_button_controller.h
+++ b/ash/system/power/tablet_power_button_controller.h
@@ -6,17 +6,26 @@
 #define ASH_SYSTEM_POWER_TABLET_POWER_BUTTON_CONTROLLER_H_
 
 #include <memory>
+#include <utility>
 
 #include "ash/ash_export.h"
 #include "ash/shell_observer.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "base/scoped_observer.h"
 #include "base/time/tick_clock.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
+#include "chromeos/accelerometer/accelerometer_reader.h"
+#include "chromeos/accelerometer/accelerometer_types.h"
 #include "chromeos/dbus/power_manager_client.h"
 #include "ui/events/devices/input_device_event_observer.h"
 #include "ui/events/event_handler.h"
+#include "ui/gfx/geometry/vector3d_f.h"
+
+namespace base {
+class CommandLine;
+}  // namespace base
 
 namespace ash {
 
@@ -25,7 +34,8 @@
 // Handles power button events on convertible/tablet device. This class is
 // instantiated and used in PowerButtonController.
 class ASH_EXPORT TabletPowerButtonController
-    : public chromeos::PowerManagerClient::Observer,
+    : public chromeos::AccelerometerReader::Observer,
+      public chromeos::PowerManagerClient::Observer,
       public ShellObserver,
       public ui::EventHandler,
       public ui::InputDeviceEventObserver {
@@ -42,12 +52,26 @@
     // Emulates |shutdown_timer_| timeout.
     void TriggerShutdownTimeout();
 
+    // Returns true if |controller_| is observing |reader|.
+    bool IsObservingAccelerometerReader(
+        chromeos::AccelerometerReader* reader) const;
+
+    // Calls |controller_|'s ParseSpuriousPowerButtonSwitches() method.
+    void ParseSpuriousPowerButtonSwitches(
+        const base::CommandLine& command_line);
+
+    // Calls |controller_|'s IsSpuriousPowerButtonEvent() method.
+    bool IsSpuriousPowerButtonEvent() const;
+
    private:
     TabletPowerButtonController* controller_;  // Not owned.
 
     DISALLOW_COPY_AND_ASSIGN(TestApi);
   };
 
+  // Public for tests.
+  static constexpr float kGravity = 9.80665f;
+
   explicit TabletPowerButtonController(LockStateController* controller);
   ~TabletPowerButtonController() override;
 
@@ -58,6 +82,10 @@
   // Handles a power button event.
   void OnPowerButtonEvent(bool down, const base::TimeTicks& timestamp);
 
+  // Overridden from chromeos::AccelerometerReader::Observer:
+  void OnAccelerometerUpdated(
+      scoped_refptr<const chromeos::AccelerometerUpdate> update) override;
+
   // Overridden from chromeos::PowerManagerClient::Observer:
   void PowerManagerRestarted() override;
   void BrightnessChanged(int level, bool user_initiated) override;
@@ -80,6 +108,15 @@
   void SetTickClockForTesting(std::unique_ptr<base::TickClock> tick_clock);
 
  private:
+  // Parses command-line switches that provide settings used to attempt to
+  // ignore accidental power button presses by looking at accelerometer data.
+  void ParseSpuriousPowerButtonSwitches(const base::CommandLine& command_line);
+
+  // Returns true if the device's accelerometers have reported enough recent
+  // movement that we should consider a power button event that was just
+  // received to be accidental and ignore it.
+  bool IsSpuriousPowerButtonEvent() const;
+
   // Updates the power manager's backlights-forced-off state and enables or
   // disables the touchscreen. No-op if |backlights_forced_off_| already equals
   // |forced_off|.
@@ -116,6 +153,10 @@
   // True if the screen was off when the power button was pressed.
   bool screen_off_when_power_button_down_ = false;
 
+  // True if the last power button down event was deemed spurious and ignored as
+  // a result.
+  bool power_button_down_was_spurious_ = false;
+
   // Time source for performed action times.
   std::unique_ptr<base::TickClock> tick_clock_;
 
@@ -135,6 +176,30 @@
 
   LockStateController* controller_;  // Not owned.
 
+  ScopedObserver<chromeos::AccelerometerReader, TabletPowerButtonController>
+      accelerometer_scoped_observer_;
+
+  // Number of recent screen and keyboard accelerometer samples to retain.
+  size_t max_accelerometer_samples_ = 0;
+
+  // Circular buffer of recent (screen, keyboard) accelerometer samples.
+  std::vector<std::pair<gfx::Vector3dF, gfx::Vector3dF>> accelerometer_samples_;
+  size_t last_accelerometer_sample_index_ = 0;
+
+  // Number of acceleration readings in |accelerometer_samples_| that must
+  // exceed the threshold in order for a power button event to be considered
+  // spurious.
+  size_t spurious_accel_count_ = 0;
+
+  // Thresholds for screen and keyboard accelerations (excluding gravity). See
+  // |spurious_accel_count_|.
+  float spurious_screen_accel_ = 0;
+  float spurious_keyboard_accel_ = 0;
+
+  // Threshold for the lid angle change seen within |accelerometer_samples_|
+  // in order for a power button event to be considered spurious.
+  float spurious_lid_angle_change_ = 0;
+
   base::WeakPtrFactory<TabletPowerButtonController> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(TabletPowerButtonController);
diff --git a/ash/system/power/tablet_power_button_controller_unittest.cc b/ash/system/power/tablet_power_button_controller_unittest.cc
index 526c9349..7a834405 100644
--- a/ash/system/power/tablet_power_button_controller_unittest.cc
+++ b/ash/system/power/tablet_power_button_controller_unittest.cc
@@ -35,6 +35,18 @@
 // A non-zero brightness used for test.
 constexpr int kNonZeroBrightness = 10;
 
+// Vector pointing up (e.g. keyboard in clamshell).
+constexpr gfx::Vector3dF kUpVector = {0, 0,
+                                      TabletPowerButtonController::kGravity};
+
+// Vector pointing down (e.g. keyboard in tablet sitting on table).
+constexpr gfx::Vector3dF kDownVector = {0, 0,
+                                        -TabletPowerButtonController::kGravity};
+
+// Vector pointing sideways (e.g. screen in 90-degree clamshell).
+constexpr gfx::Vector3dF kSidewaysVector = {
+    0, TabletPowerButtonController::kGravity, 0};
+
 void CopyResult(bool* dest, bool src) {
   *dest = src;
 }
@@ -57,7 +69,7 @@
     AshTestBase::SetUp();
     // Trigger an accelerometer update so that |tablet_controller_| can be
     // initialized.
-    SendAccelerometerUpdate();
+    SendAccelerometerUpdate(kSidewaysVector, kUpVector);
     tablet_controller_ = Shell::Get()
                              ->power_button_controller()
                              ->tablet_power_button_controller_for_test();
@@ -87,11 +99,32 @@
   }
 
  protected:
-  void SendAccelerometerUpdate() {
+  // Resets the TabletPowerButtonController and associated members.
+  void ResetTabletPowerButtonController() {
+    test_api_ = nullptr;
+    tablet_controller_ = nullptr;
+    Shell::Get()
+        ->power_button_controller()
+        ->ResetTabletPowerButtonControllerForTest();
+  }
+
+  // Sends an update with screen and keyboard accelerometer readings to
+  // PowerButtonController, and also |tablet_controller_| if it's non-null and
+  // has registered as an observer.
+  void SendAccelerometerUpdate(const gfx::Vector3dF& screen,
+                               const gfx::Vector3dF& keyboard) {
     scoped_refptr<chromeos::AccelerometerUpdate> update(
         new chromeos::AccelerometerUpdate());
-    update->Set(chromeos::ACCELEROMETER_SOURCE_SCREEN, 1.0f, 0.0f, 0.0f);
+    update->Set(chromeos::ACCELEROMETER_SOURCE_SCREEN, screen.x(), screen.y(),
+                screen.z());
+    update->Set(chromeos::ACCELEROMETER_SOURCE_ATTACHED_KEYBOARD, keyboard.x(),
+                keyboard.y(), keyboard.z());
+
     Shell::Get()->power_button_controller()->OnAccelerometerUpdated(update);
+
+    if (test_api_ && test_api_->IsObservingAccelerometerReader(
+                         chromeos::AccelerometerReader::GetInstance()))
+      tablet_controller_->OnAccelerometerUpdated(update);
   }
 
   void PressPowerButton() {
@@ -134,14 +167,14 @@
   }
 
   // Ownership is passed on to chromeos::DBusThreadManager.
-  chromeos::FakePowerManagerClient* power_manager_client_;
+  chromeos::FakePowerManagerClient* power_manager_client_ = nullptr;
 
-  LockStateController* lock_state_controller_;      // Not owned.
-  TabletPowerButtonController* tablet_controller_;  // Not owned.
+  LockStateController* lock_state_controller_ = nullptr;      // Not owned.
+  TabletPowerButtonController* tablet_controller_ = nullptr;  // Not owned.
   std::unique_ptr<TabletPowerButtonController::TestApi> test_api_;
   std::unique_ptr<LockStateControllerTestApi> lock_state_test_api_;
-  base::SimpleTestTickClock* tick_clock_;  // Not owned.
-  TestShellDelegate* shell_delegate_;      // Not owned.
+  base::SimpleTestTickClock* tick_clock_ = nullptr;  // Not owned.
+  TestShellDelegate* shell_delegate_ = nullptr;      // Not owned.
   ui::test::EventGenerator* generator_ = nullptr;
 
   DISALLOW_COPY_AND_ASSIGN(TabletPowerButtonControllerTest);
@@ -527,10 +560,8 @@
   // Simulate system reboot by resetting backlights forced off state in powerd
   // and TabletPowerButtonController.
   power_manager_client_->SetBacklightsForcedOff(false);
-  Shell::Get()
-      ->power_button_controller()
-      ->ResetTabletPowerButtonControllerForTest();
-  SendAccelerometerUpdate();
+  ResetTabletPowerButtonController();
+  SendAccelerometerUpdate(kSidewaysVector, kSidewaysVector);
 
   // Check that the local state of touchscreen enabled state is in line with
   // backlights forced off state.
@@ -542,19 +573,93 @@
 // accelerometer update, otherwise it is disabled.
 TEST_F(TabletPowerButtonControllerTest, EnableOnAccelerometerUpdate) {
   ASSERT_TRUE(tablet_controller_);
-  Shell::Get()
-      ->power_button_controller()
-      ->ResetTabletPowerButtonControllerForTest();
-  tablet_controller_ = Shell::Get()
-                           ->power_button_controller()
-                           ->tablet_power_button_controller_for_test();
-  EXPECT_FALSE(tablet_controller_);
+  ResetTabletPowerButtonController();
+  EXPECT_FALSE(Shell::Get()
+                   ->power_button_controller()
+                   ->tablet_power_button_controller_for_test());
 
-  SendAccelerometerUpdate();
-  tablet_controller_ = Shell::Get()
-                           ->power_button_controller()
-                           ->tablet_power_button_controller_for_test();
-  EXPECT_TRUE(tablet_controller_);
+  SendAccelerometerUpdate(kSidewaysVector, kSidewaysVector);
+  EXPECT_TRUE(Shell::Get()
+                  ->power_button_controller()
+                  ->tablet_power_button_controller_for_test());
+}
+
+TEST_F(TabletPowerButtonControllerTest, IgnoreSpuriousEventsForAcceleration) {
+  base::CommandLine cl(base::CommandLine::NO_PROGRAM);
+  cl.AppendSwitchASCII(switches::kSpuriousPowerButtonWindow, "3");
+  cl.AppendSwitchASCII(switches::kSpuriousPowerButtonAccelCount, "2");
+  cl.AppendSwitchASCII(switches::kSpuriousPowerButtonKeyboardAccel, "4.5");
+  cl.AppendSwitchASCII(switches::kSpuriousPowerButtonScreenAccel, "8.0");
+  test_api_->ParseSpuriousPowerButtonSwitches(cl);
+  ASSERT_FALSE(test_api_->IsSpuriousPowerButtonEvent());
+
+  // Vectors with varying amounts of acceleration beyond gravity.
+  static constexpr gfx::Vector3dF kVector0 = {
+      0, 0, TabletPowerButtonController::kGravity};
+  static constexpr gfx::Vector3dF kVector3 = {
+      0, 0, TabletPowerButtonController::kGravity + 3};
+  static constexpr gfx::Vector3dF kVector5 = {
+      0, 0, TabletPowerButtonController::kGravity + 5};
+  static constexpr gfx::Vector3dF kVector9 = {
+      0, 0, TabletPowerButtonController::kGravity + 9};
+
+  // Send two keyboard readings with vectors that exceed the threshold after
+  // subtracting gravity.
+  SendAccelerometerUpdate(kVector0, kVector5);
+  SendAccelerometerUpdate(kVector0, kVector9);
+  EXPECT_TRUE(test_api_->IsSpuriousPowerButtonEvent());
+
+  // Now send two more keyboard readings that are close to gravity. We only have
+  // one large reading saved now, so we should permit power button events again.
+  SendAccelerometerUpdate(kVector0, kVector0);
+  SendAccelerometerUpdate(kVector0, kVector0);
+  EXPECT_FALSE(test_api_->IsSpuriousPowerButtonEvent());
+
+  // Send a few large screen vectors and check that the button is again blocked.
+  SendAccelerometerUpdate(kVector9, kVector0);
+  SendAccelerometerUpdate(kVector9, kVector0);
+  EXPECT_TRUE(test_api_->IsSpuriousPowerButtonEvent());
+}
+
+TEST_F(TabletPowerButtonControllerTest, IgnoreSpuriousEventsForLidAngle) {
+  base::CommandLine cl(base::CommandLine::NO_PROGRAM);
+  cl.AppendSwitchASCII(switches::kSpuriousPowerButtonWindow, "5");
+  cl.AppendSwitchASCII(switches::kSpuriousPowerButtonLidAngleChange, "200");
+  test_api_->ParseSpuriousPowerButtonSwitches(cl);
+  ASSERT_FALSE(test_api_->IsSpuriousPowerButtonEvent());
+
+  // Send two updates in tablet mode with the screen facing up and the keyboard
+  // facing down (i.e. 360 degrees between the two).
+  SendAccelerometerUpdate(kUpVector, kDownVector);
+  SendAccelerometerUpdate(kUpVector, kDownVector);
+  EXPECT_FALSE(test_api_->IsSpuriousPowerButtonEvent());
+
+  // Now keep the screen facing up and report the keyboard as being sideways, as
+  // if it's been rotated 90 degrees.
+  SendAccelerometerUpdate(kUpVector, kSidewaysVector);
+  EXPECT_FALSE(test_api_->IsSpuriousPowerButtonEvent());
+
+  // Make the keyboard also face up (180 degrees from start).
+  SendAccelerometerUpdate(kUpVector, kUpVector);
+  EXPECT_FALSE(test_api_->IsSpuriousPowerButtonEvent());
+
+  // Now make the screen face sideways, completing the 270-degree change to
+  // a clamshell orientation. We've exceeded the threshold over the last four
+  // samples, so events should be ignored.
+  SendAccelerometerUpdate(kSidewaysVector, kUpVector);
+  EXPECT_TRUE(test_api_->IsSpuriousPowerButtonEvent());
+
+  // Make the screen travel 90 more degrees so the lid is closed (360 degrees
+  // from start).
+  SendAccelerometerUpdate(kDownVector, kUpVector);
+  EXPECT_TRUE(test_api_->IsSpuriousPowerButtonEvent());
+
+  // After two more closed samples, the 5-sample buffer just contains a
+  // 180-degree transition, so events should be accepted again.
+  SendAccelerometerUpdate(kDownVector, kUpVector);
+  EXPECT_TRUE(test_api_->IsSpuriousPowerButtonEvent());
+  SendAccelerometerUpdate(kDownVector, kUpVector);
+  EXPECT_FALSE(test_api_->IsSpuriousPowerButtonEvent());
 }
 
 // Tests that when backlights get forced off due to tablet power button, media
diff --git a/ash/system/tray/system_tray.cc b/ash/system/tray/system_tray.cc
index a9c8156..77cc034 100644
--- a/ash/system/tray/system_tray.cc
+++ b/ash/system/tray/system_tray.cc
@@ -52,6 +52,7 @@
 #include "ash/system/user/tray_user.h"
 #include "ash/system/web_notification/web_notification_tray.h"
 #include "ash/wm/container_finder.h"
+#include "ash/wm/maximize_mode/maximize_mode_controller.h"
 #include "ash/wm/widget_finder.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
@@ -169,8 +170,10 @@
     int container_id = wm::GetContainerForWindow(gained_active)->id();
 
     // Don't close the bubble if a popup notification is activated.
-    if (container_id == kShellWindowId_StatusContainer)
+    if (container_id == kShellWindowId_StatusContainer ||
+        container_id == kShellWindowId_SettingBubbleContainer) {
       return;
+    }
 
     views::Widget* bubble_widget =
         tray_->GetSystemBubble()->bubble_view()->GetWidget();
@@ -195,7 +198,8 @@
 
 // SystemTray
 
-SystemTray::SystemTray(Shelf* shelf) : TrayBackgroundView(shelf) {
+SystemTray::SystemTray(Shelf* shelf)
+    : TrayBackgroundView(shelf), shelf_(shelf) {
   SetInkDropMode(InkDropMode::ON);
 
   // Since user avatar is on the right hand side of System tray of a
@@ -620,6 +624,21 @@
   bubble_view->GetWidget()->Activate();
 }
 
+void SystemTray::OnGestureEvent(ui::GestureEvent* event) {
+  if (Shell::Get()
+          ->maximize_mode_controller()
+          ->IsMaximizeModeWindowManagerEnabled() &&
+      shelf_->IsHorizontalAlignment() && ProcessGestureEvent(*event)) {
+    event->SetHandled();
+  } else {
+    TrayBackgroundView::OnGestureEvent(event);
+  }
+}
+
+gfx::Rect SystemTray::GetWorkAreaBoundsInScreen() const {
+  return shelf_->GetUserWorkAreaBounds();
+}
+
 bool SystemTray::PerformAction(const ui::Event& event) {
   // If we're already showing a full system tray menu, either default or
   // detailed menu, hide it; otherwise, show it (and hide any popup that's
@@ -634,6 +653,96 @@
   return true;
 }
 
+bool SystemTray::ProcessGestureEvent(const ui::GestureEvent& event) {
+  if (event.type() == ui::ET_GESTURE_SCROLL_BEGIN)
+    return StartGestureDrag(event);
+
+  if (!HasSystemBubble() || !is_in_drag_)
+    return false;
+
+  if (event.type() == ui::ET_GESTURE_SCROLL_UPDATE) {
+    UpdateGestureDrag(event);
+    return true;
+  }
+
+  if (event.type() == ui::ET_GESTURE_SCROLL_END ||
+      event.type() == ui::ET_SCROLL_FLING_START) {
+    CompleteGestureDrag(event);
+    return true;
+  }
+
+  // Unexpected event. Reset the drag state and close the bubble.
+  is_in_drag_ = false;
+  CloseSystemBubble();
+  return false;
+}
+
+bool SystemTray::StartGestureDrag(const ui::GestureEvent& gesture) {
+  // Close the system bubble if there is already a full one opened. And return
+  // false to let shelf handle the event.
+  if (HasSystemBubble() && full_system_tray_menu_) {
+    system_bubble_->bubble()->Close();
+    return false;
+  }
+
+  // If the scroll sequence begins to scroll downward, return false so that the
+  // event will instead by handled by the shelf.
+  if (gesture.details().scroll_y_hint() > 0)
+    return false;
+
+  is_in_drag_ = true;
+  gesture_drag_amount_ = 0.f;
+  ShowDefaultView(BUBBLE_CREATE_NEW);
+  system_tray_bubble_bounds_ =
+      system_bubble_->bubble_view()->GetWidget()->GetWindowBoundsInScreen();
+  SetBubbleBounds(gesture.location());
+  return true;
+}
+
+void SystemTray::UpdateGestureDrag(const ui::GestureEvent& gesture) {
+  SetBubbleBounds(gesture.location());
+  gesture_drag_amount_ += gesture.details().scroll_y();
+}
+
+void SystemTray::CompleteGestureDrag(const ui::GestureEvent& gesture) {
+  const bool hide_bubble = !ShouldShowSystemBubbleAfterScrollSequence(gesture);
+  gfx::Rect target_bounds = system_tray_bubble_bounds_;
+
+  if (hide_bubble)
+    target_bounds.set_y(shelf_->GetIdealBounds().y());
+
+  system_bubble_->bubble()->AnimateToTargetBounds(target_bounds, hide_bubble);
+  is_in_drag_ = false;
+}
+
+void SystemTray::SetBubbleBounds(const gfx::Point& location) {
+  gfx::Point location_in_screen_coordinates(location);
+  View::ConvertPointToScreen(this, &location_in_screen_coordinates);
+
+  // System tray bubble should not be dragged higher than its original height.
+  if (location_in_screen_coordinates.y() < system_tray_bubble_bounds_.y())
+    location_in_screen_coordinates.set_y(system_tray_bubble_bounds_.y());
+
+  gfx::Rect bounds_on_location = system_tray_bubble_bounds_;
+  bounds_on_location.set_y(location_in_screen_coordinates.y());
+  system_bubble_->bubble_view()->GetWidget()->SetBounds(bounds_on_location);
+}
+
+bool SystemTray::ShouldShowSystemBubbleAfterScrollSequence(
+    const ui::GestureEvent& sequence_end) {
+  // If the scroll sequence terminates with a fling, show the system menu if the
+  // fling was fast enough and in the correct direction.
+  if (sequence_end.type() == ui::ET_SCROLL_FLING_START &&
+      fabs(sequence_end.details().velocity_y()) > kFlingVelocity) {
+    return sequence_end.details().velocity_y() < 0;
+  }
+
+  DCHECK(sequence_end.type() == ui::ET_GESTURE_SCROLL_END ||
+         sequence_end.type() == ui::ET_SCROLL_FLING_START);
+  // Show the system menu if it is already at least one-third visible.
+  return -gesture_drag_amount_ >= system_tray_bubble_bounds_.height() / 3.0;
+}
+
 void SystemTray::CloseSystemBubbleAndDeactivateSystemTray() {
   activation_observer_.reset();
   system_bubble_.reset();
diff --git a/ash/system/tray/system_tray.h b/ash/system/tray/system_tray.h
index 8483db92..fe8a0fd 100644
--- a/ash/system/tray/system_tray.h
+++ b/ash/system/tray/system_tray.h
@@ -46,6 +46,9 @@
 class ASH_EXPORT SystemTray : public TrayBackgroundView,
                               public views::TrayBubbleView::Delegate {
  public:
+  // The threshold of the velocity of the fling event.
+  static constexpr float kFlingVelocity = 100.0f;
+
   explicit SystemTray(Shelf* shelf);
   ~SystemTray() override;
 
@@ -146,6 +149,11 @@
   // Activates the system tray bubble.
   void ActivateBubble();
 
+  // ui::EventHandler:
+  void OnGestureEvent(ui::GestureEvent* event) override;
+
+  gfx::Rect GetWorkAreaBoundsInScreen() const;
+
  private:
   friend class SystemTrayTestApi;
   class ActivationObserver;
@@ -193,6 +201,37 @@
   // Overridden from ActionableView.
   bool PerformAction(const ui::Event& event) override;
 
+  // Gesture related functions:
+  bool ProcessGestureEvent(const ui::GestureEvent& event);
+  bool StartGestureDrag(const ui::GestureEvent& gesture);
+  void UpdateGestureDrag(const ui::GestureEvent& gesture);
+  void CompleteGestureDrag(const ui::GestureEvent& gesture);
+
+  // Update the bounds of the system tray bubble according to |location|. Note
+  // that |location| is in the local coordinate space of |this|.
+  void SetBubbleBounds(const gfx::Point& location);
+
+  // Return true if the system bubble should be shown (i.e., animated upward to
+  // be made fully visible) after a sequence of scroll events terminated by
+  // |sequence_end|. Otherwise return false, indicating that the
+  // partially-visible system bubble should be animated downward and made fully
+  // hidden.
+  bool ShouldShowSystemBubbleAfterScrollSequence(
+      const ui::GestureEvent& sequence_end);
+
+  // Shelf the system tray is in.
+  Shelf* const shelf_;
+
+  // The original bounds of the system tray bubble.
+  gfx::Rect system_tray_bubble_bounds_;
+
+  // Tracks the amount of the drag. Only valid if |is_in_drag_| is true.
+  float gesture_drag_amount_ = 0.f;
+
+  // True if the user is in the process of gesture-dragging to open the system
+  // tray bubble, false otherwise.
+  bool is_in_drag_ = false;
+
   // The web notification tray view that appears adjacent to this view.
   WebNotificationTray* web_notification_tray_ = nullptr;
 
diff --git a/ash/system/tray/system_tray_bubble.cc b/ash/system/tray/system_tray_bubble.cc
index bd639391..a9036cd 100644
--- a/ash/system/tray/system_tray_bubble.cc
+++ b/ash/system/tray/system_tray_bubble.cc
@@ -15,6 +15,7 @@
 #include "ash/system/tray/system_tray_item.h"
 #include "ash/system/tray/tray_bubble_wrapper.h"
 #include "ash/system/tray/tray_constants.h"
+#include "ash/wm/maximize_mode/maximize_mode_controller.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "ui/compositor/layer.h"
@@ -68,6 +69,26 @@
 
 }  // namespace
 
+// CloseBubbleObserver is used to delay closing the system tray bubble until the
+// animation completes.
+class CloseBubbleObserver : public ui::ImplicitAnimationObserver {
+ public:
+  explicit CloseBubbleObserver(SystemTrayBubble* system_tray_bubble)
+      : system_tray_bubble_(system_tray_bubble) {}
+
+  ~CloseBubbleObserver() override {}
+
+  void OnImplicitAnimationsCompleted() override {
+    system_tray_bubble_->Close();
+    delete this;
+  }
+
+ private:
+  SystemTrayBubble* system_tray_bubble_ = nullptr;
+
+  DISALLOW_COPY_AND_ASSIGN(CloseBubbleObserver);
+};
+
 // SystemTrayBubble
 
 SystemTrayBubble::SystemTrayBubble(
@@ -200,8 +221,27 @@
   }
 
   init_params->delegate = tray_;
-  // Place the bubble on same display as this system tray.
-  init_params->parent_window = tray_->GetBubbleWindowContainer();
+  // Place the bubble on same display as this system tray if it is not on
+  // maximize mode. Otherwise, create an clipping window to hold the system
+  // bubble. And place the clipping window on the same display as the system
+  // tray.
+  if (Shell::Get()
+          ->maximize_mode_controller()
+          ->IsMaximizeModeWindowManagerEnabled()) {
+    if (!clipping_window_.get()) {
+      clipping_window_ =
+          std::unique_ptr<aura::Window>(new aura::Window(nullptr));
+      clipping_window_->Init(ui::LAYER_NOT_DRAWN);
+      clipping_window_->layer()->SetMasksToBounds(true);
+      tray_->GetBubbleWindowContainer()->AddChild(clipping_window_.get());
+      clipping_window_->Show();
+    }
+    clipping_window_->SetBounds(tray_->GetWorkAreaBoundsInScreen());
+    init_params->parent_window = clipping_window_.get();
+  } else {
+    init_params->parent_window = tray_->GetBubbleWindowContainer();
+  }
+
   init_params->anchor_view = anchor;
   bubble_view_ = new TrayBubbleView(*init_params);
   UpdateBottomPadding();
@@ -290,6 +330,22 @@
   }
 }
 
+void SystemTrayBubble::AnimateToTargetBounds(const gfx::Rect& target_bounds,
+                                             bool close_bubble) {
+  const int kAnimationDurationMS = 200;
+
+  ui::ScopedLayerAnimationSettings settings(
+      bubble_view()->GetWidget()->GetNativeView()->layer()->GetAnimator());
+  settings.SetTransitionDuration(
+      base::TimeDelta::FromMilliseconds(kAnimationDurationMS));
+  settings.SetTweenType(gfx::Tween::EASE_OUT);
+  settings.SetPreemptionStrategy(
+      ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
+  if (close_bubble)
+    settings.AddObserver(new CloseBubbleObserver(this));
+  bubble_view()->GetWidget()->SetBounds(target_bounds);
+}
+
 void SystemTrayBubble::UpdateBottomPadding() {
   if (bubble_type_ == BUBBLE_TYPE_DEFAULT)
     bubble_view_->SetBottomPadding(kDefaultViewBottomPadding);
diff --git a/ash/system/tray/system_tray_bubble.h b/ash/system/tray/system_tray_bubble.h
index 3ee439d..89c490f 100644
--- a/ash/system/tray/system_tray_bubble.h
+++ b/ash/system/tray/system_tray_bubble.h
@@ -58,6 +58,10 @@
   // BUBBLE_TYPE_DEFAULT BubbleType.
   void RecordVisibleRowMetrics();
 
+  // Update the bounds of the system tray bubble. Close the bubble if
+  // |close_bubble| is set.
+  void AnimateToTargetBounds(const gfx::Rect& target_bounds, bool close_bubble);
+
  private:
   // Updates the bottom padding of the |bubble_view_| based on the
   // |bubble_type_|.
@@ -75,6 +79,10 @@
   int autoclose_delay_;
   base::OneShotTimer autoclose_;
 
+  // Used in maximize mode to make sure the system tray bubble only be shown in
+  // work area.
+  std::unique_ptr<aura::Window> clipping_window_;
+
   DISALLOW_COPY_AND_ASSIGN(SystemTrayBubble);
 };
 
diff --git a/ash/system/tray/system_tray_unittest.cc b/ash/system/tray/system_tray_unittest.cc
index c8dc570c..771e4de 100644
--- a/ash/system/tray/system_tray_unittest.cc
+++ b/ash/system/tray/system_tray_unittest.cc
@@ -12,6 +12,7 @@
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/root_window_controller.h"
 #include "ash/shelf/shelf.h"
+#include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
 #include "ash/system/status_area_widget.h"
 #include "ash/system/tray/system_tray_bubble.h"
@@ -21,6 +22,7 @@
 #include "ash/test/ash_test_base.h"
 #include "ash/test/status_area_widget_test_helper.h"
 #include "ash/test/test_system_tray_item.h"
+#include "ash/wm/maximize_mode/maximize_mode_controller.h"
 #include "ash/wm/window_util.h"
 #include "base/memory/ptr_util.h"
 #include "base/run_loop.h"
@@ -56,7 +58,267 @@
 
 }  // namespace
 
-typedef AshTestBase SystemTrayTest;
+class SystemTrayTest : public AshTestBase {
+ public:
+  SystemTrayTest() {}
+  ~SystemTrayTest() override {}
+
+  // Swiping on the system tray and ends with finger released.
+  void SendGestureEvent(gfx::Point& start,
+                        float delta,
+                        bool is_fling,
+                        float velocity_y) {
+    SystemTray* system_tray = GetPrimarySystemTray();
+    base::TimeTicks timestamp = base::TimeTicks::Now();
+    SendScrollStartAndUpdate(start, delta, timestamp);
+
+    ui::GestureEventDetails details =
+        is_fling
+            ? ui::GestureEventDetails(ui::ET_SCROLL_FLING_START, 0, velocity_y)
+            : ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_END);
+    ui::GestureEvent event = ui::GestureEvent(start.x(), start.y() + delta,
+                                              ui::EF_NONE, timestamp, details);
+    system_tray->OnGestureEvent(&event);
+  }
+
+  // Swiping on the system tray without releasing the finger.
+  void SendScrollStartAndUpdate(gfx::Point& start,
+                                float delta,
+                                base::TimeTicks& timestamp) {
+    SystemTray* system_tray = GetPrimarySystemTray();
+    ui::GestureEventDetails begin_details(ui::ET_GESTURE_SCROLL_BEGIN);
+    ui::GestureEvent begin_event = ui::GestureEvent(
+        start.x(), start.y(), ui::EF_NONE, timestamp, begin_details);
+    system_tray->OnGestureEvent(&begin_event);
+
+    ui::GestureEventDetails update_details(ui::ET_GESTURE_SCROLL_UPDATE, 0,
+                                           delta);
+    timestamp += base::TimeDelta::FromMilliseconds(100);
+    ui::GestureEvent update_event = ui::GestureEvent(
+        start.x(), start.y() + delta, ui::EF_NONE, timestamp, update_details);
+    system_tray->OnGestureEvent(&update_event);
+  }
+
+  // Open the default system tray bubble to get the height of the bubble and
+  // then close it.
+  float GetSystemBubbleHeight() {
+    SystemTray* system_tray = GetPrimarySystemTray();
+    system_tray->ShowDefaultView(BUBBLE_CREATE_NEW);
+    gfx::Rect bounds = GetSystemBubbleBoundsInScreen();
+    system_tray->CloseSystemBubble();
+    return bounds.height();
+  }
+
+  gfx::Rect GetSystemBubbleBoundsInScreen() {
+    return GetPrimarySystemTray()
+        ->GetSystemBubble()
+        ->bubble_view()
+        ->GetWidget()
+        ->GetWindowBoundsInScreen();
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SystemTrayTest);
+};
+
+// Swiping on the overlap area of shelf and system tray bubble during the
+// animation should close the bubble.
+TEST_F(SystemTrayTest, SwipingOnShelfDuringAnimation) {
+  Shelf* shelf = GetPrimaryShelf();
+  SystemTray* system_tray = GetPrimarySystemTray();
+  gfx::Point start = system_tray->GetLocalBounds().CenterPoint();
+  shelf->SetAlignment(SHELF_ALIGNMENT_BOTTOM);
+  Shell::Get()->maximize_mode_controller()->EnableMaximizeModeWindowManager(
+      true);
+
+  gfx::Rect shelf_bounds_in_screen =
+      shelf->shelf_widget()->GetWindowBoundsInScreen();
+
+  system_tray->ShowDefaultView(BUBBLE_CREATE_NEW);
+  gfx::Rect original_bounds = GetSystemBubbleBoundsInScreen();
+  system_tray->CloseSystemBubble();
+
+  // Enable animations so that we can make sure that they occur.
+  ui::ScopedAnimationDurationScaleMode regular_animations(
+      ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
+
+  ui::test::EventGenerator& generator = GetEventGenerator();
+  gfx::Point point_on_shelf_start =
+      gfx::Point(original_bounds.x() + 5, shelf_bounds_in_screen.y() + 5);
+  gfx::Point point_on_shelf_end(point_on_shelf_start.x(),
+                                shelf_bounds_in_screen.bottom());
+
+  // Swiping up exceed one third of the height of the bubble should show the
+  // bubble.
+  float delta = -original_bounds.height() / 2;
+  SendGestureEvent(start, delta, false, 0);
+  EXPECT_TRUE(system_tray->HasSystemBubble());
+  gfx::Rect current_bounds = GetSystemBubbleBoundsInScreen();
+
+  // Dragging the shelf during up animation should close the bubble.
+  if (current_bounds.y() != original_bounds.y()) {
+    generator.GestureScrollSequence(point_on_shelf_start, point_on_shelf_end,
+                                    base::TimeDelta::FromMilliseconds(100), 5);
+    EXPECT_FALSE(system_tray->HasSystemBubble());
+  }
+
+  // Fling down on the shelf with a velocity that exceeds |kFlingVelocity|.
+  EXPECT_FALSE(system_tray->HasSystemBubble());
+  SendGestureEvent(start, delta, true, SystemTray::kFlingVelocity + 1);
+  current_bounds = GetSystemBubbleBoundsInScreen();
+  EXPECT_TRUE(system_tray->HasSystemBubble());
+
+  // Dragging the shelf during down animation should close the bubble.
+  if (current_bounds.y() != original_bounds.y()) {
+    generator.GestureScrollSequence(point_on_shelf_start, point_on_shelf_end,
+                                    base::TimeDelta::FromMilliseconds(100), 5);
+    EXPECT_FALSE(system_tray->HasSystemBubble());
+  }
+}
+
+// Swiping on the system tray ends with fling event.
+TEST_F(SystemTrayTest, FlingOnSystemTray) {
+  Shelf* shelf = GetPrimaryShelf();
+  SystemTray* system_tray = GetPrimarySystemTray();
+  gfx::Point start = system_tray->GetBoundsInScreen().CenterPoint();
+  shelf->SetAlignment(SHELF_ALIGNMENT_BOTTOM);
+  Shell::Get()->maximize_mode_controller()->EnableMaximizeModeWindowManager(
+      true);
+
+  // Fling up on the system tray should show the bubble if the |velocity_y| is
+  // larger than |kFlingVelocity| and the dragging amount is larger than one
+  // third of the height of the bubble.
+  float delta = -GetSystemBubbleHeight();
+  SendGestureEvent(start, delta, true, -(SystemTray::kFlingVelocity + 1));
+  EXPECT_TRUE(system_tray->HasSystemBubble());
+  system_tray->CloseSystemBubble();
+
+  // Fling up on the system tray should show the bubble if the |velocity_y| is
+  // larger than |kFlingVelocity| even the dragging amount is less than one
+  // third of the height of the bubble.
+  delta /= 4;
+  SendGestureEvent(start, delta, true, -(SystemTray::kFlingVelocity + 1));
+  EXPECT_TRUE(system_tray->HasSystemBubble());
+  system_tray->CloseSystemBubble();
+
+  // Fling up on the system tray should show the bubble if the |velocity_y| is
+  // less than |kFlingVelocity| but the dragging amount if larger than one third
+  // of the height of the bubble.
+  delta = -GetSystemBubbleHeight();
+  SendGestureEvent(start, delta, true, -(SystemTray::kFlingVelocity - 1));
+  EXPECT_TRUE(system_tray->HasSystemBubble());
+  system_tray->CloseSystemBubble();
+
+  // Fling up on the system tray should close the bubble if the |velocity_y|
+  // is less than |kFlingVelocity| and the dragging amount is less than one
+  // third of the height of the bubble.
+  delta /= 4;
+  SendGestureEvent(start, delta, true, -(SystemTray::kFlingVelocity - 1));
+  EXPECT_FALSE(system_tray->HasSystemBubble());
+
+  // Fling down on the system tray should close the bubble if the |velocity_y|
+  // is larger than kFLingVelocity.
+  SendGestureEvent(start, delta, true, SystemTray::kFlingVelocity + 1);
+  EXPECT_FALSE(system_tray->HasSystemBubble());
+
+  // Fling down on the system tray should close the bubble if the |velocity_y|
+  // is larger than |kFlingVelocity| even the dragging amount is larger than one
+  // third of the height of the bubble.
+  delta = -GetSystemBubbleHeight();
+  SendGestureEvent(start, delta, true, SystemTray::kFlingVelocity + 1);
+  EXPECT_FALSE(system_tray->HasSystemBubble());
+
+  // Fling down on the system tray should open the bubble if the |velocity_y| is
+  // less than |kFlingVelocity| but the dragging amount exceed one third of the
+  // height of the bubble.
+  SendGestureEvent(start, delta, true, SystemTray::kFlingVelocity - 1);
+  EXPECT_TRUE(system_tray->HasSystemBubble());
+  system_tray->CloseSystemBubble();
+
+  // Fling down on the system tray should close the bubble if the |velocity_y|
+  // is less than |kFlingVelocity| and the dragging amount is less than one
+  // third of the height of the bubble.
+  delta /= 4;
+  SendGestureEvent(start, delta, true, SystemTray::kFlingVelocity - 1);
+  EXPECT_FALSE(system_tray->HasSystemBubble());
+}
+
+// Touch outside the system tray bubble during swiping should close the bubble.
+TEST_F(SystemTrayTest, TapOutsideCloseBubble) {
+  Shelf* shelf = GetPrimaryShelf();
+  SystemTray* system_tray = GetPrimarySystemTray();
+  gfx::Point start = system_tray->GetLocalBounds().CenterPoint();
+  shelf->SetAlignment(SHELF_ALIGNMENT_BOTTOM);
+
+  float delta = -GetSystemBubbleHeight();
+  Shell::Get()->maximize_mode_controller()->EnableMaximizeModeWindowManager(
+      true);
+  base::TimeTicks timestamp = base::TimeTicks::Now();
+  SendScrollStartAndUpdate(start, delta, timestamp);
+  EXPECT_TRUE(system_tray->HasSystemBubble());
+
+  ui::test::EventGenerator& generator = GetEventGenerator();
+  gfx::Rect bounds = GetSystemBubbleBoundsInScreen();
+  gfx::Point point_outside = gfx::Point(bounds.x() - 5, bounds.y() - 5);
+  generator.GestureTapAt(point_outside);
+  EXPECT_FALSE(system_tray->HasSystemBubble());
+}
+
+// Swiping on the system tray ends with scroll event.
+TEST_F(SystemTrayTest, SwipingOnSystemTray) {
+  Shelf* shelf = GetPrimaryShelf();
+  SystemTray* system_tray = GetPrimarySystemTray();
+  gfx::Point start = system_tray->GetLocalBounds().CenterPoint();
+  shelf->SetAlignment(SHELF_ALIGNMENT_BOTTOM);
+
+  // Swiping up on the system tray has no effect if it is not in maximize mode.
+  float delta = -GetSystemBubbleHeight();
+  Shell::Get()->maximize_mode_controller()->EnableMaximizeModeWindowManager(
+      false);
+  EXPECT_FALSE(system_tray->HasSystemBubble());
+  SendGestureEvent(start, delta, false, 0);
+  EXPECT_FALSE(system_tray->HasSystemBubble());
+
+  // Swiping up on the system tray should show the system tray bubble if it is
+  // in maximize mode.
+  Shell::Get()->maximize_mode_controller()->EnableMaximizeModeWindowManager(
+      true);
+  SendGestureEvent(start, delta, false, 0);
+  EXPECT_TRUE(system_tray->HasSystemBubble());
+  system_tray->CloseSystemBubble();
+
+  // Swiping up less than one third of the bubble's height should not show the
+  // bubble.
+  delta /= 4;
+  SendGestureEvent(start, delta, false, 0);
+  EXPECT_FALSE(system_tray->HasSystemBubble());
+
+  // Swiping up more than one third of the bubble's height should show the
+  // bubble.
+  delta = -GetSystemBubbleHeight() / 2;
+  SendGestureEvent(start, delta, false, 0);
+  EXPECT_TRUE(system_tray->HasSystemBubble());
+  system_tray->CloseSystemBubble();
+
+  // Swiping up on system tray should not show the system tray bubble if the
+  // shelf is left alignment.
+  delta = -GetSystemBubbleHeight();
+  shelf->SetAlignment(SHELF_ALIGNMENT_LEFT);
+  SendGestureEvent(start, delta, false, 0);
+  EXPECT_FALSE(system_tray->HasSystemBubble());
+
+  // Swiping up on system tray should not show the system tray bubble if the
+  // shelf is right alignment.
+  shelf->SetAlignment(SHELF_ALIGNMENT_RIGHT);
+  SendGestureEvent(start, delta, false, 0);
+  EXPECT_FALSE(system_tray->HasSystemBubble());
+
+  // Swiping down on the shelf should not show the system tray bubble.
+  shelf->SetAlignment(SHELF_ALIGNMENT_BOTTOM);
+  delta = -delta;
+  SendGestureEvent(start, delta, false, 0);
+  EXPECT_FALSE(system_tray->HasSystemBubble());
+}
 
 // Verifies only the visible default views are recorded in the
 // "Ash.SystemMenu.DefaultView.VisibleItems" histogram.
diff --git a/ash/system/tray/tray_event_filter.cc b/ash/system/tray/tray_event_filter.cc
index 43f142b..873e7df 100644
--- a/ash/system/tray/tray_event_filter.cc
+++ b/ash/system/tray/tray_event_filter.cc
@@ -5,10 +5,12 @@
 #include "ash/system/tray/tray_event_filter.h"
 
 #include "ash/public/cpp/shell_window_ids.h"
+#include "ash/shell.h"
 #include "ash/shell_port.h"
 #include "ash/system/tray/tray_background_view.h"
 #include "ash/system/tray/tray_bubble_wrapper.h"
 #include "ash/wm/container_finder.h"
+#include "ash/wm/maximize_mode/maximize_mode_controller.h"
 #include "ui/aura/window.h"
 #include "ui/views/widget/widget.h"
 
@@ -73,6 +75,19 @@
 
     gfx::Rect bounds = bubble_widget->GetWindowBoundsInScreen();
     bounds.Inset(wrapper->bubble_view()->GetBorderInsets());
+    // System tray can be dragged to show the bubble if it is in maximize mode.
+    // During the drag, the bubble's logical bounds can extend outside of the
+    // work area, but its visual bounds are only within the work area. Restrict
+    // |bounds| so that events located outside the bubble's visual bounds are
+    // treated as outside of the bubble.
+    int bubble_container_id =
+        wm::GetContainerForWindow(bubble_widget->GetNativeWindow())->id();
+    if (Shell::Get()
+            ->maximize_mode_controller()
+            ->IsMaximizeModeWindowManagerEnabled() &&
+        bubble_container_id == kShellWindowId_SettingBubbleContainer) {
+      bounds.Intersect(bubble_widget->GetWorkAreaBoundsInScreen());
+    }
     if (bounds.Contains(location_in_screen))
       continue;
     if (wrapper->tray()) {
diff --git a/base/android/java/src/org/chromium/base/ApiCompatibilityUtils.java b/base/android/java/src/org/chromium/base/ApiCompatibilityUtils.java
index d57cd3c..24f85d66 100644
--- a/base/android/java/src/org/chromium/base/ApiCompatibilityUtils.java
+++ b/base/android/java/src/org/chromium/base/ApiCompatibilityUtils.java
@@ -26,6 +26,7 @@
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.StatFs;
+import android.os.StrictMode;
 import android.os.UserManager;
 import android.provider.Settings;
 import android.text.Html;
@@ -446,10 +447,15 @@
      */
     @SuppressWarnings("deprecation")
     public static Drawable getDrawable(Resources res, int id) throws NotFoundException {
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
-            return res.getDrawable(id, null);
-        } else {
-            return res.getDrawable(id);
+        StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+        try {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+                return res.getDrawable(id, null);
+            } else {
+                return res.getDrawable(id);
+            }
+        } finally {
+            StrictMode.setThreadPolicy(oldPolicy);
         }
     }
 
diff --git a/base/android/java/src/org/chromium/base/process_launcher/ChildProcessServiceImpl.java b/base/android/java/src/org/chromium/base/process_launcher/ChildProcessServiceImpl.java
index 09f508a..e7ba8d5 100644
--- a/base/android/java/src/org/chromium/base/process_launcher/ChildProcessServiceImpl.java
+++ b/base/android/java/src/org/chromium/base/process_launcher/ChildProcessServiceImpl.java
@@ -158,11 +158,11 @@
         }
         sCreateCalled = true;
 
-        mDelegate.onServiceCreated();
-
         // Initialize the context for the application that owns this ChildProcessServiceImpl object.
         ContextUtils.initApplicationContext(context);
 
+        mDelegate.onServiceCreated();
+
         mMainThread = new Thread(new Runnable() {
             @Override
             @SuppressFBWarnings("DM_EXIT")
diff --git a/base/android/jni_generator/SampleForTests_jni.golden b/base/android/jni_generator/SampleForTests_jni.golden
index e50583ab..7b384252 100644
--- a/base/android/jni_generator/SampleForTests_jni.golden
+++ b/base/android/jni_generator/SampleForTests_jni.golden
@@ -482,35 +482,24 @@
     },
 };
 
-// TODO(agrieve): Remove these empty registration functions and functions
-// calling them. https://crbug.com/683256.
 static bool RegisterNativesImpl(JNIEnv* env) {
-  return true;
-}
+  if (jni_generator::ShouldSkipJniRegistration(false))
+    return true;
 
-}  // namespace android
-}  // namespace base
-
-JNI_REGISTRATION_EXPORT bool
-    RegisterNative_org_chromium_example_jni_1generator_SampleForTests(JNIEnv*
-    env) {
-
-  const int kMethodsInnerClassSize =
-      arraysize(base::android::kMethodsInnerClass);
+  const int kMethodsInnerClassSize = arraysize(kMethodsInnerClass);
 
   if (env->RegisterNatives(InnerClass_clazz(env),
-                           base::android::kMethodsInnerClass,
+                           kMethodsInnerClass,
                            kMethodsInnerClassSize) < 0) {
     jni_generator::HandleRegistrationError(
         env, InnerClass_clazz(env), __FILE__);
     return false;
   }
 
-  const int kMethodsSampleForTestsSize =
-      arraysize(base::android::kMethodsSampleForTests);
+  const int kMethodsSampleForTestsSize = arraysize(kMethodsSampleForTests);
 
   if (env->RegisterNatives(SampleForTests_clazz(env),
-                           base::android::kMethodsSampleForTests,
+                           kMethodsSampleForTests,
                            kMethodsSampleForTestsSize) < 0) {
     jni_generator::HandleRegistrationError(
         env, SampleForTests_clazz(env), __FILE__);
@@ -520,4 +509,7 @@
   return true;
 }
 
+}  // namespace android
+}  // namespace base
+
 #endif  // org_chromium_example_jni_generator_SampleForTests_JNI
diff --git a/base/android/jni_generator/jni_generator.py b/base/android/jni_generator/jni_generator.py
index 8249fc9e..3818009 100755
--- a/base/android/jni_generator/jni_generator.py
+++ b/base/android/jni_generator/jni_generator.py
@@ -401,7 +401,7 @@
 
 
 def IsMainDexJavaClass(contents):
-  """Returns True if the class is annotated with "@MainDex", False if not.
+  """Returns "true" if the class is annotated with "@MainDex", "false" if not.
 
   JNI registration doesn't always need to be completed for non-browser processes
   since most Java code is only used by the browser process. Classes that are
@@ -409,17 +409,8 @@
   to force JNI registration.
   """
   re_maindex = re.compile(r'@MainDex[\s\S]*class({|[\s\S]*{)')
-  return bool(re.search(re_maindex, contents))
-
-
-def GetBinaryClassName(fully_qualified_class):
-  """Returns a string concatenating the Java package and class."""
-  return fully_qualified_class.replace('_', '_1').replace('/', '_')
-
-
-def GetRegistrationFunctionName(fully_qualified_class):
-  """Returns the register name with a given class."""
-  return 'RegisterNative_' + GetBinaryClassName(fully_qualified_class)
+  found = re.search(re_maindex, contents)
+  return 'true' if found else 'false'
 
 
 def GetStaticCastForReturnType(return_type):
@@ -629,6 +620,7 @@
           is_constructor=True)]
     self.called_by_natives = MangleCalledByNatives(self.jni_params,
                                                    self.called_by_natives)
+
     self.constant_fields = []
     re_constant_field = re.compile('.*?public static final int (?P<name>.*?);')
     re_constant_field_value = re.compile(
@@ -681,12 +673,13 @@
     jni_namespace = ExtractJNINamespace(contents) or options.namespace
     natives = ExtractNatives(contents, options.ptr_type)
     called_by_natives = ExtractCalledByNatives(self.jni_params, contents)
+    maindex = IsMainDexJavaClass(contents)
     if len(natives) == 0 and len(called_by_natives) == 0:
       raise SyntaxError('Unable to find any JNI methods for %s.' %
                         fully_qualified_class)
     inl_header_file_generator = InlHeaderFileGenerator(
         jni_namespace, fully_qualified_class, natives, called_by_natives, [],
-        self.jni_params, options)
+        self.jni_params, options, maindex)
     self.content = inl_header_file_generator.GetContent()
 
   @classmethod
@@ -722,7 +715,8 @@
   """Generates an inline header file for JNI integration."""
 
   def __init__(self, namespace, fully_qualified_class, natives,
-               called_by_natives, constant_fields, jni_params, options):
+               called_by_natives, constant_fields, jni_params, options,
+               maindex='false'):
     self.namespace = namespace
     self.fully_qualified_class = fully_qualified_class
     self.class_name = self.fully_qualified_class.split('/')[-1]
@@ -730,6 +724,7 @@
     self.called_by_natives = called_by_natives
     self.header_guard = fully_qualified_class.replace('/', '_') + '_JNI'
     self.constant_fields = constant_fields
+    self.maindex = maindex
     self.jni_params = jni_params
     self.options = options
 
@@ -771,9 +766,8 @@
 
 // Step 3: RegisterNatives.
 $JNI_NATIVE_METHODS
-$REGISTER_NATIVES_EMPTY
-$CLOSE_NAMESPACE
 $REGISTER_NATIVES
+$CLOSE_NAMESPACE
 
 #endif  // ${HEADER_GUARD}
 """)
@@ -785,9 +779,8 @@
         'METHOD_STUBS': self.GetMethodStubsString(),
         'OPEN_NAMESPACE': self.GetOpenNamespaceString(),
         'JNI_NATIVE_METHODS': self.GetJNINativeMethodsString(),
-        'REGISTER_NATIVES_EMPTY': self.GetOriginalRegisterNativesString(),
-        'CLOSE_NAMESPACE': self.GetCloseNamespaceString(),
         'REGISTER_NATIVES': self.GetRegisterNativesString(),
+        'CLOSE_NAMESPACE': self.GetCloseNamespaceString(),
         'HEADER_GUARD': self.header_guard,
         'INCLUDES': self.GetIncludesString(),
     }
@@ -836,19 +829,14 @@
     return '\n'.join(ret)
 
   def SubstituteNativeMethods(self, template):
-    """Substitutes NAMESPACE, JAVA_CLASS and KMETHODS in the provided
-    template."""
+    """Substitutes JAVA_CLASS and KMETHODS in the provided template."""
     ret = []
     all_classes = self.GetUniqueClasses(self.natives)
     all_classes[self.class_name] = self.fully_qualified_class
     for clazz in all_classes:
       kmethods = self.GetKMethodsString(clazz)
-      namespace_str = ''
-      if self.namespace:
-        namespace_str = self.namespace + '::'
       if kmethods:
-        values = {'NAMESPACE': namespace_str,
-                  'JAVA_CLASS': clazz,
+        values = {'JAVA_CLASS': clazz,
                   'KMETHODS': kmethods}
         ret += [template.substitute(values)]
     if not ret: return ''
@@ -865,38 +853,32 @@
 """)
     return self.SubstituteNativeMethods(template)
 
-  # TODO(agrieve): Remove this function when deleting original registers.
-  # https://crbug.com/683256.
-  def GetOriginalRegisterNativesString(self):
-    """Return the code for original RegisterNatives"""
-    natives = self.GetRegisterNativesImplString()
-    if not natives:
-      return ''
-
-    return """
-// TODO(agrieve): Remove these empty registration functions and functions
-// calling them. https://crbug.com/683256.
-static bool RegisterNativesImpl(JNIEnv* env) {
-  return true;
-}
-"""
-
   def GetRegisterNativesString(self):
     """Returns the code for RegisterNatives."""
     natives = self.GetRegisterNativesImplString()
     if not natives:
       return ''
+
     template = Template("""\
-JNI_REGISTRATION_EXPORT bool ${REGISTER_NAME}(JNIEnv* env) {
+${REGISTER_NATIVES_SIGNATURE} {
+${EARLY_EXIT}
 ${NATIVES}
   return true;
 }
 """)
-    values = {
-        'REGISTER_NAME':
-            GetRegistrationFunctionName(self.fully_qualified_class),
-        'NATIVES': natives
-    }
+    signature = 'static bool RegisterNativesImpl(JNIEnv* env)'
+    early_exit = ''
+    if self.options.native_exports_optional:
+      early_exit = """\
+  if (jni_generator::ShouldSkipJniRegistration(%s))
+    return true;
+""" % self.maindex
+
+    values = {'REGISTER_NATIVES_SIGNATURE': signature,
+              'EARLY_EXIT': early_exit,
+              'NATIVES': natives,
+             }
+
     return template.substitute(values)
 
   def GetRegisterNativesImplString(self):
@@ -905,11 +887,10 @@
       return ''
 
     template = Template("""\
-  const int kMethods${JAVA_CLASS}Size =
-      arraysize(${NAMESPACE}kMethods${JAVA_CLASS});
+  const int kMethods${JAVA_CLASS}Size = arraysize(kMethods${JAVA_CLASS});
 
   if (env->RegisterNatives(${JAVA_CLASS}_clazz(env),
-                           ${NAMESPACE}kMethods${JAVA_CLASS},
+                           kMethods${JAVA_CLASS},
                            kMethods${JAVA_CLASS}Size) < 0) {
     jni_generator::HandleRegistrationError(
         env, ${JAVA_CLASS}_clazz(env), __FILE__);
@@ -993,7 +974,7 @@
     """
     template = Template("Java_${JAVA_NAME}_native${NAME}")
 
-    java_name = GetBinaryClassName(self.fully_qualified_class)
+    java_name = self.fully_qualified_class.replace('_', '_1').replace('/', '_')
     if native.java_class_name:
       java_name += '_00024' + native.java_class_name
 
@@ -1333,21 +1314,19 @@
     print e
     sys.exit(1)
   if output_file:
-    WriteOutput(output_file, content)
+    if not os.path.exists(os.path.dirname(os.path.abspath(output_file))):
+      os.makedirs(os.path.dirname(os.path.abspath(output_file)))
+    if options.optimize_generation and os.path.exists(output_file):
+      with file(output_file, 'r') as f:
+        existing_content = f.read()
+        if existing_content == content:
+          return
+    with file(output_file, 'w') as f:
+      f.write(content)
   else:
     print content
 
 
-def WriteOutput(output_file, content):
-  if os.path.exists(output_file):
-    with open(output_file) as f:
-      existing_content = f.read()
-      if existing_content == content:
-        return
-  with open(output_file, 'w') as f:
-    f.write(content)
-
-
 def GetScriptName():
   script_components = os.path.abspath(sys.argv[0]).split(os.path.sep)
   base_index = 0
@@ -1384,6 +1363,10 @@
   option_parser.add_option('--output_dir',
                            help='The output directory. Must be used with '
                            '--input')
+  option_parser.add_option('--optimize_generation', type="int",
+                           default=0, help='Whether we should optimize JNI '
+                           'generation by not regenerating files if they have '
+                           'not changed.')
   option_parser.add_option('--script_name', default=GetScriptName(),
                            help='The name of this script in the generated '
                            'header.')
diff --git a/base/android/jni_generator/jni_generator_helper.h b/base/android/jni_generator/jni_generator_helper.h
index 53629a657..3062806 100644
--- a/base/android/jni_generator/jni_generator_helper.h
+++ b/base/android/jni_generator/jni_generator_helper.h
@@ -30,13 +30,6 @@
 #define JNI_GENERATOR_EXPORT extern "C" __attribute__((visibility("default")))
 #endif
 
-// Used to export JNI registration functions.
-#if defined(COMPONENT_BUILD)
-#define JNI_REGISTRATION_EXPORT __attribute__((visibility("default")))
-#else
-#define JNI_REGISTRATION_EXPORT
-#endif
-
 namespace jni_generator {
 
 inline void HandleRegistrationError(JNIEnv* env,
@@ -49,14 +42,12 @@
   base::android::CheckException(env);
 }
 
-// TODO(estevenson): Remove this function since all natives are registered
-// together. Currently gvr-android-sdk stil calls it.
-// https://crbug.com/664306.
 inline bool ShouldSkipJniRegistration(bool is_maindex_class) {
   switch (base::android::GetJniRegistrationType()) {
     case base::android::ALL_JNI_REGISTRATION:
       return false;
     case base::android::NO_JNI_REGISTRATION:
+      // TODO(estevenson): Change this to a DCHECK.
       return true;
     case base::android::SELECTIVE_JNI_REGISTRATION:
       return !is_maindex_class;
diff --git a/base/android/jni_generator/jni_generator_tests.py b/base/android/jni_generator/jni_generator_tests.py
index 41e617f..d667a47 100755
--- a/base/android/jni_generator/jni_generator_tests.py
+++ b/base/android/jni_generator/jni_generator_tests.py
@@ -967,6 +967,33 @@
                                              test_options)
     self.assertGoldenTextEquals(h.GetContent())
 
+  def testMainDexFile(self):
+    test_data = """
+    package org.chromium.example.jni_generator;
+
+    @MainDex
+    class Test {
+        private static native int nativeStaticMethod(long nativeTest, int arg1);
+    }
+    """
+    options = TestOptions()
+    jni_from_java = jni_generator.JNIFromJavaSource(
+      test_data, 'org/chromium/foo/Bar', options)
+    self.assertGoldenTextEquals(jni_from_java.GetContent())
+
+  def testNonMainDexFile(self):
+    test_data = """
+    package org.chromium.example.jni_generator;
+
+    class Test {
+        private static native int nativeStaticMethod(long nativeTest, int arg1);
+    }
+    """
+    options = TestOptions()
+    jni_from_java = jni_generator.JNIFromJavaSource(
+      test_data, 'org/chromium/foo/Bar', options)
+    self.assertGoldenTextEquals(jni_from_java.GetContent())
+
   def testMainDexAnnotation(self):
     mainDexEntries = [
       '@MainDex public class Test {',
@@ -994,7 +1021,7 @@
       '@MainDex public class Test extends Testable<java.io.Serializable> {',
     ]
     for entry in mainDexEntries:
-      self.assertEquals(True, IsMainDexJavaClass(entry))
+      self.assertEquals("true", IsMainDexJavaClass(entry))
 
   def testNoMainDexAnnotation(self):
     noMainDexEntries = [
@@ -1004,7 +1031,7 @@
       'public class Test extends BaseTest {'
     ]
     for entry in noMainDexEntries:
-      self.assertEquals(False, IsMainDexJavaClass(entry))
+      self.assertEquals("false", IsMainDexJavaClass(entry))
 
   def testNativeExportsOnlyOption(self):
     test_data = """
diff --git a/base/android/jni_generator/jni_registration_generator.py b/base/android/jni_generator/jni_registration_generator.py
deleted file mode 100755
index febb5b7..0000000
--- a/base/android/jni_generator/jni_registration_generator.py
+++ /dev/null
@@ -1,179 +0,0 @@
-#!/usr/bin/env python
-# 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.
-
-"""Generate JNI registration entry points
-
-Creates a header file with two static functions: RegisterMainDexNatives() and
-RegisterNonMainDexNatives(). Together, these will use manual JNI registration
-to register all native methods that exist within an application."""
-
-import argparse
-import jni_generator
-import os
-import string
-import sys
-from util import build_utils
-
-
-def GenerateJNIHeader(java_file_paths, output_file, args):
-  """Generate a header file including two registration functions.
-
-  Forward declares all JNI registration functions created by jni_generator.py.
-  Calls the functions in RegisterMainDexNatives() if they are main dex. And
-  calls them in RegisterNonMainDexNatives() if they are non-main dex.
-
-  Args:
-      java_file_paths: A list of java file paths.
-      output_file: A relative path to output file.
-      args: All input arguments.
-  """
-  registration_dict = {
-      'FORWARD_DECLARATIONS': '',
-      'REGISTER_MAIN_DEX_NATIVES': '',
-      'REGISTER_NON_MAIN_DEX_NATIVES': ''
-  }
-  # Sort the file list to make sure the order is deterministic.
-  java_file_paths.sort()
-  for path in java_file_paths:
-    if path in args.no_register_java:
-      continue
-    with open(path) as f:
-      contents = f.read()
-    natives = jni_generator.ExtractNatives(contents, 'long')
-    if len(natives) == 0:
-      continue
-    fully_qualified_class = jni_generator.ExtractFullyQualifiedJavaClassName(
-        path, contents)
-    main_dex = jni_generator.IsMainDexJavaClass(contents)
-    header_generator = HeaderGenerator(
-        fully_qualified_class, registration_dict, main_dex)
-    registration_dict = header_generator.GetContent()
-
-  header_content = CreateFromDict(registration_dict)
-  if output_file:
-    jni_generator.WriteOutput(output_file, header_content)
-  else:
-    print header_content
-
-
-def CreateFromDict(registration_dict):
-  """Returns the content of the header file."""
-
-  template = string.Template("""\
-// 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.
-
-
-// This file is autogenerated by
-//     base/android/jni_generator/jni_registration_generator.py
-// Please do not change its content.
-
-#ifndef HEADER_GUARD
-#define HEADER_GUARD
-
-#include <jni.h>
-
-#include "base/android/jni_generator/jni_generator_helper.h"
-#include "base/android/jni_int_wrapper.h"
-
-// Step 1: Forward declaration.
-${FORWARD_DECLARATIONS}
-
-// Step 2: Main dex and non-main dex registration functions.
-
-bool RegisterMainDexNatives(JNIEnv* env) {
-${REGISTER_MAIN_DEX_NATIVES}
-
-  return true;
-}
-
-bool RegisterNonMainDexNatives(JNIEnv* env) {
-${REGISTER_NON_MAIN_DEX_NATIVES}
-
-  return true;
-}
-
-#endif  // HEADER_GUARD
-""")
-  if len(registration_dict['FORWARD_DECLARATIONS']) == 0:
-    return ''
-  return jni_generator.WrapOutput(template.substitute(registration_dict))
-
-
-class HeaderGenerator(object):
-  """Generates an inline header file for JNI registration."""
-
-  def __init__(self, fully_qualified_class, registration_dict, main_dex):
-    self.fully_qualified_class = fully_qualified_class
-    self.class_name = self.fully_qualified_class.split('/')[-1]
-    self.registration_dict = registration_dict
-    self.main_dex = main_dex
-
-  def GetContent(self):
-    self._AddForwardDeclaration()
-    self._AddRegisterNatives()
-    return self.registration_dict
-
-  def _AddForwardDeclaration(self):
-    """Add the content of the forward declaration to the dictionary."""
-    template = string.Template('JNI_REGISTRATION_EXPORT bool ${METHOD_NAME}('
-                               'JNIEnv* env);\n')
-    value = {
-        'METHOD_NAME':
-            jni_generator.GetRegistrationFunctionName(
-                self.fully_qualified_class)
-    }
-    self.registration_dict['FORWARD_DECLARATIONS'] += template.substitute(value)
-
-  def _AddRegisterNatives(self):
-    """Add the body of the RegisterNativesImpl method to the dictionary."""
-    template = string.Template("""
-if (!${REGISTER_NAME}(env))
-  return false;
-""")
-    value = {
-        'REGISTER_NAME':
-            jni_generator.GetRegistrationFunctionName(
-                self.fully_qualified_class)
-    }
-    register_body = template.substitute(value)
-    if self.main_dex:
-      self.registration_dict['REGISTER_MAIN_DEX_NATIVES'] += register_body
-    else:
-      self.registration_dict['REGISTER_NON_MAIN_DEX_NATIVES'] += register_body
-
-
-def main(argv):
-  arg_parser = argparse.ArgumentParser()
-  build_utils.AddDepfileOption(arg_parser)
-
-  arg_parser.add_argument('--sources_files',
-                          help='A list of .sources files which contain Java '
-                          'file paths. Must be used with --output.')
-  arg_parser.add_argument('--output',
-                          help='The output file path.')
-  arg_parser.add_argument('--no_register_java',
-                          help='A list of Java files which should be ignored '
-                          'by the parser.')
-  args = arg_parser.parse_args(build_utils.ExpandFileArgs(argv[1:]))
-  args.sources_files = build_utils.ParseGnList(args.sources_files)
-
-  if args.sources_files:
-    java_file_paths = []
-    for f in args.sources_files:
-      # java_file_paths stores each Java file path as a string.
-      java_file_paths += build_utils.ReadSourcesList(f)
-  else:
-    print '\nError: Must specify --sources_files.'
-    return 1
-  output_file = args.output
-  GenerateJNIHeader(java_file_paths, output_file, args)
-
-  if args.depfile:
-    build_utils.WriteDepfile(args.depfile, output_file)
-
-if __name__ == '__main__':
-  sys.exit(main(sys.argv))
diff --git a/base/android/jni_generator/testInnerClassNatives.golden b/base/android/jni_generator/testInnerClassNatives.golden
index 363f916..20b8830 100644
--- a/base/android/jni_generator/testInnerClassNatives.golden
+++ b/base/android/jni_generator/testInnerClassNatives.golden
@@ -51,16 +51,11 @@
     },
 };
 
-// TODO(agrieve): Remove these empty registration functions and functions
-// calling them. https://crbug.com/683256.
 static bool RegisterNativesImpl(JNIEnv* env) {
-  return true;
-}
+  if (jni_generator::ShouldSkipJniRegistration(false))
+    return true;
 
-JNI_REGISTRATION_EXPORT bool RegisterNative_org_chromium_TestJni(JNIEnv* env) {
-
-  const int kMethodsMyInnerClassSize =
-      arraysize(kMethodsMyInnerClass);
+  const int kMethodsMyInnerClassSize = arraysize(kMethodsMyInnerClass);
 
   if (env->RegisterNatives(MyInnerClass_clazz(env),
                            kMethodsMyInnerClass,
diff --git a/base/android/jni_generator/testInnerClassNativesBothInnerAndOuter.golden b/base/android/jni_generator/testInnerClassNativesBothInnerAndOuter.golden
index 6c07b58..67352e7 100644
--- a/base/android/jni_generator/testInnerClassNativesBothInnerAndOuter.golden
+++ b/base/android/jni_generator/testInnerClassNativesBothInnerAndOuter.golden
@@ -67,13 +67,9 @@
 "I", reinterpret_cast<void*>(Java_org_chromium_TestJni_nativeInit) },
 };
 
-// TODO(agrieve): Remove these empty registration functions and functions
-// calling them. https://crbug.com/683256.
 static bool RegisterNativesImpl(JNIEnv* env) {
-  return true;
-}
-
-JNI_REGISTRATION_EXPORT bool RegisterNative_org_chromium_TestJni(JNIEnv* env) {
+  if (jni_generator::ShouldSkipJniRegistration(false))
+    return true;
 
   const int kMethodsMyOtherInnerClassSize =
       arraysize(kMethodsMyOtherInnerClass);
@@ -86,8 +82,7 @@
     return false;
   }
 
-  const int kMethodsTestJniSize =
-      arraysize(kMethodsTestJni);
+  const int kMethodsTestJniSize = arraysize(kMethodsTestJni);
 
   if (env->RegisterNatives(TestJni_clazz(env),
                            kMethodsTestJni,
diff --git a/base/android/jni_generator/testInnerClassNativesMultiple.golden b/base/android/jni_generator/testInnerClassNativesMultiple.golden
index add5ac9..7807efa 100644
--- a/base/android/jni_generator/testInnerClassNativesMultiple.golden
+++ b/base/android/jni_generator/testInnerClassNativesMultiple.golden
@@ -74,13 +74,9 @@
     },
 };
 
-// TODO(agrieve): Remove these empty registration functions and functions
-// calling them. https://crbug.com/683256.
 static bool RegisterNativesImpl(JNIEnv* env) {
-  return true;
-}
-
-JNI_REGISTRATION_EXPORT bool RegisterNative_org_chromium_TestJni(JNIEnv* env) {
+  if (jni_generator::ShouldSkipJniRegistration(false))
+    return true;
 
   const int kMethodsMyOtherInnerClassSize =
       arraysize(kMethodsMyOtherInnerClass);
@@ -93,8 +89,7 @@
     return false;
   }
 
-  const int kMethodsMyInnerClassSize =
-      arraysize(kMethodsMyInnerClass);
+  const int kMethodsMyInnerClassSize = arraysize(kMethodsMyInnerClass);
 
   if (env->RegisterNatives(MyInnerClass_clazz(env),
                            kMethodsMyInnerClass,
diff --git a/base/android/jni_generator/testMainDexFile.golden b/base/android/jni_generator/testMainDexFile.golden
new file mode 100644
index 0000000..cbb2a7d
--- /dev/null
+++ b/base/android/jni_generator/testMainDexFile.golden
@@ -0,0 +1,67 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file is autogenerated by
+//     base/android/jni_generator/jni_generator.py
+// For
+//     org/chromium/foo/Bar
+
+#ifndef org_chromium_foo_Bar_JNI
+#define org_chromium_foo_Bar_JNI
+
+#include <jni.h>
+
+#include "base/android/jni_generator/jni_generator_helper.h"
+
+#include "base/android/jni_int_wrapper.h"
+
+// Step 1: forward declarations.
+namespace {
+const char kBarClassPath[] = "org/chromium/foo/Bar";
+// Leaking this jclass as we cannot use LazyInstance from some threads.
+base::subtle::AtomicWord g_Bar_clazz __attribute__((unused)) = 0;
+#define Bar_clazz(env) base::android::LazyGetClass(env, kBarClassPath, &g_Bar_clazz)
+
+}  // namespace
+
+// Step 2: method stubs.
+JNI_GENERATOR_EXPORT jint Java_org_chromium_foo_Bar_nativeStaticMethod(JNIEnv*
+    env, jobject jcaller,
+    jlong nativeTest,
+    jint arg1) {
+  Test* native = reinterpret_cast<Test*>(nativeTest);
+  CHECK_NATIVE_PTR(env, jcaller, native, "StaticMethod", 0);
+  return native->StaticMethod(env, base::android::JavaParamRef<jobject>(env,
+      jcaller), arg1);
+}
+
+// Step 3: RegisterNatives.
+
+static const JNINativeMethod kMethodsBar[] = {
+    { "nativeStaticMethod",
+"("
+"J"
+"I"
+")"
+"I", reinterpret_cast<void*>(Java_org_chromium_foo_Bar_nativeStaticMethod) },
+};
+
+static bool RegisterNativesImpl(JNIEnv* env) {
+  if (jni_generator::ShouldSkipJniRegistration(true))
+    return true;
+
+  const int kMethodsBarSize = arraysize(kMethodsBar);
+
+  if (env->RegisterNatives(Bar_clazz(env),
+                           kMethodsBar,
+                           kMethodsBarSize) < 0) {
+    jni_generator::HandleRegistrationError(
+        env, Bar_clazz(env), __FILE__);
+    return false;
+  }
+
+  return true;
+}
+
+#endif  // org_chromium_foo_Bar_JNI
diff --git a/base/android/jni_generator/testMultipleJNIAdditionalImport.golden b/base/android/jni_generator/testMultipleJNIAdditionalImport.golden
index 03aac48e..0eecb5a 100644
--- a/base/android/jni_generator/testMultipleJNIAdditionalImport.golden
+++ b/base/android/jni_generator/testMultipleJNIAdditionalImport.golden
@@ -75,16 +75,11 @@
 "V", reinterpret_cast<void*>(Java_org_chromium_foo_Foo_nativeDoSomething) },
 };
 
-// TODO(agrieve): Remove these empty registration functions and functions
-// calling them. https://crbug.com/683256.
 static bool RegisterNativesImpl(JNIEnv* env) {
-  return true;
-}
+  if (jni_generator::ShouldSkipJniRegistration(false))
+    return true;
 
-JNI_REGISTRATION_EXPORT bool RegisterNative_org_chromium_foo_Foo(JNIEnv* env) {
-
-  const int kMethodsFooSize =
-      arraysize(kMethodsFoo);
+  const int kMethodsFooSize = arraysize(kMethodsFoo);
 
   if (env->RegisterNatives(Foo_clazz(env),
                            kMethodsFoo,
diff --git a/base/android/jni_generator/testNatives.golden b/base/android/jni_generator/testNatives.golden
index 0bfbe04..3362c928 100644
--- a/base/android/jni_generator/testNatives.golden
+++ b/base/android/jni_generator/testNatives.golden
@@ -320,16 +320,11 @@
     },
 };
 
-// TODO(agrieve): Remove these empty registration functions and functions
-// calling them. https://crbug.com/683256.
 static bool RegisterNativesImpl(JNIEnv* env) {
-  return true;
-}
+  if (jni_generator::ShouldSkipJniRegistration(false))
+    return true;
 
-JNI_REGISTRATION_EXPORT bool RegisterNative_org_chromium_TestJni(JNIEnv* env) {
-
-  const int kMethodsTestJniSize =
-      arraysize(kMethodsTestJni);
+  const int kMethodsTestJniSize = arraysize(kMethodsTestJni);
 
   if (env->RegisterNatives(TestJni_clazz(env),
                            kMethodsTestJni,
diff --git a/base/android/jni_generator/testNativesLong.golden b/base/android/jni_generator/testNativesLong.golden
index 201a066..ec029ce3 100644
--- a/base/android/jni_generator/testNativesLong.golden
+++ b/base/android/jni_generator/testNativesLong.golden
@@ -46,16 +46,11 @@
 "V", reinterpret_cast<void*>(Java_org_chromium_TestJni_nativeDestroy) },
 };
 
-// TODO(agrieve): Remove these empty registration functions and functions
-// calling them. https://crbug.com/683256.
 static bool RegisterNativesImpl(JNIEnv* env) {
-  return true;
-}
+  if (jni_generator::ShouldSkipJniRegistration(false))
+    return true;
 
-JNI_REGISTRATION_EXPORT bool RegisterNative_org_chromium_TestJni(JNIEnv* env) {
-
-  const int kMethodsTestJniSize =
-      arraysize(kMethodsTestJni);
+  const int kMethodsTestJniSize = arraysize(kMethodsTestJni);
 
   if (env->RegisterNatives(TestJni_clazz(env),
                            kMethodsTestJni,
diff --git a/base/android/jni_generator/testNonMainDexFile.golden b/base/android/jni_generator/testNonMainDexFile.golden
new file mode 100644
index 0000000..533241e
--- /dev/null
+++ b/base/android/jni_generator/testNonMainDexFile.golden
@@ -0,0 +1,67 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file is autogenerated by
+//     base/android/jni_generator/jni_generator.py
+// For
+//     org/chromium/foo/Bar
+
+#ifndef org_chromium_foo_Bar_JNI
+#define org_chromium_foo_Bar_JNI
+
+#include <jni.h>
+
+#include "base/android/jni_generator/jni_generator_helper.h"
+
+#include "base/android/jni_int_wrapper.h"
+
+// Step 1: forward declarations.
+namespace {
+const char kBarClassPath[] = "org/chromium/foo/Bar";
+// Leaking this jclass as we cannot use LazyInstance from some threads.
+base::subtle::AtomicWord g_Bar_clazz __attribute__((unused)) = 0;
+#define Bar_clazz(env) base::android::LazyGetClass(env, kBarClassPath, &g_Bar_clazz)
+
+}  // namespace
+
+// Step 2: method stubs.
+JNI_GENERATOR_EXPORT jint Java_org_chromium_foo_Bar_nativeStaticMethod(JNIEnv*
+    env, jobject jcaller,
+    jlong nativeTest,
+    jint arg1) {
+  Test* native = reinterpret_cast<Test*>(nativeTest);
+  CHECK_NATIVE_PTR(env, jcaller, native, "StaticMethod", 0);
+  return native->StaticMethod(env, base::android::JavaParamRef<jobject>(env,
+      jcaller), arg1);
+}
+
+// Step 3: RegisterNatives.
+
+static const JNINativeMethod kMethodsBar[] = {
+    { "nativeStaticMethod",
+"("
+"J"
+"I"
+")"
+"I", reinterpret_cast<void*>(Java_org_chromium_foo_Bar_nativeStaticMethod) },
+};
+
+static bool RegisterNativesImpl(JNIEnv* env) {
+  if (jni_generator::ShouldSkipJniRegistration(false))
+    return true;
+
+  const int kMethodsBarSize = arraysize(kMethodsBar);
+
+  if (env->RegisterNatives(Bar_clazz(env),
+                           kMethodsBar,
+                           kMethodsBarSize) < 0) {
+    jni_generator::HandleRegistrationError(
+        env, Bar_clazz(env), __FILE__);
+    return false;
+  }
+
+  return true;
+}
+
+#endif  // org_chromium_foo_Bar_JNI
diff --git a/base/android/jni_generator/testSingleJNIAdditionalImport.golden b/base/android/jni_generator/testSingleJNIAdditionalImport.golden
index a367ae7..ef618da8 100644
--- a/base/android/jni_generator/testSingleJNIAdditionalImport.golden
+++ b/base/android/jni_generator/testSingleJNIAdditionalImport.golden
@@ -69,16 +69,11 @@
 "V", reinterpret_cast<void*>(Java_org_chromium_foo_Foo_nativeDoSomething) },
 };
 
-// TODO(agrieve): Remove these empty registration functions and functions
-// calling them. https://crbug.com/683256.
 static bool RegisterNativesImpl(JNIEnv* env) {
-  return true;
-}
+  if (jni_generator::ShouldSkipJniRegistration(false))
+    return true;
 
-JNI_REGISTRATION_EXPORT bool RegisterNative_org_chromium_foo_Foo(JNIEnv* env) {
-
-  const int kMethodsFooSize =
-      arraysize(kMethodsFoo);
+  const int kMethodsFooSize = arraysize(kMethodsFoo);
 
   if (env->RegisterNatives(Foo_clazz(env),
                            kMethodsFoo,
diff --git a/base/metrics/histogram.cc b/base/metrics/histogram.cc
index b7ef107..1bfcffc 100644
--- a/base/metrics/histogram.cc
+++ b/base/metrics/histogram.cc
@@ -614,12 +614,14 @@
 
 std::unique_ptr<SampleVector> Histogram::SnapshotUnloggedSamples() const {
   // TODO(bcwhite): Remove these CHECKs once crbug/734049 is resolved.
+  HistogramSamples* unlogged = unlogged_samples_.get();
   CHECK(unlogged_samples_);
   CHECK(unlogged_samples_->id());
   CHECK(bucket_ranges());
   std::unique_ptr<SampleVector> samples(
       new SampleVector(unlogged_samples_->id(), bucket_ranges()));
   samples->Add(*unlogged_samples_);
+  debug::Alias(&unlogged);
   return samples;
 }
 
diff --git a/base/test/BUILD.gn b/base/test/BUILD.gn
index 11bc3bb..6c496401 100644
--- a/base/test/BUILD.gn
+++ b/base/test/BUILD.gn
@@ -359,7 +359,6 @@
     ]
     srcjar_deps = [ ":test_support_java_aidl" ]
     java_files = [
-      "android/java/src/org/chromium/base/ChildProcessConstants.java",
       "android/java/src/org/chromium/base/MainReturnCodeResult.java",
       "android/java/src/org/chromium/base/MultiprocessTestClientLauncher.java",
       "android/java/src/org/chromium/base/MultiprocessTestClientService.java",
@@ -368,6 +367,7 @@
       "android/java/src/org/chromium/base/MultiprocessTestClientService2.java",
       "android/java/src/org/chromium/base/MultiprocessTestClientService3.java",
       "android/java/src/org/chromium/base/MultiprocessTestClientService4.java",
+      "android/java/src/org/chromium/base/MultiprocessTestClientServiceDelegate.java",
     ]
   }
 
diff --git a/base/test/android/java/src/org/chromium/base/ChildProcessConstants.java b/base/test/android/java/src/org/chromium/base/ChildProcessConstants.java
deleted file mode 100644
index 457c59cb..0000000
--- a/base/test/android/java/src/org/chromium/base/ChildProcessConstants.java
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.base;
-
-/**
- * Constants to be used by child processes.
- */
-public interface ChildProcessConstants {
-    // Key for the command line.
-    public static final String EXTRA_COMMAND_LINE = "org.chromium.base.extra.command_line";
-
-    // Key for the file descriptors that should be mapped in the child process..
-    public static final String EXTRA_FILES = "org.chromium.base.extra.extra_files";
-}
diff --git a/base/test/android/java/src/org/chromium/base/ITestCallback.aidl b/base/test/android/java/src/org/chromium/base/ITestCallback.aidl
index 48d9994..dd208d55 100644
--- a/base/test/android/java/src/org/chromium/base/ITestCallback.aidl
+++ b/base/test/android/java/src/org/chromium/base/ITestCallback.aidl
@@ -5,12 +5,19 @@
 package org.chromium.base;
 
 import org.chromium.base.ITestController;
-import org.chromium.base.MainReturnCodeResult;
 import org.chromium.base.process_launcher.FileDescriptorInfo;
 
 /**
  * This interface is called by the child process to pass its controller to its parent.
  */
-oneway interface ITestCallback {
-  void childConnected(ITestController controller);
+interface ITestCallback {
+  oneway void childConnected(ITestController controller);
+
+  /**
+    * Invoked by the service to notify that the main method returned.
+    * IMPORTANT! Should not be marked oneway as the caller will terminate the running process after
+    * this call. Marking it oneway would make the call asynchronous and the process could terminate
+    * before the call was actually sent.
+    */
+  void mainReturned(int returnCode);
 }
diff --git a/base/test/android/java/src/org/chromium/base/ITestController.aidl b/base/test/android/java/src/org/chromium/base/ITestController.aidl
index 4bf7429..d927ee5 100644
--- a/base/test/android/java/src/org/chromium/base/ITestController.aidl
+++ b/base/test/android/java/src/org/chromium/base/ITestController.aidl
@@ -4,23 +4,12 @@
 
 package org.chromium.base;
 
-import org.chromium.base.MainReturnCodeResult;
 import org.chromium.base.process_launcher.FileDescriptorInfo;
 
 /**
  * This interface is used to control child processes.
  */
 interface ITestController {
-   /**
-   * Blocks until the <code>main</code> method started with {@link #launch()} returns, or returns
-   * immediately if main has already returned.
-   * @param timeoutMs time in milliseconds after which this method returns even if the main method
-   * has not returned yet.
-   * @return a result containing whether a timeout occured and the value returned by the
-   * <code>main</code> method
-   */
-  MainReturnCodeResult waitForMainToReturn(int timeoutMs);
-
   /**
    * Forces the service process to terminate and block until the process stops.
    * @param exitCode the exit code the process should terminate with.
@@ -32,5 +21,5 @@
    * Forces the service process to terminate.
    * @param exitCode the exit code the process should terminate with.
    */
-  void forceStop(int exitCode);
+  oneway void forceStop(int exitCode);
 }
diff --git a/base/test/android/java/src/org/chromium/base/MainReturnCodeResult.aidl b/base/test/android/java/src/org/chromium/base/MainReturnCodeResult.aidl
deleted file mode 100644
index dc7501f..0000000
--- a/base/test/android/java/src/org/chromium/base/MainReturnCodeResult.aidl
+++ /dev/null
@@ -1,7 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.base;
-
-parcelable MainReturnCodeResult;
diff --git a/base/test/android/java/src/org/chromium/base/MainReturnCodeResult.java b/base/test/android/java/src/org/chromium/base/MainReturnCodeResult.java
index 05a15b58..9756c976 100644
--- a/base/test/android/java/src/org/chromium/base/MainReturnCodeResult.java
+++ b/base/test/android/java/src/org/chromium/base/MainReturnCodeResult.java
@@ -4,9 +4,6 @@
 
 package org.chromium.base;
 
-import android.os.Parcel;
-import android.os.Parcelable;
-
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
 
@@ -14,18 +11,21 @@
  * Contains the result of a native main method that ran in a child process.
  */
 @JNINamespace("base::android")
-public final class MainReturnCodeResult implements Parcelable {
+public final class MainReturnCodeResult {
     private final int mMainReturnCode;
     private final boolean mTimedOut;
 
-    public MainReturnCodeResult(int mainReturnCode, boolean timedOut) {
-        mMainReturnCode = mainReturnCode;
-        mTimedOut = timedOut;
+    public static MainReturnCodeResult createMainResult(int returnCode) {
+        return new MainReturnCodeResult(returnCode, false /* timedOut */);
     }
 
-    MainReturnCodeResult(Parcel in) {
-        mMainReturnCode = in.readInt();
-        mTimedOut = (in.readInt() != 0);
+    public static MainReturnCodeResult createTimeoutMainResult() {
+        return new MainReturnCodeResult(0, true /* timedOut */);
+    }
+
+    private MainReturnCodeResult(int mainReturnCode, boolean timedOut) {
+        mMainReturnCode = mainReturnCode;
+        mTimedOut = timedOut;
     }
 
     @CalledByNative
@@ -37,28 +37,4 @@
     public boolean hasTimedOut() {
         return mTimedOut;
     }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mMainReturnCode);
-        dest.writeInt(mTimedOut ? 1 : 0);
-    }
-
-    public static final Parcelable.Creator<MainReturnCodeResult> CREATOR =
-            new Parcelable.Creator<MainReturnCodeResult>() {
-                @Override
-                public MainReturnCodeResult createFromParcel(Parcel in) {
-                    return new MainReturnCodeResult(in);
-                }
-
-                @Override
-                public MainReturnCodeResult[] newArray(int size) {
-                    return new MainReturnCodeResult[size];
-                }
-            };
 }
diff --git a/base/test/android/java/src/org/chromium/base/MultiprocessTestClientLauncher.java b/base/test/android/java/src/org/chromium/base/MultiprocessTestClientLauncher.java
index 18fca7f3..7c19179 100644
--- a/base/test/android/java/src/org/chromium/base/MultiprocessTestClientLauncher.java
+++ b/base/test/android/java/src/org/chromium/base/MultiprocessTestClientLauncher.java
@@ -12,9 +12,11 @@
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
+import android.util.SparseArray;
 
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.process_launcher.ChildProcessConstants;
 import org.chromium.base.process_launcher.FileDescriptorInfo;
 import org.chromium.base.process_launcher.ICallbackInt;
 import org.chromium.base.process_launcher.IChildProcessService;
@@ -25,6 +27,9 @@
 import java.util.List;
 import java.util.Queue;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
 
 import javax.annotation.concurrent.GuardedBy;
 
@@ -35,7 +40,9 @@
 public final class MultiprocessTestClientLauncher {
     private static final String TAG = "cr_MProcTCLauncher";
 
-    private static ConnectionAllocator sConnectionAllocator = new ConnectionAllocator();
+    private static final ConnectionAllocator sConnectionAllocator = new ConnectionAllocator();
+
+    private static final SparseArray<Integer> sPidToMainResult = new SparseArray<>();
 
     // Not supposed to be instantiated.
     private MultiprocessTestClientLauncher() {}
@@ -108,11 +115,22 @@
         private final CountDownLatch mPidReceived = new CountDownLatch(1);
         private final int mSlot;
         private IChildProcessService mService = null;
+
         @GuardedBy("mConnectedLock")
         private boolean mConnected;
+
         private int mPid;
         private ITestController mTestController;
+
+        private final ReentrantLock mMainReturnCodeLock = new ReentrantLock();
+        private final Condition mMainReturnCodeCondition = mMainReturnCodeLock.newCondition();
+        // The return code returned by the service's main method.
+        // null if the service has not sent it yet.
+        @GuardedBy("mMainReturnCodeLock")
+        private Integer mMainReturnCode;
+
         private final ITestCallback.Stub mCallback = new ITestCallback.Stub() {
+            @Override
             public void childConnected(ITestController controller) {
                 mTestController = controller;
                 // This method can be called before onServiceConnected below has set the PID.
@@ -129,6 +147,22 @@
                     mConnectedLock.notifyAll();
                 }
             }
+
+            @Override
+            public void mainReturned(int returnCode) {
+                mMainReturnCodeLock.lock();
+                try {
+                    mMainReturnCode = returnCode;
+                    mMainReturnCodeCondition.signal();
+                } finally {
+                    mMainReturnCodeLock.unlock();
+                }
+
+                // Also store the return code in a map as the connection might get disconnected
+                // before waitForMainToReturn is called and then we would not have a way to retrieve
+                // the connection.
+                sPidToMainResult.put(mPid, returnCode);
+            }
         };
 
         ClientServiceConnection(int slot, String[] commandLine, FileDescriptorInfo[] filesToMap) {
@@ -150,6 +184,26 @@
             }
         }
 
+        public Integer getMainReturnCode(long timeoutMs) {
+            long timeoutNs = TimeUnit.MILLISECONDS.toNanos(timeoutMs);
+            mMainReturnCodeLock.lock();
+            try {
+                while (mMainReturnCode == null) {
+                    if (timeoutNs <= 0L) {
+                        return null;
+                    }
+                    try {
+                        timeoutNs = mMainReturnCodeCondition.awaitNanos(timeoutNs);
+                    } catch (InterruptedException ie) {
+                        Log.e(TAG, "Interrupted while waiting for main return code.");
+                    }
+                }
+                return mMainReturnCode;
+            } finally {
+                mMainReturnCodeLock.unlock();
+            }
+        }
+
         @Override
         public void onServiceConnected(ComponentName className, IBinder service) {
             try {
@@ -250,19 +304,23 @@
     @CalledByNative
     private static MainReturnCodeResult waitForMainToReturn(int pid, int timeoutMs) {
         ClientServiceConnection connection = sConnectionAllocator.getConnectionByPid(pid);
-        if (connection == null) {
+        if (connection != null) {
+            try {
+                Integer mainResult = connection.getMainReturnCode(timeoutMs);
+                return mainResult == null ? MainReturnCodeResult.createTimeoutMainResult()
+                                          : MainReturnCodeResult.createMainResult(mainResult);
+            } finally {
+                freeConnection(connection);
+            }
+        }
+
+        Integer mainResult = sPidToMainResult.get(pid);
+        if (mainResult == null) {
             Log.e(TAG, "waitForMainToReturn called on unknown connection for pid " + pid);
             return null;
         }
-        try {
-            ITestController testController = connection.getTestController();
-            return testController.waitForMainToReturn(timeoutMs);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Remote call to waitForMainToReturn failed.");
-            return null;
-        } finally {
-            freeConnection(connection);
-        }
+        sPidToMainResult.remove(pid);
+        return MainReturnCodeResult.createMainResult(mainResult);
     }
 
     @CalledByNative
diff --git a/base/test/android/java/src/org/chromium/base/MultiprocessTestClientService.java b/base/test/android/java/src/org/chromium/base/MultiprocessTestClientService.java
index 4ac4dd5..9b50001 100644
--- a/base/test/android/java/src/org/chromium/base/MultiprocessTestClientService.java
+++ b/base/test/android/java/src/org/chromium/base/MultiprocessTestClientService.java
@@ -4,189 +4,11 @@
 
 package org.chromium.base;
 
-import android.app.Service;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Parcelable;
-import android.os.Process;
-import android.os.RemoteException;
+import org.chromium.base.process_launcher.ChildProcessService;
 
-import org.chromium.base.annotations.SuppressFBWarnings;
-import org.chromium.base.library_loader.LibraryLoader;
-import org.chromium.base.library_loader.LibraryProcessType;
-import org.chromium.base.library_loader.ProcessInitException;
-import org.chromium.base.process_launcher.FileDescriptorInfo;
-import org.chromium.base.process_launcher.ICallbackInt;
-import org.chromium.base.process_launcher.IChildProcessService;
-import org.chromium.native_test.MainRunner;
-
-import javax.annotation.concurrent.GuardedBy;
-
-/**
- * The service implementation used to host all multiprocess test client code.
- */
-public class MultiprocessTestClientService extends Service {
-    private static final String TAG = "cr_TestClient";
-
-    private static boolean sAlreadyInitialized = false;
-
-    private final Handler mHandler = new Handler();
-
-    private final Object mResultLock = new Object();
-
-    @GuardedBy("mResultLock")
-    private MainReturnCodeResult mResult;
-
-    private final ITestController.Stub mTestController = new ITestController.Stub() {
-        @SuppressFBWarnings("RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE")
-        @Override
-        public MainReturnCodeResult waitForMainToReturn(int timeoutMs) {
-            synchronized (mResultLock) {
-                while (mResult == null) {
-                    try {
-                        mResultLock.wait(timeoutMs);
-                    } catch (InterruptedException ie) {
-                        continue;
-                    }
-                    // Check if we timed-out.
-                    if (mResult == null) {
-                        Log.e(TAG, "Failed to wait for main return value.");
-                        return new MainReturnCodeResult(0, true /* timed-out */);
-                    }
-                }
-                return mResult;
-            }
-        }
-
-        @SuppressFBWarnings("DM_EXIT")
-        @Override
-        public boolean forceStopSynchronous(int exitCode) {
-            System.exit(exitCode);
-            return true;
-        }
-
-        @SuppressFBWarnings("DM_EXIT")
-        @Override
-        public void forceStop(int exitCode) {
-            System.exit(exitCode);
-        }
-    };
-
-    private final IChildProcessService.Stub mBinder = new IChildProcessService.Stub() {
-        @Override
-        public boolean bindToCaller() {
-            return true;
-        }
-
-        @Override
-        public void setupConnection(Bundle args, ICallbackInt pidCallback, final IBinder callback) {
-            // Required to unparcel FileDescriptorInfo.
-            args.setClassLoader(getApplicationContext().getClassLoader());
-
-            final String[] commandLine =
-                    args.getStringArray(ChildProcessConstants.EXTRA_COMMAND_LINE);
-            final Parcelable[] fdInfosAsParcelable =
-                    args.getParcelableArray(ChildProcessConstants.EXTRA_FILES);
-
-            FileDescriptorInfo[] fdsToMap = new FileDescriptorInfo[fdInfosAsParcelable.length];
-            System.arraycopy(fdInfosAsParcelable, 0, fdsToMap, 0, fdInfosAsParcelable.length);
-
-            final int[] fdKeys = new int[fdsToMap.length];
-            final int[] fdFds = new int[fdsToMap.length];
-            for (int i = 0; i < fdsToMap.length; i++) {
-                fdKeys[i] = fdsToMap[i].id;
-                // Take ownership of the file descriptor so they outlive the FileDescriptorInfo
-                // instances. Native code will own them.
-                fdFds[i] = fdsToMap[i].fd.detachFd();
-            }
-
-            // Prevent potential deadlocks by letting this method return before calling back to the
-            // launcher: the childConnected implementation on the launcher side might block until
-            // this method returns.
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    try {
-                        ITestCallback testCallback = ITestCallback.Stub.asInterface(callback);
-                        testCallback.childConnected(mTestController);
-                    } catch (RemoteException re) {
-                        Log.e(TAG, "Failed to notify parent process of connection.", re);
-                    }
-                }
-            });
-
-            // Don't run main directly, it would block and the response would not be returned.
-            // We post to the main thread as this thread does not have a Looper.
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    int result = MainRunner.runMain(commandLine, fdKeys, fdFds);
-                    setMainReturnValue(result);
-                }
-            });
-
-            try {
-                pidCallback.call(Process.myPid());
-            } catch (RemoteException re) {
-                Log.e(TAG, "Service failed to report PID to launcher.", re);
-            }
-        }
-
-        @Override
-        public void crashIntentionallyForTesting() {
-            assert false : "crashIntentionallyForTesting not implemented.";
-        }
-    };
-
-    @SuppressFBWarnings("DM_EXIT")
-    @Override
-    public void onCreate() {
-        super.onCreate();
-
-        if (sAlreadyInitialized) {
-            // The framework controls how services are reused and even though nothing is bound to a
-            // service it might be kept around. Since we really want to fork a new process when we
-            // bind, we'll kill the process early when a service is reused, forcing the framework to
-            // recreate the service in a new process.
-            // This is not ideal, but there are no clear alternatives at this point.
-            Log.e(TAG, "Service being reused, forcing stoppage.");
-            System.exit(0);
-            return;
-        }
-        markInitialized();
-
-        ContextUtils.initApplicationContext(getApplicationContext());
-
-        PathUtils.setPrivateDataDirectorySuffix("chrome_multiprocess_test_client_service");
-
-        loadLibraries();
-    }
-
-    @Override
-    public IBinder onBind(Intent intent) {
-        return mBinder;
-    }
-
-    private void loadLibraries() {
-        try {
-            LibraryLoader.get(LibraryProcessType.PROCESS_CHILD).loadNow();
-        } catch (ProcessInitException pie) {
-            Log.e(TAG, "Unable to load native libraries.", pie);
-        }
-    }
-
-    private void setMainReturnValue(int result) {
-        synchronized (mResultLock) {
-            mResult = new MainReturnCodeResult(result, false /* timed-out */);
-            mResultLock.notifyAll();
-        }
-    }
-
-    private static void markInitialized() {
-        // We don't set sAlreadyInitialized directly in onCreate to avoid FindBugs complaining about
-        // a static member been set from a non-static function.
-        sAlreadyInitialized = true;
+/** The service implementation used to host all multiprocess test client code. */
+public class MultiprocessTestClientService extends ChildProcessService {
+    public MultiprocessTestClientService() {
+        super(new MultiprocessTestClientServiceDelegate());
     }
 }
diff --git a/base/test/android/java/src/org/chromium/base/MultiprocessTestClientServiceDelegate.java b/base/test/android/java/src/org/chromium/base/MultiprocessTestClientServiceDelegate.java
new file mode 100644
index 0000000..1ef4cfe
--- /dev/null
+++ b/base/test/android/java/src/org/chromium/base/MultiprocessTestClientServiceDelegate.java
@@ -0,0 +1,91 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+package org.chromium.base;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.SparseArray;
+
+import org.chromium.base.annotations.SuppressFBWarnings;
+import org.chromium.base.library_loader.LibraryLoader;
+import org.chromium.base.library_loader.LibraryProcessType;
+import org.chromium.base.library_loader.ProcessInitException;
+import org.chromium.base.process_launcher.ChildProcessServiceDelegate;
+import org.chromium.native_test.MainRunner;
+
+/** Implmentation of the ChildProcessServiceDelegate used for the Multiprocess tests. */
+public class MultiprocessTestClientServiceDelegate implements ChildProcessServiceDelegate {
+    private static final String TAG = "MPTestCSDelegate";
+
+    private ITestCallback mTestCallback;
+
+    private final ITestController.Stub mTestController = new ITestController.Stub() {
+        @SuppressFBWarnings("DM_EXIT")
+        @Override
+        public boolean forceStopSynchronous(int exitCode) {
+            System.exit(exitCode);
+            return true;
+        }
+
+        @SuppressFBWarnings("DM_EXIT")
+        @Override
+        public void forceStop(int exitCode) {
+            System.exit(exitCode);
+        }
+    };
+
+    @Override
+    public void onServiceCreated() {
+        PathUtils.setPrivateDataDirectorySuffix("chrome_multiprocess_test_client_service");
+    }
+
+    @Override
+    public void onServiceBound(Intent intent) {}
+
+    @Override
+    public void onConnectionSetup(Bundle connectionBundle, IBinder callback) {
+        mTestCallback = ITestCallback.Stub.asInterface(callback);
+    }
+
+    @Override
+    public void onDestroy() {}
+
+    @Override
+    public boolean loadNativeLibrary(Context hostContext) {
+        try {
+            LibraryLoader.get(LibraryProcessType.PROCESS_CHILD).loadNow();
+            return true;
+        } catch (ProcessInitException pie) {
+            Log.e(TAG, "Unable to load native libraries.", pie);
+            return false;
+        }
+    }
+
+    @Override
+    public SparseArray<String> getFileDescriptorsIdsToKeys() {
+        return null;
+    }
+
+    @Override
+    public void onBeforeMain() {
+        try {
+            mTestCallback.childConnected(mTestController);
+        } catch (RemoteException re) {
+            Log.e(TAG, "Failed to notify parent process of connection.");
+        }
+    }
+
+    @Override
+    public void runMain() {
+        int result = MainRunner.runMain(CommandLine.getJavaSwitchesOrNull());
+        try {
+            mTestCallback.mainReturned(result);
+        } catch (RemoteException re) {
+            Log.e(TAG, "Failed to notify parent process of main returning.");
+        }
+    }
+}
diff --git a/build/android/gradle/generate_gradle.py b/build/android/gradle/generate_gradle.py
index ba2311a..e7bf827 100755
--- a/build/android/gradle/generate_gradle.py
+++ b/build/android/gradle/generate_gradle.py
@@ -211,7 +211,7 @@
 
   def JavaFiles(self):
     if self._java_files is None:
-      java_sources_file = self.DepsInfo().get('java_sources_file')
+      java_sources_file = self.Gradle().get('java_sources_file')
       java_files = []
       if java_sources_file:
         java_sources_file = _RebasePath(java_sources_file)
diff --git a/build/android/gyp/write_build_config.py b/build/android/gyp/write_build_config.py
index 6eab4b2..c085176 100755
--- a/build/android/gyp/write_build_config.py
+++ b/build/android/gyp/write_build_config.py
@@ -413,7 +413,7 @@
     gradle['android_manifest'] = options.android_manifest
   if options.type in ('java_binary', 'java_library', 'android_apk'):
     if options.java_sources_file:
-      deps_info['java_sources_file'] = options.java_sources_file
+      gradle['java_sources_file'] = options.java_sources_file
     if options.bundled_srcjars:
       gradle['bundled_srcjars'] = (
           build_utils.ParseGnList(options.bundled_srcjars))
@@ -436,14 +436,6 @@
         gradle['dependent_java_projects'].append(c['path'])
 
 
-  if options.type == 'android_apk':
-    config['jni'] = {}
-    all_java_sources = [c['java_sources_file'] for c in all_library_deps
-                        if 'java_sources_file' in c]
-    if options.java_sources_file:
-      all_java_sources.append(options.java_sources_file)
-    config['jni']['all_source'] = all_java_sources
-
   if (options.type in ('java_binary', 'java_library')):
     deps_info['requires_android'] = options.requires_android
     deps_info['supports_android'] = options.supports_android
diff --git a/build/android/pylib/local/device/local_device_environment.py b/build/android/pylib/local/device/local_device_environment.py
index d5536c7..cc376d18 100644
--- a/build/android/pylib/local/device/local_device_environment.py
+++ b/build/android/pylib/local/device/local_device_environment.py
@@ -82,7 +82,7 @@
     self._blacklist = (device_blacklist.Blacklist(args.blacklist_file)
                        if args.blacklist_file
                        else None)
-    self._device_serial = args.test_device
+    self._device_serials = args.test_devices
     self._devices_lock = threading.Lock()
     self._devices = None
     self._concurrent_adb = args.enable_concurrent_adb
@@ -124,8 +124,8 @@
       else:
         logging.info(
             'Read device list %s from target devices file.', str(device_arg))
-    elif self._device_serial:
-      device_arg = self._device_serial
+    elif self._device_serials:
+      device_arg = self._device_serials
 
     self._devices = device_utils.DeviceUtils.HealthyDevices(
         self._blacklist, enable_device_files_cache=self._enable_device_cache,
diff --git a/build/android/test_runner.py b/build/android/test_runner.py
index 8574607..835888b0 100755
--- a/build/android/test_runner.py
+++ b/build/android/test_runner.py
@@ -216,9 +216,9 @@
       type=os.path.realpath,
       help='Device blacklist file.')
   parser.add_argument(
-      '-d', '--device',
-      dest='test_device',
-      help='Target device for the test suite to run on.')
+      '-d', '--device', nargs='+',
+      dest='test_devices',
+      help='Target device(s) for the test suite to run on.')
   parser.add_argument(
       '--enable-concurrent-adb',
       action='store_true',
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index ceffd063..38032b768 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -192,6 +192,7 @@
         "--depfile",
         rebase_path(depfile, root_build_dir),
         "--input_file={{source}}",
+        "--optimize_generation=1",
         "--ptr_type=long",
         "--output_dir",
         rebase_path(jni_output_dir, root_build_dir),
@@ -301,6 +302,7 @@
           rebase_path(jar_file, root_build_dir),
           "--input_file",
           class,
+          "--optimize_generation=1",
           "--ptr_type=long",
           "--output_dir",
           rebase_path(jni_output_dir, root_build_dir),
@@ -332,61 +334,6 @@
     }
   }
 
-  # Declare a jni registration target.
-  #
-  # This target generates a header file calling JNI registration functions
-  # created by generate_jni and generate_jar_jni.
-  #
-  # See base/android/jni_generator/jni_registration_generator.py for more info
-  # about the format of the header file.
-  #
-  # Variables
-  #   target: The Apk target to generate registrations for.
-  #   output: Path to the generated .h file.
-  #   exception_files: List of .java files that should be ignored when searching
-  #   for native methods. (optional)
-  #
-  # Example
-  #   generate_jni_registration("chrome_jni_registration") {
-  #     target = ":chrome_public_apk"
-  #     output = "$root_gen_dir/chrome/browser/android/${target_name}.h"
-  #     exception_files = [
-  #       "//base/android/java/src/org/chromium/base/library_loader/LegacyLinker.java",
-  #       "//base/android/java/src/org/chromium/base/library_loader/Linker.java",
-  #       "//base/android/java/src/org/chromium/base/library_loader/ModernLinker.java",
-  #     ]
-  #   }
-  template("generate_jni_registration") {
-    action(target_name) {
-      forward_variables_from(invoker, [ "testonly" ])
-      _build_config = get_label_info(invoker.target, "target_gen_dir") + "/" +
-                      get_label_info(invoker.target, "name") + ".build_config"
-      _rebased_build_config = rebase_path(_build_config, root_build_dir)
-
-      _rebase_exception_java_files =
-          rebase_path(invoker.exception_files, root_build_dir)
-
-      script = "//base/android/jni_generator/jni_registration_generator.py"
-      deps = [
-        "${invoker.target}__build_config",
-      ]
-      inputs = [
-        _build_config,
-      ]
-      outputs = [
-        invoker.output,
-      ]
-
-      args = [
-        # This is a list of .sources files.
-        "--sources_files=@FileArg($_rebased_build_config:jni:all_source)",
-        "--output",
-        rebase_path(invoker.output, root_build_dir),
-        "--no_register_java=$_rebase_exception_java_files",
-      ]
-    }
-  }
-
   # Declare a target for c-preprocessor-generated java files
   #
   # NOTE: For generating Java conterparts to enums prefer using the java_cpp_enum
diff --git a/build/landmines.py b/build/landmines.py
index 1b2f1cdb..991ec045 100755
--- a/build/landmines.py
+++ b/build/landmines.py
@@ -11,6 +11,16 @@
 A landmine is tripped when a builder checks out a different revision, and the
 diff between the new landmines and the old ones is non-null. At this point, the
 build is clobbered.
+
+Before adding or changing a landmine consider the consequences of doing so.
+Doing so will wipe out every output directory on every Chrome developer's
+machine. This can be particularly problematic on Windows where the directory
+deletion may well fail (locked files, command prompt in the directory, etc.),
+and generated .sln and .vcxproj files will be deleted.
+
+This output directory deletion will be repated when going back and forth across
+the change that added the landmine, adding to the cost. There are usually less
+troublesome alternatives.
 """
 
 import difflib
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index eb5167d..1bb7d1ce 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -681,7 +681,6 @@
     "../browser/android/chrome_entry_point.cc",
   ]
   deps = [
-    ":chrome_jni_registration($default_toolchain)",
     "//build/config:exe_and_shlib_deps",
     "//chrome:chrome_android_core",
   ]
@@ -706,30 +705,8 @@
 }
 
 # Ensure that .pak files are built only once (build them in the default
-# toolchain). The central header file calling JNI registration functions
-# is generated from Java code so it just needs to be generated once.
+# toolchain).
 if (current_toolchain == default_toolchain) {
-  generate_jni_registration("chrome_jni_registration") {
-    target = ":chrome_public_apk"
-    output = "$root_gen_dir/chrome/browser/android/${target_name}.h"
-    exception_files = [
-      "//base/android/java/src/org/chromium/base/library_loader/LegacyLinker.java",
-      "//base/android/java/src/org/chromium/base/library_loader/Linker.java",
-      "//base/android/java/src/org/chromium/base/library_loader/ModernLinker.java",
-    ]
-  }
-
-  generate_jni_registration("chrome_sync_shell_jni_registration") {
-    testonly = true
-    target = ":chrome_sync_shell_apk"
-    output = "$root_gen_dir/chrome/browser/android/${target_name}.h"
-    exception_files = [
-      "//base/android/java/src/org/chromium/base/library_loader/LegacyLinker.java",
-      "//base/android/java/src/org/chromium/base/library_loader/Linker.java",
-      "//base/android/java/src/org/chromium/base/library_loader/ModernLinker.java",
-    ]
-  }
-
   if (enable_resource_whitelist_generation) {
     generate_resource_whitelist("monochrome_resource_whitelist") {
       # Always use the 32-bit library's whitelist since the 64-bit one is
@@ -825,13 +802,12 @@
 shared_library("chrome_sync_shell") {
   testonly = true
   sources = [
-    "../browser/android/chrome_sync_shell_entry_point.cc",
+    "../browser/android/chrome_entry_point.cc",
     "../browser/android/chrome_sync_shell_main_delegate.cc",
     "../browser/android/chrome_sync_shell_main_delegate.h",
     "../browser/android/chrome_sync_shell_main_delegate_initializer.cc",
   ]
   deps = [
-    ":chrome_sync_shell_jni_registration($default_toolchain)",
     "//build/config:exe_and_shlib_deps",
     "//chrome:chrome_android_core",
     "//components/sync",
@@ -912,9 +888,6 @@
   apk_name = "ChromeSyncShell"
   shared_libraries = [ ":chrome_sync_shell" ]
 
-  # This exists here rather than in chrome_sync_shell_test_apk for JNI
-  # registration to be able to find the native side functions.
-  java_files = [ "sync_shell/javatests/src/org/chromium/chrome/browser/sync/FakeServerHelper.java" ]
   deps = [
     ":chrome_sync_shell_apk_template_resources",
 
@@ -922,7 +895,6 @@
     # but that code is stripped out via proguard. Adding this deps adds
     # usages and prevents removal of the proto code.
     "//components/sync:test_support_proto_java",
-    "//third_party/android_protobuf:protobuf_nano_javalib",
   ]
 }
 
@@ -1016,23 +988,11 @@
   }
 }
 
-instrumentation_test_apk("chrome_sync_shell_test_apk") {
-  apk_name = "ChromeSyncShellTest"
-  apk_under_test = ":chrome_sync_shell_apk"
-  android_manifest = chrome_sync_shell_test_apk_manifest
-  android_manifest_dep = ":chrome_sync_shell_test_apk_manifest"
-  java_files = [
-    "sync_shell/javatests/src/org/chromium/chrome/browser/sync/AutofillTest.java",
-    "sync_shell/javatests/src/org/chromium/chrome/browser/sync/BookmarksTest.java",
-    "sync_shell/javatests/src/org/chromium/chrome/browser/sync/FirstRunTest.java",
-    "sync_shell/javatests/src/org/chromium/chrome/browser/sync/GmsCoreSyncListenerTest.java",
-    "sync_shell/javatests/src/org/chromium/chrome/browser/sync/OpenTabsTest.java",
-    "sync_shell/javatests/src/org/chromium/chrome/browser/sync/SyncCustomizationFragmentTest.java",
-    "sync_shell/javatests/src/org/chromium/chrome/browser/sync/SyncTest.java",
-    "sync_shell/javatests/src/org/chromium/chrome/browser/sync/SyncTestBase.java",
-    "sync_shell/javatests/src/org/chromium/chrome/browser/sync/TypedUrlsTest.java",
-    "sync_shell/javatests/src/org/chromium/chrome/browser/sync/ui/PassphraseTypeDialogFragmentTest.java",
-  ]
+android_library("chrome_sync_shell_test_apk_java") {
+  testonly = true
+
+  # From java_sources.jni.
+  java_files = sync_shell_test_java_sources
 
   deps = [
     "//base:base_java",
@@ -1048,9 +1008,21 @@
     "//components/sync/android:sync_java",
     "//content/public/android:content_java",
     "//content/public/test/android:content_java_test_support",
+    "//third_party/android_protobuf:protobuf_nano_javalib",
     "//third_party/android_support_test_runner:runner_java",
     "//third_party/android_tools:android_support_v7_appcompat_java",
     "//ui/android:ui_java",
   ]
+}
+
+instrumentation_test_apk("chrome_sync_shell_test_apk") {
+  apk_name = "ChromeSyncShellTest"
+  apk_under_test = ":chrome_sync_shell_apk"
+  android_manifest = chrome_sync_shell_test_apk_manifest
+  android_manifest_dep = ":chrome_sync_shell_test_apk_manifest"
+  deps = [
+    ":chrome_sync_shell_test_apk_java",
+    "//third_party/android_support_test_runner:runner_java",
+  ]
   proguard_enabled = !is_java_debug
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java
index f10b515..2c400dd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java
@@ -287,7 +287,14 @@
         mRenderToSurfaceLayoutParent = new FrameLayout(mActivity) {
             @Override
             public boolean dispatchTouchEvent(MotionEvent event) {
-                getContainer().dispatchTouchEvent(event);
+                // We only want to target touch events to the actual screen to the GvrUiLayout,
+                // which is dynamically loaded and attached to the GvrLayoutImpl.
+                for (int i = 0; i < getContainer().getChildCount(); ++i) {
+                    View child = getContainer().getChildAt(i);
+                    if (child.getClass().getSimpleName().equals("GvrLayoutImpl")) {
+                        child.dispatchTouchEvent(event);
+                    }
+                }
                 return true;
             }
         };
@@ -334,6 +341,7 @@
             }
             @Override
             protected void onPostExecute(Bitmap bitmap) {
+                if (mNativeVrShell == 0) return;
                 nativeSetSplashScreenIcon(mNativeVrShell, bitmap);
             }
         }
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index 2c2e877..a72e0ac 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -1520,6 +1520,7 @@
   "javatests/src/org/chromium/chrome/browser/physicalweb/UrlManagerTest.java",
   "javatests/src/org/chromium/chrome/browser/policy/CombinedPolicyProviderTest.java",
   "javatests/src/org/chromium/chrome/browser/payments/CurrencyFormatterTest.java",
+  "javatests/src/org/chromium/chrome/browser/payments/PaymentManifestParserTest.java",
   "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestAbortTest.java",
   "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestBasicCardTest.java",
   "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestBillingAddressTest.java",
@@ -1809,6 +1810,20 @@
   "junit/src/org/chromium/chrome/browser/widget/selection/SelectionDelegateTest.java",
 ]
 
+sync_shell_test_java_sources = [
+  "sync_shell/javatests/src/org/chromium/chrome/browser/sync/AutofillTest.java",
+  "sync_shell/javatests/src/org/chromium/chrome/browser/sync/BookmarksTest.java",
+  "sync_shell/javatests/src/org/chromium/chrome/browser/sync/FakeServerHelper.java",
+  "sync_shell/javatests/src/org/chromium/chrome/browser/sync/FirstRunTest.java",
+  "sync_shell/javatests/src/org/chromium/chrome/browser/sync/GmsCoreSyncListenerTest.java",
+  "sync_shell/javatests/src/org/chromium/chrome/browser/sync/OpenTabsTest.java",
+  "sync_shell/javatests/src/org/chromium/chrome/browser/sync/SyncCustomizationFragmentTest.java",
+  "sync_shell/javatests/src/org/chromium/chrome/browser/sync/SyncTest.java",
+  "sync_shell/javatests/src/org/chromium/chrome/browser/sync/SyncTestBase.java",
+  "sync_shell/javatests/src/org/chromium/chrome/browser/sync/TypedUrlsTest.java",
+  "sync_shell/javatests/src/org/chromium/chrome/browser/sync/ui/PassphraseTypeDialogFragmentTest.java",
+]
+
 # Only used for testing, should not be shipped to end users.
 if (enable_offline_pages_harness) {
   chrome_java_sources += [ "java/src/org/chromium/chrome/browser/offlinepages/evaluation/OfflinePageEvaluationBridge.java" ]
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentManifestParserTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentManifestParserTest.java
new file mode 100644
index 0000000..a926184
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentManifestParserTest.java
@@ -0,0 +1,209 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.payments;
+
+import android.support.test.filters.MediumTest;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.Feature;
+import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.test.ChromeActivityTestRule;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.components.payments.PaymentManifestParser;
+import org.chromium.components.payments.PaymentManifestParser.ManifestParseCallback;
+import org.chromium.content.browser.test.util.Criteria;
+import org.chromium.content.browser.test.util.CriteriaHelper;
+import org.chromium.payments.mojom.WebAppManifestSection;
+
+import java.net.URI;
+
+/** An integration test for the payment manifest parser. */
+@RunWith(ChromeJUnit4ClassRunner.class)
+@CommandLineFlags.Add({
+        ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE,
+        ChromeActivityTestRule.DISABLE_NETWORK_PREDICTION_FLAG,
+})
+public class PaymentManifestParserTest implements ManifestParseCallback {
+    @Rule
+    public ChromeActivityTestRule<ChromeActivity> mRule =
+            new ChromeActivityTestRule<>(ChromeActivity.class);
+
+    private final PaymentManifestParser mParser = new PaymentManifestParser();
+    private URI[] mWebAppManifestUris;
+    private WebAppManifestSection[] mWebAppManifest;
+    private boolean mParseFailure;
+    private boolean mParsePaymentMethodManifestSuccess;
+    private boolean mParseWebAppManifestSuccess;
+
+    @Override
+    public void onPaymentMethodManifestParseSuccess(URI[] webAppManifestUris) {
+        mParsePaymentMethodManifestSuccess = true;
+        mWebAppManifestUris = webAppManifestUris.clone();
+    }
+
+    @Override
+    public void onWebAppManifestParseSuccess(WebAppManifestSection[] manifest) {
+        mParseWebAppManifestSuccess = true;
+        mWebAppManifest = manifest.clone();
+    }
+
+    @Override
+    public void onManifestParseFailure() {
+        mParseFailure = true;
+    }
+
+    @Before
+    public void setUp() throws Throwable {
+        mRule.startMainActivityOnBlankPage();
+        mRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mParser.startUtilityProcess();
+            }
+        });
+        mWebAppManifestUris = null;
+        mWebAppManifest = null;
+        mParseFailure = false;
+        mParsePaymentMethodManifestSuccess = false;
+        mParseWebAppManifestSuccess = false;
+    }
+
+    @After
+    public void tearDown() throws Throwable {
+        mRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mParser.stopUtilityProcess();
+            }
+        });
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"Payments"})
+    public void testParseInvalidPaymentMethodManifest() throws Throwable {
+        mRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mParser.parsePaymentMethodManifest(
+                        "invalid payment method manifest", PaymentManifestParserTest.this);
+            }
+        });
+        CriteriaHelper.pollInstrumentationThread(new Criteria() {
+            @Override
+            public boolean isSatisfied() {
+                return mParseFailure;
+            }
+        });
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"Payments"})
+    public void testParsePaymentMethodManifest() throws Throwable {
+        mRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mParser.parsePaymentMethodManifest("{"
+                                + "  \"default_applications\": ["
+                                + "    \"https://bobpay.com/app.json\","
+                                + "    \"https://alicepay.com/app.json\""
+                                + "  ],"
+                                + "  \"supported_origins\": ["
+                                + "    \"https://charliepay.com\","
+                                + "    \"https://evepay.com\""
+                                + "  ]"
+                                + "}",
+                        PaymentManifestParserTest.this);
+            }
+        });
+        CriteriaHelper.pollInstrumentationThread(new Criteria() {
+            @Override
+            public boolean isSatisfied() {
+                return mParsePaymentMethodManifestSuccess;
+            }
+        });
+        Assert.assertNotNull(mWebAppManifestUris);
+        Assert.assertEquals(2, mWebAppManifestUris.length);
+        Assert.assertEquals(new URI("https://bobpay.com/app.json"), mWebAppManifestUris[0]);
+        Assert.assertEquals(new URI("https://alicepay.com/app.json"), mWebAppManifestUris[1]);
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"Payments"})
+    public void testParseInvalidWebAppManifest() throws Throwable {
+        mRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mParser.parseWebAppManifest(
+                        "invalid web app manifest", PaymentManifestParserTest.this);
+            }
+        });
+        CriteriaHelper.pollInstrumentationThread(new Criteria() {
+            @Override
+            public boolean isSatisfied() {
+                return mParseFailure;
+            }
+        });
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"Payments"})
+    public void testParseWebAppManifest() throws Throwable {
+        mRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mParser.parseWebAppManifest("{"
+                                + "  \"related_applications\": [{"
+                                + "    \"platform\": \"play\", "
+                                + "    \"id\": \"com.bobpay.app\", "
+                                + "    \"min_version\": \"1\", "
+                                + "    \"fingerprints\": [{"
+                                + "      \"type\": \"sha256_cert\", "
+                                + "      \"value\": \""
+                                + "00:01:02:03:04:05:06:07:08:09:"
+                                + "A0:A1:A2:A3:A4:A5:A6:A7:A8:A9:"
+                                + "B0:B1:B2:B3:B4:B5:B6:B7:B8:B9:C0:C1\""
+                                + "    }]"
+                                + "  }]"
+                                + "}",
+                        PaymentManifestParserTest.this);
+            }
+        });
+        CriteriaHelper.pollInstrumentationThread(new Criteria() {
+            @Override
+            public boolean isSatisfied() {
+                return mParseWebAppManifestSuccess;
+            }
+        });
+        Assert.assertNotNull(mWebAppManifest);
+        Assert.assertEquals(1, mWebAppManifest.length);
+        Assert.assertNotNull(mWebAppManifest[0]);
+        Assert.assertEquals("com.bobpay.app", mWebAppManifest[0].id);
+        Assert.assertEquals(1, mWebAppManifest[0].minVersion);
+        Assert.assertNotNull(mWebAppManifest[0].fingerprints);
+        Assert.assertEquals(1, mWebAppManifest[0].fingerprints.length);
+        Assert.assertNotNull(mWebAppManifest[0].fingerprints[0]);
+        Assert.assertEquals(32, mWebAppManifest[0].fingerprints[0].length);
+        Assert.assertArrayEquals(
+                new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, (byte) 0xA0,
+                        (byte) 0xA1, (byte) 0xA2, (byte) 0xA3, (byte) 0xA4, (byte) 0xA5,
+                        (byte) 0xA6, (byte) 0xA7, (byte) 0xA8, (byte) 0xA9, (byte) 0xB0,
+                        (byte) 0xB1, (byte) 0xB2, (byte) 0xB3, (byte) 0xB4, (byte) 0xB5,
+                        (byte) 0xB6, (byte) 0xB7, (byte) 0xB8, (byte) 0xB9, (byte) 0xC0,
+                        (byte) 0xC1},
+                mWebAppManifest[0].fingerprints[0]);
+    }
+}
\ No newline at end of file
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 9ec24e72..13ea874 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -508,6 +508,45 @@
      cc::switches::kCompositedLayerBorders},
     {flag_descriptions::kUiShowCompositedLayerBordersAll,
      cc::switches::kUIShowCompositedLayerBorders, ""}};
+
+const FeatureEntry::Choice kSpuriousPowerButtonWindowChoices[] = {
+    {"0", "", ""},
+    {"5", ash::switches::kSpuriousPowerButtonWindow, "5"},
+    {"10", ash::switches::kSpuriousPowerButtonWindow, "10"},
+    {"15", ash::switches::kSpuriousPowerButtonWindow, "15"},
+    {"20", ash::switches::kSpuriousPowerButtonWindow, "20"},
+};
+const FeatureEntry::Choice kSpuriousPowerButtonAccelCountChoices[] = {
+    {"0", "", ""},
+    {"1", ash::switches::kSpuriousPowerButtonAccelCount, "1"},
+    {"2", ash::switches::kSpuriousPowerButtonAccelCount, "2"},
+    {"3", ash::switches::kSpuriousPowerButtonAccelCount, "3"},
+    {"4", ash::switches::kSpuriousPowerButtonAccelCount, "4"},
+    {"5", ash::switches::kSpuriousPowerButtonAccelCount, "5"},
+};
+const FeatureEntry::Choice kSpuriousPowerButtonScreenAccelChoices[] = {
+    {"0", "", ""},
+    {"0.2", ash::switches::kSpuriousPowerButtonScreenAccel, "0.2"},
+    {"0.4", ash::switches::kSpuriousPowerButtonScreenAccel, "0.4"},
+    {"0.6", ash::switches::kSpuriousPowerButtonScreenAccel, "0.6"},
+    {"0.8", ash::switches::kSpuriousPowerButtonScreenAccel, "0.8"},
+    {"1.0", ash::switches::kSpuriousPowerButtonScreenAccel, "1.0"},
+};
+const FeatureEntry::Choice kSpuriousPowerButtonKeyboardAccelChoices[] = {
+    {"0", "", ""},
+    {"0.2", ash::switches::kSpuriousPowerButtonKeyboardAccel, "0.2"},
+    {"0.4", ash::switches::kSpuriousPowerButtonKeyboardAccel, "0.4"},
+    {"0.6", ash::switches::kSpuriousPowerButtonKeyboardAccel, "0.6"},
+    {"0.8", ash::switches::kSpuriousPowerButtonKeyboardAccel, "0.8"},
+    {"1.0", ash::switches::kSpuriousPowerButtonKeyboardAccel, "1.0"},
+};
+const FeatureEntry::Choice kSpuriousPowerButtonLidAngleChangeChoices[] = {
+    {"0", "", ""},
+    {"45", ash::switches::kSpuriousPowerButtonLidAngleChange, "45"},
+    {"90", ash::switches::kSpuriousPowerButtonLidAngleChange, "90"},
+    {"135", ash::switches::kSpuriousPowerButtonLidAngleChange, "135"},
+    {"180", ash::switches::kSpuriousPowerButtonLidAngleChange, "180"},
+};
 #endif  // OS_CHROMEOS
 
 const FeatureEntry::Choice kV8CacheOptionsChoices[] = {
@@ -1341,6 +1380,26 @@
      kOsCrOS,
      SINGLE_VALUE_TYPE(
          proximity_auth::switches::kEnableBluetoothLowEnergyDiscovery)},
+    {"spurious-power-button-window",
+     flag_descriptions::kSpuriousPowerButtonWindowName,
+     flag_descriptions::kSpuriousPowerButtonWindowDescription, kOsCrOS,
+     MULTI_VALUE_TYPE(kSpuriousPowerButtonWindowChoices)},
+    {"spurious-power-button-accel-count",
+     flag_descriptions::kSpuriousPowerButtonAccelCountName,
+     flag_descriptions::kSpuriousPowerButtonAccelCountDescription, kOsCrOS,
+     MULTI_VALUE_TYPE(kSpuriousPowerButtonAccelCountChoices)},
+    {"spurious-power-button-screen-accel",
+     flag_descriptions::kSpuriousPowerButtonScreenAccelName,
+     flag_descriptions::kSpuriousPowerButtonScreenAccelDescription, kOsCrOS,
+     MULTI_VALUE_TYPE(kSpuriousPowerButtonScreenAccelChoices)},
+    {"spurious-power-button-keyboard-accel",
+     flag_descriptions::kSpuriousPowerButtonKeyboardAccelName,
+     flag_descriptions::kSpuriousPowerButtonKeyboardAccelDescription, kOsCrOS,
+     MULTI_VALUE_TYPE(kSpuriousPowerButtonKeyboardAccelChoices)},
+    {"spurious-power-button-lid-angle-change",
+     flag_descriptions::kSpuriousPowerButtonLidAngleChangeName,
+     flag_descriptions::kSpuriousPowerButtonLidAngleChangeDescription, kOsCrOS,
+     MULTI_VALUE_TYPE(kSpuriousPowerButtonLidAngleChangeChoices)},
 #endif  // OS_CHROMEOS
 #if defined(USE_ASH)
     {"ash-disable-night-light", flag_descriptions::kDisableNightLightName,
diff --git a/chrome/browser/android/DEPS b/chrome/browser/android/DEPS
index 8f032b7..bb55a8c 100644
--- a/chrome/browser/android/DEPS
+++ b/chrome/browser/android/DEPS
@@ -2,7 +2,6 @@
   "-components/devtools_bridge",
   "+cc/layers/layer.h",
   "+cc/output/context_provider.h",
-  "+chrome_jni_registration/chrome_jni_registration.h",
   "+components/doodle",
   "+components/ntp_snippets",
   "+components/spellcheck/browser",
diff --git a/chrome/browser/android/banners/app_banner_infobar_delegate_android.cc b/chrome/browser/android/banners/app_banner_infobar_delegate_android.cc
index b9229eb8..796faab0 100644
--- a/chrome/browser/android/banners/app_banner_infobar_delegate_android.cc
+++ b/chrome/browser/android/banners/app_banner_infobar_delegate_android.cc
@@ -41,7 +41,6 @@
 bool AppBannerInfoBarDelegateAndroid::Create(
     content::WebContents* web_contents,
     base::WeakPtr<AppBannerManager> weak_manager,
-    const base::string16& app_title,
     std::unique_ptr<ShortcutInfo> shortcut_info,
     const SkBitmap& primary_icon,
     const SkBitmap& badge_icon,
@@ -55,8 +54,8 @@
 
   auto infobar_delegate =
       base::WrapUnique(new banners::AppBannerInfoBarDelegateAndroid(
-          weak_manager, app_title, std::move(shortcut_info), primary_icon,
-          badge_icon, event_request_id, is_webapk, webapk_install_source));
+          weak_manager, std::move(shortcut_info), primary_icon, badge_icon,
+          event_request_id, is_webapk, webapk_install_source));
   auto* raw_delegate = infobar_delegate.get();
   auto infobar = base::MakeUnique<AppBannerInfoBarAndroid>(
       std::move(infobar_delegate), url, is_webapk);
@@ -203,7 +202,6 @@
 
 AppBannerInfoBarDelegateAndroid::AppBannerInfoBarDelegateAndroid(
     base::WeakPtr<AppBannerManager> weak_manager,
-    const base::string16& app_title,
     std::unique_ptr<ShortcutInfo> shortcut_info,
     const SkBitmap& primary_icon,
     const SkBitmap& badge_icon,
@@ -211,7 +209,7 @@
     bool is_webapk,
     webapk::InstallSource webapk_install_source)
     : weak_manager_(weak_manager),
-      app_title_(app_title),
+      app_title_(shortcut_info->name),
       shortcut_info_(std::move(shortcut_info)),
       primary_icon_(primary_icon),
       badge_icon_(badge_icon),
diff --git a/chrome/browser/android/banners/app_banner_infobar_delegate_android.h b/chrome/browser/android/banners/app_banner_infobar_delegate_android.h
index c3c7561..ecba8527 100644
--- a/chrome/browser/android/banners/app_banner_infobar_delegate_android.h
+++ b/chrome/browser/android/banners/app_banner_infobar_delegate_android.h
@@ -39,7 +39,6 @@
   // app, and adds the infobar to the InfoBarManager for |web_contents|.
   static bool Create(content::WebContents* web_contents,
                      base::WeakPtr<AppBannerManager> weak_manager,
-                     const base::string16& app_title,
                      std::unique_ptr<ShortcutInfo> info,
                      const SkBitmap& primary_icon,
                      const SkBitmap& badge_icon,
@@ -94,7 +93,6 @@
   // Delegate for promoting a web app.
   AppBannerInfoBarDelegateAndroid(
       base::WeakPtr<AppBannerManager> weak_manager,
-      const base::string16& app_title,
       std::unique_ptr<ShortcutInfo> info,
       const SkBitmap& primary_icon,
       const SkBitmap& badge_icon,
diff --git a/chrome/browser/android/banners/app_banner_manager_android.cc b/chrome/browser/android/banners/app_banner_manager_android.cc
index 84126a3e..f50e444 100644
--- a/chrome/browser/android/banners/app_banner_manager_android.cc
+++ b/chrome/browser/android/banners/app_banner_manager_android.cc
@@ -245,12 +245,11 @@
   DCHECK(contents);
 
   if (native_app_data_.is_null()) {
-    std::unique_ptr<ShortcutInfo> info =
-        CreateShortcutInfo(manifest_url_, manifest_, primary_icon_url_,
-                           badge_icon_url_, can_install_webapk_);
     if (AppBannerInfoBarDelegateAndroid::Create(
-            contents, GetWeakPtr(), info->name, std::move(info), primary_icon_,
-            badge_icon_, event_request_id(), can_install_webapk_,
+            contents, GetWeakPtr(),
+            CreateShortcutInfo(manifest_url_, manifest_, primary_icon_url_,
+                               badge_icon_url_, can_install_webapk_),
+            primary_icon_, badge_icon_, event_request_id(), can_install_webapk_,
             webapk::INSTALL_SOURCE_BANNER)) {
       RecordDidShowBanner("AppBanner.WebApp.Shown");
       TrackDisplayEvent(DISPLAY_EVENT_WEB_APP_BANNER_CREATED);
diff --git a/chrome/browser/android/chrome_entry_point.cc b/chrome/browser/android/chrome_entry_point.cc
index 079358b..ee46cd6c 100644
--- a/chrome/browser/android/chrome_entry_point.cc
+++ b/chrome/browser/android/chrome_entry_point.cc
@@ -7,7 +7,6 @@
 #include "base/android/library_loader/library_loader_hooks.h"
 #include "base/bind.h"
 #include "chrome/app/android/chrome_jni_onload.h"
-#include "chrome/browser/android/chrome_jni_registration.h"
 
 namespace {
 
@@ -24,19 +23,6 @@
   // Java side and only register a subset of JNI methods.
   base::android::InitVM(vm);
   JNIEnv* env = base::android::AttachCurrentThread();
-
-  if (!base::android::IsSelectiveJniRegistrationEnabled(env)) {
-    if (!RegisterNonMainDexNatives(env)) {
-      return -1;
-    }
-  }
-
-  if (!RegisterMainDexNatives(env)) {
-    return -1;
-  }
-
-  // TODO(agrieve): Delete this block, this is a no-op now.
-  // https://crbug.com/683256.
   if (base::android::IsSelectiveJniRegistrationEnabled(env)) {
     base::android::SetJniRegistrationType(
         base::android::SELECTIVE_JNI_REGISTRATION);
diff --git a/chrome/browser/android/chrome_sync_shell_entry_point.cc b/chrome/browser/android/chrome_sync_shell_entry_point.cc
deleted file mode 100644
index e14ea511..0000000
--- a/chrome/browser/android/chrome_sync_shell_entry_point.cc
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/android/jni_android.h"
-#include "base/android/jni_utils.h"
-#include "base/android/library_loader/library_loader_hooks.h"
-#include "base/bind.h"
-#include "chrome/app/android/chrome_jni_onload.h"
-#include "chrome/browser/android/chrome_sync_shell_jni_registration.h"
-
-namespace {
-
-bool NativeInit() {
-  return android::OnJNIOnLoadInit();
-}
-
-}  // namespace
-
-// This is called by the VM when the shared library is first loaded.
-JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
-  // By default, all JNI methods are registered. However, since render processes
-  // don't need very much Java code, we enable selective JNI registration on the
-  // Java side and only register a subset of JNI methods.
-  base::android::InitVM(vm);
-  JNIEnv* env = base::android::AttachCurrentThread();
-
-  if (!base::android::IsSelectiveJniRegistrationEnabled(env)) {
-    if (!RegisterNonMainDexNatives(env)) {
-      return -1;
-    }
-  }
-
-  if (!RegisterMainDexNatives(env)) {
-    return -1;
-  }
-
-  // TODO(agrieve): Delete this block, this is a no-op now.
-  // https://crbug.com/683256.
-  if (base::android::IsSelectiveJniRegistrationEnabled(env)) {
-    base::android::SetJniRegistrationType(
-        base::android::SELECTIVE_JNI_REGISTRATION);
-  }
-  if (!android::OnJNIOnLoadRegisterJNI(env)) {
-    return -1;
-  }
-  base::android::SetNativeInitializationHook(NativeInit);
-  return JNI_VERSION_1_4;
-}
diff --git a/chrome/browser/android/webapps/add_to_homescreen_manager.cc b/chrome/browser/android/webapps/add_to_homescreen_manager.cc
index f5e8ecd..bba4d5d 100644
--- a/chrome/browser/android/webapps/add_to_homescreen_manager.cc
+++ b/chrome/browser/android/webapps/add_to_homescreen_manager.cc
@@ -176,7 +176,7 @@
   banners::AppBannerManagerAndroid* app_banner_manager =
       banners::AppBannerManagerAndroid::FromWebContents(web_contents);
   banners::AppBannerInfoBarDelegateAndroid::Create(
-      web_contents, app_banner_manager->GetWeakPtr(), info.user_title,
+      web_contents, app_banner_manager->GetWeakPtr(),
       base::MakeUnique<ShortcutInfo>(info), primary_icon, badge_icon,
       -1 /* event_request_id */, true /* is_webapk */,
       webapk::INSTALL_SOURCE_MENU);
diff --git a/chrome/browser/app_controller_mac_browsertest.mm b/chrome/browser/app_controller_mac_browsertest.mm
index f87d266..212af96 100644
--- a/chrome/browser/app_controller_mac_browsertest.mm
+++ b/chrome/browser/app_controller_mac_browsertest.mm
@@ -426,7 +426,6 @@
     SessionStartupPref pref(GetParam());
     pref.urls.push_back(GURL(kPresetURL));
     SessionStartupPref::SetStartupPref(browser()->profile(), pref);
-    InProcessBrowserTest::SetUpOnMainThread();
   }
 
  protected:
@@ -504,7 +503,6 @@
     SessionStartupPref pref(session_startup_pref_);
     pref.urls.push_back(GURL(kPresetURL));
     SessionStartupPref::SetStartupPref(browser()->profile(), pref);
-    InProcessBrowserTest::SetUpOnMainThread();
   }
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
diff --git a/chrome/browser/autofill/autofill_browsertest.cc b/chrome/browser/autofill/autofill_browsertest.cc
index 62fb446..56f0fbc 100644
--- a/chrome/browser/autofill/autofill_browsertest.cc
+++ b/chrome/browser/autofill/autofill_browsertest.cc
@@ -108,7 +108,6 @@
     test::DisableSystemServices(browser()->profile()->GetPrefs());
 
     ASSERT_TRUE(embedded_test_server()->Start());
-    InProcessBrowserTest::SetUpOnMainThread();
   }
 
   void TearDownOnMainThread() override {
diff --git a/chrome/browser/banners/app_banner_manager_browsertest.cc b/chrome/browser/banners/app_banner_manager_browsertest.cc
index 7fbb4d0..34f6096 100644
--- a/chrome/browser/banners/app_banner_manager_browsertest.cc
+++ b/chrome/browser/banners/app_banner_manager_browsertest.cc
@@ -115,7 +115,6 @@
     AppBannerSettingsHelper::SetTotalEngagementToTrigger(10);
     SiteEngagementScore::SetParamValuesForTesting();
     ASSERT_TRUE(embedded_test_server()->Start());
-    InProcessBrowserTest::SetUpOnMainThread();
   }
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
diff --git a/chrome/browser/budget_service/budget_manager_browsertest.cc b/chrome/browser/budget_service/budget_manager_browsertest.cc
index 954e4d59..f72dfb2d 100644
--- a/chrome/browser/budget_service/budget_manager_browsertest.cc
+++ b/chrome/browser/budget_service/budget_manager_browsertest.cc
@@ -58,7 +58,6 @@
                                         std::string(), CONTENT_SETTING_ALLOW);
 
     LoadTestPage();
-    InProcessBrowserTest::SetUpOnMainThread();
     budget_manager_ = BudgetManagerFactory::GetForProfile(browser()->profile());
   }
 
diff --git a/chrome/browser/chromeos/accessibility/select_to_speak_browsertest.cc b/chrome/browser/chromeos/accessibility/select_to_speak_browsertest.cc
index c6cfad40..c47f338 100644
--- a/chrome/browser/chromeos/accessibility/select_to_speak_browsertest.cc
+++ b/chrome/browser/chromeos/accessibility/select_to_speak_browsertest.cc
@@ -23,8 +23,6 @@
   ~SelectToSpeakTest() override {}
 
   void SetUpOnMainThread() override {
-    InProcessBrowserTest::SetUpOnMainThread();
-
     ASSERT_FALSE(AccessibilityManager::Get()->IsSelectToSpeakEnabled());
 
     content::WindowedNotificationObserver extension_load_waiter(
@@ -42,6 +40,7 @@
   SpeechMonitor speech_monitor_;
   std::unique_ptr<ui::test::EventGenerator> generator_;
 
+ private:
   DISALLOW_COPY_AND_ASSIGN(SelectToSpeakTest);
 };
 
diff --git a/chrome/browser/chromeos/accessibility/select_to_speak_live_site_browsertest.cc b/chrome/browser/chromeos/accessibility/select_to_speak_live_site_browsertest.cc
index f1776ed..e8298190 100644
--- a/chrome/browser/chromeos/accessibility/select_to_speak_live_site_browsertest.cc
+++ b/chrome/browser/chromeos/accessibility/select_to_speak_live_site_browsertest.cc
@@ -25,7 +25,6 @@
 class SelectToSpeakLiveSiteTest : public InProcessBrowserTest {
  protected:
   void SetUpOnMainThread() override {
-    InProcessBrowserTest::SetUpOnMainThread();
 
     ASSERT_FALSE(AccessibilityManager::Get()->IsSelectToSpeakEnabled());
 
diff --git a/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_manager_browsertest.cc b/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_manager_browsertest.cc
index 4f02848..3ec20534 100644
--- a/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_manager_browsertest.cc
+++ b/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_manager_browsertest.cc
@@ -87,8 +87,6 @@
   }
 
   void SetUpOnMainThread() override {
-    InProcessBrowserTest::SetUpOnMainThread();
-
     settings_helper_.ReplaceProvider(kAccountsPrefDeviceLocalAccounts);
     owner_settings_service_ =
         settings_helper_.CreateOwnerSettingsService(browser()->profile());
diff --git a/chrome/browser/chromeos/app_mode/kiosk_app_manager_browsertest.cc b/chrome/browser/chromeos/app_mode/kiosk_app_manager_browsertest.cc
index a6b51ce..4ccc3e6 100644
--- a/chrome/browser/chromeos/app_mode/kiosk_app_manager_browsertest.cc
+++ b/chrome/browser/chromeos/app_mode/kiosk_app_manager_browsertest.cc
@@ -250,8 +250,6 @@
   }
 
   void SetUpOnMainThread() override {
-    InProcessBrowserTest::SetUpOnMainThread();
-
     host_resolver()->AddRule("*", "127.0.0.1");
 
     // Start the accept thread as the sandbox host process has already been
diff --git a/chrome/browser/chromeos/file_manager/url_util_unittest.cc b/chrome/browser/chromeos/file_manager/url_util_unittest.cc
index fa1a9a1..16d4439e 100644
--- a/chrome/browser/chromeos/file_manager/url_util_unittest.cc
+++ b/chrome/browser/chromeos/file_manager/url_util_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/json/json_writer.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
+#include "extensions/common/constants.h"
 #include "net/base/escape.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -45,7 +46,7 @@
       NULL,  // No file types
       0,  // Hence no file type index.
       FILE_PATH_LITERAL("txt"));
-  EXPECT_EQ("chrome-extension", url.scheme());
+  EXPECT_EQ(extensions::kExtensionScheme, url.scheme());
   EXPECT_EQ("hhaomjibdihmijegdhdafkllkbggdgoj", url.host());
   EXPECT_EQ("/main.html", url.path());
   // Confirm that "%20" is used instead of "+" in the query.
@@ -94,7 +95,7 @@
       &file_types,
       1,  // The file type index is 1-based.
       FILE_PATH_LITERAL("txt"));
-  EXPECT_EQ("chrome-extension", url.scheme());
+  EXPECT_EQ(extensions::kExtensionScheme, url.scheme());
   EXPECT_EQ("hhaomjibdihmijegdhdafkllkbggdgoj", url.host());
   EXPECT_EQ("/main.html", url.path());
   // Confirm that "%20" is used instead of "+" in the query.
diff --git a/chrome/browser/chromeos/fileapi/file_system_backend_unittest.cc b/chrome/browser/chromeos/fileapi/file_system_backend_unittest.cc
index a2619f0a..70eac8eb 100644
--- a/chrome/browser/chromeos/fileapi/file_system_backend_unittest.cc
+++ b/chrome/browser/chromeos/fileapi/file_system_backend_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/macros.h"
 #include "chrome/browser/chromeos/fileapi/file_system_backend_delegate.h"
 #include "chromeos/dbus/cros_disks_client.h"
+#include "extensions/common/constants.h"
 #include "storage/browser/fileapi/external_mount_points.h"
 #include "storage/browser/fileapi/file_system_url.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -108,7 +109,8 @@
 }
 
 TEST(ChromeOSFileSystemBackendTest, AccessPermissions) {
-  url::AddStandardScheme("chrome-extension", url::SCHEME_WITHOUT_PORT);
+  url::AddStandardScheme(extensions::kExtensionScheme,
+                         url::SCHEME_WITHOUT_PORT);
 
   scoped_refptr<storage::ExternalMountPoints> mount_points(
       storage::ExternalMountPoints::CreateRefCounted());
diff --git a/chrome/browser/chromeos/first_run/drive_first_run_browsertest.cc b/chrome/browser/chromeos/first_run/drive_first_run_browsertest.cc
index 7f1d08a..2350a9c2 100644
--- a/chrome/browser/chromeos/first_run/drive_first_run_browsertest.cc
+++ b/chrome/browser/chromeos/first_run/drive_first_run_browsertest.cc
@@ -88,7 +88,6 @@
     success_(false) {}
 
 void DriveFirstRunTest::SetUpOnMainThread() {
-  InProcessBrowserTest::SetUpOnMainThread();
   PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir_);
   test_data_dir_ = test_data_dir_.AppendASCII(kTestDirectory);
 
diff --git a/chrome/browser/chromeos/input_method/input_method_manager_impl.cc b/chrome/browser/chromeos/input_method/input_method_manager_impl.cc
index 2c2eff8..4c74e46 100644
--- a/chrome/browser/chromeos/input_method/input_method_manager_impl.cc
+++ b/chrome/browser/chromeos/input_method/input_method_manager_impl.cc
@@ -41,7 +41,6 @@
 #include "components/prefs/pref_service.h"
 #include "components/user_manager/user_manager.h"
 #include "third_party/icu/source/common/unicode/uloc.h"
-#include "ui/base/accelerators/accelerator.h"
 #include "ui/base/ime/chromeos/component_extension_ime_manager.h"
 #include "ui/base/ime/chromeos/extension_ime_util.h"
 #include "ui/base/ime/chromeos/fake_ime_keyboard.h"
@@ -730,7 +729,7 @@
   }
 }
 
-bool InputMethodManagerImpl::StateImpl::CanCycleInputMethod() {
+bool InputMethodManagerImpl::StateImpl::CanCycleInputMethod() const {
   // Sanity checks.
   if (active_input_method_ids.empty()) {
     DVLOG(1) << "active input method is empty";
@@ -742,13 +741,10 @@
     return false;
   }
 
-  // Do not consume key event if there is only one input method is enabled.
-  // Ctrl+Space or Alt+Shift may be used by other application.
   return active_input_method_ids.size() > 1;
 }
 
 void InputMethodManagerImpl::StateImpl::SwitchToNextInputMethod() {
-  DCHECK(CanCycleInputMethod());
   if (!CanCycleInputMethod())
     return;
 
@@ -758,7 +754,6 @@
 }
 
 void InputMethodManagerImpl::StateImpl::SwitchToPreviousInputMethod() {
-  DCHECK(CanCycleInputMethod());
   if (!CanCycleInputMethod())
     return;
 
@@ -780,25 +775,6 @@
   ChangeInputMethod(*iter, true);
 }
 
-bool InputMethodManagerImpl::StateImpl::CanSwitchInputMethod(
-    const ui::Accelerator& accelerator) {
-  // If none of the input methods associated with |accelerator| are active, we
-  // should ignore the accelerator. For example, we should just ignore
-  // VKEY_HANGUL when mozc-hangul is not active.
-  std::vector<std::string> candidate_ids;
-  GetCandidateInputMethodsForAccelerator(accelerator, &candidate_ids);
-  return !candidate_ids.empty();
-}
-
-void InputMethodManagerImpl::StateImpl::SwitchInputMethod(
-    const ui::Accelerator& accelerator) {
-  std::vector<std::string> candidate_ids;
-  GetCandidateInputMethodsForAccelerator(accelerator, &candidate_ids);
-  DCHECK(!candidate_ids.empty());
-  if (!candidate_ids.empty())
-    SwitchToNextInputMethodInternal(candidate_ids, current_input_method.id());
-}
-
 void InputMethodManagerImpl::StateImpl::SwitchToNextInputMethodInternal(
     const std::vector<std::string>& input_method_ids,
     const std::string& current_input_methodid) {
@@ -811,52 +787,6 @@
   ChangeInputMethod(*iter, true);
 }
 
-void InputMethodManagerImpl::StateImpl::GetCandidateInputMethodsForAccelerator(
-    const ui::Accelerator& accelerator,
-    std::vector<std::string>* out_candidate_ids) {
-  out_candidate_ids->clear();
-
-  // Sanity check.
-  if (active_input_method_ids.empty()) {
-    DVLOG(1) << "active input method is empty";
-    return;
-  }
-
-  std::vector<std::string> input_method_ids_to_switch;
-  switch (accelerator.key_code()) {
-    case ui::VKEY_CONVERT:  // Henkan key on JP106 keyboard
-      input_method_ids_to_switch.push_back(
-          extension_ime_util::GetInputMethodIDByEngineID("nacl_mozc_jp"));
-      break;
-    case ui::VKEY_NONCONVERT:  // Muhenkan key on JP106 keyboard
-      input_method_ids_to_switch.push_back(
-          extension_ime_util::GetInputMethodIDByEngineID("xkb:jp::jpn"));
-      break;
-    case ui::VKEY_DBE_SBCSCHAR:  // ZenkakuHankaku key on JP106 keyboard
-    case ui::VKEY_DBE_DBCSCHAR:
-      input_method_ids_to_switch.push_back(
-          extension_ime_util::GetInputMethodIDByEngineID("nacl_mozc_jp"));
-      input_method_ids_to_switch.push_back(
-          extension_ime_util::GetInputMethodIDByEngineID("xkb:jp::jpn"));
-      break;
-    default:
-      NOTREACHED();
-      break;
-  }
-  if (input_method_ids_to_switch.empty()) {
-    DVLOG(1) << "Unexpected VKEY: " << accelerator.key_code();
-    return;
-  }
-
-  // Obtain the intersection of input_method_ids_to_switch and
-  // active_input_method_ids.
-  for (size_t i = 0; i < input_method_ids_to_switch.size(); ++i) {
-    const std::string& id = input_method_ids_to_switch[i];
-    if (base::ContainsValue(active_input_method_ids, id))
-      out_candidate_ids->push_back(id);
-  }
-}
-
 InputMethodDescriptor InputMethodManagerImpl::StateImpl::GetCurrentInputMethod()
     const {
   if (current_input_method.id().empty())
diff --git a/chrome/browser/chromeos/input_method/input_method_manager_impl.h b/chrome/browser/chromeos/input_method/input_method_manager_impl.h
index b5c5d8a3..7a4dd45 100644
--- a/chrome/browser/chromeos/input_method/input_method_manager_impl.h
+++ b/chrome/browser/chromeos/input_method/input_method_manager_impl.h
@@ -66,17 +66,14 @@
         const std::vector<std::string>& input_method_ids,
         const std::string& current_input_methodid);
 
-    // Returns the IDs of the subset of input methods which are active and are
-    // associated with |accelerator|. For example,
-    // { "mozc-hangul", "xkb:kr:kr104:kor" } is returned for
-    // ui::VKEY_DBE_SBCSCHAR if the two input methods are active.
-    void GetCandidateInputMethodsForAccelerator(
-        const ui::Accelerator& accelerator,
-        std::vector<std::string>* out_candidate_ids);
-
     // Returns true if given input method requires pending extension.
     bool MethodAwaitsExtensionLoad(const std::string& input_method_id) const;
 
+    // Returns whether the input method (or keyboard layout) can be switched
+    // to the next or previous one. Returns false if only one input method is
+    // enabled.
+    bool CanCycleInputMethod() const;
+
     // InputMethodManager::State overrides.
     scoped_refptr<InputMethodManager::State> Clone() const override;
     void AddInputMethodExtension(
@@ -103,11 +100,8 @@
     void SetInputMethodLoginDefault() override;
     void SetInputMethodLoginDefaultFromVPD(const std::string& locale,
                                            const std::string& layout) override;
-    bool CanCycleInputMethod() override;
     void SwitchToNextInputMethod() override;
     void SwitchToPreviousInputMethod() override;
-    bool CanSwitchInputMethod(const ui::Accelerator& accelerator) override;
-    void SwitchInputMethod(const ui::Accelerator& accelerator) override;
     InputMethodDescriptor GetCurrentInputMethod() const override;
     bool ReplaceEnabledInputMethods(
         const std::vector<std::string>& new_active_input_method_ids) override;
diff --git a/chrome/browser/chromeos/input_method/input_method_manager_impl_unittest.cc b/chrome/browser/chromeos/input_method/input_method_manager_impl_unittest.cc
index 3e657f0..9253734 100644
--- a/chrome/browser/chromeos/input_method/input_method_manager_impl_unittest.cc
+++ b/chrome/browser/chromeos/input_method/input_method_manager_impl_unittest.cc
@@ -29,7 +29,6 @@
 #include "chrome/test/base/testing_profile_manager.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "ui/base/accelerators/accelerator.h"
 #include "ui/base/ime/chromeos/extension_ime_util.h"
 #include "ui/base/ime/chromeos/fake_ime_keyboard.h"
 #include "ui/base/ime/chromeos/fake_input_method_delegate.h"
@@ -39,7 +38,6 @@
 #include "ui/base/ime/input_method_initializer.h"
 #include "ui/chromeos/ime/input_method_menu_item.h"
 #include "ui/chromeos/ime/input_method_menu_manager.h"
-#include "ui/events/keycodes/keyboard_codes.h"
 #include "ui/keyboard/content/keyboard_content_util.h"
 
 namespace chromeos {
@@ -1013,7 +1011,6 @@
   keyboard_layouts.push_back(ImeIdFromEngineId("xkb:us::eng"));
   manager_->GetActiveIMEState()->EnableLoginLayouts("en-US", keyboard_layouts);
   EXPECT_EQ(8U, manager_->GetActiveIMEState()->GetNumActiveInputMethods());
-  EXPECT_TRUE(manager_->GetActiveIMEState()->CanCycleInputMethod());
   EXPECT_EQ(ImeIdFromEngineId("xkb:us::eng"),
             manager_->GetActiveIMEState()->GetCurrentInputMethod().id());
   EXPECT_EQ("us", keyboard_->last_layout_);
@@ -1061,160 +1058,24 @@
   manager_->RemoveObserver(&observer);
 }
 
-TEST_F(InputMethodManagerImplTest,
-       TestCanCycleInputMethodForOneActiveInputMethod) {
-  TestObserver observer;
+TEST_F(InputMethodManagerImplTest, CycleInputMethodForOneActiveInputMethod) {
   InitComponentExtension();
-  manager_->AddObserver(&observer);
 
+  // Simulate a single input method.
   std::vector<std::string> ids;
-  ids.push_back(ImeIdFromEngineId("xkb:us:dvorak:eng"));
+  ids.push_back(ImeIdFromEngineId("xkb:us::eng"));
   EXPECT_TRUE(manager_->GetActiveIMEState()->ReplaceEnabledInputMethods(ids));
   EXPECT_EQ(1U, manager_->GetActiveIMEState()->GetNumActiveInputMethods());
 
-  // CanCycleInputMethod() should return false if there is only one active input
-  // method.
-  EXPECT_FALSE(manager_->GetActiveIMEState()->CanCycleInputMethod());
-
-  manager_->RemoveObserver(&observer);
-}
-
-TEST_F(InputMethodManagerImplTest, TestSwitchInputMethodWithUsLayouts) {
-  InitComponentExtension();
-  std::vector<std::string> keyboard_layouts;
-  keyboard_layouts.push_back(ImeIdFromEngineId("xkb:us::eng"));
-  manager_->GetActiveIMEState()->EnableLoginLayouts("en-US", keyboard_layouts);
-  EXPECT_EQ(8U, manager_->GetActiveIMEState()->GetNumActiveInputMethods());
-
-  // Henkan, Muhenkan, ZenkakuHankaku should be ignored when no Japanese IMEs
-  // and keyboards are enabled.
-  EXPECT_FALSE(manager_->GetActiveIMEState()->CanSwitchInputMethod(
-      ui::Accelerator(ui::VKEY_CONVERT, ui::EF_NONE)));
-  EXPECT_FALSE(manager_->GetActiveIMEState()->CanSwitchInputMethod(
-      ui::Accelerator(ui::VKEY_NONCONVERT, ui::EF_NONE)));
-  EXPECT_FALSE(manager_->GetActiveIMEState()->CanSwitchInputMethod(
-      ui::Accelerator(ui::VKEY_DBE_SBCSCHAR, ui::EF_NONE)));
-  EXPECT_FALSE(manager_->GetActiveIMEState()->CanSwitchInputMethod(
-      ui::Accelerator(ui::VKEY_DBE_DBCSCHAR, ui::EF_NONE)));
-}
-
-TEST_F(InputMethodManagerImplTest, TestSwitchInputMethodWithJpLayout) {
-  // Enable "xkb:jp::jpn" and press Muhenkan/ZenkakuHankaku.
-  InitComponentExtension();
-
-  std::vector<std::string> keyboard_layouts;
-  keyboard_layouts.push_back(ImeIdFromEngineId("xkb:us::eng"));
-  manager_->GetActiveIMEState()->EnableLoginLayouts("ja", keyboard_layouts);
-  EXPECT_EQ(2U, manager_->GetActiveIMEState()->GetNumActiveInputMethods());
+  // Switching to next does nothing.
+  manager_->GetActiveIMEState()->SwitchToNextInputMethod();
   EXPECT_EQ(ImeIdFromEngineId("xkb:us::eng"),
             manager_->GetActiveIMEState()->GetCurrentInputMethod().id());
-  EXPECT_EQ("us", keyboard_->last_layout_);
-  EXPECT_TRUE(manager_->GetActiveIMEState()->CanSwitchInputMethod(
-      ui::Accelerator(ui::VKEY_NONCONVERT, ui::EF_NONE)));
-  manager_->GetActiveIMEState()->SwitchInputMethod(
-      ui::Accelerator(ui::VKEY_NONCONVERT, ui::EF_NONE));
-  EXPECT_EQ(ImeIdFromEngineId("xkb:jp::jpn"),
-            manager_->GetActiveIMEState()->GetCurrentInputMethod().id());
-  EXPECT_EQ("jp", keyboard_->last_layout_);
-  EXPECT_TRUE(manager_->GetActiveIMEState()->CanCycleInputMethod());
+
+  // Switching to previous does nothing.
   manager_->GetActiveIMEState()->SwitchToPreviousInputMethod();
   EXPECT_EQ(ImeIdFromEngineId("xkb:us::eng"),
             manager_->GetActiveIMEState()->GetCurrentInputMethod().id());
-  EXPECT_EQ("us", keyboard_->last_layout_);
-  EXPECT_TRUE(manager_->GetActiveIMEState()->CanSwitchInputMethod(
-      ui::Accelerator(ui::VKEY_DBE_SBCSCHAR, ui::EF_NONE)));
-  manager_->GetActiveIMEState()->SwitchInputMethod(
-      ui::Accelerator(ui::VKEY_DBE_SBCSCHAR, ui::EF_NONE));
-  EXPECT_EQ(ImeIdFromEngineId("xkb:jp::jpn"),
-            manager_->GetActiveIMEState()->GetCurrentInputMethod().id());
-  EXPECT_EQ("jp", keyboard_->last_layout_);
-  EXPECT_TRUE(manager_->GetActiveIMEState()->CanCycleInputMethod());
-  manager_->GetActiveIMEState()->SwitchToPreviousInputMethod();
-  EXPECT_EQ(ImeIdFromEngineId("xkb:us::eng"),
-            manager_->GetActiveIMEState()->GetCurrentInputMethod().id());
-  EXPECT_EQ("us", keyboard_->last_layout_);
-  EXPECT_TRUE(manager_->GetActiveIMEState()->CanSwitchInputMethod(
-      ui::Accelerator(ui::VKEY_DBE_DBCSCHAR, ui::EF_NONE)));
-  manager_->GetActiveIMEState()->SwitchInputMethod(
-      ui::Accelerator(ui::VKEY_DBE_DBCSCHAR, ui::EF_NONE));
-  EXPECT_EQ(ImeIdFromEngineId("xkb:jp::jpn"),
-            manager_->GetActiveIMEState()->GetCurrentInputMethod().id());
-  EXPECT_EQ("jp", keyboard_->last_layout_);
-}
-
-TEST_F(InputMethodManagerImplTest, TestSwitchInputMethodWithJpIme) {
-  InitComponentExtension();
-  manager_->SetUISessionState(InputMethodManager::STATE_BROWSER_SCREEN);
-  std::vector<std::string> ids;
-  ids.push_back(ImeIdFromEngineId("xkb:jp::jpn"));
-  ids.push_back(ImeIdFromEngineId(kNaclMozcJpId));
-  EXPECT_TRUE(manager_->GetActiveIMEState()->ReplaceEnabledInputMethods(ids));
-  EXPECT_EQ(ImeIdFromEngineId("xkb:jp::jpn"),
-            manager_->GetActiveIMEState()->GetCurrentInputMethod().id());
-  EXPECT_EQ("jp", keyboard_->last_layout_);
-  EXPECT_TRUE(manager_->GetActiveIMEState()->CanSwitchInputMethod(
-      ui::Accelerator(ui::VKEY_DBE_DBCSCHAR, ui::EF_NONE)));
-  manager_->GetActiveIMEState()->SwitchInputMethod(
-      ui::Accelerator(ui::VKEY_DBE_DBCSCHAR, ui::EF_NONE));
-  EXPECT_EQ(ImeIdFromEngineId(kNaclMozcJpId),
-            manager_->GetActiveIMEState()->GetCurrentInputMethod().id());
-  EXPECT_EQ("jp", keyboard_->last_layout_);
-  EXPECT_TRUE(manager_->GetActiveIMEState()->CanSwitchInputMethod(
-      ui::Accelerator(ui::VKEY_DBE_DBCSCHAR, ui::EF_NONE)));
-  manager_->GetActiveIMEState()->SwitchInputMethod(
-      ui::Accelerator(ui::VKEY_DBE_DBCSCHAR, ui::EF_NONE));
-  EXPECT_EQ(ImeIdFromEngineId("xkb:jp::jpn"),
-            manager_->GetActiveIMEState()->GetCurrentInputMethod().id());
-  EXPECT_EQ("jp", keyboard_->last_layout_);
-  EXPECT_TRUE(manager_->GetActiveIMEState()->CanSwitchInputMethod(
-      ui::Accelerator(ui::VKEY_CONVERT, ui::EF_NONE)));
-  manager_->GetActiveIMEState()->SwitchInputMethod(
-      ui::Accelerator(ui::VKEY_CONVERT, ui::EF_NONE));
-  EXPECT_EQ(ImeIdFromEngineId(kNaclMozcJpId),
-            manager_->GetActiveIMEState()->GetCurrentInputMethod().id());
-  EXPECT_EQ("jp", keyboard_->last_layout_);
-  EXPECT_TRUE(manager_->GetActiveIMEState()->CanSwitchInputMethod(
-      ui::Accelerator(ui::VKEY_CONVERT, ui::EF_NONE)));
-  manager_->GetActiveIMEState()->SwitchInputMethod(
-      ui::Accelerator(ui::VKEY_CONVERT, ui::EF_NONE));
-  EXPECT_EQ(ImeIdFromEngineId(kNaclMozcJpId),
-            manager_->GetActiveIMEState()->GetCurrentInputMethod().id());
-  EXPECT_EQ("jp", keyboard_->last_layout_);
-  EXPECT_TRUE(manager_->GetActiveIMEState()->CanSwitchInputMethod(
-      ui::Accelerator(ui::VKEY_NONCONVERT, ui::EF_NONE)));
-  manager_->GetActiveIMEState()->SwitchInputMethod(
-      ui::Accelerator(ui::VKEY_NONCONVERT, ui::EF_NONE));
-  EXPECT_EQ(ImeIdFromEngineId("xkb:jp::jpn"),
-            manager_->GetActiveIMEState()->GetCurrentInputMethod().id());
-  EXPECT_EQ("jp", keyboard_->last_layout_);
-  EXPECT_TRUE(manager_->GetActiveIMEState()->CanSwitchInputMethod(
-      ui::Accelerator(ui::VKEY_NONCONVERT, ui::EF_NONE)));
-  manager_->GetActiveIMEState()->SwitchInputMethod(
-      ui::Accelerator(ui::VKEY_NONCONVERT, ui::EF_NONE));
-  EXPECT_EQ(ImeIdFromEngineId("xkb:jp::jpn"),
-            manager_->GetActiveIMEState()->GetCurrentInputMethod().id());
-  EXPECT_EQ("jp", keyboard_->last_layout_);
-
-  // Add Dvorak.
-  ids.push_back(ImeIdFromEngineId("xkb:us:dvorak:eng"));
-  EXPECT_TRUE(manager_->GetActiveIMEState()->ReplaceEnabledInputMethods(ids));
-  EXPECT_EQ(ImeIdFromEngineId("xkb:jp::jpn"),
-            manager_->GetActiveIMEState()->GetCurrentInputMethod().id());
-  EXPECT_EQ("jp", keyboard_->last_layout_);
-  EXPECT_TRUE(manager_->GetActiveIMEState()->CanSwitchInputMethod(
-      ui::Accelerator(ui::VKEY_DBE_SBCSCHAR, ui::EF_NONE)));
-  manager_->GetActiveIMEState()->SwitchInputMethod(
-      ui::Accelerator(ui::VKEY_DBE_SBCSCHAR, ui::EF_NONE));
-  EXPECT_EQ(ImeIdFromEngineId(kNaclMozcJpId),
-            manager_->GetActiveIMEState()->GetCurrentInputMethod().id());
-  EXPECT_EQ("jp", keyboard_->last_layout_);
-  EXPECT_TRUE(manager_->GetActiveIMEState()->CanSwitchInputMethod(
-      ui::Accelerator(ui::VKEY_DBE_SBCSCHAR, ui::EF_NONE)));
-  manager_->GetActiveIMEState()->SwitchInputMethod(
-      ui::Accelerator(ui::VKEY_DBE_SBCSCHAR, ui::EF_NONE));
-  EXPECT_EQ(ImeIdFromEngineId("xkb:jp::jpn"),
-            manager_->GetActiveIMEState()->GetCurrentInputMethod().id());
-  EXPECT_EQ("jp", keyboard_->last_layout_);
 }
 
 TEST_F(InputMethodManagerImplTest, TestAddRemoveExtensionInputMethods) {
diff --git a/chrome/browser/chromeos/input_method/mode_indicator_browsertest.cc b/chrome/browser/chromeos/input_method/mode_indicator_browsertest.cc
index d9c843b7..a21a5857 100644
--- a/chrome/browser/chromeos/input_method/mode_indicator_browsertest.cc
+++ b/chrome/browser/chromeos/input_method/mode_indicator_browsertest.cc
@@ -133,7 +133,6 @@
   // Add keyboard layouts to enable the mode indicator.
   imm->GetActiveIMEState()->EnableLoginLayouts("fr", keyboard_layouts);
   ASSERT_LT(1UL, imm->GetActiveIMEState()->GetNumActiveInputMethods());
-  EXPECT_TRUE(imm->GetActiveIMEState()->CanCycleInputMethod());
 
   chromeos::IMECandidateWindowHandlerInterface* candidate_window =
       ui::IMEBridge::Get()->GetCandidateWindowHandler();
@@ -203,7 +202,6 @@
   // Add keyboard layouts to enable the mode indicator.
   imm->GetActiveIMEState()->EnableLoginLayouts("fr", keyboard_layouts);
   ASSERT_LT(1UL, imm->GetActiveIMEState()->GetNumActiveInputMethods());
-  EXPECT_TRUE(imm->GetActiveIMEState()->CanCycleInputMethod());
 
   chromeos::IMECandidateWindowHandlerInterface* candidate_window =
       ui::IMEBridge::Get()->GetCandidateWindowHandler();
diff --git a/chrome/browser/chromeos/login/mixin_based_browser_test.cc b/chrome/browser/chromeos/login/mixin_based_browser_test.cc
index 4d6cbe22..fdeaae4 100644
--- a/chrome/browser/chromeos/login/mixin_based_browser_test.cc
+++ b/chrome/browser/chromeos/login/mixin_based_browser_test.cc
@@ -36,8 +36,6 @@
   setup_was_launched_ = true;
   for (const auto& mixin : mixins_)
     mixin->SetUpOnMainThread();
-
-  InProcessBrowserTest::SetUpOnMainThread();
 }
 
 void MixinBasedBrowserTest::TearDownOnMainThread() {
diff --git a/chrome/browser/dom_distiller/distillable_page_utils_browsertest.cc b/chrome/browser/dom_distiller/distillable_page_utils_browsertest.cc
index 83085ff..1234a84 100644
--- a/chrome/browser/dom_distiller/distillable_page_utils_browsertest.cc
+++ b/chrome/browser/dom_distiller/distillable_page_utils_browsertest.cc
@@ -101,7 +101,6 @@
   }
 
   void SetUpOnMainThread() override {
-    InProcessBrowserTest::SetUpOnMainThread();
     ASSERT_TRUE(embedded_test_server()->Start());
     web_contents_ =
         browser()->tab_strip_model()->GetActiveWebContents();
diff --git a/chrome/browser/dom_distiller/tab_utils_browsertest.cc b/chrome/browser/dom_distiller/tab_utils_browsertest.cc
index ddc07da6..c494ba3 100644
--- a/chrome/browser/dom_distiller/tab_utils_browsertest.cc
+++ b/chrome/browser/dom_distiller/tab_utils_browsertest.cc
@@ -41,7 +41,6 @@
     if (!DistillerJavaScriptWorldIdIsSet()) {
       SetDistillerJavaScriptWorldId(content::ISOLATED_WORLD_ID_CONTENT_END);
     }
-    InProcessBrowserTest::SetUpOnMainThread();
   }
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
diff --git a/chrome/browser/domain_reliability/browsertest.cc b/chrome/browser/domain_reliability/browsertest.cc
index c70aacd..b87684c 100644
--- a/chrome/browser/domain_reliability/browsertest.cc
+++ b/chrome/browser/domain_reliability/browsertest.cc
@@ -36,8 +36,6 @@
   }
 
   void SetUpOnMainThread() override {
-    InProcessBrowserTest::SetUpOnMainThread();
-
     DomainReliabilityService* service = GetService();
     if (service)
       service->SetDiscardUploadsForTesting(false);
diff --git a/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc b/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
index 4dbbf13..0a4e9ba 100644
--- a/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
+++ b/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
@@ -316,7 +316,6 @@
     BrowserThread::PostTask(
         BrowserThread::IO, FROM_HERE,
         base::BindOnce(&chrome_browser_net::SetUrlRequestMocksEnabled, true));
-    InProcessBrowserTest::SetUpOnMainThread();
     GoOnTheRecord();
     CreateAndSetDownloadsDirectory();
     current_browser()->profile()->GetPrefs()->SetBoolean(
diff --git a/chrome/browser/extensions/extension_browsertest.cc b/chrome/browser/extensions/extension_browsertest.cc
index e76e4ff3..622235a 100644
--- a/chrome/browser/extensions/extension_browsertest.cc
+++ b/chrome/browser/extensions/extension_browsertest.cc
@@ -185,7 +185,6 @@
 }
 
 void ExtensionBrowserTest::SetUpOnMainThread() {
-  InProcessBrowserTest::SetUpOnMainThread();
   observer_.reset(
       new extensions::ChromeExtensionTestNotificationObserver(browser()));
   if (extension_service()->updater()) {
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index e88f4d37..0714a23 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -3049,6 +3049,37 @@
     "If enabled and the device supports ARC, the user will be asked to update "
     "the encryption of user data when the user signs in.";
 
+// Spurious power button detection
+
+const char kSpuriousPowerButtonWindowName[] = "Spurious power button window";
+const char kSpuriousPowerButtonWindowDescription[] =
+    "Number of recent accelerometer samples to examine to determine if a power "
+    "button event was spurious.";
+
+const char kSpuriousPowerButtonAccelCountName[] =
+    "Spurious power button acceleration count";
+const char kSpuriousPowerButtonAccelCountDescription[] =
+    "Number of recent acceleration samples that must meet or exceed exceed the "
+    "threshold in order for a power button event to be considered spurious.";
+
+const char kSpuriousPowerButtonScreenAccelName[] =
+    "Spurious power button screen acceleration threshold";
+const char kSpuriousPowerButtonScreenAccelDescription[] =
+    "Threshold (in m/s^2, disregarding gravity) that screen acceleration must "
+    "meet or exceed for a power button event to be considered spurious.";
+
+const char kSpuriousPowerButtonKeyboardAccelName[] =
+    "Spurious power button keyboard acceleration threshold";
+const char kSpuriousPowerButtonKeyboardAccelDescription[] =
+    "Threshold (in m/s^2, disregarding gravity) that keyboard acceleration "
+    "must meet or exceed for a power button event to be considered spurious.";
+
+const char kSpuriousPowerButtonLidAngleChangeName[] =
+    "Spurious power button lid angle change threshold";
+const char kSpuriousPowerButtonLidAngleChangeDescription[] =
+    "Change in lid angle (i.e. hinge between keyboard and screen) that must be "
+    "met or exceeded for a power button event to be considered spurious.";
+
 #endif  // #if defined(OS_CHROMEOS)
 
 #if defined(OS_ANDROID)
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 89342fc..e5a1429 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1367,6 +1367,17 @@
 extern const char kSmartVirtualKeyboardName[];
 extern const char kSmartVirtualKeyboardDescription[];
 
+extern const char kSpuriousPowerButtonWindowName[];
+extern const char kSpuriousPowerButtonWindowDescription[];
+extern const char kSpuriousPowerButtonAccelCountName[];
+extern const char kSpuriousPowerButtonAccelCountDescription[];
+extern const char kSpuriousPowerButtonScreenAccelName[];
+extern const char kSpuriousPowerButtonScreenAccelDescription[];
+extern const char kSpuriousPowerButtonKeyboardAccelName[];
+extern const char kSpuriousPowerButtonKeyboardAccelDescription[];
+extern const char kSpuriousPowerButtonLidAngleChangeName[];
+extern const char kSpuriousPowerButtonLidAngleChangeDescription[];
+
 extern const char kTeamDrivesName[];
 extern const char kTeamDrivesDescription[];
 
diff --git a/chrome/browser/installable/installable_manager_browsertest.cc b/chrome/browser/installable/installable_manager_browsertest.cc
index f9e5bf92..3ac72f6 100644
--- a/chrome/browser/installable/installable_manager_browsertest.cc
+++ b/chrome/browser/installable/installable_manager_browsertest.cc
@@ -176,7 +176,6 @@
 class InstallableManagerBrowserTest : public InProcessBrowserTest {
  public:
   void SetUpOnMainThread() override {
-    InProcessBrowserTest::SetUpOnMainThread();
     ASSERT_TRUE(embedded_test_server()->Start());
   }
 
diff --git a/chrome/browser/io_thread_browsertest.cc b/chrome/browser/io_thread_browsertest.cc
index b7c536e1..b5c3c00 100644
--- a/chrome/browser/io_thread_browsertest.cc
+++ b/chrome/browser/io_thread_browsertest.cc
@@ -73,8 +73,6 @@
 
   void SetUpOnMainThread() override {
     embedded_test_server()->StartAcceptingConnections();
-
-    InProcessBrowserTest::SetUpOnMainThread();
   }
 
   void TearDown() override {
diff --git a/chrome/browser/lifetime/browser_close_manager_browsertest.cc b/chrome/browser/lifetime/browser_close_manager_browsertest.cc
index 0a4173a..1b3e44a 100644
--- a/chrome/browser/lifetime/browser_close_manager_browsertest.cc
+++ b/chrome/browser/lifetime/browser_close_manager_browsertest.cc
@@ -223,7 +223,6 @@
       public testing::WithParamInterface<bool> {
  protected:
   void SetUpOnMainThread() override {
-    InProcessBrowserTest::SetUpOnMainThread();
     SessionStartupPref::SetStartupPref(
         browser()->profile(), SessionStartupPref(SessionStartupPref::LAST));
     browsers_.push_back(browser());
diff --git a/chrome/browser/loader/chrome_resource_dispatcher_host_delegate_browsertest.cc b/chrome/browser/loader/chrome_resource_dispatcher_host_delegate_browsertest.cc
index afd87c1..7867326 100644
--- a/chrome/browser/loader/chrome_resource_dispatcher_host_delegate_browsertest.cc
+++ b/chrome/browser/loader/chrome_resource_dispatcher_host_delegate_browsertest.cc
@@ -209,7 +209,6 @@
   ChromeResourceDispatcherHostDelegateBrowserTest() {}
 
   void SetUpOnMainThread() override {
-    InProcessBrowserTest::SetUpOnMainThread();
     // Hook navigations with our delegate.
     dispatcher_host_delegate_.reset(new TestDispatcherHostDelegate);
     content::ResourceDispatcherHost::Get()->SetDelegate(
@@ -301,7 +300,6 @@
 
 IN_PROC_BROWSER_TEST_F(ChromeResourceDispatcherHostDelegateBrowserTest,
                        PolicyHeaderForRedirect) {
-
   // Build up a URL that results in a redirect to the DMServer URL to make
   // sure the policy header is still added.
   std::string redirect_url;
diff --git a/chrome/browser/media/android/remote/remote_media_player_bridge.cc b/chrome/browser/media/android/remote/remote_media_player_bridge.cc
index 6c3e609..531e3f4 100644
--- a/chrome/browser/media/android/remote/remote_media_player_bridge.cc
+++ b/chrome/browser/media/android/remote/remote_media_player_bridge.cc
@@ -262,6 +262,7 @@
 // static
 bool RemoteMediaPlayerBridge::RegisterRemoteMediaPlayerBridge(JNIEnv* env) {
   bool ret = RegisterNativesImpl(env);
+  DCHECK(g_RemoteMediaPlayerBridge_clazz);
   return ret;
 }
 
diff --git a/chrome/browser/media/android/router/media_router_android_bridge.cc b/chrome/browser/media/android/router/media_router_android_bridge.cc
index 4c57cde..e56bbb59 100644
--- a/chrome/browser/media/android/router/media_router_android_bridge.cc
+++ b/chrome/browser/media/android/router/media_router_android_bridge.cc
@@ -29,6 +29,7 @@
 // static
 bool MediaRouterAndroidBridge::Register(JNIEnv* env) {
   bool ret = RegisterNativesImpl(env);
+  DCHECK(g_ChromeMediaRouter_clazz);
   return ret;
 }
 
diff --git a/chrome/browser/media/encrypted_media_supported_types_browsertest.cc b/chrome/browser/media/encrypted_media_supported_types_browsertest.cc
index 9f8104c..f6badf40 100644
--- a/chrome/browser/media/encrypted_media_supported_types_browsertest.cc
+++ b/chrome/browser/media/encrypted_media_supported_types_browsertest.cc
@@ -191,8 +191,6 @@
 #endif  // BUILDFLAG(ENABLE_PEPPER_CDMS)
 
   void SetUpOnMainThread() override {
-    InProcessBrowserTest::SetUpOnMainThread();
-
     // Load the test page needed so that checkKeySystemWithMediaMimeType()
     // is available.
     std::unique_ptr<net::EmbeddedTestServer> http_test_server(
diff --git a/chrome/browser/media/router/media_router_dialog_controller.cc b/chrome/browser/media/router/media_router_dialog_controller.cc
index 43ffd5f..31cdd96 100644
--- a/chrome/browser/media/router/media_router_dialog_controller.cc
+++ b/chrome/browser/media/router/media_router_dialog_controller.cc
@@ -10,13 +10,9 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_delegate.h"
-#include "device/vr/features/features.h"
 
 #if defined(OS_ANDROID)
 #include "chrome/browser/media/android/router/media_router_dialog_controller_android.h"
-#if BUILDFLAG(ENABLE_VR)
-#include "chrome/browser/android/vr_shell/vr_tab_helper.h"
-#endif  // BUILDFLAG(ENABLE_VR)
 #else
 #include "chrome/browser/ui/webui/media_router/media_router_dialog_controller_impl.h"
 #endif
@@ -28,11 +24,6 @@
 MediaRouterDialogController::GetOrCreateForWebContents(
     content::WebContents* contents) {
 #if defined(OS_ANDROID)
-#if BUILDFLAG(ENABLE_VR)
-  if (vr_shell::VrTabHelper::IsInVr(contents)) {
-    return nullptr;
-  }
-#endif
   return MediaRouterDialogControllerAndroid::GetOrCreateForWebContents(
       contents);
 #else
diff --git a/chrome/browser/metrics/antivirus_metrics_provider_win_unittest.cc b/chrome/browser/metrics/antivirus_metrics_provider_win_unittest.cc
index 3e027e0..dbfa3b6 100644
--- a/chrome/browser/metrics/antivirus_metrics_provider_win_unittest.cc
+++ b/chrome/browser/metrics/antivirus_metrics_provider_win_unittest.cc
@@ -109,8 +109,8 @@
   ASSERT_TRUE(thread_checker_.CalledOnValidThread());
   base::HistogramTester histograms;
   SetFullNamesFeatureEnabled(expect_unhashed_value_);
-  // Make sure the I/O is happening on the FILE thread by disallowing it on
-  // the main thread.
+  // Make sure the I/O is happening on a valid thread by disallowing it on the
+  // main thread.
   bool previous_value = base::ThreadRestrictions::SetIOAllowed(false);
   provider_->GetAntiVirusMetrics(
       base::Bind(&AntiVirusMetricsProviderTest::GetMetricsCallback,
diff --git a/chrome/browser/net/proxy_browsertest.cc b/chrome/browser/net/proxy_browsertest.cc
index a5f0c1c..326ee58 100644
--- a/chrome/browser/net/proxy_browsertest.cc
+++ b/chrome/browser/net/proxy_browsertest.cc
@@ -312,8 +312,6 @@
                    ALLOW_ADDITIONAL_CONNECTIONS);
     embedded_test_server()->SetConnectionListener(connection_listener_.get());
     embedded_test_server()->StartAcceptingConnections();
-
-    InProcessBrowserTest::SetUpOnMainThread();
   }
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
diff --git a/chrome/browser/notifications/platform_notification_service_interactive_uitest.cc b/chrome/browser/notifications/platform_notification_service_interactive_uitest.cc
index 8eef933..0b6f6cd0 100644
--- a/chrome/browser/notifications/platform_notification_service_interactive_uitest.cc
+++ b/chrome/browser/notifications/platform_notification_service_interactive_uitest.cc
@@ -169,7 +169,6 @@
   display_service_.reset(
       new MessageCenterDisplayService(browser()->profile(), ui_manager_.get()));
   service()->SetNotificationDisplayServiceForTesting(display_service_.get());
-  InProcessBrowserTest::SetUpOnMainThread();
 }
 
 void PlatformNotificationServiceBrowserTest::TearDown() {
diff --git a/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_browsertest.cc b/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_browsertest.cc
index 8623212..01535a4 100644
--- a/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_browsertest.cc
+++ b/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_browsertest.cc
@@ -28,7 +28,6 @@
 
   void SetUpOnMainThread() override {
     ASSERT_TRUE(embedded_test_server()->Start());
-    InProcessBrowserTest::SetUpOnMainThread();
   }
 
  private:
diff --git a/chrome/browser/page_load_metrics/observers/multi_tab_loading_page_load_metrics_observer_browsertest.cc b/chrome/browser/page_load_metrics/observers/multi_tab_loading_page_load_metrics_observer_browsertest.cc
index eab2896b..da96052 100644
--- a/chrome/browser/page_load_metrics/observers/multi_tab_loading_page_load_metrics_observer_browsertest.cc
+++ b/chrome/browser/page_load_metrics/observers/multi_tab_loading_page_load_metrics_observer_browsertest.cc
@@ -30,7 +30,6 @@
 
   void SetUpOnMainThread() override {
     ASSERT_TRUE(embedded_test_server()->Start());
-    InProcessBrowserTest::SetUpOnMainThread();
   }
 };
 
diff --git a/chrome/browser/payments/payment_manifest_parser_host_browsertest.cc b/chrome/browser/payments/payment_manifest_parser_host_browsertest.cc
new file mode 100644
index 0000000..7975d58
--- /dev/null
+++ b/chrome/browser/payments/payment_manifest_parser_host_browsertest.cc
@@ -0,0 +1,359 @@
+// 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/content/payment_manifest_parser_host.h"
+
+#include <utility>
+
+#include "base/run_loop.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace payments {
+
+// Test fixture for payment manifest parser host.
+class PaymentManifestParserHostTest : public InProcessBrowserTest {
+ public:
+  PaymentManifestParserHostTest() : all_origins_supported_(false) {}
+  ~PaymentManifestParserHostTest() override {}
+
+  // Starts the utility process. If this is not called, all parse requests fail.
+  void StartUtilityProcess() { host_.StartUtilityProcess(); }
+
+  // Sends the |content| to the utility process to parse as a web app manifest
+  // and waits until the utility process responds.
+  void ParseWebAppManifest(const std::string& content) {
+    base::RunLoop run_loop;
+    host_.ParseWebAppManifest(
+        content,
+        base::BindOnce(&PaymentManifestParserHostTest::OnWebAppManifestParsed,
+                       base::Unretained(this), run_loop.QuitClosure()));
+    run_loop.Run();
+  }
+
+  // Sets up the internal state of the utility process host as if it sent a
+  // request to parse a web app manifest, although nothing is sent to the
+  // utility process.
+  void MockParseWebAppManifest() {
+    host_.pending_web_app_callbacks_.insert(std::make_pair(
+        0,
+        base::BindOnce(&PaymentManifestParserHostTest::OnWebAppManifestParsed,
+                       base::Unretained(this), base::Closure())));
+  }
+
+  // Directly invokes the host's callback for web app manifest parsing.
+  void InvokeOnWebAppManifestParsed(
+      std::vector<mojom::WebAppManifestSectionPtr> web_app_manifest) {
+    host_.OnWebAppParse(0, std::move(web_app_manifest));
+  }
+
+  // Sends the |content| to the utility process to parse as a payment method
+  // manifest and waits until the utility process responds.
+  void ParsePaymentMethodManifest(const std::string& content) {
+    base::RunLoop run_loop;
+    host_.ParsePaymentMethodManifest(
+        content,
+        base::BindOnce(
+            &PaymentManifestParserHostTest::OnPaymentMethodManifestParsed,
+            base::Unretained(this), run_loop.QuitClosure()));
+    run_loop.Run();
+  }
+
+  // Sets up the internal state of the utility process host as if it sent a
+  // request to parse a payment method manifest, although nothing is sent to the
+  // utility process.
+  void MockParsePaymentMethodManifest() {
+    host_.pending_payment_method_callbacks_.insert(std::make_pair(
+        0, base::BindOnce(
+               &PaymentManifestParserHostTest::OnPaymentMethodManifestParsed,
+               base::Unretained(this), base::Closure())));
+  }
+
+  // Directly invokes the host's callback for payment method manifest parsing.
+  void InvokeOnPaymentMethodManifestParse(
+      const std::vector<GURL>& web_app_manifest_urls,
+      const std::vector<url::Origin>& supported_origins,
+      bool all_origins_supported) {
+    host_.OnPaymentMethodParse(0, web_app_manifest_urls, supported_origins,
+                               all_origins_supported);
+  }
+
+  // The parsed web app manifest.
+  const std::vector<mojom::WebAppManifestSectionPtr>& web_app_manifest() const {
+    return web_app_manifest_;
+  }
+
+  // The parsed web app manifest URLs from the payment method manifest.
+  const std::vector<GURL>& web_app_manifest_urls() const {
+    return web_app_manifest_urls_;
+  }
+
+  // The parsed supported origins from the payment method manifest.
+  const std::vector<url::Origin>& supported_origins() const {
+    return supported_origins_;
+  }
+
+  // Whether the parsed payment method manifest allows all origins to use the
+  // payment method.
+  bool all_origins_supported() const { return all_origins_supported_; }
+
+ private:
+  // Called after the utility process has parsed the web app manifest.
+  void OnWebAppManifestParsed(
+      const base::Closure& resume_test,
+      std::vector<mojom::WebAppManifestSectionPtr> web_app_manifest) {
+    web_app_manifest_ = std::move(web_app_manifest);
+    if (!resume_test.is_null())
+      resume_test.Run();
+  }
+
+  // Called after the utility process has parsed the payment method manifest.
+  void OnPaymentMethodManifestParsed(
+      const base::Closure& resume_test,
+      const std::vector<GURL>& web_app_manifest_urls,
+      const std::vector<url::Origin>& supported_origins,
+      bool all_origins_supported) {
+    web_app_manifest_urls_ = web_app_manifest_urls;
+    supported_origins_ = supported_origins;
+    all_origins_supported_ = all_origins_supported;
+    if (!resume_test.is_null())
+      resume_test.Run();
+  }
+
+  PaymentManifestParserHost host_;
+  std::vector<mojom::WebAppManifestSectionPtr> web_app_manifest_;
+  std::vector<GURL> web_app_manifest_urls_;
+  std::vector<url::Origin> supported_origins_;
+  bool all_origins_supported_;
+
+  DISALLOW_COPY_AND_ASSIGN(PaymentManifestParserHostTest);
+};
+
+// If the utility process has not been started, parsing a payment method
+// manifest should fail.
+IN_PROC_BROWSER_TEST_F(PaymentManifestParserHostTest,
+                       NotStarted_PaymentMethodManifest) {
+  ParsePaymentMethodManifest("{\"supported_origins\": \"*\"}");
+
+  EXPECT_TRUE(web_app_manifest_urls().empty());
+  EXPECT_TRUE(supported_origins().empty());
+  EXPECT_FALSE(all_origins_supported());
+}
+
+// Handle a utility process that returns a result unexpectedly.
+IN_PROC_BROWSER_TEST_F(PaymentManifestParserHostTest,
+                       UnexpectedOnPaymentMethodParse) {
+  InvokeOnPaymentMethodManifestParse(std::vector<GURL>(),
+                                     std::vector<url::Origin>(), true);
+
+  EXPECT_FALSE(all_origins_supported());
+}
+
+// Handle a utility process that returns more than 100 web app
+// manifest URLs.
+IN_PROC_BROWSER_TEST_F(PaymentManifestParserHostTest, TooManyWebAppUrls) {
+  MockParsePaymentMethodManifest();
+
+  InvokeOnPaymentMethodManifestParse(
+      std::vector<GURL>(101, GURL("https://bobpay.com/manifest.json")),
+      std::vector<url::Origin>(), false);
+
+  EXPECT_TRUE(web_app_manifest_urls().empty());
+}
+
+// Handle a utility process that returns more than 100 supported
+// origins.
+IN_PROC_BROWSER_TEST_F(PaymentManifestParserHostTest, TooManySupportedOrigins) {
+  MockParsePaymentMethodManifest();
+
+  InvokeOnPaymentMethodManifestParse(
+      std::vector<GURL>(),
+      std::vector<url::Origin>(101, url::Origin(GURL("https://bobpay.com"))),
+      false);
+
+  EXPECT_TRUE(supported_origins().empty());
+}
+
+// Handle a utility process that returns an insecure supported
+// origin.
+IN_PROC_BROWSER_TEST_F(PaymentManifestParserHostTest, InsecureSupportedOrigin) {
+  MockParsePaymentMethodManifest();
+
+  InvokeOnPaymentMethodManifestParse(
+      std::vector<GURL>(),
+      std::vector<url::Origin>(1, url::Origin(GURL("http://bobpay.com"))),
+      false);
+
+  EXPECT_TRUE(supported_origins().empty());
+}
+
+// Handle a utility process that returns an insecure web app manifest
+// URL.
+IN_PROC_BROWSER_TEST_F(PaymentManifestParserHostTest,
+                       InsecureWebAppManifestUrl) {
+  MockParsePaymentMethodManifest();
+
+  InvokeOnPaymentMethodManifestParse(
+      std::vector<GURL>(1, GURL("http://bobpay.com/manifest.json")),
+      std::vector<url::Origin>(), false);
+
+  EXPECT_TRUE(web_app_manifest_urls().empty());
+}
+
+// Handle a utility process that returns an invalid supported origin.
+IN_PROC_BROWSER_TEST_F(PaymentManifestParserHostTest, InvalidSupportedOrigin) {
+  MockParsePaymentMethodManifest();
+
+  InvokeOnPaymentMethodManifestParse(
+      std::vector<GURL>(), std::vector<url::Origin>(1, url::Origin(GURL())),
+      false);
+
+  EXPECT_TRUE(supported_origins().empty());
+}
+
+// Handle a utility process that returns an invalid web app manifest URL.
+IN_PROC_BROWSER_TEST_F(PaymentManifestParserHostTest,
+                       InvalidWebAppManifestUrl) {
+  MockParsePaymentMethodManifest();
+
+  InvokeOnPaymentMethodManifestParse(std::vector<GURL>(1, GURL()),
+                                     std::vector<url::Origin>(), false);
+
+  EXPECT_TRUE(web_app_manifest_urls().empty());
+}
+
+// Handle a utility process that returns both a list of supported origins and
+// the indicator that all origins are supported.
+IN_PROC_BROWSER_TEST_F(PaymentManifestParserHostTest,
+                       InvalidAllOriginsSupported) {
+  MockParsePaymentMethodManifest();
+
+  InvokeOnPaymentMethodManifestParse(
+      std::vector<GURL>(),
+      std::vector<url::Origin>(1, url::Origin(GURL("https://bobpay.com"))),
+      true);
+
+  EXPECT_TRUE(supported_origins().empty());
+  EXPECT_FALSE(all_origins_supported());
+}
+
+// Verify that the utility process correctly parses a payment method manifest
+// that allows all origins to use the corresponding payment method name.
+IN_PROC_BROWSER_TEST_F(PaymentManifestParserHostTest, AllOriginsSupported) {
+  StartUtilityProcess();
+
+  ParsePaymentMethodManifest("{\"supported_origins\": \"*\"}");
+
+  EXPECT_TRUE(web_app_manifest_urls().empty());
+  EXPECT_TRUE(supported_origins().empty());
+  EXPECT_TRUE(all_origins_supported());
+}
+
+// Verify that the utility process correctly parses a payment method manifest
+// with default applications and some supported origins.
+IN_PROC_BROWSER_TEST_F(PaymentManifestParserHostTest, UrlsAndOrigins) {
+  StartUtilityProcess();
+
+  ParsePaymentMethodManifest(
+      "{\"default_applications\": "
+      "[\"https://alicepay.com/web-app-manifest.json\"], "
+      "\"supported_origins\": [\"https://bobpay.com\"]}");
+
+  EXPECT_EQ(
+      std::vector<GURL>(1, GURL("https://alicepay.com/web-app-manifest.json")),
+      web_app_manifest_urls());
+  EXPECT_EQ(
+      std::vector<url::Origin>(1, url::Origin(GURL("https://bobpay.com"))),
+      supported_origins());
+  EXPECT_FALSE(all_origins_supported());
+}
+
+// If the utility process has not been started, parsing a web app manifest
+// should fail.
+IN_PROC_BROWSER_TEST_F(PaymentManifestParserHostTest,
+                       NotStarted_WebAppManifest) {
+  ParseWebAppManifest(
+      "{"
+      "  \"related_applications\": [{"
+      "    \"platform\": \"play\", "
+      "    \"id\": \"com.bobpay.app\", "
+      "    \"min_version\": \"1\", "
+      "    \"fingerprints\": [{"
+      "      \"type\": \"sha256_cert\", "
+      "      \"value\": \"00:01:02:03:04:05:06:07:08:09:A0:A1:A2:A3:A4:A5:A6:A7"
+      ":A8:A9:B0:B1:B2:B3:B4:B5:B6:B7:B8:B9:C0:C1\""
+      "    }]"
+      "  }]"
+      "}");
+
+  EXPECT_TRUE(web_app_manifest().empty());
+}
+
+// Handle a utility process that returns a result unexpectedly.
+IN_PROC_BROWSER_TEST_F(PaymentManifestParserHostTest, UnexpectedOnWebAppParse) {
+  std::vector<mojom::WebAppManifestSectionPtr> manifest;
+  manifest.push_back(mojom::WebAppManifestSection::New());
+
+  InvokeOnWebAppManifestParsed(std::move(manifest));
+
+  EXPECT_TRUE(web_app_manifest().empty());
+}
+
+// Verify that the utility process parses correctly a valid web app manifest.
+IN_PROC_BROWSER_TEST_F(PaymentManifestParserHostTest, WebAppManifest) {
+  StartUtilityProcess();
+
+  ParseWebAppManifest(
+      "{"
+      "  \"related_applications\": [{"
+      "    \"platform\": \"play\", "
+      "    \"id\": \"com.bobpay.app\", "
+      "    \"min_version\": \"1\", "
+      "    \"fingerprints\": [{"
+      "      \"type\": \"sha256_cert\", "
+      "      \"value\": \"00:01:02:03:04:05:06:07:08:09:A0:A1:A2:A3:A4:A5:A6:A7"
+      ":A8:A9:B0:B1:B2:B3:B4:B5:B6:B7:B8:B9:C0:C1\""
+      "    }]"
+      "  }]"
+      "}");
+
+  ASSERT_EQ(1U, web_app_manifest().size());
+  EXPECT_EQ("com.bobpay.app", web_app_manifest().front()->id);
+  EXPECT_EQ(1, web_app_manifest().front()->min_version);
+  ASSERT_EQ(1U, web_app_manifest().front()->fingerprints.size());
+  EXPECT_EQ(
+      std::vector<uint8_t>({0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+                            0x08, 0x09, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5,
+                            0xA6, 0xA7, 0xA8, 0xA9, 0xB0, 0xB1, 0xB2, 0xB3,
+                            0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xC0, 0xC1}),
+      web_app_manifest().front()->fingerprints.front());
+}
+
+// Handle a utility process that returns too many web app manifest sections.
+IN_PROC_BROWSER_TEST_F(PaymentManifestParserHostTest,
+                       TooManyWebAppManifestSections) {
+  MockParseWebAppManifest();
+  std::vector<mojom::WebAppManifestSectionPtr> manifest;
+  for (size_t i = 0; i < 101; ++i) {
+    manifest.push_back(mojom::WebAppManifestSection::New());
+  }
+
+  InvokeOnWebAppManifestParsed(std::move(manifest));
+
+  EXPECT_TRUE(web_app_manifest().empty());
+}
+
+// Handle a utility process that returns too many fingerprints.
+IN_PROC_BROWSER_TEST_F(PaymentManifestParserHostTest, TooManyFingerprints) {
+  MockParseWebAppManifest();
+  std::vector<mojom::WebAppManifestSectionPtr> manifest;
+  manifest.push_back(mojom::WebAppManifestSection::New());
+  manifest.back()->fingerprints.assign(101, std::vector<uint8_t>());
+
+  InvokeOnWebAppManifestParsed(std::move(manifest));
+
+  EXPECT_TRUE(web_app_manifest().empty());
+}
+
+}  // namespace payments
diff --git a/chrome/browser/permissions/permission_request_manager_browsertest.cc b/chrome/browser/permissions/permission_request_manager_browsertest.cc
index a6de20a..86a3c39 100644
--- a/chrome/browser/permissions/permission_request_manager_browsertest.cc
+++ b/chrome/browser/permissions/permission_request_manager_browsertest.cc
@@ -78,7 +78,6 @@
   ~PermissionRequestManagerBrowserTest() override = default;
 
   void SetUpOnMainThread() override {
-    InProcessBrowserTest::SetUpOnMainThread();
     PermissionRequestManager* manager = GetPermissionRequestManager();
     mock_permission_prompt_factory_.reset(
         new MockPermissionPromptFactory(manager));
@@ -127,7 +126,6 @@
   void SetUpOnMainThread() override {
     // Skip super: It will install a mock permission UI factory, but for this
     // test we want to show "real" UI.
-    InProcessBrowserTest::SetUpOnMainThread();
     ui_test_utils::NavigateToURL(browser(), GetUrl());
   }
 
diff --git a/chrome/browser/permissions/permissions_browsertest.cc b/chrome/browser/permissions/permissions_browsertest.cc
index 06bd4ed..92440a7 100644
--- a/chrome/browser/permissions/permissions_browsertest.cc
+++ b/chrome/browser/permissions/permissions_browsertest.cc
@@ -21,8 +21,6 @@
 PermissionsBrowserTest::~PermissionsBrowserTest() {}
 
 void PermissionsBrowserTest::SetUpOnMainThread() {
-  InProcessBrowserTest::SetUpOnMainThread();
-
   PermissionRequestManager* manager = PermissionRequestManager::FromWebContents(
       browser()->tab_strip_model()->GetActiveWebContents());
   prompt_factory_.reset(new MockPermissionPromptFactory(manager));
diff --git a/chrome/browser/plugins/plugin_power_saver_browsertest.cc b/chrome/browser/plugins/plugin_power_saver_browsertest.cc
index c87fc065..fc613dc 100644
--- a/chrome/browser/plugins/plugin_power_saver_browsertest.cc
+++ b/chrome/browser/plugins/plugin_power_saver_browsertest.cc
@@ -259,8 +259,6 @@
   }
 
   void SetUpOnMainThread() override {
-    InProcessBrowserTest::SetUpOnMainThread();
-
     embedded_test_server()->ServeFilesFromDirectory(
         ui_test_utils::GetTestFilePath(
             base::FilePath(FILE_PATH_LITERAL("plugin_power_saver")),
diff --git a/chrome/browser/policy/policy_helpers.cc b/chrome/browser/policy/policy_helpers.cc
index 662f3b7..0fc1133 100644
--- a/chrome/browser/policy/policy_helpers.cc
+++ b/chrome/browser/policy/policy_helpers.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/policy/policy_helpers.h"
 
 #include "build/build_config.h"
+#include "extensions/features/features.h"
 #include "net/base/net_errors.h"
 #include "url/gurl.h"
 
@@ -17,6 +18,10 @@
 #include "google_apis/gaia/gaia_urls.h"
 #endif
 
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+#include "extensions/common/constants.h"
+#endif
+
 namespace policy {
 
 bool OverrideBlacklistForURL(const GURL& url, bool* block, int* reason) {
@@ -27,10 +32,12 @@
     return false;
   }
 
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   // Don't block internal pages and extensions.
-  if (url.SchemeIs("chrome") || url.SchemeIs("chrome-extension")) {
+  if (url.SchemeIs("chrome") || url.SchemeIs(extensions::kExtensionScheme)) {
     return false;
   }
+#endif
 
   // Don't block Google's support web site.
   if (url.SchemeIs(url::kHttpsScheme) && url.DomainIs("support.google.com")) {
diff --git a/chrome/browser/prefetch/prefetch_browsertest.cc b/chrome/browser/prefetch/prefetch_browsertest.cc
index 19112909..032a31c 100644
--- a/chrome/browser/prefetch/prefetch_browsertest.cc
+++ b/chrome/browser/prefetch/prefetch_browsertest.cc
@@ -54,7 +54,6 @@
 
   void SetUpOnMainThread() override {
     ASSERT_TRUE(embedded_test_server()->Start());
-    InProcessBrowserTest::SetUpOnMainThread();
   }
 
   void SetPreference(NetworkPredictionOptions value) {
diff --git a/chrome/browser/prerender/prerender_util.cc b/chrome/browser/prerender/prerender_util.cc
index b35ad93..dec66b23 100644
--- a/chrome/browser/prerender/prerender_util.cc
+++ b/chrome/browser/prerender/prerender_util.cc
@@ -6,8 +6,13 @@
 
 #include "base/metrics/histogram_macros.h"
 #include "components/google/core/browser/google_util.h"
+#include "extensions/features/features.h"
 #include "url/gurl.h"
 
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+#include "extensions/common/constants.h"
+#endif
+
 namespace prerender {
 
 namespace {
@@ -68,9 +73,11 @@
     ReportPrerenderSchemeCancelReason(PRERENDER_SCHEME_CANCEL_REASON_FTP);
   } else if (url.SchemeIs("chrome")) {
     ReportPrerenderSchemeCancelReason(PRERENDER_SCHEME_CANCEL_REASON_CHROME);
-  } else if (url.SchemeIs("chrome-extension")) {
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+  } else if (url.SchemeIs(extensions::kExtensionScheme)) {
     ReportPrerenderSchemeCancelReason(
         PRERENDER_SCHEME_CANCEL_REASON_CHROME_EXTENSION);
+#endif
   } else if (url.SchemeIs("about")) {
     ReportPrerenderSchemeCancelReason(PRERENDER_SCHEME_CANCEL_REASON_ABOUT);
   } else {
diff --git a/chrome/browser/previews/previews_service.cc b/chrome/browser/previews/previews_service.cc
index ead4e6e..d1037404 100644
--- a/chrome/browser/previews/previews_service.cc
+++ b/chrome/browser/previews/previews_service.cc
@@ -10,6 +10,7 @@
 #include "base/sequenced_task_runner.h"
 #include "base/task_scheduler/post_task.h"
 #include "chrome/common/chrome_constants.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_features.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
 #include "components/previews/core/previews_experiments.h"
 #include "components/previews/core/previews_io_data.h"
@@ -22,15 +23,19 @@
 
 // Returns true if previews can be shown for |type|.
 bool IsPreviewsTypeEnabled(previews::PreviewsType type) {
+  bool server_previews_enabled = base::FeatureList::IsEnabled(
+      data_reduction_proxy::features::kDataReductionProxyDecidesTransform);
   switch (type) {
     case previews::PreviewsType::OFFLINE:
       return previews::params::IsOfflinePreviewsEnabled();
     case previews::PreviewsType::LOFI:
-      return previews::params::IsClientLoFiEnabled() ||
+      return server_previews_enabled ||
+             previews::params::IsClientLoFiEnabled() ||
              data_reduction_proxy::params::IsLoFiOnViaFlags() ||
              data_reduction_proxy::params::IsIncludedInLoFiEnabledFieldTrial();
     case previews::PreviewsType::LITE_PAGE:
-      return (data_reduction_proxy::params::IsLoFiOnViaFlags() &&
+      return server_previews_enabled ||
+             (data_reduction_proxy::params::IsLoFiOnViaFlags() &&
               data_reduction_proxy::params::AreLitePagesEnabledViaFlags()) ||
              data_reduction_proxy::params::IsIncludedInLitePageFieldTrial();
     case previews::PreviewsType::NONE:
diff --git a/chrome/browser/previews/previews_service_unittest.cc b/chrome/browser/previews/previews_service_unittest.cc
index 39d8f8c..f2b7e07 100644
--- a/chrome/browser/previews/previews_service_unittest.cc
+++ b/chrome/browser/previews/previews_service_unittest.cc
@@ -16,6 +16,8 @@
 #include "base/metrics/field_trial_params.h"
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
+#include "base/test/scoped_feature_list.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_features.h"
 #include "components/previews/core/previews_io_data.h"
 #include "components/previews/core/previews_ui_service.h"
 #include "components/variations/variations_associated_data.h"
@@ -66,7 +68,7 @@
  public:
   PreviewsServiceTest()
 
-      : field_trial_list_(nullptr) {}
+      : field_trial_list_(nullptr), scoped_feature_list_() {}
 
   void SetUp() override {
     io_data_ = base::MakeUnique<TestPreviewsIOData>();
@@ -77,6 +79,8 @@
                          content::BrowserThread::GetTaskRunnerForThread(
                              content::BrowserThread::UI),
                          file_path);
+    scoped_feature_list_.InitAndDisableFeature(
+        data_reduction_proxy::features::kDataReductionProxyDecidesTransform);
   }
 
   void TearDown() override { variations::testing::ClearAllVariationParams(); }
@@ -90,6 +94,7 @@
   base::FieldTrialList field_trial_list_;
   std::unique_ptr<TestPreviewsIOData> io_data_;
   std::unique_ptr<PreviewsService> service_;
+  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
 }  // namespace
@@ -146,6 +151,9 @@
 }
 
 TEST_F(PreviewsServiceTest, TestLitePageFieldTrialEnabledPreview) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndDisableFeature(
+      data_reduction_proxy::features::kDataReductionProxyDecidesTransform);
   base::FieldTrialList::CreateFieldTrial("DataCompressionProxyLoFi",
                                          "Enabled_Preview");
   EXPECT_TRUE(io_data()->IsPreviewEnabled(previews::PreviewsType::LITE_PAGE));
@@ -165,3 +173,17 @@
 TEST_F(PreviewsServiceTest, TestLitePageFieldTrialNotSet) {
   EXPECT_FALSE(io_data()->IsPreviewEnabled(previews::PreviewsType::LITE_PAGE));
 }
+
+TEST_F(PreviewsServiceTest, TestServerLoFiProxyDecidesTransform) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      data_reduction_proxy::features::kDataReductionProxyDecidesTransform);
+  EXPECT_TRUE(io_data()->IsPreviewEnabled(previews::PreviewsType::LITE_PAGE));
+}
+
+TEST_F(PreviewsServiceTest, TestLitePageProxyDecidesTransform) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      data_reduction_proxy::features::kDataReductionProxyDecidesTransform);
+  EXPECT_TRUE(io_data()->IsPreviewEnabled(previews::PreviewsType::LITE_PAGE));
+}
diff --git a/chrome/browser/profiles/profile_list_desktop_browsertest.cc b/chrome/browser/profiles/profile_list_desktop_browsertest.cc
index 2a18925..b4db78a 100644
--- a/chrome/browser/profiles/profile_list_desktop_browsertest.cc
+++ b/chrome/browser/profiles/profile_list_desktop_browsertest.cc
@@ -59,6 +59,9 @@
 // This test doesn't make sense for Chrome OS since it has a different
 // multi-profiles menu in the system tray instead.
 #define MAYBE_SignOut DISABLED_SignOut
+#elif defined(OS_LINUX)
+// Flaky on Linux debug builds with libc++ (https://crbug.com/734875)
+#define MAYBE_SignOut DISABLED_SignOut
 #else
 #define MAYBE_SignOut SignOut
 #endif
diff --git a/chrome/browser/push_messaging/push_messaging_browsertest.cc b/chrome/browser/push_messaging/push_messaging_browsertest.cc
index 8a8fe225..34d127ec 100644
--- a/chrome/browser/push_messaging/push_messaging_browsertest.cc
+++ b/chrome/browser/push_messaging/push_messaging_browsertest.cc
@@ -164,7 +164,6 @@
         display_service_.get());
 
     LoadTestPage();
-    InProcessBrowserTest::SetUpOnMainThread();
   }
 
   // Calls should be wrapped in the ASSERT_NO_FATAL_FAILURE() macro.
diff --git a/chrome/browser/push_messaging/push_messaging_service_impl.h b/chrome/browser/push_messaging/push_messaging_service_impl.h
index b195d0db..7ffd501 100644
--- a/chrome/browser/push_messaging/push_messaging_service_impl.h
+++ b/chrome/browser/push_messaging/push_messaging_service_impl.h
@@ -22,6 +22,7 @@
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/content_settings/core/common/content_settings_types.h"
 #include "components/gcm_driver/common/gcm_messages.h"
+#include "components/gcm_driver/crypto/gcm_encryption_provider.h"
 #include "components/gcm_driver/gcm_app_handler.h"
 #include "components/gcm_driver/gcm_client.h"
 #include "components/gcm_driver/instance_id/instance_id.h"
diff --git a/chrome/browser/resource_coordinator/tab_manager.cc b/chrome/browser/resource_coordinator/tab_manager.cc
index 85c08294..122f6bd7 100644
--- a/chrome/browser/resource_coordinator/tab_manager.cc
+++ b/chrome/browser/resource_coordinator/tab_manager.cc
@@ -35,6 +35,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/resource_coordinator/tab_manager_observer.h"
 #include "chrome/browser/resource_coordinator/tab_manager_web_contents_data.h"
+#include "chrome/browser/sessions/session_restore.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_window.h"
@@ -126,6 +127,29 @@
 ////////////////////////////////////////////////////////////////////////////////
 // TabManager
 
+class TabManager::TabManagerSessionRestoreObserver
+    : public SessionRestoreObserver {
+ public:
+  explicit TabManagerSessionRestoreObserver(TabManager* tab_manager)
+      : tab_manager_(tab_manager) {
+    SessionRestore::AddObserver(this);
+  }
+
+  ~TabManagerSessionRestoreObserver() { SessionRestore::RemoveObserver(this); }
+
+  // SessionRestoreObserver implementation:
+  void OnSessionRestoreStartedLoadingTabs() override {
+    tab_manager_->OnSessionRestoreStartedLoadingTabs();
+  }
+
+  void OnSessionRestoreFinishedLoadingTabs() override {
+    tab_manager_->OnSessionRestoreFinishedLoadingTabs();
+  }
+
+ private:
+  TabManager* tab_manager_;
+};
+
 constexpr base::TimeDelta TabManager::kDefaultMinTimeToPurge;
 
 TabManager::TabManager()
@@ -137,11 +161,13 @@
 #endif
       browser_tab_strip_tracker_(this, nullptr, nullptr),
       test_tick_clock_(nullptr),
+      is_session_restore_loading_tabs_(false),
       weak_ptr_factory_(this) {
 #if defined(OS_CHROMEOS)
   delegate_.reset(new TabManagerDelegate(weak_ptr_factory_.GetWeakPtr()));
 #endif
   browser_tab_strip_tracker_.Init();
+  session_restore_observer_.reset(new TabManagerSessionRestoreObserver(this));
 }
 
 TabManager::~TabManager() {
@@ -478,6 +504,16 @@
   return reinterpret_cast<int64_t>(web_contents);
 }
 
+void TabManager::OnSessionRestoreStartedLoadingTabs() {
+  DCHECK(!is_session_restore_loading_tabs_);
+  is_session_restore_loading_tabs_ = true;
+}
+
+void TabManager::OnSessionRestoreFinishedLoadingTabs() {
+  DCHECK(is_session_restore_loading_tabs_);
+  is_session_restore_loading_tabs_ = false;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // TabManager, private:
 
diff --git a/chrome/browser/resource_coordinator/tab_manager.h b/chrome/browser/resource_coordinator/tab_manager.h
index 3cd08c9..414b743 100644
--- a/chrome/browser/resource_coordinator/tab_manager.h
+++ b/chrome/browser/resource_coordinator/tab_manager.h
@@ -24,6 +24,7 @@
 #include "build/build_config.h"
 #include "chrome/browser/resource_coordinator/tab_manager_observer.h"
 #include "chrome/browser/resource_coordinator/tab_stats.h"
+#include "chrome/browser/sessions/session_restore_observer.h"
 #include "chrome/browser/ui/browser_tab_strip_tracker.h"
 #include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
 #include "ui/gfx/geometry/rect.h"
@@ -156,6 +157,11 @@
   // the WebContents could be deleted if the user closed the tab.
   static int64_t IdFromWebContents(content::WebContents* web_contents);
 
+  // Return whether tabs are being loaded during session restore.
+  bool IsSessionRestoreLoadingTabs() const {
+    return is_session_restore_loading_tabs_;
+  }
+
  private:
   FRIEND_TEST_ALL_PREFIXES(TabManagerTest, PurgeBackgroundRenderer);
   FRIEND_TEST_ALL_PREFIXES(TabManagerTest, ActivateTabResetPurgeState);
@@ -321,6 +327,9 @@
   // list is constructed from either |test_browser_info_list_| or BrowserList.
   std::vector<BrowserInfo> GetBrowserInfoList() const;
 
+  void OnSessionRestoreStartedLoadingTabs();
+  void OnSessionRestoreFinishedLoadingTabs();
+
   // Timer to periodically update the stats of the renderers.
   base::RepeatingTimer update_timer_;
 
@@ -383,6 +392,11 @@
   // List of observers that will receive notifications on state changes.
   base::ObserverList<TabManagerObserver> observers_;
 
+  bool is_session_restore_loading_tabs_;
+
+  class TabManagerSessionRestoreObserver;
+  std::unique_ptr<TabManagerSessionRestoreObserver> session_restore_observer_;
+
   // Weak pointer factory used for posting delayed tasks.
   base::WeakPtrFactory<TabManager> weak_ptr_factory_;
 
diff --git a/chrome/browser/resource_coordinator/tab_manager_unittest.cc b/chrome/browser/resource_coordinator/tab_manager_unittest.cc
index afec480..238610c 100644
--- a/chrome/browser/resource_coordinator/tab_manager_unittest.cc
+++ b/chrome/browser/resource_coordinator/tab_manager_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <algorithm>
 #include <map>
+#include <memory>
 #include <vector>
 
 #include "base/logging.h"
@@ -17,9 +18,11 @@
 #include "base/test/simple_test_tick_clock.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
+#include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/resource_coordinator/tab_manager_web_contents_data.h"
 #include "chrome/browser/resource_coordinator/tab_stats.h"
+#include "chrome/browser/sessions/tab_loader.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/tabs/test_tab_strip_model_delegate.h"
 #include "chrome/common/chrome_features.h"
@@ -578,4 +581,24 @@
   EXPECT_FALSE(tab_stats[5].is_in_visible_window);
 }
 
+TEST_F(TabManagerTest, OnSessionRestoreStartedAndFinishedLoadingTabs) {
+  std::unique_ptr<content::WebContents> test_contents(
+      WebContentsTester::CreateTestWebContents(browser_context(), nullptr));
+
+  std::vector<SessionRestoreDelegate::RestoredTab> restored_tabs{
+      SessionRestoreDelegate::RestoredTab(test_contents.get(), false, false,
+                                          false)};
+
+  TabManager* tab_manager = g_browser_process->GetTabManager();
+  EXPECT_FALSE(tab_manager->IsSessionRestoreLoadingTabs());
+
+  TabLoader::RestoreTabs(restored_tabs, base::TimeTicks());
+  EXPECT_TRUE(tab_manager->IsSessionRestoreLoadingTabs());
+
+  WebContentsTester::For(test_contents.get())
+      ->NavigateAndCommit(GURL("about:blank"));
+  WebContentsTester::For(test_contents.get())->TestSetIsLoading(false);
+  EXPECT_FALSE(tab_manager->IsSessionRestoreLoadingTabs());
+}
+
 }  // namespace resource_coordinator
diff --git a/chrome/browser/safe_browsing/certificate_reporting_service_browsertest.cc b/chrome/browser/safe_browsing/certificate_reporting_service_browsertest.cc
index be1fa7f..297ec1c 100644
--- a/chrome/browser/safe_browsing/certificate_reporting_service_browsertest.cc
+++ b/chrome/browser/safe_browsing/certificate_reporting_service_browsertest.cc
@@ -88,7 +88,6 @@
         ->SetServiceResetCallbackForTesting(
             base::Bind(&CertificateReportingServiceObserver::OnServiceReset,
                        base::Unretained(&service_observer_)));
-    InProcessBrowserTest::SetUpOnMainThread();
   }
 
   void TearDownOnMainThread() override {
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/reporter_runner_browsertest_win.cc b/chrome/browser/safe_browsing/chrome_cleaner/reporter_runner_browsertest_win.cc
index a04005c..b0a6baf 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/reporter_runner_browsertest_win.cc
+++ b/chrome/browser/safe_browsing/chrome_cleaner/reporter_runner_browsertest_win.cc
@@ -75,8 +75,6 @@
     ASSERT_NE(mock_time_task_runner_, saved_task_runner_);
     base::MessageLoop::current()->SetTaskRunner(mock_time_task_runner_);
 
-    InProcessBrowserTest::SetUpOnMainThread();
-
     // SetDateInLocalState calculates a time as Now() minus an offset. Move the
     // simulated clock ahead far enough that this calculation won't underflow.
     mock_time_task_runner_->FastForwardBy(
diff --git a/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc b/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc
index a38a9c9..dc1d6be3 100644
--- a/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc
@@ -545,7 +545,6 @@
   }
 
   void SetUpOnMainThread() override {
-    InProcessBrowserTest::SetUpOnMainThread();
     g_browser_process->safe_browsing_service()->ui_manager()->AddObserver(
         &observer_);
   }
diff --git a/chrome/browser/sessions/session_restore.cc b/chrome/browser/sessions/session_restore.cc
index 2818f7f..49831a2 100644
--- a/chrome/browser/sessions/session_restore.cc
+++ b/chrome/browser/sessions/session_restore.cc
@@ -865,5 +865,19 @@
 }
 
 // static
+void SessionRestore::AddObserver(SessionRestoreObserver* observer) {
+  observers().AddObserver(observer);
+}
+
+// static
+void SessionRestore::RemoveObserver(SessionRestoreObserver* observer) {
+  observers().RemoveObserver(observer);
+}
+
+// static
 base::CallbackList<void(int)>*
     SessionRestore::on_session_restored_callbacks_ = nullptr;
+
+// static
+base::ObserverList<SessionRestoreObserver>* SessionRestore::observers_ =
+    nullptr;
diff --git a/chrome/browser/sessions/session_restore.h b/chrome/browser/sessions/session_restore.h
index e7ad887c..10dabff1 100644
--- a/chrome/browser/sessions/session_restore.h
+++ b/chrome/browser/sessions/session_restore.h
@@ -12,6 +12,8 @@
 
 #include "base/callback_list.h"
 #include "base/macros.h"
+#include "base/observer_list.h"
+#include "chrome/browser/sessions/session_restore_observer.h"
 #include "components/history/core/browser/history_service.h"
 #include "components/sessions/core/session_types.h"
 #include "ui/base/window_open_disposition.h"
@@ -106,6 +108,18 @@
   static void AddURLsToOpen(const Profile* profile,
                             const std::vector<GURL>& urls);
 
+  // Add/remove an observer to/from this session restore.
+  static void AddObserver(SessionRestoreObserver* observer);
+  static void RemoveObserver(SessionRestoreObserver* observer);
+
+  // Accessor for the observer list. Create the list the first time to always
+  // return a valid reference.
+  static base::ObserverList<SessionRestoreObserver>& observers() {
+    if (!observers_)
+      observers_ = new base::ObserverList<SessionRestoreObserver>();
+    return *observers_;
+  }
+
  private:
   SessionRestore();
 
@@ -120,6 +134,9 @@
   // Contains all registered callbacks for session restore notifications.
   static CallbackList* on_session_restored_callbacks_;
 
+  // Contains all registered observers for session restore events.
+  static base::ObserverList<SessionRestoreObserver>* observers_;
+
   DISALLOW_COPY_AND_ASSIGN(SessionRestore);
 };
 
diff --git a/chrome/browser/sessions/session_restore_browsertest.cc b/chrome/browser/sessions/session_restore_browsertest.cc
index ef42430..596deefb 100644
--- a/chrome/browser/sessions/session_restore_browsertest.cc
+++ b/chrome/browser/sessions/session_restore_browsertest.cc
@@ -106,8 +106,6 @@
       helper.ReleaseService();
     }
 #endif
-
-    InProcessBrowserTest::SetUpOnMainThread();
   }
 
   bool SetUpUserDataDirectory() override {
diff --git a/chrome/browser/sessions/session_restore_observer.h b/chrome/browser/sessions/session_restore_observer.h
new file mode 100644
index 0000000..8d51103
--- /dev/null
+++ b/chrome/browser/sessions/session_restore_observer.h
@@ -0,0 +1,21 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SESSIONS_SESSION_RESTORE_OBSERVER_H_
+#define CHROME_BROWSER_SESSIONS_SESSION_RESTORE_OBSERVER_H_
+
+// Observer of events during session restore.
+class SessionRestoreObserver {
+ public:
+  // OnSessionRestoreStartedLoadingTabs is triggered when the browser starts
+  // loading tabs in session restore.
+  virtual void OnSessionRestoreStartedLoadingTabs() {}
+
+  // OnSessionRestoreFinishedLoadingTabs is triggered when the browser finishes
+  // loading tabs in session restore. In case of memory pressure, not all
+  // WebContents may have been created (i.e., some are deferred).
+  virtual void OnSessionRestoreFinishedLoadingTabs() {}
+};
+
+#endif  // CHROME_BROWSER_SESSIONS_SESSION_RESTORE_OBSERVER_H_
diff --git a/chrome/browser/sessions/session_restore_observer_unittest.cc b/chrome/browser/sessions/session_restore_observer_unittest.cc
new file mode 100644
index 0000000..9d1b39a
--- /dev/null
+++ b/chrome/browser/sessions/session_restore_observer_unittest.cc
@@ -0,0 +1,148 @@
+// 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/sessions/session_restore_observer.h"
+
+#include "chrome/browser/sessions/session_restore.h"
+#include "chrome/browser/sessions/tab_loader.h"
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/web_contents_tester.h"
+
+using content::WebContentsTester;
+
+namespace {
+
+const char kDefaultUrl[] = "https://www.google.com";
+
+}  // namespace
+
+class MockSessionRestoreObserver : public SessionRestoreObserver {
+ public:
+  MockSessionRestoreObserver() { SessionRestore::AddObserver(this); }
+
+  ~MockSessionRestoreObserver() { SessionRestore::RemoveObserver(this); }
+
+  enum class SessionRestoreEvent {
+    STARTED_LOADING_TABS,
+    FINISHED_LOADING_TABS
+  };
+
+  const std::vector<SessionRestoreEvent>& session_restore_events() const {
+    return session_restore_events_;
+  }
+
+  // SessionRestoreObserver implementation:
+  void OnSessionRestoreStartedLoadingTabs() override {
+    session_restore_events_.emplace_back(
+        SessionRestoreEvent::STARTED_LOADING_TABS);
+  }
+
+  void OnSessionRestoreFinishedLoadingTabs() override {
+    session_restore_events_.emplace_back(
+        SessionRestoreEvent::FINISHED_LOADING_TABS);
+  }
+
+ private:
+  std::vector<SessionRestoreEvent> session_restore_events_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockSessionRestoreObserver);
+};
+
+class SessionRestoreObserverTest : public ChromeRenderViewHostTestHarness {
+ public:
+  using RestoredTab = SessionRestoreDelegate::RestoredTab;
+
+  SessionRestoreObserverTest() {}
+
+  // testing::Test:
+  void SetUp() override {
+    ChromeRenderViewHostTestHarness::SetUp();
+    restored_tabs_.emplace_back(web_contents(), false, false, false);
+  }
+
+  void TearDown() override {
+    ChromeRenderViewHostTestHarness::TearDown();
+    restored_tabs_.clear();
+  }
+
+ protected:
+  void RestoreTabs() {
+    TabLoader::RestoreTabs(restored_tabs_, base::TimeTicks());
+  }
+
+  void LoadWebContents(content::WebContents* contents) {
+    WebContentsTester::For(contents)->NavigateAndCommit(GURL(kDefaultUrl));
+    WebContentsTester::For(contents)->TestSetIsLoading(false);
+  }
+
+  const std::vector<MockSessionRestoreObserver::SessionRestoreEvent>&
+  session_restore_events() const {
+    return mock_observer_.session_restore_events();
+  }
+
+  size_t number_of_session_restore_events() const {
+    return session_restore_events().size();
+  }
+
+ private:
+  MockSessionRestoreObserver mock_observer_;
+  std::vector<RestoredTab> restored_tabs_;
+
+  DISALLOW_COPY_AND_ASSIGN(SessionRestoreObserverTest);
+};
+
+TEST_F(SessionRestoreObserverTest, SingleSessionRestore) {
+  RestoreTabs();
+  ASSERT_EQ(1u, number_of_session_restore_events());
+  EXPECT_EQ(
+      MockSessionRestoreObserver::SessionRestoreEvent::STARTED_LOADING_TABS,
+      session_restore_events()[0]);
+
+  LoadWebContents(web_contents());
+  ASSERT_EQ(2u, number_of_session_restore_events());
+  EXPECT_EQ(
+      MockSessionRestoreObserver::SessionRestoreEvent::FINISHED_LOADING_TABS,
+      session_restore_events()[1]);
+}
+
+TEST_F(SessionRestoreObserverTest, SequentialSessionRestore) {
+  const int number_of_session_restores = 3;
+  size_t event_index = 0;
+  for (int i = 0; i < number_of_session_restores; ++i) {
+    RestoreTabs();
+    ASSERT_EQ(event_index + 1, number_of_session_restore_events());
+    EXPECT_EQ(
+        MockSessionRestoreObserver::SessionRestoreEvent::STARTED_LOADING_TABS,
+        session_restore_events()[event_index++]);
+
+    LoadWebContents(web_contents());
+    ASSERT_EQ(event_index + 1, number_of_session_restore_events());
+    EXPECT_EQ(
+        MockSessionRestoreObserver::SessionRestoreEvent::FINISHED_LOADING_TABS,
+        session_restore_events()[event_index++]);
+  }
+}
+
+TEST_F(SessionRestoreObserverTest, ConcurrentSessionRestore) {
+  std::vector<RestoredTab> another_restored_tabs;
+  std::unique_ptr<content::WebContents> test_contents(
+      WebContentsTester::CreateTestWebContents(browser_context(), nullptr));
+  another_restored_tabs.emplace_back(test_contents.get(), false, false, false);
+
+  RestoreTabs();
+  TabLoader::RestoreTabs(another_restored_tabs, base::TimeTicks());
+  ASSERT_EQ(1u, number_of_session_restore_events());
+  EXPECT_EQ(
+      MockSessionRestoreObserver::SessionRestoreEvent::STARTED_LOADING_TABS,
+      session_restore_events()[0]);
+
+  LoadWebContents(web_contents());
+  LoadWebContents(test_contents.get());
+
+  ASSERT_EQ(2u, number_of_session_restore_events());
+  EXPECT_EQ(
+      MockSessionRestoreObserver::SessionRestoreEvent::FINISHED_LOADING_TABS,
+      session_restore_events()[1]);
+}
diff --git a/chrome/browser/sessions/tab_loader.cc b/chrome/browser/sessions/tab_loader.cc
index 753f4e04..80aa058 100644
--- a/chrome/browser/sessions/tab_loader.cc
+++ b/chrome/browser/sessions/tab_loader.cc
@@ -103,6 +103,8 @@
   shared_tab_loader_ = this;
   this_retainer_ = this;
   base::MemoryCoordinatorClientRegistry::GetInstance()->Register(this);
+  for (auto& observer : SessionRestore::observers())
+    observer.OnSessionRestoreStartedLoadingTabs();
 }
 
 TabLoader::~TabLoader() {
@@ -110,6 +112,8 @@
   DCHECK(shared_tab_loader_ == this);
   shared_tab_loader_ = nullptr;
   base::MemoryCoordinatorClientRegistry::GetInstance()->Unregister(this);
+  for (auto& observer : SessionRestore::observers())
+    observer.OnSessionRestoreFinishedLoadingTabs();
 }
 
 void TabLoader::StartLoading(const std::vector<RestoredTab>& tabs) {
diff --git a/chrome/browser/sessions/tab_restore_browsertest.cc b/chrome/browser/sessions/tab_restore_browsertest.cc
index 70fc7a7..7e2dcd4 100644
--- a/chrome/browser/sessions/tab_restore_browsertest.cc
+++ b/chrome/browser/sessions/tab_restore_browsertest.cc
@@ -100,7 +100,6 @@
  protected:
   void SetUpOnMainThread() override {
     active_browser_list_ = BrowserList::GetInstance();
-    InProcessBrowserTest::SetUpOnMainThread();
   }
 
   Browser* GetBrowser(int index) {
diff --git a/chrome/browser/signin/chrome_signin_client.cc b/chrome/browser/signin/chrome_signin_client.cc
index 11ea969..161c030 100644
--- a/chrome/browser/signin/chrome_signin_client.cc
+++ b/chrome/browser/signin/chrome_signin_client.cc
@@ -273,19 +273,37 @@
     const base::Callback<void()>& sign_out,
     signin_metrics::ProfileSignout signout_source_metric) {
 #if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
-  if (signin_util::IsForceSigninEnabled() && !profile_->IsSystemProfile() &&
-      !profile_->IsGuestSession() && !profile_->IsSupervised()) {
-    // TODO(zmin): force window closing based on the reason of sign-out.
-    // This will be updated after force window closing CL is commited.
 
-    // User can't abort the window closing unless user sign out manually.
-    BrowserList::CloseAllBrowsersWithProfile(
-        profile_,
-        base::Bind(&ChromeSigninClient::OnCloseBrowsersSuccess,
-                   base::Unretained(this), sign_out, signout_source_metric),
-        base::Bind(&ChromeSigninClient::OnCloseBrowsersAborted,
-                   base::Unretained(this)),
-        false);
+  // These sign out won't remove the policy cache, keep the window opened.
+  bool keep_window_opened =
+      signout_source_metric ==
+          signin_metrics::GOOGLE_SERVICE_NAME_PATTERN_CHANGED ||
+      signout_source_metric == signin_metrics::SERVER_FORCED_DISABLE ||
+      signout_source_metric == signin_metrics::SIGNOUT_PREF_CHANGED;
+  if (signin_util::IsForceSigninEnabled() && !profile_->IsSystemProfile() &&
+      !profile_->IsGuestSession() && !profile_->IsSupervised() &&
+      !keep_window_opened) {
+    if (signout_source_metric ==
+        signin_metrics::SIGNIN_PREF_CHANGED_DURING_SIGNIN) {
+      // SIGNIN_PREF_CHANGED_DURING_SIGNIN will be triggered when SigninManager
+      // is initialized before window opening, there is no need to close window.
+      // Call OnCloseBrowsersSuccess to continue sign out and show UserManager
+      // afterwards.
+      should_display_user_manager_ = false;  // Don't show UserManager twice.
+      OnCloseBrowsersSuccess(sign_out, signout_source_metric,
+                             profile_->GetPath());
+    } else {
+      BrowserList::CloseAllBrowsersWithProfile(
+          profile_,
+          base::Bind(&ChromeSigninClient::OnCloseBrowsersSuccess,
+                     base::Unretained(this), sign_out, signout_source_metric),
+          base::Bind(&ChromeSigninClient::OnCloseBrowsersAborted,
+                     base::Unretained(this)),
+          signout_source_metric == signin_metrics::ABORT_SIGNIN ||
+              signout_source_metric ==
+                  signin_metrics::AUTHENTICATION_FAILED_WITH_FORCE_SIGNIN ||
+              signout_source_metric == signin_metrics::TRANSFER_CREDENTIALS);
+    }
   } else {
 #else
   {
@@ -441,8 +459,10 @@
     const signin_metrics::ProfileSignout signout_source_metric,
     const base::FilePath& profile_path) {
 #if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
-  if (signin_util::IsForceSigninEnabled() && force_signin_verifier_.get())
+  if (signin_util::IsForceSigninEnabled() && force_signin_verifier_.get()) {
     force_signin_verifier_->Cancel();
+    force_signin_verifier_->AbortSignoutCountdownIfExisted();
+  }
 #endif
   SigninClient::PreSignOut(sign_out, signout_source_metric);
 
diff --git a/chrome/browser/signin/chrome_signin_client_unittest.cc b/chrome/browser/signin/chrome_signin_client_unittest.cc
index 16d57bb..704c7b7 100644
--- a/chrome/browser/signin/chrome_signin_client_unittest.cc
+++ b/chrome/browser/signin/chrome_signin_client_unittest.cc
@@ -175,7 +175,7 @@
 
 TEST_F(ChromeSigninClientSignoutTest, SignOut) {
   signin_metrics::ProfileSignout source_metric =
-      signin_metrics::ProfileSignout::SIGNOUT_TEST;
+      signin_metrics::ProfileSignout::ABORT_SIGNIN;
   signin_metrics::SignoutDelete delete_metric =
       signin_metrics::SignoutDelete::IGNORE_METRIC;
 
@@ -190,7 +190,7 @@
 
 TEST_F(ChromeSigninClientSignoutTest, SignOutWithoutManager) {
   signin_metrics::ProfileSignout source_metric =
-      signin_metrics::ProfileSignout::SIGNOUT_TEST;
+      signin_metrics::ProfileSignout::ABORT_SIGNIN;
   signin_metrics::SignoutDelete delete_metric =
       signin_metrics::SignoutDelete::IGNORE_METRIC;
 
@@ -220,7 +220,7 @@
   manager_.reset(new MockSigninManager(client_.get()));
 
   signin_metrics::ProfileSignout source_metric =
-      signin_metrics::ProfileSignout::SIGNOUT_TEST;
+      signin_metrics::ProfileSignout::ABORT_SIGNIN;
   signin_metrics::SignoutDelete delete_metric =
       signin_metrics::SignoutDelete::IGNORE_METRIC;
 
@@ -241,7 +241,7 @@
   manager_.reset(new MockSigninManager(client_.get()));
 
   signin_metrics::ProfileSignout source_metric =
-      signin_metrics::ProfileSignout::SIGNOUT_TEST;
+      signin_metrics::ProfileSignout::ABORT_SIGNIN;
   signin_metrics::SignoutDelete delete_metric =
       signin_metrics::SignoutDelete::IGNORE_METRIC;
 
diff --git a/chrome/browser/signin/force_signin_verifier.cc b/chrome/browser/signin/force_signin_verifier.cc
index 9afd378..0311d85 100644
--- a/chrome/browser/signin/force_signin_verifier.cc
+++ b/chrome/browser/signin/force_signin_verifier.cc
@@ -4,10 +4,12 @@
 
 #include <string>
 
+#include "base/bind.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/force_signin_verifier.h"
 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
 #include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/browser/ui/forced_reauthentication_dialog.h"
 #include "components/signin/core/browser/signin_manager.h"
 #include "google_apis/gaia/gaia_constants.h"
 
@@ -23,10 +25,22 @@
     false           // Do not always use initial delay.
 };
 
+// The duration of window closing countdown when verification failed. Use the
+// short countdown if the verfication is finished in
+// |kShortCountdownVerificationTimeLimitInSeconds|, otherwise use the normal
+// countdown.
+const int kShortCountdownVerificationTimeLimitInSeconds = 3;
+const int kWindowClosingNormalCountdownDurationInSecond = 300;
+const int kWindowClosingShortCountdownDurationInSecond = 30;
+
 }  // namespace
 
 ForceSigninVerifier::ForceSigninVerifier(Profile* profile)
     : OAuth2TokenService::Consumer("force_signin_verifier"),
+#if !defined(OS_MACOSX)
+      profile_(profile),
+      dialog_(ForcedReauthenticationDialog::Create()),
+#endif
       has_token_verified_(false),
       backoff_entry_(&kBackoffPolicy),
       oauth2_token_service_(
@@ -90,6 +104,10 @@
   return has_token_verified_;
 }
 
+void ForceSigninVerifier::AbortSignoutCountdownIfExisted() {
+  window_close_timer_.Stop();
+}
+
 void ForceSigninVerifier::SendRequest() {
   if (!ShouldSendRequest())
     return;
@@ -107,8 +125,38 @@
          signin_manager_->IsAuthenticated();
 }
 
+base::TimeDelta ForceSigninVerifier::StartCountdown() {
+  base::TimeDelta countdown_duration;
+  if (base::Time::Now() - token_request_time_ >
+      base::TimeDelta::FromSeconds(
+          kShortCountdownVerificationTimeLimitInSeconds)) {
+    countdown_duration = base::TimeDelta::FromSeconds(
+        kWindowClosingNormalCountdownDurationInSecond);
+  } else {
+    countdown_duration = base::TimeDelta::FromSeconds(
+        kWindowClosingShortCountdownDurationInSecond);
+  }
+
+  window_close_timer_.Start(FROM_HERE, countdown_duration, this,
+                            &ForceSigninVerifier::CloseAllBrowserWindows);
+  return countdown_duration;
+}
+
 void ForceSigninVerifier::ShowDialog() {
-  // TODO(zmin): Show app modal dialog.
+#if !defined(OS_MACOSX)
+  base::TimeDelta countdown_duration = StartCountdown();
+  dialog_->ShowDialog(profile_, signin_manager_, countdown_duration);
+#endif
+}
+
+void ForceSigninVerifier::CloseAllBrowserWindows() {
+  // Do not close window if there is ongoing reauth. If it fails later, the
+  // signin process should take care of the signout.
+  if (signin_manager_->AuthInProgress())
+    return;
+  signin_manager_->SignOut(
+      signin_metrics::AUTHENTICATION_FAILED_WITH_FORCE_SIGNIN,
+      signin_metrics::SignoutDelete::IGNORE_METRIC);
 }
 
 OAuth2TokenService::Request* ForceSigninVerifier::GetRequestForTesting() {
@@ -122,3 +170,7 @@
 base::OneShotTimer* ForceSigninVerifier::GetOneShotTimerForTesting() {
   return &backoff_request_timer_;
 }
+
+base::OneShotTimer* ForceSigninVerifier::GetWindowCloseTimerForTesting() {
+  return &window_close_timer_;
+}
diff --git a/chrome/browser/signin/force_signin_verifier.h b/chrome/browser/signin/force_signin_verifier.h
index 03548c03..5106bee 100644
--- a/chrome/browser/signin/force_signin_verifier.h
+++ b/chrome/browser/signin/force_signin_verifier.h
@@ -15,6 +15,7 @@
 #include "net/base/backoff_entry.h"
 #include "net/base/network_change_notifier.h"
 
+class ForcedReauthenticationDialog;
 class Profile;
 class SigninManager;
 
@@ -28,14 +29,14 @@
   explicit ForceSigninVerifier(Profile* profile);
   ~ForceSigninVerifier() override;
 
-  // OAuth2TokenService::Consumer implementation
+  // override OAuth2TokenService::Consumer
   void OnGetTokenSuccess(const OAuth2TokenService::Request* request,
                          const std::string& access_token,
                          const base::Time& expiration_time) override;
   void OnGetTokenFailure(const OAuth2TokenService::Request* request,
                          const GoogleServiceAuthError& error) override;
 
-  // net::NetworkChangeNotifier::NetworkChangeObserver
+  // override net::NetworkChangeNotifier::NetworkChangeObserver
   void OnNetworkChanged(
       net::NetworkChangeNotifier::ConnectionType type) override;
 
@@ -45,6 +46,9 @@
   // Return the value of |has_token_verified_|.
   bool HasTokenBeenVerified();
 
+  // Abort signout countdown.
+  void AbortSignoutCountdownIfExisted();
+
  protected:
   // Send the token verification request. The request will be sent only if
   //   - The token has never been verified before.
@@ -60,13 +64,25 @@
   // browser window.
   virtual void ShowDialog();
 
+  // Start the window closing countdown, return the duration.
+  base::TimeDelta StartCountdown();
+
   OAuth2TokenService::Request* GetRequestForTesting();
   net::BackoffEntry* GetBackoffEntryForTesting();
   base::OneShotTimer* GetOneShotTimerForTesting();
+  base::OneShotTimer* GetWindowCloseTimerForTesting();
 
  private:
+  void CloseAllBrowserWindows();
+
   std::unique_ptr<OAuth2TokenService::Request> access_token_request_;
 
+#if !defined(OS_MACOSX)
+  Profile* profile_;
+
+  std::unique_ptr<ForcedReauthenticationDialog> dialog_;
+#endif
+
   // Indicates whether the verification is finished successfully or with a
   // persistent error.
   bool has_token_verified_;
@@ -78,6 +94,9 @@
 
   base::Time token_request_time_;
 
+  // The countdown of window closing.
+  base::OneShotTimer window_close_timer_;
+
   DISALLOW_COPY_AND_ASSIGN(ForceSigninVerifier);
 };
 
diff --git a/chrome/browser/signin/force_signin_verifier_unittest.cc b/chrome/browser/signin/force_signin_verifier_unittest.cc
index d5f2f555..23cf4f1a 100644
--- a/chrome/browser/signin/force_signin_verifier_unittest.cc
+++ b/chrome/browser/signin/force_signin_verifier_unittest.cc
@@ -24,6 +24,13 @@
 
   OAuth2TokenService::Request* request() { return GetRequestForTesting(); }
 
+  bool IsCountdownTimerRunning() {
+    base::Timer* timer = GetWindowCloseTimerForTesting();
+    return timer && timer->IsRunning();
+  }
+
+  void OnShowDialog() { StartCountdown(); }
+
   MOCK_METHOD0(ShowDialog, void(void));
 };
 
@@ -124,3 +131,16 @@
   ASSERT_NE(nullptr, verifier_->request());
   ASSERT_FALSE(verifier_->IsDelayTaskPosted());
 }
+
+TEST_F(ForceSigninVerifierTest, OnGetTokenPersistentFailureAndStartCountdown) {
+  ASSERT_EQ(nullptr, verifier_->request());
+  ASSERT_FALSE(verifier_->IsCountdownTimerRunning());
+  EXPECT_CALL(*verifier_.get(), ShowDialog())
+      .WillOnce(::testing::Invoke(verifier_.get(),
+                                  &MockForceSigninVerifier::OnShowDialog));
+
+  verifier_->SendTestRequest();
+  verifier_->OnGetTokenFailure(verifier_->request(), persistent_error_);
+
+  ASSERT_TRUE(verifier_->IsCountdownTimerRunning());
+}
diff --git a/chrome/browser/translate/translate_browsertest.cc b/chrome/browser/translate/translate_browsertest.cc
index d8f5f33..503193d9 100644
--- a/chrome/browser/translate/translate_browsertest.cc
+++ b/chrome/browser/translate/translate_browsertest.cc
@@ -70,7 +70,6 @@
     net::EmbeddedTestServer* test_server = embedded_test_server();
     test_server->ServeFilesFromSourceDirectory(kTranslateRoot);
     ASSERT_TRUE(test_server->Start());
-    InProcessBrowserTest::SetUpOnMainThread();
   }
 
  protected:
diff --git a/chrome/browser/translate/translate_manager_browsertest.cc b/chrome/browser/translate/translate_manager_browsertest.cc
index 28215907..a76421f 100644
--- a/chrome/browser/translate/translate_manager_browsertest.cc
+++ b/chrome/browser/translate/translate_manager_browsertest.cc
@@ -82,7 +82,6 @@
   }
   void SetUpOnMainThread() override {
     ResetObserver();
-    InProcessBrowserTest::SetUpOnMainThread();
   }
 
  private:
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index bcf223ed..7d94805 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -112,6 +112,7 @@
     "find_bar/find_notification_details.h",
     "find_bar/find_tab_helper.cc",
     "find_bar/find_tab_helper.h",
+    "forced_reauthentication_dialog.h",
     "history_ui.cc",
     "history_ui.h",
     "javascript_dialogs/chrome_javascript_native_dialog_factory.h",
@@ -1656,8 +1657,8 @@
         "views/frame/opaque_browser_frame_view_linux.h",
         "views/frame/opaque_browser_frame_view_platform_specific.cc",
         "views/frame/opaque_browser_frame_view_platform_specific.h",
-        "views/profiles/forced_reauthentication_dialog.cc",
-        "views/profiles/forced_reauthentication_dialog.h",
+        "views/profiles/forced_reauthentication_dialog_view.cc",
+        "views/profiles/forced_reauthentication_dialog_view.h",
         "views/profiles/profile_chooser_view.cc",
         "views/profiles/profile_chooser_view.h",
         "views/screen_capture_notification_ui_views.cc",
diff --git a/chrome/browser/ui/app_list/speech_recognizer_browsertest.cc b/chrome/browser/ui/app_list/speech_recognizer_browsertest.cc
index 8429e95e..a3913944 100644
--- a/chrome/browser/ui/app_list/speech_recognizer_browsertest.cc
+++ b/chrome/browser/ui/app_list/speech_recognizer_browsertest.cc
@@ -49,8 +49,6 @@
   AppListSpeechRecognizerBrowserTest() {}
 
   void SetUpOnMainThread() override {
-    InProcessBrowserTest::SetUpOnMainThread();
-
     fake_speech_recognition_manager_.reset(
         new content::FakeSpeechRecognitionManager());
     fake_speech_recognition_manager_->set_should_send_fake_response(true);
diff --git a/chrome/browser/ui/ash/ime_controller_client.cc b/chrome/browser/ui/ash/ime_controller_client.cc
index 18ccedbf..cd34faf 100644
--- a/chrome/browser/ui/ash/ime_controller_client.cc
+++ b/chrome/browser/ui/ash/ime_controller_client.cc
@@ -75,15 +75,24 @@
 
 // ash::mojom::ImeControllerClient:
 void ImeControllerClient::SwitchToNextIme() {
-  NOTIMPLEMENTED();
+  InputMethodManager::State* state =
+      input_method_manager_->GetActiveIMEState().get();
+  if (state)
+    state->SwitchToNextInputMethod();
 }
 
 void ImeControllerClient::SwitchToPreviousIme() {
-  NOTIMPLEMENTED();
+  InputMethodManager::State* state =
+      input_method_manager_->GetActiveIMEState().get();
+  if (state)
+    state->SwitchToPreviousInputMethod();
 }
 
 void ImeControllerClient::SwitchImeById(const std::string& id) {
-  NOTIMPLEMENTED();
+  InputMethodManager::State* state =
+      input_method_manager_->GetActiveIMEState().get();
+  if (state)
+    state->ChangeInputMethod(id, true /* show_message */);
 }
 
 void ImeControllerClient::ActivateImeProperty(const std::string& key) {
diff --git a/chrome/browser/ui/ash/ime_controller_client_unittest.cc b/chrome/browser/ui/ash/ime_controller_client_unittest.cc
index 457756fb..e4d74e4 100644
--- a/chrome/browser/ui/ash/ime_controller_client_unittest.cc
+++ b/chrome/browser/ui/ash/ime_controller_client_unittest.cc
@@ -59,6 +59,11 @@
     }
 
     // MockInputMethodManager::State:
+    void ChangeInputMethod(const std::string& input_method_id,
+                           bool show_message) override {
+      ++change_input_method_count_;
+      current_ime_id_ = input_method_id;
+    }
     std::unique_ptr<std::vector<InputMethodDescriptor>> GetActiveInputMethods()
         const override {
       return base::MakeUnique<std::vector<InputMethodDescriptor>>(
@@ -79,9 +84,16 @@
       }
       return InputMethodUtil::GetFallbackInputMethodDescriptor();
     }
+    void SwitchToNextInputMethod() override { ++next_input_method_count_; }
+    void SwitchToPreviousInputMethod() override {
+      ++previous_input_method_count_;
+    }
 
     std::string current_ime_id_;
     std::vector<InputMethodDescriptor> input_methods_;
+    int next_input_method_count_ = 0;
+    int previous_input_method_count_ = 0;
+    int change_input_method_count_ = 0;
 
    protected:
     friend base::RefCounted<InputMethodManager::State>;
@@ -271,4 +283,23 @@
   EXPECT_FALSE(ime_controller_.menu_items_[1]->checked);
 }
 
+TEST_F(ImeControllerClientTest, SwitchToNextIme) {
+  ImeControllerClient client(&input_method_manager_);
+  client.SwitchToNextIme();
+  EXPECT_EQ(1, input_method_manager_.state_->next_input_method_count_);
+}
+
+TEST_F(ImeControllerClientTest, SwitchToPreviousIme) {
+  ImeControllerClient client(&input_method_manager_);
+  client.SwitchToPreviousIme();
+  EXPECT_EQ(1, input_method_manager_.state_->previous_input_method_count_);
+}
+
+TEST_F(ImeControllerClientTest, SwitchImeById) {
+  ImeControllerClient client(&input_method_manager_);
+  client.SwitchImeById("ime2");
+  EXPECT_EQ(1, input_method_manager_.state_->change_input_method_count_);
+  EXPECT_EQ("ime2", input_method_manager_.state_->current_ime_id_);
+}
+
 }  // namespace
diff --git a/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc b/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc
index 2a9fd4f..2c4d4c28 100644
--- a/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc
+++ b/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc
@@ -116,8 +116,6 @@
   ~PopupBlockerBrowserTest() override {}
 
   void SetUpOnMainThread() override {
-    InProcessBrowserTest::SetUpOnMainThread();
-
     host_resolver()->AddRule("*", "127.0.0.1");
     ASSERT_TRUE(embedded_test_server()->Start());
   }
diff --git a/chrome/browser/ui/cocoa/applescript/bookmark_applescript_utils_test.mm b/chrome/browser/ui/cocoa/applescript/bookmark_applescript_utils_test.mm
index 975601d..231c39b 100644
--- a/chrome/browser/ui/cocoa/applescript/bookmark_applescript_utils_test.mm
+++ b/chrome/browser/ui/cocoa/applescript/bookmark_applescript_utils_test.mm
@@ -49,7 +49,6 @@
 }
 
 void BookmarkAppleScriptTest::SetUpOnMainThread() {
-  InProcessBrowserTest::SetUpOnMainThread();
   ASSERT_TRUE(profile());
 
   BookmarkModel* model = BookmarkModelFactory::GetForBrowserContext(profile());
diff --git a/chrome/browser/ui/cocoa/notifications/notification_service_delegate.h b/chrome/browser/ui/cocoa/notifications/notification_service_delegate.h
index 38ce5296..ad12f31 100644
--- a/chrome/browser/ui/cocoa/notifications/notification_service_delegate.h
+++ b/chrome/browser/ui/cocoa/notifications/notification_service_delegate.h
@@ -9,10 +9,6 @@
 
 @interface ServiceDelegate
     : NSObject<NSXPCListenerDelegate, NSUserNotificationCenterDelegate>
-
-// The connection used to talk back to Chrome.
-@property(assign, nonatomic, nullable) NSXPCConnection* connection;
-
 @end
 
 #endif  // CHROME_BROWSER_UI_COCOA_NOTIFICATIONS_NOTIFICATION_SERVICE_DELEGATE_H_
diff --git a/chrome/browser/ui/cocoa/notifications/notification_service_delegate.mm b/chrome/browser/ui/cocoa/notifications/notification_service_delegate.mm
index b5e9cc9..0d529ae 100644
--- a/chrome/browser/ui/cocoa/notifications/notification_service_delegate.mm
+++ b/chrome/browser/ui/cocoa/notifications/notification_service_delegate.mm
@@ -15,10 +15,14 @@
 @class NSUserNotificationCenter;
 
 @implementation ServiceDelegate {
+  // Helper to manage the XPC transaction reference count with respect to
+  // still-visible notifications.
   base::scoped_nsobject<XPCTransactionHandler> transactionHandler_;
-}
 
-@synthesize connection = connection_;
+  // Client connection accepted from the browser process, to which messages
+  // are sent in response to notification actions.
+  base::scoped_nsobject<NSXPCConnection> connection_;
+}
 
 - (instancetype)init {
   if ((self = [super init])) {
@@ -52,7 +56,7 @@
   newConnection.exportedObject = object.get();
   newConnection.remoteObjectInterface =
       [NSXPCInterface interfaceWithProtocol:@protocol(NotificationReply)];
-  connection_ = newConnection;
+  connection_.reset(newConnection, base::scoped_policy::RETAIN);
   [newConnection resume];
 
   return YES;
diff --git a/chrome/browser/ui/forced_reauthentication_dialog.h b/chrome/browser/ui/forced_reauthentication_dialog.h
new file mode 100644
index 0000000..edcb757c
--- /dev/null
+++ b/chrome/browser/ui/forced_reauthentication_dialog.h
@@ -0,0 +1,41 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_FORCED_REAUTHENTICATION_DIALOG_H_
+#define CHROME_BROWSER_UI_FORCED_REAUTHENTICATION_DIALOG_H_
+
+#include <memory>
+
+#include "base/macros.h"
+
+class Profile;
+class SigninManager;
+
+namespace base {
+class TimeDelta;
+}  // namespace base
+
+// The virtual class of ForcedReauthenticationDialog.
+class ForcedReauthenticationDialog {
+ public:
+  static std::unique_ptr<ForcedReauthenticationDialog> Create();
+
+  virtual ~ForcedReauthenticationDialog() {}
+  // Show the ForcedReauthenticationDialog for |profile|. If there're no opened
+  // browser windows for |profile|, |signin_manager| will be called to signed
+  // out immediately. Otherwise, dialog will be closed with all browser windows
+  // are assoicated to |profile| after |countdown_duration| if there is no
+  // reauth.
+  virtual void ShowDialog(Profile* profile,
+                          SigninManager* signin_manager,
+                          base::TimeDelta countdown_duration) = 0;
+
+ protected:
+  ForcedReauthenticationDialog() {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ForcedReauthenticationDialog);
+};
+
+#endif  // CHROME_BROWSER_UI_FORCED_REAUTHENTICATION_DIALOG_H_
diff --git a/chrome/browser/ui/toolbar/component_toolbar_actions_browsertest.cc b/chrome/browser/ui/toolbar/component_toolbar_actions_browsertest.cc
index b3f00cb0..66d9de0 100644
--- a/chrome/browser/ui/toolbar/component_toolbar_actions_browsertest.cc
+++ b/chrome/browser/ui/toolbar/component_toolbar_actions_browsertest.cc
@@ -21,8 +21,6 @@
   ~ComponentToolbarActionsBrowserTest() override {}
 
   void SetUpOnMainThread() override {
-    InProcessBrowserTest::SetUpOnMainThread();
-
     // Replace the actions factory with a mock one.
     toolbar_model_ = ToolbarActionsModel::Get(browser()->profile());
     toolbar_model_->SetMockActionsFactoryForTest(
diff --git a/chrome/browser/ui/views/arc_app_dialog_view_browsertest.cc b/chrome/browser/ui/views/arc_app_dialog_view_browsertest.cc
index 2df9319..5c0e839 100644
--- a/chrome/browser/ui/views/arc_app_dialog_view_browsertest.cc
+++ b/chrome/browser/ui/views/arc_app_dialog_view_browsertest.cc
@@ -44,7 +44,6 @@
   }
 
   void SetUpOnMainThread() override {
-    InProcessBrowserTest::SetUpOnMainThread();
     profile_ = browser()->profile();
     arc_app_list_pref_ = ArcAppListPrefs::Get(profile_);
     if (!arc_app_list_pref_) {
diff --git a/chrome/browser/ui/views/media_router/media_router_ui_browsertest.cc b/chrome/browser/ui/views/media_router/media_router_ui_browsertest.cc
index 0de5911..0a5081c 100644
--- a/chrome/browser/ui/views/media_router/media_router_ui_browsertest.cc
+++ b/chrome/browser/ui/views/media_router/media_router_ui_browsertest.cc
@@ -49,8 +49,6 @@
   ~MediaRouterUIBrowserTest() override {}
 
   void SetUpOnMainThread() override {
-    InProcessBrowserTest::SetUpOnMainThread();
-
     BrowserActionsContainer* browser_actions_container =
         BrowserView::GetBrowserViewForBrowser(browser())
             ->toolbar()
diff --git a/chrome/browser/ui/views/profiles/forced_reauthentication_dialog.h b/chrome/browser/ui/views/profiles/forced_reauthentication_dialog.h
deleted file mode 100644
index 3e377d10..0000000
--- a/chrome/browser/ui/views/profiles/forced_reauthentication_dialog.h
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_VIEWS_PROFILES_FORCED_REAUTHENTICATION_DIALOG_H_
-#define CHROME_BROWSER_UI_VIEWS_PROFILES_FORCED_REAUTHENTICATION_DIALOG_H_
-
-#include <memory>
-#include <string>
-
-#include "base/callback.h"
-#include "base/macros.h"
-#include "base/timer/timer.h"
-#include "ui/views/controls/button/button.h"
-#include "ui/views/window/dialog_delegate.h"
-
-class Browser;
-class Profile;
-class SigninManager;
-
-// A modal dialog that displays a warning message of the auth failure
-// and ask user to sign in again.
-class ForcedReauthenticationDialog : public views::DialogDelegateView {
- public:
-  ~ForcedReauthenticationDialog() override;
-
-  // Shows a warning dialog for |profile|. If there are no Browser windows
-  // associated with |profile|, signs out the profile immediately, otherwise the
-  // user can clicks accept to sign in again. Dialog will be closed after
-  // |countdown_duration| seconds.
-  // Dialog will delete itself after closing.
-  static ForcedReauthenticationDialog* ShowDialog(
-      Profile* profile,
-      SigninManager* signin_manager,
-      const base::TimeDelta& countdown_duration);
-
-  // override views::DialogDelegateView
-  bool Accept() override;
-  bool Cancel() override;
-  void WindowClosing() override;
-  base::string16 GetWindowTitle() const override;
-  base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
-  ui::ModalType GetModalType() const override;
-
-  // override views::View
-  void AddedToWidget() override;
-
- private:
-  // Show the dialog for |browser|. The dialog will delete itself after closing.
-  ForcedReauthenticationDialog(Browser* browser,
-                               SigninManager* signin_manager,
-                               const base::TimeDelta& countdown_duration);
-
-  void OnCountDown();
-  base::TimeDelta GetTimeRemaining() const;
-
-  Browser* browser_;
-  SigninManager* signin_manager_;
-
-  const base::TimeTicks desired_close_time_;
-
-  // The timer which is used to refresh the dialog title to display the
-  // remaining time.
-  base::RepeatingTimer refresh_timer_;
-
-  DISALLOW_COPY_AND_ASSIGN(ForcedReauthenticationDialog);
-};
-
-#endif  // CHROME_BROWSER_UI_VIEWS_PROFILES_FORCED_REAUTHENTICATION_DIALOG_H_
diff --git a/chrome/browser/ui/views/profiles/forced_reauthentication_dialog.cc b/chrome/browser/ui/views/profiles/forced_reauthentication_dialog_view.cc
similarity index 80%
rename from chrome/browser/ui/views/profiles/forced_reauthentication_dialog.cc
rename to chrome/browser/ui/views/profiles/forced_reauthentication_dialog_view.cc
index d02b926..997d433 100644
--- a/chrome/browser/ui/views/profiles/forced_reauthentication_dialog.cc
+++ b/chrome/browser/ui/views/profiles/forced_reauthentication_dialog_view.cc
@@ -2,8 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/views/profiles/forced_reauthentication_dialog.h"
+#include "chrome/browser/ui/views/profiles/forced_reauthentication_dialog_view.h"
 
+#include <map>
 #include <memory>
 #include <string>
 #include <utility>
@@ -82,13 +83,16 @@
 
 }  // namespace
 
-ForcedReauthenticationDialog::ForcedReauthenticationDialog(
+// ForcedReauthenticationDialogView
+
+ForcedReauthenticationDialogView::ForcedReauthenticationDialogView(
     Browser* browser,
     SigninManager* signin_manager,
-    const base::TimeDelta& countdown_duration)
+    base::TimeDelta countdown_duration)
     : browser_(browser),
       signin_manager_(signin_manager),
-      desired_close_time_(base::TimeTicks::Now() + countdown_duration) {
+      desired_close_time_(base::TimeTicks::Now() + countdown_duration),
+      weak_factory_(this) {
   constrained_window::CreateBrowserModalDialogViews(
       this, browser->window()->GetNativeWindow())
       ->Show();
@@ -96,13 +100,13 @@
   browser->window()->Activate();
 }
 
-ForcedReauthenticationDialog::~ForcedReauthenticationDialog() {}
+ForcedReauthenticationDialogView::~ForcedReauthenticationDialogView() {}
 
 // static
-ForcedReauthenticationDialog* ForcedReauthenticationDialog::ShowDialog(
+ForcedReauthenticationDialogView* ForcedReauthenticationDialogView::ShowDialog(
     Profile* profile,
     SigninManager* signin_manager,
-    const base::TimeDelta& countdown_duration) {
+    base::TimeDelta countdown_duration) {
   Browser* browser = FindBrowserWithProfile(profile);
   if (browser == nullptr) {  // If there is no browser, we can just sign
                              // out profile directly.
@@ -110,11 +114,11 @@
     return nullptr;
   }
 
-  return new ForcedReauthenticationDialog(browser, signin_manager,
-                                          countdown_duration);
+  return new ForcedReauthenticationDialogView(browser, signin_manager,
+                                              countdown_duration);
 }
 
-bool ForcedReauthenticationDialog::Accept() {
+bool ForcedReauthenticationDialogView::Accept() {
   if (GetTimeRemaining() < base::TimeDelta::FromSeconds(kCloseDirectlyTimer)) {
     Signout(signin_manager_);
   } else {
@@ -125,22 +129,22 @@
   return true;
 }
 
-bool ForcedReauthenticationDialog::Cancel() {
+bool ForcedReauthenticationDialogView::Cancel() {
   return true;
 }
 
-void ForcedReauthenticationDialog::WindowClosing() {
+void ForcedReauthenticationDialogView::WindowClosing() {
   refresh_timer_.Stop();
 }
 
-base::string16 ForcedReauthenticationDialog::GetWindowTitle() const {
+base::string16 ForcedReauthenticationDialogView::GetWindowTitle() const {
   base::TimeDelta time_left = GetTimeRemaining();
   return base::i18n::MessageFormatter::FormatWithNumberedArgs(
       l10n_util::GetStringUTF16(IDS_ENTERPRISE_FORCE_SIGNOUT_TITLE),
       time_left.InMinutes(), time_left.InSeconds() % 60);
 }
 
-base::string16 ForcedReauthenticationDialog::GetDialogButtonLabel(
+base::string16 ForcedReauthenticationDialogView::GetDialogButtonLabel(
     ui::DialogButton button) const {
   if (button == ui::DIALOG_BUTTON_OK) {
     return l10n_util::GetStringUTF16(
@@ -149,11 +153,11 @@
   return l10n_util::GetStringUTF16(IDS_ENTERPRISE_FORCE_SIGNOUT_CLOSE_DELAY);
 }
 
-ui::ModalType ForcedReauthenticationDialog::GetModalType() const {
+ui::ModalType ForcedReauthenticationDialogView::GetModalType() const {
   return ui::MODAL_TYPE_WINDOW;
 }
 
-void ForcedReauthenticationDialog::AddedToWidget() {
+void ForcedReauthenticationDialogView::AddedToWidget() {
   const SkColor prompt_bar_background_color =
       GetSigninConfirmationPromptBarColor(
           GetNativeTheme(), ui::kSigninConfirmationPromptBarBackgroundAlpha);
@@ -231,10 +235,14 @@
                          explanation_label->GetHeightForWidth(kPreferredWidth));
   refresh_timer_.Start(FROM_HERE,
                        base::TimeDelta::FromSeconds(kRefreshTitleTimer), this,
-                       &ForcedReauthenticationDialog::OnCountDown);
+                       &ForcedReauthenticationDialogView::OnCountDown);
 }
 
-void ForcedReauthenticationDialog::OnCountDown() {
+void ForcedReauthenticationDialogView::CloseDialog() {
+  GetWidget()->Close();
+}
+
+void ForcedReauthenticationDialogView::OnCountDown() {
   if (desired_close_time_ <= base::TimeTicks::Now()) {
     Cancel();
     GetWidget()->Close();
@@ -242,9 +250,34 @@
   GetWidget()->UpdateWindowTitle();
 }
 
-base::TimeDelta ForcedReauthenticationDialog::GetTimeRemaining() const {
+base::TimeDelta ForcedReauthenticationDialogView::GetTimeRemaining() const {
   base::TimeTicks now = base::TimeTicks::Now();
   if (desired_close_time_ <= now)
     return base::TimeDelta();
   return desired_close_time_ - now;
 }
+
+// ForcedReauthenticationDialogImpl
+
+ForcedReauthenticationDialogImpl::ForcedReauthenticationDialogImpl() {}
+ForcedReauthenticationDialogImpl::~ForcedReauthenticationDialogImpl() {
+  if (dialog_view_)
+    dialog_view_->CloseDialog();
+}
+
+void ForcedReauthenticationDialogImpl::ShowDialog(
+    Profile* profile,
+    SigninManager* signin_manager,
+    base::TimeDelta countdown_duration) {
+  dialog_view_ = ForcedReauthenticationDialogView::ShowDialog(
+                     profile, signin_manager, countdown_duration)
+                     ->AsWeakPtr();
+}
+
+// ForcedReauthenticationDialog
+
+// static
+std::unique_ptr<ForcedReauthenticationDialog>
+ForcedReauthenticationDialog::Create() {
+  return base::MakeUnique<ForcedReauthenticationDialogImpl>();
+}
diff --git a/chrome/browser/ui/views/profiles/forced_reauthentication_dialog_view.h b/chrome/browser/ui/views/profiles/forced_reauthentication_dialog_view.h
new file mode 100644
index 0000000..9530c953
--- /dev/null
+++ b/chrome/browser/ui/views/profiles/forced_reauthentication_dialog_view.h
@@ -0,0 +1,94 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_PROFILES_FORCED_REAUTHENTICATION_DIALOG_VIEW_H_
+#define CHROME_BROWSER_UI_VIEWS_PROFILES_FORCED_REAUTHENTICATION_DIALOG_VIEW_H_
+
+#include <memory>
+#include <string>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/timer/timer.h"
+#include "chrome/browser/ui/forced_reauthentication_dialog.h"
+#include "ui/views/controls/button/button.h"
+#include "ui/views/window/dialog_delegate.h"
+
+class Browser;
+class Profile;
+class SigninManager;
+
+// A modal dialog that displays a warning message of the auth failure
+// and ask user to sign in again.
+class ForcedReauthenticationDialogView : public views::DialogDelegateView {
+ public:
+  ~ForcedReauthenticationDialogView() override;
+
+  // Shows a warning dialog for |profile|. If there are no Browser windows
+  // associated with |profile|, signs out the profile immediately, otherwise the
+  // user can clicks accept to sign in again. Dialog will be closed after
+  // |countdown_duration| seconds.
+  // Dialog will delete itself after closing.
+  static ForcedReauthenticationDialogView* ShowDialog(
+      Profile* profile,
+      SigninManager* signin_manager,
+      base::TimeDelta countdown_duration);
+
+  // override views::DialogDelegateView
+  bool Accept() override;
+  bool Cancel() override;
+  void WindowClosing() override;
+  base::string16 GetWindowTitle() const override;
+  base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
+  ui::ModalType GetModalType() const override;
+
+  // override views::View
+  void AddedToWidget() override;
+
+  // Close the dialog
+  void CloseDialog();
+
+  base::WeakPtr<ForcedReauthenticationDialogView> AsWeakPtr() {
+    return weak_factory_.GetWeakPtr();
+  }
+
+ private:
+  // Show the dialog for |browser|. The dialog will delete itself after closing.
+  ForcedReauthenticationDialogView(Browser* browser,
+                                   SigninManager* signin_manager,
+                                   base::TimeDelta countdown_duration);
+
+  void OnCountDown();
+  base::TimeDelta GetTimeRemaining() const;
+
+  Browser* browser_;
+  SigninManager* signin_manager_;
+
+  const base::TimeTicks desired_close_time_;
+
+  // The timer which is used to refresh the dialog title to display the
+  // remaining time.
+  base::RepeatingTimer refresh_timer_;
+
+  base::WeakPtrFactory<ForcedReauthenticationDialogView> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(ForcedReauthenticationDialogView);
+};
+
+class ForcedReauthenticationDialogImpl : public ForcedReauthenticationDialog {
+ public:
+  ForcedReauthenticationDialogImpl();
+  ~ForcedReauthenticationDialogImpl() override;
+
+  // override ForcedReauthenticationDialog
+  void ShowDialog(Profile* profile,
+                  SigninManager* signin_manager,
+                  base::TimeDelta countdown_duration) override;
+
+ private:
+  base::WeakPtr<ForcedReauthenticationDialogView> dialog_view_;
+};
+
+#endif  // CHROME_BROWSER_UI_VIEWS_PROFILES_FORCED_REAUTHENTICATION_DIALOG_VIEW_H_
diff --git a/chrome/browser/ui/views/profiles/forced_reauthentication_dialog_browsertest.cc b/chrome/browser/ui/views/profiles/forced_reauthentication_dialog_view_browsertest.cc
similarity index 73%
rename from chrome/browser/ui/views/profiles/forced_reauthentication_dialog_browsertest.cc
rename to chrome/browser/ui/views/profiles/forced_reauthentication_dialog_view_browsertest.cc
index 4639449..33d1dfd77 100644
--- a/chrome/browser/ui/views/profiles/forced_reauthentication_dialog_browsertest.cc
+++ b/chrome/browser/ui/views/profiles/forced_reauthentication_dialog_view_browsertest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/views/profiles/forced_reauthentication_dialog.h"
+#include "chrome/browser/ui/views/profiles/forced_reauthentication_dialog_view.h"
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
@@ -15,48 +15,48 @@
 #include "components/signin/core/browser/signin_manager.h"
 #include "ui/base/ui_base_types.h"
 
-class ForcedReauthenticationDialogBrowserTest : public DialogBrowserTest {
+class ForcedReauthenticationDialogViewBrowserTest : public DialogBrowserTest {
  public:
-  ForcedReauthenticationDialogBrowserTest() {}
+  ForcedReauthenticationDialogViewBrowserTest() {}
 
   // override DialogBrowserTest
   void ShowDialog(const std::string& name) override {
     Profile* profile = browser()->profile();
     SigninManager* manager = SigninManagerFactory::GetForProfile(profile);
     manager->SetAuthenticatedAccountInfo("test1", "test@xyz.com");
-    ForcedReauthenticationDialog::ShowDialog(profile, manager,
-                                             base::TimeDelta::FromSeconds(60));
+    ForcedReauthenticationDialogView::ShowDialog(
+        profile, manager, base::TimeDelta::FromSeconds(60));
   }
 
   // An integer represents the buttons of dialog.
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(ForcedReauthenticationDialogBrowserTest);
+  DISALLOW_COPY_AND_ASSIGN(ForcedReauthenticationDialogViewBrowserTest);
 };
 
-IN_PROC_BROWSER_TEST_F(ForcedReauthenticationDialogBrowserTest,
+IN_PROC_BROWSER_TEST_F(ForcedReauthenticationDialogViewBrowserTest,
                        InvokeDialog_default) {
   RunDialog();
 }
 
 // Dialog will not be display if there is no valid browser window.
-IN_PROC_BROWSER_TEST_F(ForcedReauthenticationDialogBrowserTest,
+IN_PROC_BROWSER_TEST_F(ForcedReauthenticationDialogViewBrowserTest,
                        NotOpenDialogDueToNoBrowser) {
   Profile* profile = browser()->profile();
   CloseBrowserSynchronously(browser());
-  EXPECT_EQ(nullptr, ForcedReauthenticationDialog::ShowDialog(
+  EXPECT_EQ(nullptr, ForcedReauthenticationDialogView::ShowDialog(
                          profile, SigninManagerFactory::GetForProfile(profile),
                          base::TimeDelta::FromSeconds(60)));
 }
 
-IN_PROC_BROWSER_TEST_F(ForcedReauthenticationDialogBrowserTest,
+IN_PROC_BROWSER_TEST_F(ForcedReauthenticationDialogViewBrowserTest,
                        NotOpenDialogDueToNoTabs) {
   Profile* profile = browser()->profile();
   TabStripModel* model = browser()->tab_strip_model();
   ASSERT_EQ(1, model->count());
   model->CloseWebContentsAt(0, TabStripModel::CLOSE_NONE);
   ASSERT_TRUE(model->empty());
-  EXPECT_EQ(nullptr, ForcedReauthenticationDialog::ShowDialog(
+  EXPECT_EQ(nullptr, ForcedReauthenticationDialogView::ShowDialog(
                          profile, SigninManagerFactory::GetForProfile(profile),
                          base::TimeDelta::FromSeconds(60)));
 }
diff --git a/chrome/browser/ui/views/translate/translate_bubble_view_browsertest.cc b/chrome/browser/ui/views/translate/translate_bubble_view_browsertest.cc
index 3bb84430..df2c59c5 100644
--- a/chrome/browser/ui/views/translate/translate_bubble_view_browsertest.cc
+++ b/chrome/browser/ui/views/translate/translate_bubble_view_browsertest.cc
@@ -24,9 +24,6 @@
  public:
   TranslateBubbleViewBrowserTest() {}
   ~TranslateBubbleViewBrowserTest() override {}
-  void SetUpOnMainThread() override {
-    InProcessBrowserTest::SetUpOnMainThread();
-  }
 
  private:
   DISALLOW_COPY_AND_ASSIGN(TranslateBubbleViewBrowserTest);
diff --git a/chrome/browser/ui/webui/bookmarks_ui_browsertest.cc b/chrome/browser/ui/webui/bookmarks_ui_browsertest.cc
index dbed0b1d..ccdee6ab 100644
--- a/chrome/browser/ui/webui/bookmarks_ui_browsertest.cc
+++ b/chrome/browser/ui/webui/bookmarks_ui_browsertest.cc
@@ -21,8 +21,6 @@
   BookmarksTest() {}
 
   void SetUpOnMainThread() override {
-    InProcessBrowserTest::SetUpOnMainThread();
-
     // Re-enable accessibility checks when audit failures are resolved.
     // AX_TEXT_01: http://crbug.com/559201
     // AX_ARIA_08: http://crbug.com/559202
diff --git a/chrome/browser/ui/webui/signin/inline_login_ui_browsertest.cc b/chrome/browser/ui/webui/signin/inline_login_ui_browsertest.cc
index 1802b08..1c9fdf9 100644
--- a/chrome/browser/ui/webui/signin/inline_login_ui_browsertest.cc
+++ b/chrome/browser/ui/webui/signin/inline_login_ui_browsertest.cc
@@ -482,8 +482,6 @@
   }
 
   void SetUpOnMainThread() override {
-    InProcessBrowserTest::SetUpOnMainThread();
-
     // Grab references to the fake signin manager and token service.
     Profile* profile = browser()->profile();
     signin_manager_ = static_cast<FakeSigninManagerForTesting*>(
diff --git a/chrome/common/extensions/manifest_tests/extension_manifests_initvalue_unittest.cc b/chrome/common/extensions/manifest_tests/extension_manifests_initvalue_unittest.cc
index 22e6322..5eebc0c 100644
--- a/chrome/common/extensions/manifest_tests/extension_manifests_initvalue_unittest.cc
+++ b/chrome/common/extensions/manifest_tests/extension_manifests_initvalue_unittest.cc
@@ -10,6 +10,7 @@
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/extensions/manifest_tests/chrome_manifest_test.h"
 #include "components/crx_file/id_util.h"
+#include "extensions/common/constants.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/features/simple_feature.h"
 #include "extensions/common/manifest_constants.h"
@@ -119,7 +120,7 @@
 
   // Test with an options page.
   extension = LoadAndExpectSuccess("init_valid_options.json");
-  EXPECT_EQ("chrome-extension",
+  EXPECT_EQ(extensions::kExtensionScheme,
             OptionsPageInfo::GetOptionsPage(extension.get()).scheme());
   EXPECT_EQ("/options.html",
             OptionsPageInfo::GetOptionsPage(extension.get()).path());
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
index 3a01ea6b..d3d31b7 100644
--- a/chrome/renderer/chrome_content_renderer_client.cc
+++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -141,6 +141,7 @@
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/common/extensions/chrome_extensions_client.h"
 #include "chrome/renderer/extensions/chrome_extensions_renderer_client.h"
+#include "extensions/common/constants.h"
 #include "extensions/common/extension_urls.h"
 #include "extensions/common/switches.h"
 #include "extensions/renderer/dispatcher.h"
@@ -1014,7 +1015,7 @@
 #if BUILDFLAG(ENABLE_EXTENSIONS)
   bool is_extension_from_webstore = extension && extension->from_webstore();
 
-  bool is_invoked_by_extension = app_url.SchemeIs("chrome-extension");
+  bool is_invoked_by_extension = app_url.SchemeIs(extensions::kExtensionScheme);
   bool is_invoked_by_hosted_app = extension &&
       extension->is_hosted_app() &&
       extension->web_extent().MatchesURL(app_url);
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 4fb0caf..884c2e8 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -2046,6 +2046,7 @@
     }
     if (toolkit_views) {
       sources += [
+        "../browser/payments/payment_manifest_parser_host_browsertest.cc",
         "../browser/payments/site_per_process_payments_browsertest.cc",
         "../browser/ui/global_error/global_error_service_browsertest.cc",
         "../browser/ui/views/bookmarks/bookmark_editor_view_browsertest.cc",
@@ -2085,7 +2086,7 @@
       deps += [ "//ui/views" ]
       if (!is_chromeos && (!is_mac || mac_views_browser)) {
         sources += [
-          "../browser/ui/views/profiles/forced_reauthentication_dialog_browsertest.cc",
+          "../browser/ui/views/profiles/forced_reauthentication_dialog_view_browsertest.cc",
           "../browser/ui/views/profiles/profile_chooser_view_browsertest.cc",
         ]
       }
@@ -4411,6 +4412,7 @@
   }
   if (enable_session_service) {
     sources += [
+      "../browser/sessions/session_restore_observer_unittest.cc",
       "../browser/sessions/session_restore_stats_collector_unittest.cc",
       "../browser/sessions/session_service_unittest.cc",
       "../browser/sessions/tab_loader_unittest.cc",
diff --git a/chrome/test/media_router/telemetry/benchmarks/pagesets/media_router_perf_pages.py b/chrome/test/media_router/telemetry/benchmarks/pagesets/media_router_perf_pages.py
index c39ef194..803bb6f 100644
--- a/chrome/test/media_router/telemetry/benchmarks/pagesets/media_router_perf_pages.py
+++ b/chrome/test/media_router/telemetry/benchmarks/pagesets/media_router_perf_pages.py
@@ -31,11 +31,11 @@
   """Cast page to open a cast-enabled page and open media router dialog."""
 
   def __init__(self, page_set, url='file://basic_test.html',
-               shared_page_state_class=shared_page_state.SharedPageState,
-               name='basic_test.html'):
+               shared_page_state_class=shared_page_state.SharedPageState):
     super(CastDialogPage, self).__init__(
         url=url, page_set=page_set,
-        shared_page_state_class=shared_page_state_class)
+        shared_page_state_class=shared_page_state_class,
+        name='basic_test.html')
 
   def RunPageInteractions(self, action_runner):
     # Wait for 5s after Chrome is opened in order to get consistent results.
diff --git a/chrome/test/perf/mach_ports_performancetest.cc b/chrome/test/perf/mach_ports_performancetest.cc
index e7b1d78..77c5db9 100644
--- a/chrome/test/perf/mach_ports_performancetest.cc
+++ b/chrome/test/perf/mach_ports_performancetest.cc
@@ -30,7 +30,6 @@
   }
 
   void SetUpOnMainThread() override {
-    InProcessBrowserTest::SetUpOnMainThread();
     ASSERT_TRUE(embedded_test_server()->Start());
   }
 
diff --git a/chrome/test/vr/perf/BUILD.gn b/chrome/test/vr/perf/BUILD.gn
index 95ec12e..d4d724e 100644
--- a/chrome/test/vr/perf/BUILD.gn
+++ b/chrome/test/vr/perf/BUILD.gn
@@ -2,17 +2,38 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+common_data = [
+  "./__init__.py",
+  "./android_vr_perf_test.py",
+  "./vr_perf_test.py",
+  "//third_party/android_tools/sdk/platform-tools/adb",
+  "//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk",
+]
+
 group("motopho_latency_test") {
   testonly = true
-  data = [
+  data = common_data + [
     "./latency/__init__.py",
     "./latency/android_webvr_latency_test.py",
+    "./latency/latency_test_config.py",
     "./latency/motopho_thread.py",
     "./latency/robot_arm.py",
     "./latency/run_latency_test.py",
     "./latency/webvr_latency_test.py",
-    "//third_party/android_tools/sdk/platform-tools/adb",
-    "//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk",
+  ]
+  data_deps = [
+    "//chrome/android:chrome_public_apk",
+  ]
+}
+
+group("vrcore_fps_test") {
+  testonly = true
+  data = common_data + [
+    "./vrcore_fps/__init__.py",
+    "./vrcore_fps/run_vrcore_fps_test.py",
+    "./vrcore_fps/vrcore_fps_test.py",
+    "./vrcore_fps/vrcore_fps_test_config.py",
+    "./vrcore_fps/vr_perf_summary.py",
   ]
   data_deps = [
     "//chrome/android:chrome_public_apk",
diff --git a/chrome/test/vr/perf/__init__.py b/chrome/test/vr/perf/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/chrome/test/vr/perf/__init__.py
diff --git a/chrome/test/vr/perf/android_vr_perf_test.py b/chrome/test/vr/perf/android_vr_perf_test.py
new file mode 100644
index 0000000..e82c704
--- /dev/null
+++ b/chrome/test/vr/perf/android_vr_perf_test.py
@@ -0,0 +1,127 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import vr_perf_test
+
+import logging
+import os
+import time
+
+DEFAULT_SCREEN_WIDTH = 720
+DEFAULT_SCREEN_HEIGHT = 1280
+NUM_VR_ENTRY_ATTEMPTS = 5
+
+class AndroidVrPerfTest(vr_perf_test.VrPerfTest):
+  """Base class for VrPerfTests running on Android.
+
+  Handles the setup and teardown portions of the test. The _Run function
+  that actually runs the test is to be implemented in a subclass or in
+  a separate class that a subclass also inherits from.
+  """
+  def __init__(self, args):
+    super(AndroidVrPerfTest, self).__init__(args)
+    # Swarming stuff seems to routinely kill off adbd once a minute or so,
+    # which often causes adb's startup message to appear in the output. We need
+    # to remove this before getting the device name.
+    # TODO(bsheedy): Look into preventing adbd from being killed altogether
+    # instead of working around it.
+    self._device_name = self._Adb(['shell', 'getprop',
+                                   'ro.product.name']).strip().split('\n')[-1]
+
+  def _Adb(self, cmd):
+    """Runs the given command via adb.
+
+    Returns:
+      A string containing the stdout and stderr of the adb command.
+    """
+    # TODO(bsheedy): Maybe migrate to use Devil (overkill?).
+    return self._RunCommand([self._args.adb_path] + cmd)
+
+  def _OneTimeSetup(self):
+    self._Adb(['root'])
+
+    # Install the latest VrCore and Chrome APKs.
+    self._Adb(['install', '-r', '-d',
+               '../../third_party/gvr-android-sdk/test-apks/vr_services'
+               '/vr_services_current.apk'])
+    # TODO(bsheedy): Make APK path configurable so usable with other channels.
+    self._Adb(['install', '-r', 'apks/ChromePublic.apk'])
+
+    # Force WebVR support, remove open tabs, and don't have first run
+    # experience.
+    self._SetChromeCommandLineFlags(['--enable-webvr', '--no-restore-state',
+                                     '--disable-fre'])
+    # Wake up the device and sleep, otherwise WebGL can crash on startup.
+    self._Adb(['shell', 'input', 'keyevent', 'KEYCODE_WAKEUP'])
+    time.sleep(1)
+
+  def _Setup(self, url):
+    # Start Chrome
+    self._Adb(['shell', 'am', 'start',
+               '-a', 'android.intent.action.MAIN',
+               '-n', 'org.chromium.chrome/com.google.android.apps.chrome.Main',
+               url])
+    # TODO(bsheedy): Remove this sleep - poll for magic window GVR
+    # initialization to know when page fully loaded and ready?
+    time.sleep(10)
+
+    # Tap the center of the screen to start presenting.
+    # It's technically possible that the screen tap won't enter VR on the first
+    # time, so try several times by checking for the logcat output from
+    # entering VR.
+    (width, height) = self._GetScreenResolution()
+    entered_vr = False
+    for _ in xrange(NUM_VR_ENTRY_ATTEMPTS):
+      self._Adb(['logcat', '-c'])
+      self._Adb(['shell', 'input', 'touchscreen', 'tap', str(width/2),
+                 str(height/2)])
+      time.sleep(5)
+      output = self._Adb(['logcat', '-d'])
+      if 'Initialized GVR version' in output:
+        entered_vr = True
+        break
+      logging.warning('Failed to enter VR, retrying')
+    if not entered_vr:
+      raise RuntimeError('Failed to enter VR after %d attempts'
+                         % NUM_VR_ENTRY_ATTEMPTS)
+
+  def _Teardown(self):
+    # Exit VR and close Chrome.
+    self._Adb(['shell', 'input', 'keyevent', 'KEYCODE_BACK'])
+    self._Adb(['shell', 'am', 'force-stop', 'org.chromium.chrome'])
+
+  def _OneTimeTeardown(self):
+    # Turn off the screen.
+    self._Adb(['shell', 'input', 'keyevent', 'KEYCODE_POWER'])
+
+  def _SetChromeCommandLineFlags(self, flags):
+    """Sets the Chrome command line flags to the given list."""
+    self._Adb(['shell', "echo 'chrome " + ' '.join(flags) + "' > "
+               + '/data/local/tmp/chrome-command-line'])
+
+  def _GetScreenResolution(self):
+    """Retrieves the device's screen resolution, or a default if not found.
+
+    Returns:
+      A tuple (width, height).
+    """
+    output = self._Adb(['shell', 'dumpsys', 'display'])
+    width = None
+    height = None
+    for line in output.split('\n'):
+      if 'mDisplayWidth' in line:
+        width = int(line.split('=')[1])
+      elif 'mDisplayHeight' in line:
+        height = int(line.split('=')[1])
+      if width and height:
+        break
+    if not width:
+      logging.warning('Could not get actual screen width, defaulting to %d',
+                      DEFAULT_SCREEN_WIDTH)
+      width = DEFAULT_SCREEN_WIDTH
+    if not height:
+      logging.warning('Could not get actual screen height, defaulting to %d',
+                      DEFAULT_SCREEN_HEIGHT)
+      height = DEFAULT_SCREEN_HEIGHT
+    return (width, height)
diff --git a/chrome/test/vr/perf/latency/android_webvr_latency_test.py b/chrome/test/vr/perf/latency/android_webvr_latency_test.py
index 6608dc5b..5ebc1b4 100644
--- a/chrome/test/vr/perf/latency/android_webvr_latency_test.py
+++ b/chrome/test/vr/perf/latency/android_webvr_latency_test.py
@@ -2,145 +2,11 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import android_vr_perf_test
 import webvr_latency_test
 
-import logging
-import os
-import time
-
-
-DEFAULT_SCREEN_WIDTH = 720
-DEFAULT_SCREEN_HEIGHT = 1280
-NUM_VR_ENTRY_ATTEMPTS = 5
-
-
-class AndroidWebVrLatencyTest(webvr_latency_test.WebVrLatencyTest):
+class AndroidWebVrLatencyTest(webvr_latency_test.WebVrLatencyTest,
+                              android_vr_perf_test.AndroidVrPerfTest):
   """Android implementation of the WebVR latency test."""
   def __init__(self, args):
     super(AndroidWebVrLatencyTest, self).__init__(args)
-    # Swarming stuff seems to routinely kill off adbd once a minute or so,
-    # which often causes adb's startup message to appear in the output. We need
-    # to remove this before getting the device name
-    # TODO(bsheedy): Look into preventing adbd from being killed altogether
-    # instead of working around it
-    self._device_name = self._Adb(['shell', 'getprop',
-                                   'ro.product.name']).strip().split('\n')[-1]
-
-  def _OneTimeSetup(self):
-    self._Adb(['root'])
-
-    # Install the latest VrCore and Chrome APKs
-    self._Adb(['install', '-r', '-d',
-               '../../third_party/gvr-android-sdk/test-apks/vr_services'
-               '/vr_services_current.apk'])
-    self._SaveInstalledVrCoreVersion()
-    # TODO(bsheedy): Make APK path configurable so usable with other channels
-    self._Adb(['install', '-r', 'apks/ChromePublic.apk'])
-
-    # Force WebVR support, remove open tabs, and don't have first run
-    # experience.
-    self._SetChromeCommandLineFlags(['--enable-webvr', '--no-restore-state',
-                                     '--disable-fre'])
-    # Wake up the device and sleep, otherwise WebGL can crash on startup.
-    self._Adb(['shell', 'input', 'keyevent', 'KEYCODE_WAKEUP'])
-    time.sleep(1)
-
-
-  def _Setup(self, url):
-    # Start Chrome
-    self._Adb(['shell', 'am', 'start',
-               '-a', 'android.intent.action.MAIN',
-               '-n', 'org.chromium.chrome/com.google.android.apps.chrome.Main',
-               url])
-    time.sleep(10)
-
-    # Tap the center of the screen to start presenting.
-    # It's technically possible that the screen tap won't enter VR on the first
-    # time, so try several times by checking for the logcat output from
-    # entering VR
-    (width, height) = self._GetScreenResolution()
-    entered_vr = False
-    for _ in xrange(NUM_VR_ENTRY_ATTEMPTS):
-      self._Adb(['logcat', '-c'])
-      self._Adb(['shell', 'input', 'touchscreen', 'tap', str(width/2),
-                 str(height/2)])
-      time.sleep(5)
-      output = self._Adb(['logcat', '-d'])
-      if 'Initialized GVR version' in output:
-        entered_vr = True
-        break
-      logging.warning('Failed to enter VR, retrying')
-    if not entered_vr:
-      raise RuntimeError('Failed to enter VR after %d attempts'
-                         % NUM_VR_ENTRY_ATTEMPTS)
-
-  def _Teardown(self):
-    # Exit VR and close Chrome
-    self._Adb(['shell', 'input', 'keyevent', 'KEYCODE_BACK'])
-    self._Adb(['shell', 'am', 'force-stop', 'org.chromium.chrome'])
-
-  def _OneTimeTeardown(self):
-    # Perform teardown again in case an exception was thrown
-    self._Teardown()
-    # Turn off the screen
-    self._Adb(['shell', 'input', 'keyevent', 'KEYCODE_POWER'])
-
-  def _Adb(self, cmd):
-    """Runs the given command via adb.
-
-    Returns:
-      A string containing the stdout and stderr of the adb command.
-    """
-    # TODO(bsheedy): Maybe migrate to use Devil (overkill?)
-    return self._RunCommand([self.args.adb_path] + cmd)
-
-  def _SaveInstalledVrCoreVersion(self):
-    """Retrieves the VrCore version and saves it for dashboard uploading."""
-    output = self._Adb(['shell', 'dumpsys', 'package', 'com.google.vr.vrcore'])
-    version = None
-    for line in output.split('\n'):
-      if 'versionName' in line:
-        version = line.split('=')[1]
-        break
-    if version:
-      logging.info('VrCore version is %s', version)
-    else:
-      logging.info('VrCore version not retrieved')
-      version = '0'
-    if not (self.args.output_dir and os.path.isdir(self.args.output_dir)):
-      logging.warning('No output directory, not saving VrCore version')
-      return
-    with file(os.path.join(self.args.output_dir,
-                           self.args.vrcore_version_file), 'w') as outfile:
-      outfile.write(version)
-
-  def _SetChromeCommandLineFlags(self, flags):
-    """Sets the Chrome command line flags to the given list."""
-    self._Adb(['shell', "echo 'chrome " + ' '.join(flags) + "' > "
-               + '/data/local/tmp/chrome-command-line'])
-
-  def _GetScreenResolution(self):
-    """Retrieves the device's screen resolution, or a default if not found.
-
-    Returns:
-      A tuple (width, height).
-    """
-    output = self._Adb(['shell', 'dumpsys', 'display'])
-    width = None
-    height = None
-    for line in output.split('\n'):
-      if 'mDisplayWidth' in line:
-        width = int(line.split('=')[1])
-      elif 'mDisplayHeight' in line:
-        height = int(line.split('=')[1])
-      if width and height:
-        break
-    if not width:
-      logging.warning('Could not get actual screen width, defaulting to %d',
-                      DEFAULT_SCREEN_WIDTH)
-      width = DEFAULT_SCREEN_WIDTH
-    if not height:
-      logging.warning('Could not get actual screen height, defaulting to %d',
-                      DEFAULT_SCREEN_HEIGHT)
-      height = DEFAULT_SCREEN_HEIGHT
-    return (width, height)
diff --git a/chrome/test/vr/perf/latency/latency_test_config.py b/chrome/test/vr/perf/latency/latency_test_config.py
new file mode 100644
index 0000000..58acb7c
--- /dev/null
+++ b/chrome/test/vr/perf/latency/latency_test_config.py
@@ -0,0 +1,10 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import sys
+
+# Add //chrome/test/vr/perf to system path.
+sys.path.append(os.path.join(os.path.dirname(__file__),
+                             os.pardir))
diff --git a/chrome/test/vr/perf/latency/motopho_thread.py b/chrome/test/vr/perf/latency/motopho_thread.py
index aca45614..0681752f 100644
--- a/chrome/test/vr/perf/latency/motopho_thread.py
+++ b/chrome/test/vr/perf/latency/motopho_thread.py
@@ -16,7 +16,7 @@
     self._correlations = []
     # Threads can't be restarted, so in order to gather multiple samples, we
     # need to either re-create the thread for every iteration or use a loop
-    # and locks in a single thread -> use the latter solution
+    # and locks in a single thread -> use the latter solution.
     self._start_lock = threading.Event()
     self._finish_lock = threading.Event()
     self.BlockNextIteration()
diff --git a/chrome/test/vr/perf/latency/robot_arm.py b/chrome/test/vr/perf/latency/robot_arm.py
index 052b9617..18d574b 100644
--- a/chrome/test/vr/perf/latency/robot_arm.py
+++ b/chrome/test/vr/perf/latency/robot_arm.py
@@ -29,7 +29,7 @@
       return
     # If the servo stopped very close to the desired position, it can just
     # vibrate instead of moving, so move away before going to the reset
-    # position
+    # position.
     self._connection.write('5 300 0 5\n')
     time.sleep(0.5)
     self._connection.write('5 250 0 5\n')
diff --git a/chrome/test/vr/perf/latency/run_latency_test.py b/chrome/test/vr/perf/latency/run_latency_test.py
index cd96b164..3f5c747 100644
--- a/chrome/test/vr/perf/latency/run_latency_test.py
+++ b/chrome/test/vr/perf/latency/run_latency_test.py
@@ -10,7 +10,8 @@
 a set of servos, which physically moves the test device and Motopho during the
 latency test.
 """
-
+# Must be first import in order to add parent directory to system path.
+import latency_test_config
 import android_webvr_latency_test
 
 import argparse
@@ -21,7 +22,7 @@
 DEFAULT_ADB_PATH = os.path.realpath('../../third_party/android_tools/sdk/'
                                     'platform-tools/adb')
 # TODO(bsheedy): See about adding tool via DEPS instead of relying on it
-# existing on the bot already
+# existing on the bot already.
 DEFAULT_MOTOPHO_PATH = os.path.join(os.path.expanduser('~'), 'motopho/Motopho')
 DEFAULT_NUM_SAMPLES = 10
 DEFAULT_RESULTS_FILE = 'results-chart.json'
@@ -71,10 +72,6 @@
   parser.add_argument('-v', '--verbose',
                       dest='verbose_count', default=0, action='count',
                       help='Verbose level (multiple times for more)')
-  parser.add_argument('--vrcore-version-file',
-                      default=DEFAULT_VRCORE_VERSION_FILE,
-                      help='The name of the text file that the VrCore APK '
-                           'version number will be saved to')
   (args, unknown_args) = parser.parse_known_args()
   SetLogLevel(args.verbose_count)
   if unknown_args:
diff --git a/chrome/test/vr/perf/latency/webvr_latency_test.py b/chrome/test/vr/perf/latency/webvr_latency_test.py
index 4f69c287..25dc048 100644
--- a/chrome/test/vr/perf/latency/webvr_latency_test.py
+++ b/chrome/test/vr/perf/latency/webvr_latency_test.py
@@ -20,28 +20,28 @@
 DEFAULT_URLS = [
     # TODO(bsheedy): See about having versioned copies of the flicker app
     # instead of using personal github.
-    # Purely a flicker app - no additional CPU/GPU load
+    # Purely a flicker app - no additional CPU/GPU load.
     'https://weableandbob.github.io/Motopho/'
     'flicker_apps/webvr/webvr-flicker-app-klaus.html?'
     'polyfill=0\&canvasClickPresents=1',
-    # URLs that render 3D scenes in addition to the Motopho patch
-    # Heavy CPU load, moderate GPU load
+    # URLs that render 3D scenes in addition to the Motopho patch.
+    # Heavy CPU load, moderate GPU load.
     'https://webvr.info/samples/test-slow-render.html?'
     'latencyPatch=1\&canvasClickPresents=1\&'
     'heavyGpu=1\&workTime=20\&cubeCount=8\&cubeScale=0.4',
-    # Moderate CPU load, light GPU load
+    # Moderate CPU load, light GPU load.
     'https://webvr.info/samples/test-slow-render.html?'
     'latencyPatch=1\&canvasClickPresents=1\&'
     'heavyGpu=1\&workTime=12\&cubeCount=8\&cubeScale=0.3',
-    # Light CPU load, moderate GPU load
+    # Light CPU load, moderate GPU load.
     'https://webvr.info/samples/test-slow-render.html?'
     'latencyPatch=1\&canvasClickPresents=1\&'
     'heavyGpu=1\&workTime=5\&cubeCount=8\&cubeScale=0.4',
-    # Heavy CPU load, very light GPU load
+    # Heavy CPU load, very light GPU load.
     'https://webvr.info/samples/test-slow-render.html?'
     'latencyPatch=1\&canvasClickPresents=1\&'
     'workTime=20',
-    # No additional CPU load, very light GPU load
+    # No additional CPU load, very light GPU load.
     'https://webvr.info/samples/test-slow-render.html?'
     'latencyPatch=1\&canvasClickPresents=1',
 ]
@@ -90,72 +90,44 @@
 class WebVrLatencyTest(object):
   """Base class for all WebVR latency tests.
 
-  This is meant to be subclassed for each platform the test is run on. While
-  the latency test itself is cross-platform, the setup and teardown for
-  tests is platform-dependent.
+  This handles the platform-independent _Run and _SaveResultsToFile functions.
+  Platform-specific setup and teardown should be somehow handled by classes
+  that inherit from this one.
   """
   def __init__(self, args):
-    self.args = args
+    super(WebVrLatencyTest, self).__init__(args)
     self._num_samples = args.num_samples
     self._test_urls = args.urls or DEFAULT_URLS
     assert (self._num_samples > 0),'Number of samples must be greater than 0'
-    self._device_name = 'generic_device'
     self._test_results = {}
 
-    # Connect to the Arduino that drives the servos
+    # Connect to the Arduino that drives the servos.
     devices = GetTtyDevices(r'ttyACM\d+', [0x2a03, 0x2341])
     assert (len(devices) == 1),'Found %d devices, expected 1' % len(devices)
     self.robot_arm = ra.RobotArm(devices[0])
 
-  def RunTests(self):
-    """Runs latency tests on all the URLs provided to the test on creation.
-
-    Repeatedly runs the steps to start Chrome, measure/store latency, and
-    clean up before storing all results to a single file for dashboard
-    uploading.
-    """
-    try:
-      self._OneTimeSetup()
-      for url in self._test_urls:
-        self._Setup(url)
-        self._Run(url)
-        self._Teardown()
-      self._SaveResultsToFile()
-    finally:
-      self._OneTimeTeardown()
-
-  def _OneTimeSetup(self):
-    """Performs any platform-specific setup once before any tests."""
-    raise NotImplementedError(
-        'Platform-specific setup must be implemented in subclass')
-
-  def _Setup(self, url):
-    """Performs any platform-specific setup before each test."""
-    raise NotImplementedError(
-        'Platform-specific setup must be implemented in subclass')
-
   def _Run(self, url):
     """Run the latency test.
 
     Handles the actual latency measurement, which is identical across
     different platforms, as well as result storing.
     """
-    # Motopho scripts use relative paths, so switch to the Motopho directory
-    os.chdir(self.args.motopho_path)
+    # Motopho scripts use relative paths, so switch to the Motopho directory.
+    os.chdir(self._args.motopho_path)
 
-    # Set up the thread that runs the Motopho script
+    # Set up the thread that runs the Motopho script.
     motopho_thread = mt.MotophoThread(self._num_samples)
     motopho_thread.start()
 
-    # Run multiple times so we can get an average and standard deviation
+    # Run multiple times so we can get an average and standard deviation.
     for _ in xrange(self._num_samples):
       self.robot_arm.ResetPosition()
-      # Start the Motopho script
+      # Start the Motopho script.
       motopho_thread.StartIteration()
-      # Let the Motopho be stationary so the script can calculate the bias
+      # Let the Motopho be stationary so the script can calculate the bias.
       time.sleep(3)
       motopho_thread.BlockNextIteration()
-      # Move so we can measure latency
+      # Move so we can measure latency.
       self.robot_arm.StartMotophoMovement()
       if not motopho_thread.WaitForIterationEnd(MOTOPHO_THREAD_TIMEOUT):
         # TODO(bsheedy): Look into ways to prevent Motopho from not sending any
@@ -167,34 +139,6 @@
     self._StoreResults(motopho_thread.latencies, motopho_thread.correlations,
                        url)
 
-  def _Teardown(self):
-    """Performs any platform-specific teardown after each test."""
-    raise NotImplementedError(
-        'Platform-specific teardown must be implemented in subclass')
-
-  def _OneTimeTeardown(self):
-    """Performs any platform-specific teardown after all tests."""
-    raise NotImplementedError(
-        'Platform-specific teardown must be implemented in sublcass')
-
-  def _RunCommand(self, cmd):
-    """Runs the given cmd list and returns its output.
-
-    Prints the command's output and exits if any error occurs.
-
-    Returns:
-      A string containing the stdout and stderr of the command.
-    """
-    try:
-      return subprocess.check_output(cmd, stderr=subprocess.STDOUT)
-    except subprocess.CalledProcessError as e:
-      logging.error('Failed command output: %s', e.output)
-      raise e
-
-  def _SetChromeCommandLineFlags(self, flags):
-    raise NotImplementedError(
-        'Command-line flag setting must be implemented in subclass')
-
   def _StoreResults(self, latencies, correlations, url):
     """Temporarily stores the results of a test.
 
@@ -220,7 +164,7 @@
     }
 
   def _SaveResultsToFile(self):
-    if not (self.args.output_dir and os.path.isdir(self.args.output_dir)):
+    if not (self._args.output_dir and os.path.isdir(self._args.output_dir)):
       logging.warning('No output directory set, not saving results to file')
       return
 
@@ -279,6 +223,6 @@
       'charts': charts,
     }
 
-    with file(os.path.join(self.args.output_dir,
-                           self.args.results_file), 'w') as outfile:
+    with file(os.path.join(self._args.output_dir,
+                           self._args.results_file), 'w') as outfile:
       json.dump(results, outfile)
diff --git a/chrome/test/vr/perf/vr_perf_test.py b/chrome/test/vr/perf/vr_perf_test.py
new file mode 100644
index 0000000..5b10ed4b
--- /dev/null
+++ b/chrome/test/vr/perf/vr_perf_test.py
@@ -0,0 +1,85 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import subprocess
+
+class VrPerfTest(object):
+  """Base class for all non-Telemetry VR perf tests.
+
+  This class is meant to be subclassed for each platform and test type to
+  be run.
+  """
+  def __init__(self, args):
+    super(VrPerfTest, self).__init__()
+    self._args = args
+    self._test_urls = []
+
+  def RunTests(self):
+    """Runs some test on all the URLs provided to the test on creation.
+
+    Repeatedly runs the steps to start Chrome, measure/store metrics, and
+    clean up before storing all results to a single file for dashboard
+    uploading.
+    """
+    try:
+      self._OneTimeSetup()
+      for url in self._test_urls:
+        try:
+          self._Setup(url)
+          self._Run(url)
+        finally:
+          # We always want to perform teardown even if an exception gets raised.
+          self._Teardown()
+      self._SaveResultsToFile()
+    finally:
+      self._OneTimeTeardown()
+
+  def _OneTimeSetup(self):
+    """Performs any platform-specific setup once before any tests."""
+    raise NotImplementedError(
+        'Platform-specific setup must be implemented in subclass')
+
+  def _Setup(self, url):
+    """Performs any platform-specific setup before each test."""
+    raise NotImplementedError(
+        'Platform-specific setup must be implemented in subclass')
+
+  def _Run(self, url):
+    """Performs the actual test."""
+    raise NotImplementedError('Test must be implemented in subclass')
+
+  def _Teardown(self):
+    """Performs any platform-specific teardown after each test."""
+    raise NotImplementedError(
+        'Platform-specific teardown must be implemented in subclass')
+
+  def _OneTimeTeardown(self):
+    """Performs any platform-specific teardown after all tests."""
+    raise NotImplementedError(
+        'Platform-specific teardown must be implemented in subclass')
+
+  def _RunCommand(self, cmd):
+    """Runs the given cmd list and returns its output.
+
+    Prints the command's output and exits if any error occurs.
+
+    Returns:
+      A string containing the stdout and stderr of the command.
+    """
+    try:
+      return subprocess.check_output(cmd, stderr=subprocess.STDOUT)
+    except subprocess.CalledProcessError as e:
+      logging.error('Failed command output: %s', e.output)
+      raise e
+
+  def _SetChromeCommandLineFlags(self, flags):
+    """Sets the commandline flags that Chrome reads on startup."""
+    raise NotImplementedError(
+        'Command-line flag setting must be implemented in subclass')
+
+  def _SaveResultsToFile():
+    """Saves test results to a Chrome perf dashboard-compatible JSON file."""
+    raise NotImplementedError(
+        'Result saving must be implemented in subclass')
+
diff --git a/chrome/test/vr/perf/vrcore_fps/__init__.py b/chrome/test/vr/perf/vrcore_fps/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/chrome/test/vr/perf/vrcore_fps/__init__.py
diff --git a/chrome/test/vr/perf/vrcore_fps/run_vrcore_fps_test.py b/chrome/test/vr/perf/vrcore_fps/run_vrcore_fps_test.py
new file mode 100644
index 0000000..b17bb48
--- /dev/null
+++ b/chrome/test/vr/perf/vrcore_fps/run_vrcore_fps_test.py
@@ -0,0 +1,86 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Script for automatically measuring FPS for VR via VrCore perf logging.
+
+Android only.
+VrCore has a developer option to log performance data to logcat. This test
+visits various WebVR URLs and collects the FPS data reported by VrCore.
+"""
+
+# Needs to be imported first in order to add the parent directory to path.
+import vrcore_fps_test_config
+import vrcore_fps_test
+
+import argparse
+import logging
+import os
+
+DEFAULT_ADB_PATH = os.path.realpath('../../third_party/android_tools/sdk/'
+                                    'platform-tools/adb')
+DEFAULT_DURATION_SECONDS = 30
+DEFAULT_RESULTS_FILE = 'results-chart.json'
+
+
+# TODO(bsheedy): Move common arg parsing code to shared file.
+def GetParsedArgs():
+  """Parses the command line arguments passed to the script.
+
+  Fails if any unknown arguments are present.
+
+  Returns:
+    An object containing all known, parsed arguments.
+  """
+  parser = argparse.ArgumentParser()
+  parser.add_argument('--adb-path',
+                      type=os.path.realpath,
+                      help='The absolute path to adb',
+                      default=DEFAULT_ADB_PATH)
+  parser.add_argument('--duration',
+                      default=DEFAULT_DURATION_SECONDS,
+                      type=int,
+                      help='The duration spent collecting data from each URL')
+  parser.add_argument('--output-dir',
+                      type=os.path.realpath,
+                      help='The directory where the script\'s output files '
+                           'will be saved')
+  parser.add_argument('--results-file',
+                      default=DEFAULT_RESULTS_FILE,
+                      help='The name of the JSON file the results will be '
+                           'saved to')
+  parser.add_argument('--url',
+                      action='append',
+                      default=[],
+                      dest='urls',
+                      help='The URL of a WebVR app to use. Defaults to a '
+                           'set of URLs with various CPU and GPU loads')
+  parser.add_argument('-v', '--verbose',
+                      dest='verbose_count', default=0, action='count',
+                      help='Verbose level (multiple times for more)')
+  (args, unknown_args) = parser.parse_known_args()
+  SetLogLevel(args.verbose_count)
+  if unknown_args:
+    parser.error('Received unknown arguments: %s' % ' '.join(unknown_args))
+  return args
+
+
+def SetLogLevel(verbose_count):
+  """Sets the log level based on the command line arguments."""
+  log_level = logging.WARNING
+  if verbose_count == 1:
+    log_level = logging.INFO
+  elif verbose_count >= 2:
+    log_level = logging.DEBUG
+  logger = logging.getLogger()
+  logger.setLevel(log_level)
+
+
+def main():
+  args = GetParsedArgs()
+  test = vrcore_fps_test.VrCoreFpsTest(args)
+  test.RunTests()
+
+
+if __name__ == '__main__':
+  main()
diff --git a/chrome/test/vr/perf/vrcore_fps/vr_perf_summary.py b/chrome/test/vr/perf/vrcore_fps/vr_perf_summary.py
new file mode 100644
index 0000000..02880dc3
--- /dev/null
+++ b/chrome/test/vr/perf/vrcore_fps/vr_perf_summary.py
@@ -0,0 +1,186 @@
+# 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.
+
+"""Parses logcat output for VrCore performance logs and computes stats.
+
+Can be used either as a standalone manual script or as part of the automated
+VR performance tests.
+
+For manual use, simply pipe logcat output into the script.
+"""
+
+import logging
+import math
+import sys
+
+LINE_SESSION_START = 'PerfMon: Start of session'
+LINE_SESSION_END = 'PerfMon: End of session'
+
+LINE_ASYNC_FPS = 'Async reprojection thread FPS: '
+LINE_APP_FPS = 'Application FPS: '
+LINE_BLOCKED_FRAME = 'Application frame submits blocked on GPU in FPS window: '
+LINE_MISSED_VSYNC = 'Async reprojection thread missed vsync (late by '
+
+APP_FPS_KEY ='app_fps_data'
+ASYNC_FPS_KEY = 'async_fps_data'
+ASYNC_MISSED_KEY = 'asymc_missed_data'
+BLOCKED_SUBMISSION_KEY = 'blocked_submission_key'
+
+def CalculateAverage(inputs):
+  return sum(inputs) / len(inputs)
+
+# Implement own standard deviation function instead of using numpy so that
+# script has no dependencies on third-party libraries.
+def CalculateStandardDeviation(inputs):
+  average = sum(inputs) / len(inputs)
+  sum_of_squares = 0
+  for element in inputs:
+    sum_of_squares += math.pow(element - average, 2)
+  sum_of_squares /= len(inputs)
+  return math.sqrt(sum_of_squares)
+
+def GetInputFromStdin():
+  """Turns stdin input into a single string.
+
+  Returns:
+    A single string containing all lines input via Stdin
+  """
+  print 'Waiting for stdin input.'
+  lines = []
+  while True:
+    try:
+      line = sys.stdin.readline()
+    except KeyboardInterrupt:
+      break
+    if not line:
+      break
+    lines.append(line)
+
+  return '\n'.join(lines)
+
+def ParseLinesIntoSessions(lines):
+  """Parses a string for VR performance logs.
+
+  lines: A string containing lines of logcat output.
+
+  Returns:
+    A list of strings, where each element contains all the lines of interest
+    for a single session
+  """
+  logging_sessions = []
+  lines_of_interest = [LINE_ASYNC_FPS, LINE_APP_FPS, LINE_BLOCKED_FRAME,
+                       LINE_MISSED_VSYNC]
+  for line in lines.split('\n'):
+    if LINE_SESSION_START in line:
+      logging_sessions.append("")
+    elif LINE_SESSION_END in line:
+      continue
+    for loi in lines_of_interest:
+      if loi in line:
+        logging_sessions[-1] += (line + "\n")
+        break
+  return logging_sessions
+
+def ComputeSessionStatistics(session):
+  """Extracts raw statistical data from a session string.
+
+  session: A string containing all the performance logging lines from
+    a single VR session
+
+  Returns:
+    A dictionary containing the raw statistics
+  """
+  app_fps_data = []
+  async_fps_data = []
+  async_missed_data = []
+  blocked_submission_data = []
+
+  for line in session.split("\n"):
+    if LINE_ASYNC_FPS in line: # Async thread FPS.
+      async_fps_data.append( float(line.split(LINE_ASYNC_FPS)[1]) )
+    elif LINE_APP_FPS in line: # Application FPS.
+      app_fps_data.append( float(line.split(LINE_APP_FPS)[1]) )
+    elif LINE_BLOCKED_FRAME in line: # Application frame blocked.
+      blocked_submission_data.append(int(line.split(LINE_BLOCKED_FRAME)[1]))
+    elif LINE_MISSED_VSYNC in line: # Async thread missed vsync.
+      # Convert to milliseconds as well.
+      async_missed_data.append( float(
+          line.split(LINE_MISSED_VSYNC)[1].split("us")[0]) / 1000)
+
+  return {
+      APP_FPS_KEY: app_fps_data,
+      ASYNC_FPS_KEY: async_fps_data,
+      ASYNC_MISSED_KEY: async_missed_data,
+      BLOCKED_SUBMISSION_KEY: blocked_submission_data,
+  }
+
+def StringifySessionStatistics(statistics):
+  """Turns a raw statistics dictionary into a formatted string.
+
+  statistics: A raw statistics dictionary
+
+  Returns:
+    A string with min/max/average calculations
+  """
+  app_fps_data = statistics[APP_FPS_KEY]
+  async_fps_data = statistics[ASYNC_FPS_KEY]
+  async_missed_data = statistics[ASYNC_MISSED_KEY]
+  blocked_submission_data = statistics[BLOCKED_SUBMISSION_KEY]
+
+  output = """
+  Min application FPS: %(min_app_fps).4f
+  Max application FPS: %(max_app_fps).4f
+  Average application FPS: %(avg_app_fps).4f +/- %(std_app_fps).4f
+  %(blocked)d total application frame submissions blocked on GPU
+  ---
+  Min application FPS after first second: %(min_app_fps_after).4f
+  Max application FPS after first second: %(max_app_fps_after).4f
+  Average application FPS after first second: %(avg_app_fps_after).4f +/- %(std_app_fps_after).4f
+  ---
+  Min async reprojection thread FPS: %(min_async_fps).4f
+  Max async reprojection thread FPS: %(max_async_fps).4f
+  Average async reprojection thread FPS: %(avg_async_fps).4f +/- %(std_async_fps).4f
+  Async reprojection thread missed %(async_missed)d vsyncs
+  """ % ({
+      'min_app_fps': min(app_fps_data),
+      'max_app_fps': max(app_fps_data),
+      'avg_app_fps': CalculateAverage(app_fps_data),
+      'std_app_fps': CalculateStandardDeviation(app_fps_data),
+      'blocked': sum(blocked_submission_data),
+      'min_app_fps_after': min(app_fps_data[1:]),
+      'max_app_fps_after': max(app_fps_data[1:]),
+      'avg_app_fps_after': CalculateAverage(app_fps_data[1:]),
+      'std_app_fps_after': CalculateStandardDeviation(app_fps_data[1:]),
+      'min_async_fps': min(async_fps_data),
+      'max_async_fps': max(async_fps_data),
+      'avg_async_fps': CalculateAverage(async_fps_data),
+      'std_async_fps': CalculateStandardDeviation(async_fps_data),
+      'async_missed': len(async_missed_data)})
+
+
+  if len(async_missed_data) > 0:
+    output += 'Average vsync miss time: %.4f +/- %.4f ms' % (
+        CalculateAverage(async_missed_data),
+        CalculateStandardDeviation(async_missed_data))
+  return output
+
+
+def ComputeAndPrintStatistics(session):
+  if len(session) == 0:
+    logging.warning('No data collected for session')
+    return
+  logging.warning(StringifySessionStatistics(ComputeSessionStatistics(session)))
+
+
+def main():
+  logging_sessions = ParseLinesIntoSessions(GetInputFromStdin())
+  logging.warning('Found %d sessions', len(logging_sessions))
+  counter = 1
+  for session in logging_sessions:
+    logging.warning('\n#### Session %d ####', counter)
+    ComputeAndPrintStatistics(session)
+    counter += 1
+
+if __name__ == '__main__':
+  main()
diff --git a/chrome/test/vr/perf/vrcore_fps/vrcore_fps_test.py b/chrome/test/vr/perf/vrcore_fps/vrcore_fps_test.py
new file mode 100644
index 0000000..e73e26c
--- /dev/null
+++ b/chrome/test/vr/perf/vrcore_fps/vrcore_fps_test.py
@@ -0,0 +1,174 @@
+# 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.
+
+"""Visits various WebVR app URLs and records performance metrics from VrCore.
+"""
+
+import android_vr_perf_test
+import vr_perf_summary
+
+import json
+import logging
+import numpy
+import os
+import time
+
+DEFAULT_URLS = [
+  # Standard WebVR sample app with no changes.
+  'https://webvr.info/samples/test-slow-render.html?'
+  'canvasClickPresents=1\&renderScale=1',
+  # Increased render scale.
+  'https://webvr.info/samples/test-slow-render.html?'
+  'canvasClickPresents=1\&renderScale=1.5',
+  # Default render scale, but increased load.
+  'https://webvr.info/samples/test-slow-render.html?'
+  'canvasClickPresents=1\&'
+  'renderScale=1\&heavyGpu=1\&cubeScale=0.2\&workTime=5',
+  # Further increased load.
+  'https://webvr.info/samples/test-slow-render.html?'
+  'canvasClickPresents=1\&'
+  'renderScale=1\&heavyGpu=1\&cubeScale=0.3\&workTime=10',
+]
+
+
+class VrCoreFpsTest(android_vr_perf_test.AndroidVrPerfTest):
+  def __init__(self, args):
+    super(VrCoreFpsTest, self).__init__(args)
+    self._duration = args.duration
+    assert (self._duration > 0),'Duration must be positive'
+    self._test_urls = args.urls or DEFAULT_URLS
+    self._test_results = {}
+
+  def _Run(self, url):
+    # We're already in VR and logcat is cleared during setup, so wait for
+    # the test duration.
+    time.sleep(self._duration)
+
+    # Exit VR so that VrCore stops logging performance data.
+    self._Adb(['shell', 'input', 'keyevent', 'KEYCODE_BACK'])
+    time.sleep(1)
+
+    output = self._Adb(['logcat', '-d'])
+    logging_sessions = vr_perf_summary.ParseLinesIntoSessions(output)
+    if len(logging_sessions) != 1:
+      raise RuntimeError('Expected 1 VR session, found %d' %
+                         len(logging_sessions))
+    session = logging_sessions[0]
+    if len(session) == 0:
+      raise RuntimeError('No data actually collected in logging session')
+    self._StoreResults(url, vr_perf_summary.ComputeSessionStatistics(session))
+
+  def _StoreResults(self, url, results):
+    """Temporarily stores the results of a test.
+
+    Stores the given results in memory to be later retrieved and written to
+    a file in _SaveResultsToFile once all tests are done. Also logs the raw
+    data and calculated statistics.
+    """
+    logging.info('\nURL: %s\n'
+                 'Raw app FPS: %s\n'
+                 'Raw asynchronous reprojection thread FPS: %s\n'
+                 'Raw asynchronous reprojection thread missed frames: %s\n'
+                 'Raw frame submissions blocked on gpu: %s\n'
+                 '%s\n' %
+                 (url, str(results[vr_perf_summary.APP_FPS_KEY]),
+                  str(results[vr_perf_summary.ASYNC_FPS_KEY]),
+                  str(results[vr_perf_summary.ASYNC_MISSED_KEY]),
+                  str(results[vr_perf_summary.BLOCKED_SUBMISSION_KEY]),
+                  vr_perf_summary.StringifySessionStatistics(results)))
+    self._test_results[url] = results
+
+  def _SaveResultsToFile(self):
+    if not (self._args.output_dir and os.path.isdir(self._args.output_dir)):
+      logging.warning('No output directory set, not saving results to file')
+      return
+
+    # TODO(bsheedy): Move this to a common place so other tests can use it.
+    def _GenerateTrace(name, improvement_direction, std=None,
+                       value_type=None, units=None, values=None):
+      return {
+          'improvement_direction': improvement_direction,
+          'name': name,
+          'std': std or 0.0,
+          'type': value_type or 'list_of_scalar_values',
+          'units': units or '',
+          'values': values or [],
+      }
+
+    def _GenerateBaseChart(name, improvement_direction, std=None,
+                           value_type=None, units=None, values=None):
+      return {'summary': _GenerateTrace(name, improvement_direction, std=std,
+                                        value_type=value_type, units=units,
+                                        values=values)}
+
+    registered_charts = {}
+    def _RegisterChart(name, improvement_direction, value_func, std_func,
+                       results_key, units=None):
+      registered_charts[self._device_name + name] = {
+          'improvement_direction': improvement_direction,
+          'value_func': value_func,
+          'std_func': std_func,
+          'results_key': results_key,
+          'units': units or ''
+      }
+
+    # value/summary functions.
+    def f_identity(x): return x
+    def f_min(x): return [min(x)]
+    def f_max(x): return [max(x)]
+    # std functions.
+    def f_zero(x): return 0.0
+    def f_numpy(x): return numpy.std(x)
+
+    _RegisterChart('_avg_app_fps', 'up', f_identity, f_numpy,
+                   vr_perf_summary.APP_FPS_KEY, units='FPS')
+    _RegisterChart('_min_app_fps', 'up', f_min, f_zero,
+                   vr_perf_summary.APP_FPS_KEY, units='FPS')
+    _RegisterChart('_max_app_fps', 'up', f_max, f_zero,
+                   vr_perf_summary.APP_FPS_KEY, units='FPS')
+    _RegisterChart('_avg_async_thread_fps', 'up', f_identity, f_numpy,
+                   vr_perf_summary.ASYNC_FPS_KEY, units='FPS')
+    _RegisterChart('_min_async_thread_fps', 'up', f_min, f_zero,
+                   vr_perf_summary.ASYNC_FPS_KEY, units='FPS')
+    _RegisterChart('_max_async_thread_fps', 'up', f_max, f_zero,
+                   vr_perf_summary.ASYNC_FPS_KEY, units='FPS')
+    # Unlike the rest of the data, missed async reprojection thread vsyncs are
+    # only logged when they happen so it's normal to get an empty list, which
+    # makes numpy and the perf dashboard unhappy.
+    _RegisterChart('_avg_async_thread_vsync_miss_time', 'down',
+                   lambda x: x or [0.0],
+                   lambda x: 0.0 if len(x) == 0 else numpy.std(x),
+                   vr_perf_summary.ASYNC_MISSED_KEY, units='ms')
+    _RegisterChart('_num_async_thread_vsync_misses', 'down', lambda x: [len(x)],
+                   f_zero, vr_perf_summary.ASYNC_MISSED_KEY)
+    _RegisterChart('_num_blocked_frame_submissions', 'down', lambda x: [sum(x)],
+                   f_zero, vr_perf_summary.BLOCKED_SUBMISSION_KEY)
+
+    charts = {}
+    # Set up empty summary charts.
+    for chart, config in registered_charts.iteritems():
+      charts[chart] = _GenerateBaseChart(chart, config['improvement_direction'],
+                                         units=config['units'])
+
+    # Populate a trace for each URL in each chart.
+    for url, results in self._test_results.iteritems():
+      for chart, config in registered_charts.iteritems():
+        result = results[config['results_key']]
+        charts[chart][url] = (
+            _GenerateTrace(chart, config['improvement_direction'],
+                           units=config['units'],
+                           std=config['std_func'](result),
+                           values=config['value_func'](result)))
+        charts[chart]['summary']['values'].extend(config['value_func'](result))
+
+    results = {
+        'format_version': '1.0',
+        'benchmarck_name': 'vrcore_fps',
+        'benchmark_description': 'Measures VR FPS using VrCore perf logging',
+        'charts': charts,
+    }
+
+    with file(os.path.join(self._args.output_dir,
+                           self._args.results_file), 'w') as outfile:
+      json.dump(results, outfile)
diff --git a/chrome/test/vr/perf/vrcore_fps/vrcore_fps_test_config.py b/chrome/test/vr/perf/vrcore_fps/vrcore_fps_test_config.py
new file mode 100644
index 0000000..58acb7c
--- /dev/null
+++ b/chrome/test/vr/perf/vrcore_fps/vrcore_fps_test_config.py
@@ -0,0 +1,10 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import sys
+
+# Add //chrome/test/vr/perf to system path.
+sys.path.append(os.path.join(os.path.dirname(__file__),
+                             os.pardir))
diff --git a/chromecast/BUILD.gn b/chromecast/BUILD.gn
index 93f65e7..08cbad2c 100644
--- a/chromecast/BUILD.gn
+++ b/chromecast/BUILD.gn
@@ -481,15 +481,6 @@
 }
 
 if (is_android) {
-  generate_jni_registration("cast_shell_jni_registration") {
-    target = ":cast_shell_apk"
-    output = "$root_gen_dir/chromecast/android/${target_name}.h"
-    exception_files = [
-      "//base/android/java/src/org/chromium/base/library_loader/LegacyLinker.java",
-      "//base/android/java/src/org/chromium/base/library_loader/Linker.java",
-      "//base/android/java/src/org/chromium/base/library_loader/ModernLinker.java",
-    ]
-  }
   android_assets("cast_shell_apk_assets") {
     assert(v8_use_external_startup_data)
 
diff --git a/chromecast/android/BUILD.gn b/chromecast/android/BUILD.gn
index 321a3d2..1d29974 100644
--- a/chromecast/android/BUILD.gn
+++ b/chromecast/android/BUILD.gn
@@ -27,7 +27,6 @@
   deps = [
     ":platform_jni_loader",
     "//base",
-    "//chromecast:cast_shell_jni_registration",
     "//chromecast:cast_shell_lib",
     "//chromecast:chromecast_features",
     "//chromecast/app",
diff --git a/chromecast/app/android/cast_jni_loader.cc b/chromecast/app/android/cast_jni_loader.cc
index 73b829bb..79a32fe 100644
--- a/chromecast/app/android/cast_jni_loader.cc
+++ b/chromecast/app/android/cast_jni_loader.cc
@@ -6,7 +6,6 @@
 #include "base/android/library_loader/library_loader_hooks.h"
 #include "base/bind.h"
 #include "chromecast/android/cast_jni_registrar.h"
-#include "chromecast/android/cast_shell_jni_registration.h"
 #include "chromecast/android/platform_jni_loader.h"
 #include "chromecast/app/cast_main_delegate.h"
 #include "chromecast/browser/android/jni_registrar.h"
@@ -43,12 +42,6 @@
 JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
   base::android::InitVM(vm);
   JNIEnv* env = base::android::AttachCurrentThread();
-  if (!RegisterMainDexNatives(env) || !RegisterNonMainDexNatives(env)) {
-    return -1;
-  }
-
-  // TODO(agrieve): Delete this block, this is a no-op now.
-  // https://crbug.com/683256.
   if (!content::android::OnJNIOnLoadRegisterJNI(env) || !RegisterJNI(env) ||
       !NativeInit()) {
     return -1;
diff --git a/components/BUILD.gn b/components/BUILD.gn
index c61ae0ad..fb0cac8 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -225,7 +225,6 @@
       "//components/subresource_filter/content/renderer:unit_tests",
       "//components/tracing:unit_tests",
       "//components/visitedlink/test:unit_tests",
-      "//components/viz/common:unit_tests",
       "//components/viz/host:unit_tests",
       "//components/viz/service:unit_tests",
       "//components/wallpaper:unit_tests",
diff --git a/components/cronet/android/BUILD.gn b/components/cronet/android/BUILD.gn
index 2fdc2d87..5e727a3 100644
--- a/components/cronet/android/BUILD.gn
+++ b/components/cronet/android/BUILD.gn
@@ -25,16 +25,6 @@
   jni_package = "cronet"
 }
 
-generate_jni_registration("cronet_jni_registration") {
-  target = ":cronet_sample_apk"
-  output = "$root_gen_dir/components/cronet/android/${target_name}.h"
-  exception_files = [
-    "//base/android/java/src/org/chromium/base/library_loader/LegacyLinker.java",
-    "//base/android/java/src/org/chromium/base/library_loader/Linker.java",
-    "//base/android/java/src/org/chromium/base/library_loader/ModernLinker.java",
-  ]
-}
-
 java_cpp_enum("effective_connection_type_java") {
   sources = [
     "//net/nqe/effective_connection_type.h",
@@ -175,7 +165,6 @@
     deps = [
       ":cronet_android_cert_proto",
       ":cronet_jni_headers",
-      ":cronet_jni_registration",
       ":cronet_version_header",
       "//base",
       "//base/third_party/dynamic_annotations",
@@ -540,9 +529,6 @@
   ]
 
   include_dirs = [ _cronet_version_header_include_dir ]
-
-  configs -= [ "//build/config/android:hide_all_but_jni_onload" ]
-  configs += [ "//build/config/android:hide_all_but_jni" ]
 }
 
 android_resources("cronet_test_apk_resources") {
diff --git a/components/cronet/android/cronet_library_loader.cc b/components/cronet/android/cronet_library_loader.cc
index b7aa07a..72c416e 100644
--- a/components/cronet/android/cronet_library_loader.cc
+++ b/components/cronet/android/cronet_library_loader.cc
@@ -19,7 +19,6 @@
 #include "base/message_loop/message_loop.h"
 #include "base/metrics/statistics_recorder.h"
 #include "components/cronet/android/cronet_bidirectional_stream_adapter.h"
-#include "components/cronet/android/cronet_jni_registration.h"
 #include "components/cronet/android/cronet_upload_data_stream_adapter.h"
 #include "components/cronet/android/cronet_url_request_adapter.h"
 #include "components/cronet/android/cronet_url_request_context_adapter.h"
@@ -85,11 +84,6 @@
 jint CronetOnLoad(JavaVM* vm, void* reserved) {
   base::android::InitVM(vm);
   JNIEnv* env = base::android::AttachCurrentThread();
-  if (!RegisterMainDexNatives(env) || !RegisterNonMainDexNatives(env)) {
-    return -1;
-  }
-  // TODO(agrieve): Delete this block, this is a no-op now.
-  // https://crbug.com/683256.
   if (!RegisterJNI(env) || !NativeInit()) {
     return -1;
   }
diff --git a/components/cronet/android/cronet_url_request_context_adapter.cc b/components/cronet/android/cronet_url_request_context_adapter.cc
index 5274d9e..c8b2ea2 100644
--- a/components/cronet/android/cronet_url_request_context_adapter.cc
+++ b/components/cronet/android/cronet_url_request_context_adapter.cc
@@ -63,6 +63,7 @@
 #include "net/nqe/network_quality_estimator_params.h"
 #include "net/proxy/proxy_config_service_android.h"
 #include "net/proxy/proxy_service.h"
+#include "net/quic/core/quic_versions.h"
 #include "net/sdch/sdch_owner.h"
 #include "net/ssl/channel_id_service.h"
 #include "net/url_request/url_request_context.h"
@@ -758,8 +759,9 @@
       net::AlternativeService alternative_service(
           net::kProtoQUIC, "",
           static_cast<uint16_t>(quic_hint->alternate_port));
-      context_->http_server_properties()->SetAlternativeService(
-          quic_server, alternative_service, base::Time::Max());
+      context_->http_server_properties()->SetQuicAlternativeService(
+          quic_server, alternative_service, base::Time::Max(),
+          net::QuicVersionVector());
     }
   }
 
diff --git a/components/cronet/ios/cronet_environment.mm b/components/cronet/ios/cronet_environment.mm
index 0154346..be5ee3c 100644
--- a/components/cronet/ios/cronet_environment.mm
+++ b/components/cronet/ios/cronet_environment.mm
@@ -41,6 +41,7 @@
 #include "net/log/net_log_capture_mode.h"
 #include "net/log/write_to_file_net_log_observer.h"
 #include "net/proxy/proxy_service.h"
+#include "net/quic/core/quic_versions.h"
 #include "net/socket/ssl_client_socket.h"
 #include "net/ssl/channel_id_service.h"
 #include "net/url_request/http_user_agent_settings.h"
@@ -333,13 +334,15 @@
 
   std::unique_ptr<net::HttpServerProperties> http_server_properties(
       new net::HttpServerPropertiesImpl());
+
   for (const auto& quic_hint : quic_hints_) {
     net::AlternativeService alternative_service(net::kProtoQUIC, "",
                                                 quic_hint.port());
     url::SchemeHostPort quic_hint_server("https", quic_hint.host(),
                                          quic_hint.port());
-    http_server_properties->SetAlternativeService(
-        quic_hint_server, alternative_service, base::Time::Max());
+    http_server_properties->SetQuicAlternativeService(
+        quic_hint_server, alternative_service, base::Time::Max(),
+        net::QuicVersionVector());
   }
 
   context_builder.SetHttpServerProperties(std::move(http_server_properties));
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc
index 15e9b94..a9154966 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc
@@ -1168,12 +1168,6 @@
   return false;
 }
 
-void DataReductionProxyConfig::GetNetworkList(
-    net::NetworkInterfaceList* interfaces,
-    int policy) {
-  net::GetNetworkList(interfaces, policy);
-}
-
 const std::vector<base::TimeDelta>&
 DataReductionProxyConfig::GetLofiAccuracyRecordingIntervals() const {
   DCHECK(thread_checker_.CalledOnValidThread());
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h
index ec1889f..b53ea782 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h
@@ -22,7 +22,6 @@
 #include "components/previews/core/previews_experiments.h"
 #include "net/base/net_errors.h"
 #include "net/base/network_change_notifier.h"
-#include "net/base/network_interfaces.h"
 #include "net/log/net_log_with_source.h"
 #include "net/nqe/effective_connection_type.h"
 #include "net/nqe/network_quality_estimator.h"
@@ -218,11 +217,6 @@
   std::vector<DataReductionProxyServer> GetProxiesForHttp() const;
 
  protected:
-  // Virtualized for mocking. Returns the list of network interfaces in use.
-  // |interfaces| can be null.
-  virtual void GetNetworkList(net::NetworkInterfaceList* interfaces,
-                              int policy);
-
   // Virtualized for testing. Returns the list of intervals at which accuracy of
   // network quality prediction should be recorded.
   virtual const std::vector<base::TimeDelta>&
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.cc
index ae158e4..ce2beb1 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.cc
@@ -53,7 +53,6 @@
       network_quality_prohibitively_slow_(false),
       lofi_accuracy_recording_intervals_set_(false),
       is_captive_portal_(false) {
-  network_interfaces_.reset(new net::NetworkInterfaceList());
 }
 
 TestDataReductionProxyConfig::~TestDataReductionProxyConfig() {
@@ -67,13 +66,6 @@
       network_quality_estimator);
 }
 
-void TestDataReductionProxyConfig::GetNetworkList(
-    net::NetworkInterfaceList* interfaces,
-    int policy) {
-  for (size_t i = 0; i < network_interfaces_->size(); ++i)
-    interfaces->push_back(network_interfaces_->at(i));
-}
-
 void TestDataReductionProxyConfig::ResetParamFlagsForTest() {
   config_values_ = base::MakeUnique<TestDataReductionProxyParams>();
 }
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h
index 11476d8..5e3b62ca3 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h
@@ -13,7 +13,6 @@
 #include "base/optional.h"
 #include "base/time/time.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h"
-#include "net/base/network_interfaces.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 namespace base {
@@ -58,9 +57,6 @@
 
   ~TestDataReductionProxyConfig() override;
 
-  void GetNetworkList(net::NetworkInterfaceList* interfaces,
-                      int policy) override;
-
   // Allows tests to reset the params being used for configuration.
   void ResetParamFlagsForTest();
 
@@ -80,10 +76,6 @@
   bool IsNetworkQualityProhibitivelySlow(
       const net::NetworkQualityEstimator* network_quality_estimator) override;
 
-  net::NetworkInterfaceList* interfaces() {
-    return network_interfaces_.get();
-  }
-
   void SetLofiAccuracyRecordingIntervals(
       const std::vector<base::TimeDelta>& lofi_accuracy_recording_intervals);
 
@@ -129,8 +121,6 @@
   base::Optional<bool> was_data_reduction_proxy_used_;
   base::Optional<int> proxy_index_;
 
-  std::unique_ptr<net::NetworkInterfaceList> network_interfaces_;
-
   bool network_quality_prohibitively_slow_set_;
   // True if the network quality is slow enough to turn Lo-Fi ON.
   bool network_quality_prohibitively_slow_;
diff --git a/components/exo/pointer.cc b/components/exo/pointer.cc
index ea1e1e2..a270ad00 100644
--- a/components/exo/pointer.cc
+++ b/components/exo/pointer.cc
@@ -61,14 +61,14 @@
   return offset.LengthSquared() < (2 * kLocatedEventEpsilonSquared);
 }
 
-float GetCaptureScale() {
-  float capture_scale = 1.0f;
+display::ManagedDisplayInfo GetCaptureDisplayInfo() {
+  display::ManagedDisplayInfo capture_info;
   for (const auto& display : display::Screen::GetScreen()->GetAllDisplays()) {
     const auto& info = WMHelper::GetInstance()->GetDisplayInfo(display.id());
-    if (info.device_scale_factor() > capture_scale)
-      capture_scale = info.device_scale_factor();
+    if (info.device_scale_factor() >= capture_info.device_scale_factor())
+      capture_info = info;
   }
-  return capture_scale;
+  return capture_info;
 }
 
 }  // namespace
@@ -79,7 +79,8 @@
 Pointer::Pointer(PointerDelegate* delegate)
     : delegate_(delegate),
       cursor_(ui::CursorType::kNull),
-      capture_scale_(GetCaptureScale()),
+      capture_scale_(GetCaptureDisplayInfo().device_scale_factor()),
+      capture_ratio_(GetCaptureDisplayInfo().GetDensityRatio()),
       cursor_capture_source_id_(base::UnguessableToken::Create()),
       cursor_capture_weak_ptr_factory_(this) {
   auto* helper = WMHelper::GetInstance();
@@ -277,7 +278,9 @@
 
 void Pointer::OnDisplayConfigurationChanged() {
   UpdatePointerSurface(surface_);
-  capture_scale_ = GetCaptureScale();
+  auto info = GetCaptureDisplayInfo();
+  capture_scale_ = info.device_scale_factor();
+  capture_ratio_ = info.GetDensityRatio();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -387,12 +390,12 @@
     cursor_ = ui::CursorType::kNone;
   } else {
     SkBitmap bitmap = cursor_bitmap_;
-    gfx::Point hotspot = gfx::ScaleToFlooredPoint(hotspot_, capture_scale_);
+    gfx::Point hotspot = gfx::ScaleToFlooredPoint(hotspot_, capture_ratio_);
 
     auto* helper = WMHelper::GetInstance();
     const display::Display& display = helper->GetCursorDisplay();
-    float scale = helper->GetDisplayInfo(display.id()).device_scale_factor() /
-                  capture_scale_;
+    float scale =
+        helper->GetDisplayInfo(display.id()).GetDensityRatio() / capture_ratio_;
 
     if (helper->GetCursorSize() == ui::CursorSize::kLarge)
       scale *= kLargeCursorScale;
diff --git a/components/exo/pointer.h b/components/exo/pointer.h
index 6dc1a0b..c64d1af 100644
--- a/components/exo/pointer.h
+++ b/components/exo/pointer.h
@@ -111,10 +111,13 @@
   // The current cursor.
   ui::Cursor cursor_;
 
-  // Scale at which cursor snapshot is captured. The resulting bitmap is scaled
-  // on displays whose DSF does not match this scale.
+  // Scale at which cursor snapshot is captured.
   float capture_scale_;
 
+  // Density ratio of the cursor snapshot. The bitmap is scaled on displays with
+  // a different ratio.
+  float capture_ratio_;
+
   // Source used for cursor capture copy output requests.
   const base::UnguessableToken cursor_capture_source_id_;
 
diff --git a/components/gcm_driver/crypto/BUILD.gn b/components/gcm_driver/crypto/BUILD.gn
index 20d24ee..5ee3c7d 100644
--- a/components/gcm_driver/crypto/BUILD.gn
+++ b/components/gcm_driver/crypto/BUILD.gn
@@ -8,6 +8,8 @@
   sources = [
     "encryption_header_parsers.cc",
     "encryption_header_parsers.h",
+    "gcm_decryption_result.cc",
+    "gcm_decryption_result.h",
     "gcm_encryption_provider.cc",
     "gcm_encryption_provider.h",
     "gcm_key_store.cc",
diff --git a/components/gcm_driver/crypto/gcm_decryption_result.cc b/components/gcm_driver/crypto/gcm_decryption_result.cc
new file mode 100644
index 0000000..099e7f5
--- /dev/null
+++ b/components/gcm_driver/crypto/gcm_decryption_result.cc
@@ -0,0 +1,39 @@
+// 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/gcm_driver/crypto/gcm_decryption_result.h"
+
+#include "base/logging.h"
+
+namespace gcm {
+
+std::string ToGCMDecryptionResultDetailsString(GCMDecryptionResult result) {
+  switch (result) {
+    case GCMDecryptionResult::UNENCRYPTED:
+      return "Message was not encrypted";
+    case GCMDecryptionResult::DECRYPTED_DRAFT_03:
+      return "Message decrypted (draft 03)";
+    case GCMDecryptionResult::DECRYPTED_DRAFT_08:
+      return "Message decrypted (draft 08)";
+    case GCMDecryptionResult::INVALID_ENCRYPTION_HEADER:
+      return "Invalid format for the Encryption header";
+    case GCMDecryptionResult::INVALID_CRYPTO_KEY_HEADER:
+      return "Invalid format for the Crypto-Key header";
+    case GCMDecryptionResult::NO_KEYS:
+      return "There are no associated keys with the subscription";
+    case GCMDecryptionResult::INVALID_SHARED_SECRET:
+      return "The shared secret cannot be derived from the keying material";
+    case GCMDecryptionResult::INVALID_PAYLOAD:
+      return "AES-GCM decryption failed";
+    case GCMDecryptionResult::INVALID_BINARY_HEADER:
+      return "The message's binary header could not be parsed.";
+    case GCMDecryptionResult::ENUM_SIZE:
+      break;  // deliberate fall-through
+  }
+
+  NOTREACHED();
+  return "(invalid result)";
+}
+
+}  // namespace gcm
diff --git a/components/gcm_driver/crypto/gcm_decryption_result.h b/components/gcm_driver/crypto/gcm_decryption_result.h
new file mode 100644
index 0000000..0126a5a6
--- /dev/null
+++ b/components/gcm_driver/crypto/gcm_decryption_result.h
@@ -0,0 +1,58 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_GCM_DRIVER_CRYPTO_GCM_DECRYPTION_RESULT_H_
+#define COMPONENTS_GCM_DRIVER_CRYPTO_GCM_DECRYPTION_RESULT_H_
+
+#include <string>
+
+namespace gcm {
+
+// Result of decrypting an incoming message. The values of these reasons must
+// not be changed as they are being recorded using UMA. When adding a value,
+// please update GCMDecryptionResult in //tools/metrics/histograms/enums.xml.
+enum class GCMDecryptionResult {
+  // The message had not been encrypted by the sender.
+  UNENCRYPTED = 0,
+
+  // The message had been encrypted by the sender, and could successfully be
+  // decrypted for the registration it has been received for. The encryption
+  // scheme used for the message was draft-ietf-webpush-encryption-03.
+  DECRYPTED_DRAFT_03 = 1,
+
+  // The contents of the Encryption HTTP header could not be parsed.
+  INVALID_ENCRYPTION_HEADER = 2,
+
+  // The contents of the Crypto-Key HTTP header could not be parsed.
+  INVALID_CRYPTO_KEY_HEADER = 3,
+
+  // No public/private key-pair was associated with the app_id.
+  NO_KEYS = 4,
+
+  // The shared secret cannot be derived from the keying material.
+  INVALID_SHARED_SECRET = 5,
+
+  // The payload could not be decrypted as AES-128-GCM.
+  INVALID_PAYLOAD = 6,
+
+  // The binary header leading the ciphertext could not be parsed. Only
+  // applicable to messages encrypted per draft-ietf-webpush-encryption-08.
+  INVALID_BINARY_HEADER = 7,
+
+  // The message had been encrypted by the sender, and could successfully be
+  // decrypted for the registration it has been received for. The encryption
+  // scheme used for the message was draft-ietf-webpush-encryption-08.
+  DECRYPTED_DRAFT_08 = 8,
+
+  // Should be one more than the otherwise highest value in this enumeration.
+  ENUM_SIZE = DECRYPTED_DRAFT_08 + 1
+};
+
+// Converts the GCMDecryptionResult value to a string that can be used to
+// explain the issue on chrome://gcm-internals/.
+std::string ToGCMDecryptionResultDetailsString(GCMDecryptionResult result);
+
+}  // namespace gcm
+
+#endif  // COMPONENTS_GCM_DRIVER_CRYPTO_GCM_DECRYPTION_RESULT_H_
diff --git a/components/gcm_driver/crypto/gcm_encryption_provider.cc b/components/gcm_driver/crypto/gcm_encryption_provider.cc
index 6a4bdc5f..20f6d8e4 100644
--- a/components/gcm_driver/crypto/gcm_encryption_provider.cc
+++ b/components/gcm_driver/crypto/gcm_encryption_provider.cc
@@ -11,6 +11,7 @@
 #include "base/logging.h"
 #include "components/gcm_driver/common/gcm_messages.h"
 #include "components/gcm_driver/crypto/encryption_header_parsers.h"
+#include "components/gcm_driver/crypto/gcm_decryption_result.h"
 #include "components/gcm_driver/crypto/gcm_key_store.h"
 #include "components/gcm_driver/crypto/gcm_message_cryptographer.h"
 #include "components/gcm_driver/crypto/message_payload_parser.h"
@@ -34,33 +35,6 @@
 
 }  // namespace
 
-std::string GCMEncryptionProvider::ToDecryptionResultDetailsString(
-    DecryptionResult result) {
-  switch (result) {
-    case DECRYPTION_RESULT_UNENCRYPTED:
-      return "Message was not encrypted";
-    case DECRYPTION_RESULT_DECRYPTED_DRAFT_03:
-      return "Message decrypted (draft 03)";
-    case DECRYPTION_RESULT_DECRYPTED_DRAFT_08:
-      return "Message decrypted (draft 08)";
-    case DECRYPTION_RESULT_INVALID_ENCRYPTION_HEADER:
-      return "Invalid format for the Encryption header";
-    case DECRYPTION_RESULT_INVALID_CRYPTO_KEY_HEADER:
-      return "Invalid format for the Crypto-Key header";
-    case DECRYPTION_RESULT_NO_KEYS:
-      return "There are no associated keys with the subscription";
-    case DECRYPTION_RESULT_INVALID_SHARED_SECRET:
-      return "The shared secret cannot be derived from the keying material";
-    case DECRYPTION_RESULT_INVALID_PAYLOAD:
-      return "AES-GCM decryption failed";
-    case DECRYPTION_RESULT_INVALID_BINARY_HEADER:
-      return "The message's binary header could not be parsed.";
-  }
-
-  NOTREACHED();
-  return "(invalid result)";
-}
-
 GCMEncryptionProvider::GCMEncryptionProvider()
     : weak_ptr_factory_(this) {
 }
@@ -129,7 +103,7 @@
     const MessageCallback& callback) {
   DCHECK(key_store_);
   if (!IsEncryptedMessage(message)) {
-    callback.Run(DECRYPTION_RESULT_UNENCRYPTED, message);
+    callback.Run(GCMDecryptionResult::UNENCRYPTED, message);
     return;
   }
 
@@ -146,7 +120,8 @@
     MessagePayloadParser parser(message.raw_data);
     if (!parser.IsValid()) {
       DLOG(ERROR) << "Unable to parse the message's binary header";
-      callback.Run(DECRYPTION_RESULT_INVALID_BINARY_HEADER, IncomingMessage());
+      callback.Run(GCMDecryptionResult::INVALID_BINARY_HEADER,
+                   IncomingMessage());
       return;
     }
 
@@ -170,7 +145,7 @@
         encryption_header->second.begin(), encryption_header->second.end());
     if (!encryption_header_iterator.GetNext()) {
       DLOG(ERROR) << "Unable to parse the value of the Encryption header";
-      callback.Run(DECRYPTION_RESULT_INVALID_ENCRYPTION_HEADER,
+      callback.Run(GCMDecryptionResult::INVALID_ENCRYPTION_HEADER,
                    IncomingMessage());
       return;
     }
@@ -178,7 +153,7 @@
     if (encryption_header_iterator.salt().size() !=
         GCMMessageCryptographer::kSaltSize) {
       DLOG(ERROR) << "Invalid values supplied in the Encryption header";
-      callback.Run(DECRYPTION_RESULT_INVALID_ENCRYPTION_HEADER,
+      callback.Run(GCMDecryptionResult::INVALID_ENCRYPTION_HEADER,
                    IncomingMessage());
       return;
     }
@@ -190,7 +165,7 @@
         crypto_key_header->second.begin(), crypto_key_header->second.end());
     if (!crypto_key_header_iterator.GetNext()) {
       DLOG(ERROR) << "Unable to parse the value of the Crypto-Key header";
-      callback.Run(DECRYPTION_RESULT_INVALID_CRYPTO_KEY_HEADER,
+      callback.Run(GCMDecryptionResult::INVALID_CRYPTO_KEY_HEADER,
                    IncomingMessage());
       return;
     }
@@ -219,7 +194,7 @@
 
     if (!valid_crypto_key_header) {
       DLOG(ERROR) << "Invalid values supplied in the Crypto-Key header";
-      callback.Run(DECRYPTION_RESULT_INVALID_CRYPTO_KEY_HEADER,
+      callback.Run(GCMDecryptionResult::INVALID_CRYPTO_KEY_HEADER,
                    IncomingMessage());
       return;
     }
@@ -284,7 +259,7 @@
     const std::string& auth_secret) {
   if (!pair.IsInitialized()) {
     DLOG(ERROR) << "Unable to retrieve the keys for the incoming message.";
-    callback.Run(DECRYPTION_RESULT_NO_KEYS, IncomingMessage());
+    callback.Run(GCMDecryptionResult::NO_KEYS, IncomingMessage());
     return;
   }
 
@@ -294,7 +269,7 @@
   if (!ComputeSharedP256Secret(pair.private_key(), pair.public_key_x509(),
                                public_key, &shared_secret)) {
     DLOG(ERROR) << "Unable to calculate the shared secret.";
-    callback.Run(DECRYPTION_RESULT_INVALID_SHARED_SECRET, IncomingMessage());
+    callback.Run(GCMDecryptionResult::INVALID_SHARED_SECRET, IncomingMessage());
     return;
   }
 
@@ -306,7 +281,7 @@
                              auth_secret, salt, ciphertext, record_size,
                              &plaintext)) {
     DLOG(ERROR) << "Unable to decrypt the incoming data.";
-    callback.Run(DECRYPTION_RESULT_INVALID_PAYLOAD, IncomingMessage());
+    callback.Run(GCMDecryptionResult::INVALID_PAYLOAD, IncomingMessage());
     return;
   }
 
@@ -321,8 +296,8 @@
   DCHECK_EQ(0u, decrypted_message.data.size());
 
   callback.Run(version == GCMMessageCryptographer::Version::DRAFT_03
-                   ? DECRYPTION_RESULT_DECRYPTED_DRAFT_03
-                   : DECRYPTION_RESULT_DECRYPTED_DRAFT_08,
+                   ? GCMDecryptionResult::DECRYPTED_DRAFT_03
+                   : GCMDecryptionResult::DECRYPTED_DRAFT_08,
                decrypted_message);
 }
 
diff --git a/components/gcm_driver/crypto/gcm_encryption_provider.h b/components/gcm_driver/crypto/gcm_encryption_provider.h
index 65c1c9cc..7d65b17e0 100644
--- a/components/gcm_driver/crypto/gcm_encryption_provider.h
+++ b/components/gcm_driver/crypto/gcm_encryption_provider.h
@@ -23,6 +23,7 @@
 
 namespace gcm {
 
+enum class GCMDecryptionResult;
 class GCMKeyStore;
 struct IncomingMessage;
 class KeyPair;
@@ -31,44 +32,6 @@
 // and decryption of incoming messages.
 class GCMEncryptionProvider {
  public:
-  // Result of decrypting an incoming message. The values of these reasons must
-  // not be changed, because they are being recorded using UMA.
-  enum DecryptionResult {
-    // The message had not been encrypted by the sender.
-    DECRYPTION_RESULT_UNENCRYPTED = 0,
-
-    // The message had been encrypted by the sender, and could successfully be
-    // decrypted for the registration it has been received for. The encryption
-    // scheme used for the message was draft-ietf-webpush-encryption-03.
-    DECRYPTION_RESULT_DECRYPTED_DRAFT_03 = 1,
-
-    // The contents of the Encryption HTTP header could not be parsed.
-    DECRYPTION_RESULT_INVALID_ENCRYPTION_HEADER = 2,
-
-    // The contents of the Crypto-Key HTTP header could not be parsed.
-    DECRYPTION_RESULT_INVALID_CRYPTO_KEY_HEADER = 3,
-
-    // No public/private key-pair was associated with the app_id.
-    DECRYPTION_RESULT_NO_KEYS = 4,
-
-    // The shared secret cannot be derived from the keying material.
-    DECRYPTION_RESULT_INVALID_SHARED_SECRET = 5,
-
-    // The payload could not be decrypted as AES-128-GCM.
-    DECRYPTION_RESULT_INVALID_PAYLOAD = 6,
-
-    // The binary header leading the ciphertext could not be parsed. Only
-    // applicable to messages encrypted per draft-ietf-webpush-encryption-08.
-    DECRYPTION_RESULT_INVALID_BINARY_HEADER = 7,
-
-    // The message had been encrypted by the sender, and could successfully be
-    // decrypted for the registration it has been received for. The encryption
-    // scheme used for the message was draft-ietf-webpush-encryption-08.
-    DECRYPTION_RESULT_DECRYPTED_DRAFT_08 = 8,
-
-    DECRYPTION_RESULT_LAST = DECRYPTION_RESULT_DECRYPTED_DRAFT_08
-  };
-
   // Callback to be invoked when the public key and auth secret are available.
   using EncryptionInfoCallback =
       base::Callback<void(const std::string& p256dh,
@@ -77,12 +40,9 @@
   // Callback to be invoked when a message may have been decrypted, as indicated
   // by the |result|. The |message| contains the dispatchable message in success
   // cases, or will be initialized to an empty, default state for failure.
-  using MessageCallback = base::Callback<void(DecryptionResult result,
+  using MessageCallback = base::Callback<void(GCMDecryptionResult result,
                                               const IncomingMessage& message)>;
 
-  // Converts |result| to a string describing the details of said result.
-  static std::string ToDecryptionResultDetailsString(DecryptionResult result);
-
   GCMEncryptionProvider();
   ~GCMEncryptionProvider();
 
diff --git a/components/gcm_driver/crypto/gcm_encryption_provider_unittest.cc b/components/gcm_driver/crypto/gcm_encryption_provider_unittest.cc
index 0f0fc0c4..3d18c61f 100644
--- a/components/gcm_driver/crypto/gcm_encryption_provider_unittest.cc
+++ b/components/gcm_driver/crypto/gcm_encryption_provider_unittest.cc
@@ -21,6 +21,7 @@
 #include "base/strings/string_util.h"
 #include "base/test/histogram_tester.h"
 #include "components/gcm_driver/common/gcm_messages.h"
+#include "components/gcm_driver/crypto/gcm_decryption_result.h"
 #include "components/gcm_driver/crypto/gcm_key_store.h"
 #include "components/gcm_driver/crypto/gcm_message_cryptographer.h"
 #include "components/gcm_driver/crypto/p256_key_util.h"
@@ -128,9 +129,7 @@
   }
 
   // Returns the result of the previous decryption operation.
-  GCMEncryptionProvider::DecryptionResult decryption_result() {
-    return decryption_result_;
-  }
+  GCMDecryptionResult decryption_result() { return decryption_result_; }
 
   // Returns the message resulting from the previous decryption operation.
   const IncomingMessage& decrypted_message() { return decrypted_message_; }
@@ -146,7 +145,7 @@
                                GCMMessageCryptographer::Version version);
 
  private:
-  void DidDecryptMessage(GCMEncryptionProvider::DecryptionResult result,
+  void DidDecryptMessage(GCMDecryptionResult result,
                          const IncomingMessage& message) {
     decryption_result_ = result;
     decrypted_message_ = message;
@@ -158,8 +157,7 @@
 
   std::unique_ptr<GCMEncryptionProvider> encryption_provider_;
 
-  GCMEncryptionProvider::DecryptionResult decryption_result_ =
-      GCMEncryptionProvider::DECRYPTION_RESULT_UNENCRYPTED;
+  GCMDecryptionResult decryption_result_ = GCMDecryptionResult::UNENCRYPTED;
 
   IncomingMessage decrypted_message_;
 };
@@ -205,7 +203,7 @@
   invalid_message.raw_data = "foo";
 
   ASSERT_NO_FATAL_FAILURE(Decrypt(invalid_message));
-  EXPECT_EQ(GCMEncryptionProvider::DECRYPTION_RESULT_INVALID_ENCRYPTION_HEADER,
+  EXPECT_EQ(GCMDecryptionResult::INVALID_ENCRYPTION_HEADER,
             decryption_result());
 
   IncomingMessage valid_message;
@@ -214,7 +212,7 @@
   valid_message.raw_data = "foo";
 
   ASSERT_NO_FATAL_FAILURE(Decrypt(valid_message));
-  EXPECT_NE(GCMEncryptionProvider::DECRYPTION_RESULT_INVALID_ENCRYPTION_HEADER,
+  EXPECT_NE(GCMDecryptionResult::INVALID_ENCRYPTION_HEADER,
             decryption_result());
 }
 
@@ -228,7 +226,7 @@
   invalid_message.raw_data = "foo";
 
   ASSERT_NO_FATAL_FAILURE(Decrypt(invalid_message));
-  EXPECT_EQ(GCMEncryptionProvider::DECRYPTION_RESULT_INVALID_CRYPTO_KEY_HEADER,
+  EXPECT_EQ(GCMDecryptionResult::INVALID_CRYPTO_KEY_HEADER,
             decryption_result());
 
   IncomingMessage valid_message;
@@ -237,7 +235,7 @@
   valid_message.raw_data = "foo";
 
   ASSERT_NO_FATAL_FAILURE(Decrypt(valid_message));
-  EXPECT_NE(GCMEncryptionProvider::DECRYPTION_RESULT_INVALID_CRYPTO_KEY_HEADER,
+  EXPECT_NE(GCMDecryptionResult::INVALID_CRYPTO_KEY_HEADER,
             decryption_result());
 }
 
@@ -251,7 +249,7 @@
   valid_message.raw_data = "foo";
 
   ASSERT_NO_FATAL_FAILURE(Decrypt(valid_message));
-  EXPECT_NE(GCMEncryptionProvider::DECRYPTION_RESULT_INVALID_CRYPTO_KEY_HEADER,
+  EXPECT_NE(GCMDecryptionResult::INVALID_CRYPTO_KEY_HEADER,
             decryption_result());
 }
 
@@ -265,7 +263,7 @@
   valid_message.raw_data = "foo";
 
   ASSERT_NO_FATAL_FAILURE(Decrypt(valid_message));
-  EXPECT_EQ(GCMEncryptionProvider::DECRYPTION_RESULT_INVALID_CRYPTO_KEY_HEADER,
+  EXPECT_EQ(GCMDecryptionResult::INVALID_CRYPTO_KEY_HEADER,
             decryption_result());
 }
 
@@ -279,8 +277,7 @@
   message.raw_data = "foo";
 
   ASSERT_NO_FATAL_FAILURE(Decrypt(message));
-  EXPECT_EQ(GCMEncryptionProvider::DECRYPTION_RESULT_NO_KEYS,
-            decryption_result());
+  EXPECT_EQ(GCMDecryptionResult::NO_KEYS, decryption_result());
 
   std::string public_key, auth_secret;
   encryption_provider()->GetEncryptionInfo(
@@ -295,8 +292,7 @@
   ASSERT_GT(auth_secret.size(), 0u);
 
   ASSERT_NO_FATAL_FAILURE(Decrypt(message));
-  EXPECT_NE(GCMEncryptionProvider::DECRYPTION_RESULT_NO_KEYS,
-            decryption_result());
+  EXPECT_NE(GCMDecryptionResult::NO_KEYS, decryption_result());
 }
 
 TEST_F(GCMEncryptionProviderTest, VerifiesKeyRemovalGCMRegistration) {
@@ -566,8 +562,8 @@
   // Decrypt the message, and expect everything to go wonderfully well.
   ASSERT_NO_FATAL_FAILURE(Decrypt(message));
   ASSERT_EQ(version == GCMMessageCryptographer::Version::DRAFT_03
-                ? GCMEncryptionProvider::DECRYPTION_RESULT_DECRYPTED_DRAFT_03
-                : GCMEncryptionProvider::DECRYPTION_RESULT_DECRYPTED_DRAFT_08,
+                ? GCMDecryptionResult::DECRYPTED_DRAFT_03
+                : GCMDecryptionResult::DECRYPTED_DRAFT_08,
             decryption_result());
 
   EXPECT_TRUE(decrypted_message().decrypted);
diff --git a/components/gcm_driver/fake_gcm_client.cc b/components/gcm_driver/fake_gcm_client.cc
index 2f992c52..38a1688c97 100644
--- a/components/gcm_driver/fake_gcm_client.cc
+++ b/components/gcm_driver/fake_gcm_client.cc
@@ -166,9 +166,8 @@
                             weak_ptr_factory_.GetWeakPtr(), app_id, message));
 }
 
-void FakeGCMClient::RecordDecryptionFailure(
-    const std::string& app_id,
-    GCMEncryptionProvider::DecryptionResult result) {
+void FakeGCMClient::RecordDecryptionFailure(const std::string& app_id,
+                                            GCMDecryptionResult result) {
   recorder_.RecordDecryptionFailure(app_id, result);
 }
 
diff --git a/components/gcm_driver/fake_gcm_client.h b/components/gcm_driver/fake_gcm_client.h
index 0199b3ab7..9abb5f8 100644
--- a/components/gcm_driver/fake_gcm_client.h
+++ b/components/gcm_driver/fake_gcm_client.h
@@ -62,8 +62,7 @@
             const std::string& receiver_id,
             const OutgoingMessage& message) override;
   void RecordDecryptionFailure(const std::string& app_id,
-                               GCMEncryptionProvider::DecryptionResult result)
-      override;
+                               GCMDecryptionResult result) override;
   void SetRecording(bool recording) override;
   void ClearActivityLogs() override;
   GCMStatistics GetStatistics() const override;
diff --git a/components/gcm_driver/fake_gcm_driver.cc b/components/gcm_driver/fake_gcm_driver.cc
index 9974f92bf..89ceea45 100644
--- a/components/gcm_driver/fake_gcm_driver.cc
+++ b/components/gcm_driver/fake_gcm_driver.cc
@@ -86,10 +86,8 @@
                              const OutgoingMessage& message) {
 }
 
-void FakeGCMDriver::RecordDecryptionFailure(
-    const std::string& app_id,
-    GCMEncryptionProvider::DecryptionResult result) {
-}
+void FakeGCMDriver::RecordDecryptionFailure(const std::string& app_id,
+                                            GCMDecryptionResult result) {}
 
 void FakeGCMDriver::SetAccountTokens(
     const std::vector<GCMClient::AccountTokenInfo>& account_tokens) {
diff --git a/components/gcm_driver/fake_gcm_driver.h b/components/gcm_driver/fake_gcm_driver.h
index 131802e2..ba1664ec 100644
--- a/components/gcm_driver/fake_gcm_driver.h
+++ b/components/gcm_driver/fake_gcm_driver.h
@@ -65,8 +65,7 @@
                 const std::string& receiver_id,
                 const OutgoingMessage& message) override;
   void RecordDecryptionFailure(const std::string& app_id,
-                               GCMEncryptionProvider::DecryptionResult result)
-      override;
+                               GCMDecryptionResult result) override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(FakeGCMDriver);
diff --git a/components/gcm_driver/gcm_client.h b/components/gcm_driver/gcm_client.h
index 921c414..64cbd56 100644
--- a/components/gcm_driver/gcm_client.h
+++ b/components/gcm_driver/gcm_client.h
@@ -14,7 +14,6 @@
 
 #include "base/memory/linked_ptr.h"
 #include "components/gcm_driver/common/gcm_messages.h"
-#include "components/gcm_driver/crypto/gcm_encryption_provider.h"
 #include "components/gcm_driver/gcm_activity.h"
 #include "components/gcm_driver/registration_info.h"
 
@@ -33,8 +32,9 @@
 
 namespace gcm {
 
-class Encryptor;
 struct AccountMapping;
+class Encryptor;
+enum class GCMDecryptionResult;
 
 // Interface that encapsulates the network communications with the Google Cloud
 // Messaging server. This interface is not supposed to be thread-safe.
@@ -288,9 +288,8 @@
                     const OutgoingMessage& message) = 0;
 
   // Records a decryption failure due to |result| for the |app_id|.
-  virtual void RecordDecryptionFailure(
-      const std::string& app_id,
-      GCMEncryptionProvider::DecryptionResult result) = 0;
+  virtual void RecordDecryptionFailure(const std::string& app_id,
+                                       GCMDecryptionResult result) = 0;
 
   // Enables or disables internal activity recording.
   virtual void SetRecording(bool recording) = 0;
diff --git a/components/gcm_driver/gcm_client_impl.cc b/components/gcm_driver/gcm_client_impl.cc
index 8754ba2..9cd3eb9 100644
--- a/components/gcm_driver/gcm_client_impl.cc
+++ b/components/gcm_driver/gcm_client_impl.cc
@@ -24,6 +24,7 @@
 #include "base/time/default_clock.h"
 #include "base/timer/timer.h"
 #include "components/crx_file/id_util.h"
+#include "components/gcm_driver/crypto/gcm_decryption_result.h"
 #include "components/gcm_driver/gcm_account_mapper.h"
 #include "components/gcm_driver/gcm_backoff_policy.h"
 #include "google_apis/gcm/base/encryptor.h"
@@ -1217,9 +1218,8 @@
   return std::string();
 }
 
-void GCMClientImpl::RecordDecryptionFailure(
-    const std::string& app_id,
-    GCMEncryptionProvider::DecryptionResult result) {
+void GCMClientImpl::RecordDecryptionFailure(const std::string& app_id,
+                                            GCMDecryptionResult result) {
   recorder_.RecordDecryptionFailure(app_id, result);
 }
 
diff --git a/components/gcm_driver/gcm_client_impl.h b/components/gcm_driver/gcm_client_impl.h
index 62c4d3b..68c9e2e4 100644
--- a/components/gcm_driver/gcm_client_impl.h
+++ b/components/gcm_driver/gcm_client_impl.h
@@ -125,8 +125,7 @@
             const std::string& receiver_id,
             const OutgoingMessage& message) override;
   void RecordDecryptionFailure(const std::string& app_id,
-                               GCMEncryptionProvider::DecryptionResult result)
-      override;
+                               GCMDecryptionResult result) override;
   void SetRecording(bool recording) override;
   void ClearActivityLogs() override;
   GCMStatistics GetStatistics() const override;
diff --git a/components/gcm_driver/gcm_driver.cc b/components/gcm_driver/gcm_driver.cc
index 0f797f7..c13ae9e 100644
--- a/components/gcm_driver/gcm_driver.cc
+++ b/components/gcm_driver/gcm_driver.cc
@@ -12,6 +12,7 @@
 #include "base/files/file_path.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
+#include "components/gcm_driver/crypto/gcm_decryption_result.h"
 #include "components/gcm_driver/gcm_app_handler.h"
 
 namespace gcm {
@@ -279,17 +280,16 @@
                                   weak_ptr_factory_.GetWeakPtr(), app_id));
 }
 
-void GCMDriver::DispatchMessageInternal(
-    const std::string& app_id,
-    GCMEncryptionProvider::DecryptionResult result,
-    const IncomingMessage& message) {
+void GCMDriver::DispatchMessageInternal(const std::string& app_id,
+                                        GCMDecryptionResult result,
+                                        const IncomingMessage& message) {
   UMA_HISTOGRAM_ENUMERATION("GCM.Crypto.DecryptMessageResult", result,
-                            GCMEncryptionProvider::DECRYPTION_RESULT_LAST + 1);
+                            GCMDecryptionResult::ENUM_SIZE);
 
   switch (result) {
-    case GCMEncryptionProvider::DECRYPTION_RESULT_UNENCRYPTED:
-    case GCMEncryptionProvider::DECRYPTION_RESULT_DECRYPTED_DRAFT_03:
-    case GCMEncryptionProvider::DECRYPTION_RESULT_DECRYPTED_DRAFT_08: {
+    case GCMDecryptionResult::UNENCRYPTED:
+    case GCMDecryptionResult::DECRYPTED_DRAFT_03:
+    case GCMDecryptionResult::DECRYPTED_DRAFT_08: {
       GCMAppHandler* handler = GetAppHandler(app_id);
       if (handler)
         handler->OnMessage(app_id, message);
@@ -298,14 +298,16 @@
       // chrome://gcm-internals and send a delivery receipt.
       return;
     }
-    case GCMEncryptionProvider::DECRYPTION_RESULT_INVALID_ENCRYPTION_HEADER:
-    case GCMEncryptionProvider::DECRYPTION_RESULT_INVALID_CRYPTO_KEY_HEADER:
-    case GCMEncryptionProvider::DECRYPTION_RESULT_NO_KEYS:
-    case GCMEncryptionProvider::DECRYPTION_RESULT_INVALID_SHARED_SECRET:
-    case GCMEncryptionProvider::DECRYPTION_RESULT_INVALID_PAYLOAD:
-    case GCMEncryptionProvider::DECRYPTION_RESULT_INVALID_BINARY_HEADER:
+    case GCMDecryptionResult::INVALID_ENCRYPTION_HEADER:
+    case GCMDecryptionResult::INVALID_CRYPTO_KEY_HEADER:
+    case GCMDecryptionResult::NO_KEYS:
+    case GCMDecryptionResult::INVALID_SHARED_SECRET:
+    case GCMDecryptionResult::INVALID_PAYLOAD:
+    case GCMDecryptionResult::INVALID_BINARY_HEADER:
       RecordDecryptionFailure(app_id, result);
       return;
+    case GCMDecryptionResult::ENUM_SIZE:
+      break;  // deliberate fall-through
   }
 
   NOTREACHED();
diff --git a/components/gcm_driver/gcm_driver.h b/components/gcm_driver/gcm_driver.h
index 8d671b7d..0bdb150 100644
--- a/components/gcm_driver/gcm_driver.h
+++ b/components/gcm_driver/gcm_driver.h
@@ -27,6 +27,7 @@
 
 class GCMAppHandler;
 class GCMConnectionObserver;
+enum class GCMDecryptionResult;
 struct AccountMapping;
 
 // Provides the InstanceID support via GCMDriver.
@@ -279,9 +280,8 @@
                         const OutgoingMessage& message) = 0;
 
   // Platform-specific implementation of recording message decryption failures.
-  virtual void RecordDecryptionFailure(
-      const std::string& app_id,
-      GCMEncryptionProvider::DecryptionResult result) = 0;
+  virtual void RecordDecryptionFailure(const std::string& app_id,
+                                       GCMDecryptionResult result) = 0;
 
   // Runs the Register callback.
   void RegisterFinished(const std::string& app_id,
@@ -323,7 +323,7 @@
   // if |result| indicates that it is safe to do so, or will report a decryption
   // failure for the |app_id| otherwise.
   void DispatchMessageInternal(const std::string& app_id,
-                               GCMEncryptionProvider::DecryptionResult result,
+                               GCMDecryptionResult result,
                                const IncomingMessage& message);
 
   // Called after unregistration completes in order to trigger the pending
diff --git a/components/gcm_driver/gcm_driver_android.cc b/components/gcm_driver/gcm_driver_android.cc
index b7230f2..702ac28 100644
--- a/components/gcm_driver/gcm_driver_android.cc
+++ b/components/gcm_driver/gcm_driver_android.cc
@@ -267,9 +267,8 @@
   NOTIMPLEMENTED();
 }
 
-void GCMDriverAndroid::RecordDecryptionFailure(
-    const std::string& app_id,
-    GCMEncryptionProvider::DecryptionResult result) {
+void GCMDriverAndroid::RecordDecryptionFailure(const std::string& app_id,
+                                               GCMDecryptionResult result) {
   recorder_.RecordDecryptionFailure(app_id, result);
 }
 
diff --git a/components/gcm_driver/gcm_driver_android.h b/components/gcm_driver/gcm_driver_android.h
index fb37f754..7cfcd63 100644
--- a/components/gcm_driver/gcm_driver_android.h
+++ b/components/gcm_driver/gcm_driver_android.h
@@ -99,8 +99,7 @@
                 const std::string& receiver_id,
                 const OutgoingMessage& message) override;
   void RecordDecryptionFailure(const std::string& app_id,
-                               GCMEncryptionProvider::DecryptionResult result)
-      override;
+                               GCMDecryptionResult result) override;
 
  private:
   base::android::ScopedJavaGlobalRef<jobject> java_ref_;
diff --git a/components/gcm_driver/gcm_driver_desktop.cc b/components/gcm_driver/gcm_driver_desktop.cc
index 9c3fa552..67867c3 100644
--- a/components/gcm_driver/gcm_driver_desktop.cc
+++ b/components/gcm_driver/gcm_driver_desktop.cc
@@ -114,7 +114,7 @@
                    const std::string& scope);
 
   void RecordDecryptionFailure(const std::string& app_id,
-                               GCMEncryptionProvider::DecryptionResult result);
+                               GCMDecryptionResult result);
 
   // For testing purpose. Can be called from UI thread. Use with care.
   GCMClient* gcm_client_for_testing() const { return gcm_client_.get(); }
@@ -518,7 +518,7 @@
 
 void GCMDriverDesktop::IOWorker::RecordDecryptionFailure(
     const std::string& app_id,
-    GCMEncryptionProvider::DecryptionResult result) {
+    GCMDecryptionResult result) {
   DCHECK(io_thread_->RunsTasksInCurrentSequence());
   gcm_client_->RecordDecryptionFailure(app_id, result);
 }
@@ -798,9 +798,8 @@
                  message));
 }
 
-void GCMDriverDesktop::RecordDecryptionFailure(
-    const std::string& app_id,
-    GCMEncryptionProvider::DecryptionResult result) {
+void GCMDriverDesktop::RecordDecryptionFailure(const std::string& app_id,
+                                               GCMDecryptionResult result) {
   DCHECK(ui_thread_->RunsTasksInCurrentSequence());
   io_thread_->PostTask(
       FROM_HERE,
diff --git a/components/gcm_driver/gcm_driver_desktop.h b/components/gcm_driver/gcm_driver_desktop.h
index c7568035..90e5d1c 100644
--- a/components/gcm_driver/gcm_driver_desktop.h
+++ b/components/gcm_driver/gcm_driver_desktop.h
@@ -17,6 +17,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "base/tuple.h"
+#include "components/gcm_driver/crypto/gcm_decryption_result.h"
 #include "components/gcm_driver/gcm_channel_status_syncer.h"
 #include "components/gcm_driver/gcm_client.h"
 #include "components/gcm_driver/gcm_connection_observer.h"
@@ -38,6 +39,7 @@
 class GCMAccountMapper;
 class GCMAppHandler;
 class GCMClientFactory;
+enum class GCMDecryptionResult;
 class GCMDelayedTaskController;
 
 // GCMDriver implementation for desktop and Chrome OS, using GCMClient.
@@ -107,8 +109,7 @@
                 const std::string& receiver_id,
                 const OutgoingMessage& message) override;
   void RecordDecryptionFailure(const std::string& app_id,
-                               GCMEncryptionProvider::DecryptionResult result)
-      override;
+                               GCMDecryptionResult result) override;
 
   // InstanceIDHandler implementation:
   void GetToken(const std::string& app_id,
diff --git a/components/gcm_driver/gcm_stats_recorder_android.cc b/components/gcm_driver/gcm_stats_recorder_android.cc
index fedd7ba..dc3137be 100644
--- a/components/gcm_driver/gcm_stats_recorder_android.cc
+++ b/components/gcm_driver/gcm_stats_recorder_android.cc
@@ -4,6 +4,7 @@
 
 #include <stddef.h>
 
+#include "components/gcm_driver/crypto/gcm_decryption_result.h"
 #include "components/gcm_driver/gcm_stats_recorder_android.h"
 
 namespace gcm {
@@ -125,19 +126,16 @@
 
 void GCMStatsRecorderAndroid::RecordDecryptionFailure(
     const std::string& app_id,
-    GCMEncryptionProvider::DecryptionResult result) {
-  DCHECK_NE(result, GCMEncryptionProvider::DECRYPTION_RESULT_UNENCRYPTED);
-  DCHECK_NE(result,
-            GCMEncryptionProvider::DECRYPTION_RESULT_DECRYPTED_DRAFT_03);
-  DCHECK_NE(result,
-            GCMEncryptionProvider::DECRYPTION_RESULT_DECRYPTED_DRAFT_08);
+    GCMDecryptionResult result) {
+  DCHECK_NE(result, GCMDecryptionResult::UNENCRYPTED);
+  DCHECK_NE(result, GCMDecryptionResult::DECRYPTED_DRAFT_03);
+  DCHECK_NE(result, GCMDecryptionResult::DECRYPTED_DRAFT_08);
   if (!is_recording_)
     return;
 
   DecryptionFailureActivity activity;
   activity.app_id = app_id;
-  activity.details =
-      GCMEncryptionProvider::ToDecryptionResultDetailsString(result);
+  activity.details = ToGCMDecryptionResultDetailsString(result);
 
   decryption_failure_activities_.push_front(activity);
   if (decryption_failure_activities_.size() > MAX_LOGGED_ACTIVITY_COUNT)
diff --git a/components/gcm_driver/gcm_stats_recorder_android.h b/components/gcm_driver/gcm_stats_recorder_android.h
index 64d0575..6a0c79a 100644
--- a/components/gcm_driver/gcm_stats_recorder_android.h
+++ b/components/gcm_driver/gcm_stats_recorder_android.h
@@ -9,11 +9,12 @@
 #include <string>
 
 #include "base/macros.h"
-#include "components/gcm_driver/crypto/gcm_encryption_provider.h"
 #include "components/gcm_driver/gcm_activity.h"
 
 namespace gcm {
 
+enum class GCMDecryptionResult;
+
 // Stats recorder for Android, used for recording stats and activities on the
 // GCM Driver level for debugging purposes. Based on the GCMStatsRecorder, as
 // defined in the GCM Engine, which does not exist on Android.
@@ -62,7 +63,7 @@
 
   // Records a message decryption failure caused by |result| for |app_id|.
   void RecordDecryptionFailure(const std::string& app_id,
-                               GCMEncryptionProvider::DecryptionResult result);
+                               GCMDecryptionResult result);
 
   bool is_recording() const { return is_recording_; }
   void set_is_recording(bool recording) { is_recording_ = recording; }
diff --git a/components/gcm_driver/gcm_stats_recorder_android_unittest.cc b/components/gcm_driver/gcm_stats_recorder_android_unittest.cc
index e7b3267..d789e769 100644
--- a/components/gcm_driver/gcm_stats_recorder_android_unittest.cc
+++ b/components/gcm_driver/gcm_stats_recorder_android_unittest.cc
@@ -6,7 +6,7 @@
 
 #include <stddef.h>
 
-#include "components/gcm_driver/crypto/gcm_encryption_provider.h"
+#include "components/gcm_driver/crypto/gcm_decryption_result.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace gcm {
@@ -58,8 +58,8 @@
                                      42 /* message_byte_size */);
   EXPECT_EQ(5u, activity_recorded_calls());
 
-  recorder.RecordDecryptionFailure(
-      kTestAppId, GCMEncryptionProvider::DECRYPTION_RESULT_INVALID_PAYLOAD);
+  recorder.RecordDecryptionFailure(kTestAppId,
+                                   GCMDecryptionResult::INVALID_PAYLOAD);
   EXPECT_EQ(6u, activity_recorded_calls());
 
   RecordedActivities activities;
diff --git a/components/gcm_driver/gcm_stats_recorder_impl.cc b/components/gcm_driver/gcm_stats_recorder_impl.cc
index 14544a9..cf511d8 100644
--- a/components/gcm_driver/gcm_stats_recorder_impl.cc
+++ b/components/gcm_driver/gcm_stats_recorder_impl.cc
@@ -12,6 +12,8 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
+#include "components/gcm_driver/crypto/gcm_decryption_result.h"
+#include "components/gcm_driver/crypto/gcm_encryption_provider.h"
 
 namespace gcm {
 
@@ -187,14 +189,11 @@
     delegate_->OnActivityRecorded();
 }
 
-void GCMStatsRecorderImpl::RecordDecryptionFailure(
-    const std::string& app_id,
-    GCMEncryptionProvider::DecryptionResult result) {
-  DCHECK_NE(result, GCMEncryptionProvider::DECRYPTION_RESULT_UNENCRYPTED);
-  DCHECK_NE(result,
-            GCMEncryptionProvider::DECRYPTION_RESULT_DECRYPTED_DRAFT_03);
-  DCHECK_NE(result,
-            GCMEncryptionProvider::DECRYPTION_RESULT_DECRYPTED_DRAFT_08);
+void GCMStatsRecorderImpl::RecordDecryptionFailure(const std::string& app_id,
+                                                   GCMDecryptionResult result) {
+  DCHECK_NE(result, GCMDecryptionResult::UNENCRYPTED);
+  DCHECK_NE(result, GCMDecryptionResult::DECRYPTED_DRAFT_03);
+  DCHECK_NE(result, GCMDecryptionResult::DECRYPTED_DRAFT_08);
   if (!is_recording_)
     return;
 
@@ -202,8 +201,7 @@
   DecryptionFailureActivity* inserted_data = InsertCircularBuffer(
       &decryption_failure_activities_, data);
   inserted_data->app_id = app_id;
-  inserted_data->details =
-      GCMEncryptionProvider::ToDecryptionResultDetailsString(result);
+  inserted_data->details = ToGCMDecryptionResultDetailsString(result);
 
   NotifyActivityRecorded();
 }
diff --git a/components/gcm_driver/gcm_stats_recorder_impl.h b/components/gcm_driver/gcm_stats_recorder_impl.h
index a2c46d1f..77af2970 100644
--- a/components/gcm_driver/gcm_stats_recorder_impl.h
+++ b/components/gcm_driver/gcm_stats_recorder_impl.h
@@ -13,7 +13,6 @@
 
 #include "base/macros.h"
 #include "base/time/time.h"
-#include "components/gcm_driver/crypto/gcm_encryption_provider.h"
 #include "components/gcm_driver/gcm_activity.h"
 #include "google_apis/gcm/engine/connection_factory.h"
 #include "google_apis/gcm/engine/mcs_client.h"
@@ -23,6 +22,8 @@
 
 namespace gcm {
 
+enum class GCMDecryptionResult;
+
 // Records GCM internal stats and activities for debugging purpose. Recording
 // can be turned on/off by calling set_is_recording(...) function. It is turned
 // off by default.
@@ -41,7 +42,7 @@
 
   // Records a message decryption failure caused by |result| for |app_id|.
   void RecordDecryptionFailure(const std::string& app_id,
-                               GCMEncryptionProvider::DecryptionResult result);
+                               GCMDecryptionResult result);
 
   // GCMStatsRecorder implementation:
   void RecordCheckinInitiated(uint64_t android_id) override;
diff --git a/components/gcm_driver/gcm_stats_recorder_impl_unittest.cc b/components/gcm_driver/gcm_stats_recorder_impl_unittest.cc
index eae8909..a73a78b 100644
--- a/components/gcm_driver/gcm_stats_recorder_impl_unittest.cc
+++ b/components/gcm_driver/gcm_stats_recorder_impl_unittest.cc
@@ -10,6 +10,7 @@
 #include <string>
 #include <vector>
 
+#include "components/gcm_driver/crypto/gcm_decryption_result.h"
 #include "components/gcm_driver/crypto/gcm_encryption_provider.h"
 #include "google_apis/gcm/engine/mcs_client.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -99,8 +100,8 @@
 static const char kIncomingSendErrorEvent[] = "Received 'send error' msg";
 static const char kIncomingSendErrorDetails[] = "";
 
-static const GCMEncryptionProvider::DecryptionResult kDecryptionResultFailure =
-    GCMEncryptionProvider::DECRYPTION_RESULT_INVALID_PAYLOAD;
+static const GCMDecryptionResult kDecryptionResultFailure =
+    GCMDecryptionResult::INVALID_PAYLOAD;
 
 }  // namespace
 
@@ -301,10 +302,9 @@
     const auto& queue = recorder_.decryption_failure_activities();
 
     EXPECT_EQ(kAppId, queue.front().app_id) << remark;
-    EXPECT_EQ(
-        GCMEncryptionProvider::ToDecryptionResultDetailsString(
-            kDecryptionResultFailure),
-        queue.front().details) << remark;
+    EXPECT_EQ(ToGCMDecryptionResultDetailsString(kDecryptionResultFailure),
+              queue.front().details)
+        << remark;
   }
 
  protected:
diff --git a/components/grpc_support/test/get_stream_engine.cc b/components/grpc_support/test/get_stream_engine.cc
index 8385f54..a4dbae0 100644
--- a/components/grpc_support/test/get_stream_engine.cc
+++ b/components/grpc_support/test/get_stream_engine.cc
@@ -53,8 +53,9 @@
       params->enable_http2 = true;
       net::AlternativeService alternative_service(net::kProtoQUIC, "", 443);
       url::SchemeHostPort quic_hint_server("https", kTestServerHost, 443);
-      server_properties_->SetAlternativeService(
-          quic_hint_server, alternative_service, base::Time::Max());
+      server_properties_->SetQuicAlternativeService(
+          quic_hint_server, alternative_service, base::Time::Max(),
+          params->quic_supported_versions);
 
       request_context_->set_cert_verifier(mock_cert_verifier_.get());
       request_context_->set_host_resolver(host_resolver_.get());
diff --git a/components/payments/content/android/java/src/org/chromium/components/payments/PaymentManifestParser.java b/components/payments/content/android/java/src/org/chromium/components/payments/PaymentManifestParser.java
index 289c5eee..56ff80a5 100644
--- a/components/payments/content/android/java/src/org/chromium/components/payments/PaymentManifestParser.java
+++ b/components/payments/content/android/java/src/org/chromium/components/payments/PaymentManifestParser.java
@@ -4,6 +4,7 @@
 
 package org.chromium.components.payments;
 
+import org.chromium.base.ThreadUtils;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.payments.mojom.WebAppManifestSection;
@@ -42,6 +43,7 @@
 
     /** Starts the utility process. */
     public void startUtilityProcess() {
+        ThreadUtils.assertOnUiThread();
         assert mNativePaymentManifestParserAndroid == 0;
         mNativePaymentManifestParserAndroid = nativeCreatePaymentManifestParserAndroid();
         nativeStartUtilityProcess(mNativePaymentManifestParserAndroid);
@@ -49,6 +51,7 @@
 
     /** Stops the utility process. */
     public void stopUtilityProcess() {
+        ThreadUtils.assertOnUiThread();
         assert mNativePaymentManifestParserAndroid != 0;
         nativeStopUtilityProcess(mNativePaymentManifestParserAndroid);
         mNativePaymentManifestParserAndroid = 0;
@@ -56,6 +59,7 @@
 
     /** @return Whether the utility process is running. */
     public boolean isUtilityProcessRunning() {
+        ThreadUtils.assertOnUiThread();
         return mNativePaymentManifestParserAndroid != 0;
     }
 
@@ -66,6 +70,8 @@
      * @param callback The callback to invoke when finished parsing.
      */
     public void parsePaymentMethodManifest(String content, ManifestParseCallback callback) {
+        ThreadUtils.assertOnUiThread();
+        assert mNativePaymentManifestParserAndroid != 0;
         nativeParsePaymentMethodManifest(mNativePaymentManifestParserAndroid, content, callback);
     }
 
@@ -76,6 +82,8 @@
      * @param callback The callback to invoke when finished parsing.
      */
     public void parseWebAppManifest(String content, ManifestParseCallback callback) {
+        ThreadUtils.assertOnUiThread();
+        assert mNativePaymentManifestParserAndroid != 0;
         nativeParseWebAppManifest(mNativePaymentManifestParserAndroid, content, callback);
     }
 
diff --git a/components/payments/content/android/payment_manifest_parser_android.cc b/components/payments/content/android/payment_manifest_parser_android.cc
index 65a3e26..7587d0a 100644
--- a/components/payments/content/android/payment_manifest_parser_android.cc
+++ b/components/payments/content/android/payment_manifest_parser_android.cc
@@ -26,7 +26,10 @@
   ~ParseCallback() {}
 
   // Copies payment method manifest into Java.
-  void OnPaymentMethodManifestParsed(std::vector<GURL> web_app_manifest_urls) {
+  void OnPaymentMethodManifestParsed(
+      const std::vector<GURL>& web_app_manifest_urls,
+      const std::vector<url::Origin>& unused_supported_origins,
+      bool unused_all_origins_supported) {
     DCHECK_GE(100U, web_app_manifest_urls.size());
     JNIEnv* env = base::android::AttachCurrentThread();
 
diff --git a/components/payments/content/payment_manifest_parser_host.cc b/components/payments/content/payment_manifest_parser_host.cc
index 0af9a38..aaea07c4 100644
--- a/components/payments/content/payment_manifest_parser_host.cc
+++ b/components/payments/content/payment_manifest_parser_host.cc
@@ -17,6 +17,11 @@
 #include "url/url_constants.h"
 
 namespace payments {
+namespace {
+
+const size_t kMaximumNumberOfItems = 100U;
+
+}  // namespace
 
 PaymentManifestParserHost::PaymentManifestParserHost() : callback_counter_(0) {}
 
@@ -37,7 +42,8 @@
     const std::string& content,
     PaymentMethodCallback callback) {
   if (!mojo_client_) {
-    std::move(callback).Run(std::vector<GURL>());
+    std::move(callback).Run(std::vector<GURL>(), std::vector<url::Origin>(),
+                            false);
     return;
   }
 
@@ -72,7 +78,9 @@
 
 void PaymentManifestParserHost::OnPaymentMethodParse(
     int64_t callback_identifier,
-    const std::vector<GURL>& web_app_manifest_urls) {
+    const std::vector<GURL>& web_app_manifest_urls,
+    const std::vector<url::Origin>& supported_origins,
+    bool all_origins_supported) {
   const auto& pending_callback_it =
       pending_payment_method_callbacks_.find(callback_identifier);
   if (pending_callback_it == pending_payment_method_callbacks_.end()) {
@@ -82,8 +90,8 @@
     return;
   }
 
-  const size_t kMaximumNumberOfWebAppUrls = 100U;
-  if (web_app_manifest_urls.size() > kMaximumNumberOfWebAppUrls) {
+  if (web_app_manifest_urls.size() > kMaximumNumberOfItems ||
+      supported_origins.size() > kMaximumNumberOfItems) {
     // If more than 100 items, then something went wrong in the utility
     // process. Stop the utility process and notify all callbacks.
     OnUtilityProcessStopped();
@@ -99,12 +107,30 @@
     }
   }
 
+  if (all_origins_supported && !supported_origins.empty()) {
+    // The format of the payment method manifest does not allow for both of
+    // these conditions to be true. Something went wrong in the utility process.
+    // Stop the utility process and notify all callbacks.
+    OnUtilityProcessStopped();
+    return;
+  }
+
+  for (const auto& origin : supported_origins) {
+    if (!origin.GetURL().is_valid() || origin.scheme() != url::kHttpsScheme) {
+      // If not a valid origin with HTTPS scheme, then something went wrong in
+      // the utility process. Stop the utility process and notify all callbacks.
+      OnUtilityProcessStopped();
+      return;
+    }
+  }
+
   PaymentMethodCallback callback = std::move(pending_callback_it->second);
   pending_payment_method_callbacks_.erase(pending_callback_it);
 
   // Can trigger synchronous deletion of this object, so can't access any of
   // the member variables after this block.
-  std::move(callback).Run(web_app_manifest_urls);
+  std::move(callback).Run(web_app_manifest_urls, supported_origins,
+                          all_origins_supported);
 }
 
 void PaymentManifestParserHost::OnWebAppParse(
@@ -119,8 +145,7 @@
     return;
   }
 
-  const size_t kMaximumNumberOfSections = 100U;
-  if (manifest.size() > kMaximumNumberOfSections) {
+  if (manifest.size() > kMaximumNumberOfItems) {
     // If more than 100 items, then something went wrong in the utility
     // process. Stop the utility process and notify all callbacks.
     OnUtilityProcessStopped();
@@ -128,8 +153,7 @@
   }
 
   for (size_t i = 0; i < manifest.size(); ++i) {
-    const size_t kMaximumNumberOfFingerprints = 100U;
-    if (manifest[i]->fingerprints.size() > kMaximumNumberOfFingerprints) {
+    if (manifest[i]->fingerprints.size() > kMaximumNumberOfItems) {
       // If more than 100 items, then something went wrong in the utility
       // process. Stop the utility process and notify all callbacks.
       OnUtilityProcessStopped();
@@ -148,15 +172,16 @@
 void PaymentManifestParserHost::OnUtilityProcessStopped() {
   mojo_client_.reset();
 
-  std::unordered_map<int64_t, PaymentMethodCallback> payment_method_callbacks =
+  std::map<int64_t, PaymentMethodCallback> payment_method_callbacks =
       std::move(pending_payment_method_callbacks_);
-  std::unordered_map<int64_t, WebAppCallback> web_app_callbacks =
+  std::map<int64_t, WebAppCallback> web_app_callbacks =
       std::move(pending_web_app_callbacks_);
 
   for (auto& callback : payment_method_callbacks) {
     // Can trigger synchronous deletion of this object, so can't access any of
     // the member variables after this line.
-    std::move(callback.second).Run(std::vector<GURL>());
+    std::move(callback.second)
+        .Run(std::vector<GURL>(), std::vector<url::Origin>(), false);
   }
 
   for (auto& callback : web_app_callbacks) {
diff --git a/components/payments/content/payment_manifest_parser_host.h b/components/payments/content/payment_manifest_parser_host.h
index eb68875..7d358328 100644
--- a/components/payments/content/payment_manifest_parser_host.h
+++ b/components/payments/content/payment_manifest_parser_host.h
@@ -7,14 +7,16 @@
 
 #include <stdint.h>
 
+#include <map>
 #include <memory>
-#include <unordered_map>
+#include <string>
 #include <vector>
 
 #include "base/callback_forward.h"
 #include "base/macros.h"
 #include "components/payments/mojom/payment_manifest_parser.mojom.h"
 #include "url/gurl.h"
+#include "url/origin.h"
 
 namespace content {
 template <class MojoInterface>
@@ -23,12 +25,15 @@
 
 namespace payments {
 
+class PaymentManifestParserHostTest;
+
 // Host of the utility process that parses manifest contents.
 class PaymentManifestParserHost {
  public:
-  // Called on successful parsing of a payment method manifest. The result is a
-  // move-only vector, which is empty on parse failure.
-  using PaymentMethodCallback = base::OnceCallback<void(std::vector<GURL>)>;
+  // Called on successful parsing of a payment method manifest. Parse failure
+  // results in empty vectors and "false".
+  using PaymentMethodCallback = base::OnceCallback<
+      void(const std::vector<GURL>&, const std::vector<url::Origin>&, bool)>;
   // Called on successful parsing of a web app manifest. The result is a
   // move-only vector, which is empty on parse failure.
   using WebAppCallback =
@@ -48,8 +53,12 @@
   void ParseWebAppManifest(const std::string& content, WebAppCallback callback);
 
  private:
+  friend class PaymentManifestParserHostTest;
+
   void OnPaymentMethodParse(int64_t callback_identifier,
-                            const std::vector<GURL>& web_app_manifest_urls);
+                            const std::vector<GURL>& web_app_manifest_urls,
+                            const std::vector<url::Origin>& supported_origins,
+                            bool all_origins_supported);
   void OnWebAppParse(int64_t callback_identifier,
                      std::vector<mojom::WebAppManifestSectionPtr> manifest);
 
@@ -60,9 +69,8 @@
       mojo_client_;
 
   int64_t callback_counter_;
-  std::unordered_map<int64_t, PaymentMethodCallback>
-      pending_payment_method_callbacks_;
-  std::unordered_map<int64_t, WebAppCallback> pending_web_app_callbacks_;
+  std::map<int64_t, PaymentMethodCallback> pending_payment_method_callbacks_;
+  std::map<int64_t, WebAppCallback> pending_web_app_callbacks_;
 
   DISALLOW_COPY_AND_ASSIGN(PaymentManifestParserHost);
 };
diff --git a/components/payments/content/utility/fingerprint_parser.cc b/components/payments/content/utility/fingerprint_parser.cc
index 594d589..98cb171 100644
--- a/components/payments/content/utility/fingerprint_parser.cc
+++ b/components/payments/content/utility/fingerprint_parser.cc
@@ -24,11 +24,17 @@
 
 std::vector<uint8_t> FingerprintStringToByteArray(const std::string& input) {
   std::vector<uint8_t> output;
-  if (input.size() != 32 * 3 - 1)
+  const size_t kLength = 32 * 3 - 1;
+  if (input.size() != kLength) {
+    LOG(ERROR) << "Fingerprint \"" << input << "\" should contain exactly "
+               << kLength << " characters.";
     return output;
+  }
 
   for (size_t i = 0; i < input.size(); i += 3) {
     if (i < input.size() - 2 && input[i + 2] != ':') {
+      LOG(ERROR) << "Bytes in fingerprint \"" << input
+                 << "\" should separated by \":\" characters.";
       output.clear();
       return output;
     }
@@ -36,6 +42,8 @@
     char big_end = input[i];
     char little_end = input[i + 1];
     if (!IsUpperCaseHexDigit(big_end) || !IsUpperCaseHexDigit(little_end)) {
+      LOG(ERROR) << "Bytes in fingerprint \"" << input
+                 << "\" should be upper case hex digits 0-9 and A-F.";
       output.clear();
       return output;
     }
diff --git a/components/payments/content/utility/payment_manifest_parser.cc b/components/payments/content/utility/payment_manifest_parser.cc
index f3a4aaa..2bdf4b4 100644
--- a/components/payments/content/utility/payment_manifest_parser.cc
+++ b/components/payments/content/utility/payment_manifest_parser.cc
@@ -10,6 +10,7 @@
 #include <utility>
 
 #include "base/json/json_reader.h"
+#include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
@@ -19,6 +20,118 @@
 #include "url/url_constants.h"
 
 namespace payments {
+namespace {
+
+const size_t kMaximumNumberOfEntries = 100U;
+const char* const kDefaultApplications = "default_applications";
+const char* const kSupportedOrigins = "supported_origins";
+
+// Parses the "default_applications": ["https://some/url"] from |dict| into
+// |web_app_manifest_urls|. Returns 'false' for invalid data.
+bool ParseDefaultApplications(base::DictionaryValue* dict,
+                              std::vector<GURL>* web_app_manifest_urls) {
+  DCHECK(dict);
+  DCHECK(web_app_manifest_urls);
+
+  base::ListValue* list = nullptr;
+  if (!dict->GetList(kDefaultApplications, &list)) {
+    LOG(ERROR) << "\"" << kDefaultApplications << "\" must be a list.";
+    return false;
+  }
+
+  size_t apps_number = list->GetSize();
+  if (apps_number > kMaximumNumberOfEntries) {
+    LOG(ERROR) << "\"" << kDefaultApplications << "\" must contain at most "
+               << kMaximumNumberOfEntries << " entries.";
+    return false;
+  }
+
+  std::string item;
+  for (size_t i = 0; i < apps_number; ++i) {
+    if (!list->GetString(i, &item) && item.empty()) {
+      LOG(ERROR) << "Each entry in \"" << kDefaultApplications
+                 << "\" must be a string.";
+      web_app_manifest_urls->clear();
+      return false;
+    }
+
+    GURL url(item);
+    if (!url.is_valid() || !url.SchemeIs(url::kHttpsScheme)) {
+      LOG(ERROR) << "\"" << item << "\" entry in \"" << kDefaultApplications
+                 << "\" is not a valid URL with HTTPS scheme.";
+      web_app_manifest_urls->clear();
+      return false;
+    }
+
+    web_app_manifest_urls->push_back(url);
+  }
+
+  return true;
+}
+
+// Parses the "supported_origins": "*" (or ["https://some.origin"]) from |dict|
+// into |supported_origins| and |all_origins_supported|. Returns 'false' for
+// invalid data.
+bool ParseSupportedOrigins(base::DictionaryValue* dict,
+                           std::vector<url::Origin>* supported_origins,
+                           bool* all_origins_supported) {
+  DCHECK(dict);
+  DCHECK(supported_origins);
+  DCHECK(all_origins_supported);
+
+  *all_origins_supported = false;
+
+  std::string item;
+  if (dict->GetString(kSupportedOrigins, &item)) {
+    if (item != "*") {
+      LOG(ERROR) << "\"" << item << "\" is not a valid value for \""
+                 << kSupportedOrigins
+                 << "\". Must be either \"*\" or a list of origins.";
+      return false;
+    }
+
+    *all_origins_supported = true;
+    return true;
+  }
+
+  base::ListValue* list = nullptr;
+  if (!dict->GetList(kSupportedOrigins, &list)) {
+    LOG(ERROR) << "\"" << kSupportedOrigins
+               << "\" must be either \"*\" or a list of origins.";
+    return false;
+  }
+
+  size_t supported_origins_number = list->GetSize();
+  if (supported_origins_number > kMaximumNumberOfEntries) {
+    LOG(ERROR) << "\"" << kSupportedOrigins << "\" must contain at most "
+               << kMaximumNumberOfEntries << " entires.";
+    return false;
+  }
+
+  for (size_t i = 0; i < supported_origins_number; ++i) {
+    if (!list->GetString(i, &item) && item.empty()) {
+      LOG(ERROR) << "Each entry in \"" << kSupportedOrigins
+                 << "\" must be a string.";
+      supported_origins->clear();
+      return false;
+    }
+
+    GURL url(item);
+    if (!url.is_valid() || !url.SchemeIs(url::kHttpsScheme) ||
+        url.path() != "/" || url.has_query() || url.has_ref()) {
+      LOG(ERROR) << "\"" << item << "\" entry in \"" << kSupportedOrigins
+                 << "\" is not a valid origin with HTTPS scheme.";
+      supported_origins->clear();
+      return false;
+    }
+
+    supported_origins->push_back(url::Origin(url));
+  }
+
+  return true;
+}
+
+}  // namespace
 
 // static
 void PaymentManifestParser::Create(
@@ -29,44 +142,40 @@
 }
 
 // static
-std::vector<GURL> PaymentManifestParser::ParsePaymentMethodManifestIntoVector(
-    const std::string& input) {
-  std::vector<GURL> output;
+void PaymentManifestParser::ParsePaymentMethodManifestIntoVectors(
+    const std::string& input,
+    std::vector<GURL>* web_app_manifest_urls,
+    std::vector<url::Origin>* supported_origins,
+    bool* all_origins_supported) {
+  DCHECK(web_app_manifest_urls);
+  DCHECK(supported_origins);
+  DCHECK(all_origins_supported);
+
+  *all_origins_supported = false;
+
   std::unique_ptr<base::Value> value(base::JSONReader::Read(input));
-  if (!value)
-    return output;
+  if (!value) {
+    LOG(ERROR) << "Payment method manifest must be in JSON format.";
+    return;
+  }
 
   std::unique_ptr<base::DictionaryValue> dict =
       base::DictionaryValue::From(std::move(value));
-  if (!dict)
-    return output;
-
-  base::ListValue* list = nullptr;
-  if (!dict->GetList("default_applications", &list))
-    return output;
-
-  size_t apps_number = list->GetSize();
-  const size_t kMaximumNumberOfApps = 100U;
-  if (apps_number > kMaximumNumberOfApps)
-    return output;
-
-  std::string item;
-  for (size_t i = 0; i < apps_number; ++i) {
-    if (!list->GetString(i, &item) && item.empty()) {
-      output.clear();
-      return output;
-    }
-
-    GURL url(item);
-    if (!url.is_valid() || !url.SchemeIs(url::kHttpsScheme)) {
-      output.clear();
-      return output;
-    }
-
-    output.push_back(url);
+  if (!dict) {
+    LOG(ERROR) << "Payment method manifest must be a JSON dictionary.";
+    return;
   }
 
-  return output;
+  if (dict->HasKey(kDefaultApplications) &&
+      !ParseDefaultApplications(dict.get(), web_app_manifest_urls)) {
+    return;
+  }
+
+  if (dict->HasKey(kSupportedOrigins) &&
+      !ParseSupportedOrigins(dict.get(), supported_origins,
+                             all_origins_supported)) {
+    web_app_manifest_urls->clear();
+  }
 }
 
 // static
@@ -74,22 +183,29 @@
 PaymentManifestParser::ParseWebAppManifestIntoVector(const std::string& input) {
   std::vector<mojom::WebAppManifestSectionPtr> output;
   std::unique_ptr<base::Value> value(base::JSONReader::Read(input));
-  if (!value)
+  if (!value) {
+    LOG(ERROR) << "Web app manifest must be in JSON format.";
     return output;
+  }
 
   std::unique_ptr<base::DictionaryValue> dict =
       base::DictionaryValue::From(std::move(value));
-  if (!dict)
+  if (!dict) {
+    LOG(ERROR) << "Web app manifest must be a JSON dictionary.";
     return output;
+  }
 
   base::ListValue* list = nullptr;
-  if (!dict->GetList("related_applications", &list))
+  if (!dict->GetList("related_applications", &list)) {
+    LOG(ERROR) << "\"related_applications\" must be a list.";
     return output;
+  }
 
   size_t related_applications_size = list->GetSize();
   for (size_t i = 0; i < related_applications_size; ++i) {
     base::DictionaryValue* related_application = nullptr;
     if (!list->GetDictionary(i, &related_application) || !related_application) {
+      LOG(ERROR) << "\"related_applications\" must be a list of dictionaries.";
       output.clear();
       return output;
     }
@@ -100,8 +216,10 @@
       continue;
     }
 
-    const size_t kMaximumNumberOfRelatedApplications = 100U;
-    if (output.size() >= kMaximumNumberOfRelatedApplications) {
+    if (output.size() >= kMaximumNumberOfEntries) {
+      LOG(ERROR) << "\"related_applications\" must contain at most "
+                 << kMaximumNumberOfEntries
+                 << " entries with \"platform\": \"play\".";
       output.clear();
       return output;
     }
@@ -112,7 +230,10 @@
     if (!related_application->HasKey(kId) ||
         !related_application->HasKey(kMinVersion) ||
         !related_application->HasKey(kFingerprints)) {
-      output.clear();
+      LOG(ERROR) << "Each \"platform\": \"play\" entry in "
+                    "\"related_applications\" must contain \""
+                 << kId << "\", \"" << kMinVersion << "\", and \""
+                 << kFingerprints << "\".";
       return output;
     }
 
@@ -122,6 +243,7 @@
 
     if (!related_application->GetString(kId, &section->id) ||
         section->id.empty() || !base::IsStringASCII(section->id)) {
+      LOG(ERROR) << "\"" << kId << "\" must be a non-empty ASCII string.";
       output.clear();
       return output;
     }
@@ -130,15 +252,19 @@
     if (!related_application->GetString(kMinVersion, &min_version) ||
         min_version.empty() || !base::IsStringASCII(min_version) ||
         !base::StringToInt64(min_version, &section->min_version)) {
+      LOG(ERROR) << "\"" << kMinVersion
+                 << "\" must be a string convertible into a number.";
       output.clear();
       return output;
     }
 
-    const size_t kMaximumNumberOfFingerprints = 100U;
     base::ListValue* fingerprints_list = nullptr;
     if (!related_application->GetList(kFingerprints, &fingerprints_list) ||
         fingerprints_list->empty() ||
-        fingerprints_list->GetSize() > kMaximumNumberOfFingerprints) {
+        fingerprints_list->GetSize() > kMaximumNumberOfEntries) {
+      LOG(ERROR) << "\"" << kFingerprints
+                 << "\" must be a non-empty list of at most "
+                 << kMaximumNumberOfEntries << " items.";
       output.clear();
       return output;
     }
@@ -153,7 +279,11 @@
           !fingerprint_dict->GetString("type", &fingerprint_type) ||
           fingerprint_type != "sha256_cert" ||
           !fingerprint_dict->GetString("value", &fingerprint_value) ||
-          fingerprint_value.empty()) {
+          fingerprint_value.empty() ||
+          !base::IsStringASCII(fingerprint_value)) {
+        LOG(ERROR) << "Each entry in \"" << kFingerprints
+                   << "\" must be a dictionary with \"type\": "
+                      "\"sha256_cert\" and a non-empty ASCII string \"value\".";
         output.clear();
         return output;
       }
@@ -181,7 +311,16 @@
 void PaymentManifestParser::ParsePaymentMethodManifest(
     const std::string& content,
     ParsePaymentMethodManifestCallback callback) {
-  std::move(callback).Run(ParsePaymentMethodManifestIntoVector(content));
+  std::vector<GURL> web_app_manifest_urls;
+  std::vector<url::Origin> supported_origins;
+  bool all_origins_supported = false;
+
+  ParsePaymentMethodManifestIntoVectors(content, &web_app_manifest_urls,
+                                        &supported_origins,
+                                        &all_origins_supported);
+
+  std::move(callback).Run(web_app_manifest_urls, supported_origins,
+                          all_origins_supported);
 }
 
 void PaymentManifestParser::ParseWebAppManifest(
diff --git a/components/payments/content/utility/payment_manifest_parser.h b/components/payments/content/utility/payment_manifest_parser.h
index cabdc1592..4b2bc4b3 100644
--- a/components/payments/content/utility/payment_manifest_parser.h
+++ b/components/payments/content/utility/payment_manifest_parser.h
@@ -11,6 +11,7 @@
 #include "base/macros.h"
 #include "components/payments/mojom/payment_manifest_parser.mojom.h"
 #include "url/gurl.h"
+#include "url/origin.h"
 
 namespace service_manager {
 struct BindSourceInfo;
@@ -21,11 +22,18 @@
 // Parser for payment method manifests and web app manifests. Should be used
 // only in a sandboxed utility process.
 //
-// Example valid payment method manifest structure:
+// Example 1 of valid payment method manifest structure:
 //
 // {
 //   "default_applications": ["https://bobpay.com/payment-app.json"],
-//   "supported_origins": ["https://alicepay.com"]  // Not yet parsed or used.
+//   "supported_origins": ["https://alicepay.com"]
+// }
+//
+// Example 2 of valid payment method manifest structure:
+//
+// {
+//   "default_applications": ["https://bobpay.com/payment-app.json"],
+//   "supported_origins": "*"
 // }
 //
 // Example valid web app manifest structure:
@@ -49,8 +57,11 @@
   static void Create(const service_manager::BindSourceInfo& source_info,
                      mojom::PaymentManifestParserRequest request);
 
-  static std::vector<GURL> ParsePaymentMethodManifestIntoVector(
-      const std::string& input);
+  static void ParsePaymentMethodManifestIntoVectors(
+      const std::string& input,
+      std::vector<GURL>* web_app_manifest_urls,
+      std::vector<url::Origin>* supported_origins,
+      bool* all_origins_supported);
 
   // The return value is move-only, so no copying occurs.
   static std::vector<mojom::WebAppManifestSectionPtr>
diff --git a/components/payments/content/utility/payment_manifest_parser_unittest.cc b/components/payments/content/utility/payment_manifest_parser_unittest.cc
index de80499..f1d9391e 100644
--- a/components/payments/content/utility/payment_manifest_parser_unittest.cc
+++ b/components/payments/content/utility/payment_manifest_parser_unittest.cc
@@ -11,109 +11,230 @@
 
 // Payment method manifest parsing:
 
+void ExpectUnableToParsePaymentMethodManifest(const std::string& input) {
+  std::vector<GURL> actual_web_app_urls;
+  std::vector<url::Origin> actual_supported_origins;
+  bool actual_all_origins_supported = false;
+
+  PaymentManifestParser::ParsePaymentMethodManifestIntoVectors(
+      input, &actual_web_app_urls, &actual_supported_origins,
+      &actual_all_origins_supported);
+
+  EXPECT_TRUE(actual_web_app_urls.empty());
+  EXPECT_TRUE(actual_supported_origins.empty());
+  EXPECT_FALSE(actual_all_origins_supported);
+}
+
+void ExpectParsedPaymentMethodManifest(
+    const std::string& input,
+    const std::vector<GURL>& expected_web_app_urls,
+    const std::vector<url::Origin>& expected_supported_origins,
+    bool expected_all_origins_supported) {
+  std::vector<GURL> actual_web_app_urls;
+  std::vector<url::Origin> actual_supported_origins;
+  bool actual_all_origins_supported = false;
+
+  PaymentManifestParser::ParsePaymentMethodManifestIntoVectors(
+      input, &actual_web_app_urls, &actual_supported_origins,
+      &actual_all_origins_supported);
+
+  EXPECT_EQ(expected_web_app_urls, actual_web_app_urls);
+  EXPECT_EQ(expected_supported_origins, actual_supported_origins);
+  EXPECT_EQ(expected_all_origins_supported, actual_all_origins_supported);
+}
+
 TEST(PaymentManifestParserTest, NullPaymentMethodManifestIsMalformed) {
-  EXPECT_TRUE(
-      PaymentManifestParser::ParsePaymentMethodManifestIntoVector(std::string())
-          .empty());
+  ExpectUnableToParsePaymentMethodManifest(std::string());
 }
 
 TEST(PaymentManifestParserTest, NonJsonPaymentMethodManifestIsMalformed) {
-  EXPECT_TRUE(PaymentManifestParser::ParsePaymentMethodManifestIntoVector(
-                  "this is not json")
-                  .empty());
+  ExpectUnableToParsePaymentMethodManifest("this is not json");
 }
 
 TEST(PaymentManifestParserTest, StringPaymentMethodManifestIsMalformed) {
-  EXPECT_TRUE(PaymentManifestParser::ParsePaymentMethodManifestIntoVector(
-                  "\"this is a string\"")
-                  .empty());
+  ExpectUnableToParsePaymentMethodManifest("\"this is a string\"");
 }
 
 TEST(PaymentManifestParserTest,
      EmptyDictionaryPaymentMethodManifestIsMalformed) {
-  EXPECT_TRUE(PaymentManifestParser::ParsePaymentMethodManifestIntoVector("{}")
-                  .empty());
+  ExpectUnableToParsePaymentMethodManifest("{}");
 }
 
 TEST(PaymentManifestParserTest, NullDefaultApplicationIsMalformed) {
-  EXPECT_TRUE(PaymentManifestParser::ParsePaymentMethodManifestIntoVector(
-                  "{\"default_applications\": null}")
-                  .empty());
+  ExpectUnableToParsePaymentMethodManifest("{\"default_applications\": null}");
 }
 
 TEST(PaymentManifestParserTest, NumberDefaultApplicationIsMalformed) {
-  EXPECT_TRUE(PaymentManifestParser::ParsePaymentMethodManifestIntoVector(
-                  "{\"default_applications\": 0}")
-                  .empty());
+  ExpectUnableToParsePaymentMethodManifest("{\"default_applications\": 0}");
 }
 
 TEST(PaymentManifestParserTest, ListOfNumbersDefaultApplicationIsMalformed) {
-  EXPECT_TRUE(PaymentManifestParser::ParsePaymentMethodManifestIntoVector(
-                  "{\"default_applications\": [0]}")
-                  .empty());
+  ExpectUnableToParsePaymentMethodManifest("{\"default_applications\": [0]}");
 }
 
 TEST(PaymentManifestParserTest, EmptyListOfDefaultApplicationsIsMalformed) {
-  EXPECT_TRUE(PaymentManifestParser::ParsePaymentMethodManifestIntoVector(
-                  "{\"default_applications\": []}")
-                  .empty());
+  ExpectUnableToParsePaymentMethodManifest("{\"default_applications\": []}");
 }
 
 TEST(PaymentManifestParserTest, ListOfEmptyDefaultApplicationsIsMalformed) {
-  EXPECT_TRUE(PaymentManifestParser::ParsePaymentMethodManifestIntoVector(
-                  "{\"default_applications\": [\"\"]}")
-                  .empty());
+  ExpectUnableToParsePaymentMethodManifest(
+      "{\"default_applications\": [\"\"]}");
 }
 
 TEST(PaymentManifestParserTest, DefaultApplicationsShouldNotHaveNulCharacters) {
-  EXPECT_TRUE(
-      PaymentManifestParser::ParsePaymentMethodManifestIntoVector(
-          "{\"default_applications\": [\"https://bobpay.com/app\0json\"]}")
-          .empty());
+  ExpectUnableToParsePaymentMethodManifest(
+      "{\"default_applications\": [\"https://bobpay.com/app\0json\"]}");
 }
 
 TEST(PaymentManifestParserTest, DefaultApplicationKeyShouldBeLowercase) {
-  EXPECT_TRUE(
-      PaymentManifestParser::ParsePaymentMethodManifestIntoVector(
-          "{\"Default_Applications\": [\"https://bobpay.com/app.json\"]}")
-          .empty());
+  ExpectUnableToParsePaymentMethodManifest(
+      "{\"Default_Applications\": [\"https://bobpay.com/app.json\"]}");
 }
 
 TEST(PaymentManifestParserTest, DefaultApplicationsShouldBeAbsoluteUrls) {
-  EXPECT_TRUE(PaymentManifestParser::ParsePaymentMethodManifestIntoVector(
-                  "{\"default_applications\": ["
-                  "\"https://bobpay.com/app.json\","
-                  "\"app.json\"]}")
-                  .empty());
+  ExpectUnableToParsePaymentMethodManifest(
+      "{\"default_applications\": ["
+      "\"https://bobpay.com/app.json\","
+      "\"app.json\"]}");
 }
 
 TEST(PaymentManifestParserTest, DefaultApplicationsShouldBeHttps) {
-  EXPECT_TRUE(PaymentManifestParser::ParsePaymentMethodManifestIntoVector(
-                  "{\"default_applications\": ["
-                  "\"https://bobpay.com/app.json\","
-                  "\"http://alicepay.com/app.json\"]}")
-                  .empty());
+  ExpectUnableToParsePaymentMethodManifest(
+      "{\"default_applications\": ["
+      "\"https://bobpay.com/app.json\","
+      "\"http://alicepay.com/app.json\"]}");
 }
 
-TEST(PaymentManifestParserTest, WellFormedPaymentMethodManifest) {
-  std::vector<GURL> expected = {GURL("https://bobpay.com/app.json"),
-                                GURL("https://alicepay.com/app.json")};
-  EXPECT_EQ(expected,
-            PaymentManifestParser::ParsePaymentMethodManifestIntoVector(
-                "{\"default_applications\": ["
-                "\"https://bobpay.com/app.json\","
-                "\"https://alicepay.com/app.json\"]}"));
+TEST(PaymentManifestParserTest, NullSupportedOriginsIsMalformed) {
+  ExpectUnableToParsePaymentMethodManifest("{\"supported_origins\": null}");
+}
+
+TEST(PaymentManifestParserTest, NumberSupportedOriginsIsMalformed) {
+  ExpectUnableToParsePaymentMethodManifest("{\"supported_origins\": 0}");
+}
+
+TEST(PaymentManifestParserTest, EmptyListSupportedOriginsIsMalformed) {
+  ExpectUnableToParsePaymentMethodManifest("{\"supported_origins\": []}");
+}
+
+TEST(PaymentManifestParserTest, ListOfNumbersSupportedOriginsIsMalformed) {
+  ExpectUnableToParsePaymentMethodManifest("{\"supported_origins\": [0]}");
+}
+
+TEST(PaymentManifestParserTest, ListOfEmptySupportedOriginsIsMalformed) {
+  ExpectUnableToParsePaymentMethodManifest("{\"supported_origins\": [\"\"]}");
+}
+
+TEST(PaymentManifestParserTest, SupportedOriginsShouldNotHaveNulCharacters) {
+  ExpectUnableToParsePaymentMethodManifest(
+      "{\"supported_origins\": [\"https://bob\0pay.com\"]}");
+}
+
+TEST(PaymentManifestParserTest, SupportedOriginsShouldBeHttps) {
+  ExpectUnableToParsePaymentMethodManifest(
+      "{\"supported_origins\": [\"http://bobpay.com\"]}");
+}
+
+TEST(PaymentManifestParserTest, SupportedOriginsShouldNotHavePath) {
+  ExpectUnableToParsePaymentMethodManifest(
+      "{\"supported_origins\": [\"https://bobpay.com/webpay\"]}");
+}
+
+TEST(PaymentManifestParserTest, SupportedOriginsShouldNotHaveQuery) {
+  ExpectUnableToParsePaymentMethodManifest(
+      "{\"supported_origins\": [\"https://bobpay.com/?action=webpay\"]}");
+}
+
+TEST(PaymentManifestParserTest, SupportedOriginsShouldNotHaveRef) {
+  ExpectUnableToParsePaymentMethodManifest(
+      "{\"supported_origins\": [\"https://bobpay.com/#webpay\"]}");
+}
+
+TEST(PaymentManifestParserTest, SupportedOriginsShouldBeList) {
+  ExpectUnableToParsePaymentMethodManifest(
+      "{\"supported_origins\": \"https://bobpay.com\"}");
+}
+
+TEST(PaymentManifestParserTest, WellFormedPaymentMethodManifestWithApps) {
+  ExpectParsedPaymentMethodManifest(
+      "{\"default_applications\": ["
+      "\"https://bobpay.com/app.json\","
+      "\"https://alicepay.com/app.json\"]}",
+      {GURL("https://bobpay.com/app.json"),
+       GURL("https://alicepay.com/app.json")},
+      std::vector<url::Origin>(), false);
+}
+
+TEST(PaymentManifestParserTest,
+     WellFormedPaymentMethodManifestWithAppsAndAllSupportedOrigins) {
+  ExpectParsedPaymentMethodManifest(
+      "{\"default_applications\": [\"https://bobpay.com/app.json\", "
+      "\"https://alicepay.com/app.json\"], \"supported_origins\": \"*\"}",
+      {GURL("https://bobpay.com/app.json"),
+       GURL("https://alicepay.com/app.json")},
+      std::vector<url::Origin>(), true);
+}
+
+TEST(PaymentManifestParserTest, AllOriginsSupported) {
+  ExpectParsedPaymentMethodManifest("{\"supported_origins\": \"*\"}",
+                                    std::vector<GURL>(),
+                                    std::vector<url::Origin>(), true);
+}
+
+TEST(PaymentManifestParserTest,
+     InvalidDefaultAppsWillPreventParsingSupportedOrigins) {
+  ExpectUnableToParsePaymentMethodManifest(
+      "{\"default_applications\": [\"http://bobpay.com/app.json\"], "
+      "\"supported_origins\": \"*\"}");
+}
+
+TEST(PaymentManifestParserTest,
+     InvalidSupportedOriginsWillPreventParsingDefaultApps) {
+  ExpectUnableToParsePaymentMethodManifest(
+      "{\"default_applications\": [\"https://bobpay.com/app.json\"], "
+      "\"supported_origins\": \"+\"}");
+}
+
+TEST(PaymentManifestParserTest,
+     WellFormedPaymentMethodManifestWithAppsAndSomeSupportedOrigins) {
+  ExpectParsedPaymentMethodManifest(
+      "{\"default_applications\": [\"https://bobpay.com/app.json\", "
+      "\"https://alicepay.com/app.json\"], \"supported_origins\": "
+      "[\"https://charliepay.com\", \"https://evepay.com\"]}",
+      {GURL("https://bobpay.com/app.json"),
+       GURL("https://alicepay.com/app.json")},
+      {url::Origin(GURL("https://charliepay.com")),
+       url::Origin(GURL("https://evepay.com"))},
+      false);
+}
+
+TEST(PaymentManifestParserTest,
+     WellFormedPaymentMethodManifestWithSomeSupportedOrigins) {
+  ExpectParsedPaymentMethodManifest(
+      "{\"supported_origins\": [\"https://charliepay.com\", "
+      "\"https://evepay.com\"]}",
+      std::vector<GURL>(),
+      {url::Origin(GURL("https://charliepay.com")),
+       url::Origin(GURL("https://evepay.com"))},
+      false);
+}
+
+TEST(PaymentManifestParserTest,
+     WellFormedPaymentMethodManifestWithAllSupportedOrigins) {
+  ExpectParsedPaymentMethodManifest("{\"supported_origins\": \"*\"}",
+                                    std::vector<GURL>(),
+                                    std::vector<url::Origin>(), true);
 }
 
 // Web app manifest parsing:
 
-void ExpectUnableToParse(const std::string& input) {
+void ExpectUnableToParseWebAppManifest(const std::string& input) {
   std::vector<mojom::WebAppManifestSectionPtr> actual_output =
       PaymentManifestParser::ParseWebAppManifestIntoVector(input);
   EXPECT_TRUE(actual_output.empty());
 }
 
-void ExpectParsed(
+void ExpectParsedWebAppManifest(
     const std::string& input,
     const std::string& expected_id,
     int64_t expected_min_version,
@@ -127,41 +248,41 @@
 }
 
 TEST(PaymentManifestParserTest, NullContentIsMalformed) {
-  ExpectUnableToParse(std::string());
+  ExpectUnableToParseWebAppManifest(std::string());
 }
 
 TEST(PaymentManifestParserTest, NonJsonContentIsMalformed) {
-  ExpectUnableToParse("this is not json");
+  ExpectUnableToParseWebAppManifest("this is not json");
 }
 
 TEST(PaymentManifestParserTest, StringContentIsMalformed) {
-  ExpectUnableToParse("\"this is a string\"");
+  ExpectUnableToParseWebAppManifest("\"this is a string\"");
 }
 
 TEST(PaymentManifestParserTest, EmptyDictionaryIsMalformed) {
-  ExpectUnableToParse("{}");
+  ExpectUnableToParseWebAppManifest("{}");
 }
 
 TEST(PaymentManifestParserTest, NullRelatedApplicationsSectionIsMalformed) {
-  ExpectUnableToParse("{\"related_applications\": null}");
+  ExpectUnableToParseWebAppManifest("{\"related_applications\": null}");
 }
 
 TEST(PaymentManifestParserTest, NumberRelatedApplicationsectionIsMalformed) {
-  ExpectUnableToParse("{\"related_applications\": 0}");
+  ExpectUnableToParseWebAppManifest("{\"related_applications\": 0}");
 }
 
 TEST(PaymentManifestParserTest,
      ListOfNumbersRelatedApplicationsSectionIsMalformed) {
-  ExpectUnableToParse("{\"related_applications\": [0]}");
+  ExpectUnableToParseWebAppManifest("{\"related_applications\": [0]}");
 }
 
 TEST(PaymentManifestParserTest,
      ListOfEmptyDictionariesRelatedApplicationsSectionIsMalformed) {
-  ExpectUnableToParse("{\"related_applications\": [{}]}");
+  ExpectUnableToParseWebAppManifest("{\"related_applications\": [{}]}");
 }
 
 TEST(PaymentManifestParserTest, NoPlayPlatformIsMalformed) {
-  ExpectUnableToParse(
+  ExpectUnableToParseWebAppManifest(
       "{"
       "  \"related_applications\": [{"
       "    \"id\": \"com.bobpay.app\", "
@@ -176,7 +297,7 @@
 }
 
 TEST(PaymentManifestParserTest, NoPackageNameIsMalformed) {
-  ExpectUnableToParse(
+  ExpectUnableToParseWebAppManifest(
       "{"
       "  \"related_applications\": [{"
       "    \"platform\": \"play\", "
@@ -191,7 +312,7 @@
 }
 
 TEST(PaymentManifestParserTest, NoVersionIsMalformed) {
-  ExpectUnableToParse(
+  ExpectUnableToParseWebAppManifest(
       "{"
       "  \"related_applications\": [{"
       "    \"platform\": \"play\", "
@@ -206,7 +327,7 @@
 }
 
 TEST(PaymentManifestParserTest, NoFingerprintIsMalformed) {
-  ExpectUnableToParse(
+  ExpectUnableToParseWebAppManifest(
       "{"
       "  \"related_applications\": [{"
       "    \"platform\": \"play\", "
@@ -217,7 +338,7 @@
 }
 
 TEST(PaymentManifestParserTest, EmptyFingerprintsIsMalformed) {
-  ExpectUnableToParse(
+  ExpectUnableToParseWebAppManifest(
       "{"
       "  \"related_applications\": [{"
       "    \"platform\": \"play\", "
@@ -229,7 +350,7 @@
 }
 
 TEST(PaymentManifestParserTest, EmptyFingerprintsDictionaryIsMalformed) {
-  ExpectUnableToParse(
+  ExpectUnableToParseWebAppManifest(
       "{"
       "  \"related_applications\": [{"
       "    \"platform\": \"play\", "
@@ -241,7 +362,7 @@
 }
 
 TEST(PaymentManifestParserTest, NoFingerprintTypeIsMalformed) {
-  ExpectUnableToParse(
+  ExpectUnableToParseWebAppManifest(
       "{"
       "  \"related_applications\": [{"
       "    \"platform\": \"play\", "
@@ -256,7 +377,7 @@
 }
 
 TEST(PaymentManifestParserTest, NoFingerprintValueIsMalformed) {
-  ExpectUnableToParse(
+  ExpectUnableToParseWebAppManifest(
       "{"
       "  \"related_applications\": [{"
       "    \"platform\": \"play\", "
@@ -270,7 +391,7 @@
 }
 
 TEST(PaymentManifestParserTest, PlatformShouldNotHaveNullCharacters) {
-  ExpectUnableToParse(
+  ExpectUnableToParseWebAppManifest(
       "{"
       "  \"related_applications\": [{"
       "    \"platform\": \"pl\0ay\", "
@@ -286,7 +407,7 @@
 }
 
 TEST(PaymentManifestParserTest, PackageNameShouldNotHaveNullCharacters) {
-  ExpectUnableToParse(
+  ExpectUnableToParseWebAppManifest(
       "{"
       "  \"related_applications\": [{"
       "    \"platform\": \"play\", "
@@ -302,7 +423,7 @@
 }
 
 TEST(PaymentManifestParserTest, VersionShouldNotHaveNullCharacters) {
-  ExpectUnableToParse(
+  ExpectUnableToParseWebAppManifest(
       "{"
       "  \"related_applications\": [{"
       "    \"platform\": \"play\", "
@@ -318,7 +439,7 @@
 }
 
 TEST(PaymentManifestParserTest, FingerprintTypeShouldNotHaveNullCharacters) {
-  ExpectUnableToParse(
+  ExpectUnableToParseWebAppManifest(
       "{"
       "  \"related_applications\": [{"
       "    \"platform\": \"play\", "
@@ -334,7 +455,7 @@
 }
 
 TEST(PaymentManifestParserTest, FingerprintValueShouldNotHaveNullCharacters) {
-  ExpectUnableToParse(
+  ExpectUnableToParseWebAppManifest(
       "{"
       "  \"related_applications\": [{"
       "    \"platform\": \"play\", "
@@ -351,7 +472,7 @@
 }
 
 TEST(PaymentManifestParserTest, KeysShouldBeLowerCase) {
-  ExpectUnableToParse(
+  ExpectUnableToParseWebAppManifest(
       "{"
       "  \"Related_applications\": [{"
       "    \"Platform\": \"play\", "
@@ -367,7 +488,7 @@
 }
 
 TEST(PaymentManifestParserTest, FingerprintsShouldBeSha256) {
-  ExpectUnableToParse(
+  ExpectUnableToParseWebAppManifest(
       "{"
       "  \"related_applications\": [{"
       "    \"platform\": \"play\", "
@@ -383,7 +504,7 @@
 }
 
 TEST(PaymentManifestParserTest, FingerprintBytesShouldBeColonSeparated) {
-  ExpectUnableToParse(
+  ExpectUnableToParseWebAppManifest(
       "{"
       "  \"related_applications\": [{"
       "    \"platform\": \"play\", "
@@ -391,15 +512,15 @@
       "    \"min_version\": \"1\", "
       "    \"fingerprints\": [{"
       "      \"type\": \"sha256_cert\", "
-      "      \"value\" \"00010203040506070809A0A1A2A3A4A5A6A7A8A9B0B1B2B3B4B5B6"
-      "B7B8B9C0C1\""
+      "      \"value\": \"000010020030040050060070080090A00A10A20A30A40A50A60A7"
+      "0A80A90B00B10B20B30B40B50B60B70B80B90C00C1\""
       "    }]"
       "  }]"
       "}");
 }
 
 TEST(PaymentManifestParserTest, FingerprintBytesShouldBeUpperCase) {
-  ExpectUnableToParse(
+  ExpectUnableToParseWebAppManifest(
       "{"
       "  \"related_applications\": [{"
       "    \"platform\": \"play\", "
@@ -415,7 +536,7 @@
 }
 
 TEST(PaymentManifestParserTest, FingerprintsShouldContainsThirtyTwoBytes) {
-  ExpectUnableToParse(
+  ExpectUnableToParseWebAppManifest(
       "{"
       "  \"related_applications\": [{"
       "    \"platform\": \"play\", "
@@ -428,7 +549,7 @@
       "    }]"
       "  }]"
       "}");
-  ExpectUnableToParse(
+  ExpectUnableToParseWebAppManifest(
       "{"
       "  \"related_applications\": [{"
       "    \"platform\": \"play\", "
@@ -444,7 +565,7 @@
 }
 
 TEST(PaymentManifestParserTest, WellFormed) {
-  ExpectParsed(
+  ExpectParsedWebAppManifest(
       "{"
       "  \"related_applications\": [{"
       "    \"platform\": \"play\", "
@@ -464,7 +585,7 @@
 }
 
 TEST(PaymentManifestParserTest, DuplicateSignaturesWellFormed) {
-  ExpectParsed(
+  ExpectParsedWebAppManifest(
       "{"
       "  \"related_applications\": [{"
       "    \"platform\": \"play\", "
diff --git a/components/payments/mojom/BUILD.gn b/components/payments/mojom/BUILD.gn
index 2f529d48..740c592f 100644
--- a/components/payments/mojom/BUILD.gn
+++ b/components/payments/mojom/BUILD.gn
@@ -11,5 +11,6 @@
 
   public_deps = [
     "//url/mojo:url_mojom_gurl",
+    "//url/mojo:url_mojom_origin",
   ]
 }
diff --git a/components/payments/mojom/payment_manifest_parser.mojom b/components/payments/mojom/payment_manifest_parser.mojom
index 96c3d3c..b99828e 100644
--- a/components/payments/mojom/payment_manifest_parser.mojom
+++ b/components/payments/mojom/payment_manifest_parser.mojom
@@ -5,6 +5,7 @@
 [JavaPackage="org.chromium.payments.mojom"]
 module payments.mojom;
 
+import "url/mojo/origin.mojom";
 import "url/mojo/url.mojom";
 
 struct WebAppManifestSection {
@@ -19,7 +20,9 @@
 
 interface PaymentManifestParser {
   ParsePaymentMethodManifest(string content)
-      => (array<url.mojom.Url> webAppManifestUrls);
+      => (array<url.mojom.Url> web_app_manifest_urls,
+          array<url.mojom.Origin> supported_origins,
+          bool all_origins_supported);
   ParseWebAppManifest(string content)
       => (array<WebAppManifestSection> manifest);
 };
diff --git a/components/viz/common/BUILD.gn b/components/viz/common/BUILD.gn
deleted file mode 100644
index e703006..0000000
--- a/components/viz/common/BUILD.gn
+++ /dev/null
@@ -1,36 +0,0 @@
-# Copyright 2017 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-source_set("common") {
-  sources = [
-    "server_gpu_memory_buffer_manager.cc",
-    "server_gpu_memory_buffer_manager.h",
-  ]
-
-  deps = [
-    "//gpu/ipc/client",
-    "//gpu/ipc/common",
-    "//services/ui/gpu/interfaces",
-  ]
-
-  public_deps = [
-    "//gpu/command_buffer/client",
-    "//gpu/ipc/host",
-    "//ui/gfx",
-  ]
-}
-
-source_set("unit_tests") {
-  testonly = true
-  sources = [
-    "server_gpu_memory_buffer_manager_unittest.cc",
-  ]
-
-  deps = [
-    ":common",
-    "//base/test:test_support",
-    "//services/ui/gpu/interfaces",
-    "//testing/gtest",
-  ]
-}
diff --git a/components/viz/common/DEPS b/components/viz/common/DEPS
deleted file mode 100644
index f3d7ee5..0000000
--- a/components/viz/common/DEPS
+++ /dev/null
@@ -1,9 +0,0 @@
-include_rules = [
-  "+services/ui/gpu/interfaces",
-]
-
-specific_include_rules = {
-  "server_gpu_memory_buffer_manager_unittest\.cc": [
-    "+ui/gfx",
-  ]
-}
diff --git a/components/viz/host/BUILD.gn b/components/viz/host/BUILD.gn
index 53a3de2a3..e928add 100644
--- a/components/viz/host/BUILD.gn
+++ b/components/viz/host/BUILD.gn
@@ -9,6 +9,8 @@
     "frame_sink_observer.h",
     "host_frame_sink_manager.cc",
     "host_frame_sink_manager.h",
+    "server_gpu_memory_buffer_manager.cc",
+    "server_gpu_memory_buffer_manager.h",
     "viz_host_export.h",
   ]
 
@@ -16,6 +18,14 @@
     "//base",
     "//cc/ipc:interfaces",
     "//cc/surfaces",
+    "//gpu/ipc/client",
+    "//gpu/ipc/common",
+    "//services/ui/gpu/interfaces",
+  ]
+
+  public_deps = [
+    "//gpu/command_buffer/client",
+    "//gpu/ipc/host",
   ]
 }
 
@@ -24,15 +34,18 @@
 
   sources = [
     "host_frame_sink_manager_unittests.cc",
+    "server_gpu_memory_buffer_manager_unittest.cc",
   ]
 
   deps = [
+    ":host",
     "//base",
     "//base/test:test_support",
     "//cc/ipc:interfaces",
     "//cc/surfaces",
-    "//components/viz/host",
+    "//gpu/ipc/host",
     "//mojo/public/cpp/bindings",
+    "//services/ui/gpu/interfaces",
     "//testing/gmock",
     "//testing/gtest",
   ]
diff --git a/components/viz/host/DEPS b/components/viz/host/DEPS
index 7245cd4..c4eca43 100644
--- a/components/viz/host/DEPS
+++ b/components/viz/host/DEPS
@@ -1,4 +1,11 @@
 include_rules = [
   "+cc/ipc",
   "+cc/surfaces",
+  "+services/ui/gpu/interfaces",
 ]
+
+specific_include_rules = {
+  "server_gpu_memory_buffer_manager_unittest\.cc": [
+    "+ui/gfx",
+  ]
+}
diff --git a/components/viz/common/server_gpu_memory_buffer_manager.cc b/components/viz/host/server_gpu_memory_buffer_manager.cc
similarity index 98%
rename from components/viz/common/server_gpu_memory_buffer_manager.cc
rename to components/viz/host/server_gpu_memory_buffer_manager.cc
index 8b31b11..460cbf6 100644
--- a/components/viz/common/server_gpu_memory_buffer_manager.cc
+++ b/components/viz/host/server_gpu_memory_buffer_manager.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/viz/common/server_gpu_memory_buffer_manager.h"
+#include "components/viz/host/server_gpu_memory_buffer_manager.h"
 
 #include "base/logging.h"
 #include "base/threading/sequenced_task_runner_handle.h"
diff --git a/components/viz/common/server_gpu_memory_buffer_manager.h b/components/viz/host/server_gpu_memory_buffer_manager.h
similarity index 88%
rename from components/viz/common/server_gpu_memory_buffer_manager.h
rename to components/viz/host/server_gpu_memory_buffer_manager.h
index f546bc7..2ee15b6 100644
--- a/components/viz/common/server_gpu_memory_buffer_manager.h
+++ b/components/viz/host/server_gpu_memory_buffer_manager.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 COMPONENTS_VIZ_COMMON_SERVER_GPU_MEMORY_BUFFER_MANAGER_H_
-#define COMPONENTS_VIZ_COMMON_SERVER_GPU_MEMORY_BUFFER_MANAGER_H_
+#ifndef COMPONENTS_VIZ_HOST_SERVER_GPU_MEMORY_BUFFER_MANAGER_H_
+#define COMPONENTS_VIZ_HOST_SERVER_GPU_MEMORY_BUFFER_MANAGER_H_
 
 #include <memory>
 
@@ -11,6 +11,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/sequenced_task_runner.h"
 #include "base/synchronization/waitable_event.h"
+#include "components/viz/host/viz_host_export.h"
 #include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
 #include "gpu/ipc/host/gpu_memory_buffer_support.h"
 
@@ -18,7 +19,7 @@
 namespace mojom {
 class GpuService;
 }
-}
+}  // namespace ui
 
 namespace viz {
 
@@ -26,7 +27,8 @@
 // from the gpu process over the mojom.GpuService api.
 // Note that |CreateGpuMemoryBuffer()| can be called on any thread. All the rest
 // of the functions must be called on the thread this object is created on.
-class ServerGpuMemoryBufferManager : public gpu::GpuMemoryBufferManager {
+class VIZ_HOST_EXPORT ServerGpuMemoryBufferManager
+    : public gpu::GpuMemoryBufferManager {
  public:
   ServerGpuMemoryBufferManager(ui::mojom::GpuService* gpu_service,
                                int client_id);
@@ -81,4 +83,4 @@
 
 }  // namespace viz
 
-#endif  // COMPONENTS_VIZ_COMMON_SERVER_GPU_MEMORY_BUFFER_MANAGER_H_
+#endif  // COMPONENTS_VIZ_HOST_SERVER_GPU_MEMORY_BUFFER_MANAGER_H_
diff --git a/components/viz/common/server_gpu_memory_buffer_manager_unittest.cc b/components/viz/host/server_gpu_memory_buffer_manager_unittest.cc
similarity index 98%
rename from components/viz/common/server_gpu_memory_buffer_manager_unittest.cc
rename to components/viz/host/server_gpu_memory_buffer_manager_unittest.cc
index fdf7e3f..5d5e60b2 100644
--- a/components/viz/common/server_gpu_memory_buffer_manager_unittest.cc
+++ b/components/viz/host/server_gpu_memory_buffer_manager_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/viz/common/server_gpu_memory_buffer_manager.h"
+#include "components/viz/host/server_gpu_memory_buffer_manager.h"
 
 #include "base/test/scoped_task_environment.h"
 #include "gpu/ipc/host/gpu_memory_buffer_support.h"
diff --git a/content/browser/android/content_view_core_impl.cc b/content/browser/android/content_view_core_impl.cc
index 003339c..3849406 100644
--- a/content/browser/android/content_view_core_impl.cc
+++ b/content/browser/android/content_view_core_impl.cc
@@ -208,7 +208,6 @@
     : WebContentsObserver(web_contents),
       java_ref_(env, obj),
       web_contents_(static_cast<WebContentsImpl*>(web_contents)),
-      page_scale_(1),
       dpi_scale_(dpi_scale),
       device_orientation_(0) {
   GetViewAndroid()->SetLayer(cc::Layer::Create());
@@ -402,10 +401,9 @@
   if (obj.is_null() || !GetWindowAndroid())
     return;
 
-  GetViewAndroid()->set_content_offset(content_offset);
-  GetViewAndroid()->set_viewport_size(viewport_size);
-
-  page_scale_ = page_scale_factor;
+  GetViewAndroid()->UpdateFrameInfo({
+      viewport_size, page_scale_factor, content_offset,
+  });
 
   Java_ContentViewCore_updateFrameInfo(
       env, obj, scroll_offset.x(), scroll_offset.y(), page_scale_factor,
diff --git a/content/browser/android/content_view_core_impl.h b/content/browser/android/content_view_core_impl.h
index cb7e7a4..48c93b14 100644
--- a/content/browser/android/content_view_core_impl.h
+++ b/content/browser/android/content_view_core_impl.h
@@ -343,9 +343,6 @@
   // display in the ContentViewCore.
   WebContentsImpl* web_contents_;
 
-  // Page scale factor.
-  float page_scale_;
-
   // Device scale factor.
   float dpi_scale_;
 
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index 9d0d0d4..5efa919 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -119,6 +119,7 @@
 #include "skia/ext/event_tracer_impl.h"
 #include "skia/ext/skia_memory_dump_provider.h"
 #include "sql/sql_memory_dump_provider.h"
+#include "third_party/boringssl/src/include/openssl/evp.h"
 #include "ui/base/clipboard/clipboard.h"
 #include "ui/gfx/switches.h"
 
@@ -874,6 +875,9 @@
     RenderProcessHost::SetRunRendererInProcess(true);
 #endif
 
+  EVP_set_buggy_rsa_parser(
+      base::FeatureList::IsEnabled(features::kBuggyRSAParser));
+
   return result_code_;
 }
 
diff --git a/content/browser/devtools/devtools_http_handler.cc b/content/browser/devtools/devtools_http_handler.cc
index 3a00577..6ecc1be4 100644
--- a/content/browser/devtools/devtools_http_handler.cc
+++ b/content/browser/devtools/devtools_http_handler.cc
@@ -20,6 +20,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
+#include "base/task_scheduler/post_task.h"
 #include "base/threading/thread.h"
 #include "base/values.h"
 #include "build/build_config.h"
@@ -170,20 +171,18 @@
 
 // Thread and ServerWrapper lifetime management ------------------------------
 
-void TerminateOnUI(base::Thread* thread,
-                   ServerWrapper* server_wrapper,
-                   DevToolsSocketFactory* socket_factory) {
+void TerminateOnUI(std::unique_ptr<base::Thread> thread,
+                   std::unique_ptr<ServerWrapper> server_wrapper,
+                   std::unique_ptr<DevToolsSocketFactory> socket_factory) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (server_wrapper) {
-    DCHECK(thread);
-    thread->task_runner()->DeleteSoon(FROM_HERE, server_wrapper);
-  }
-  if (socket_factory) {
-    DCHECK(thread);
-    thread->task_runner()->DeleteSoon(FROM_HERE, socket_factory);
-  }
+  if (server_wrapper)
+    thread->task_runner()->DeleteSoon(FROM_HERE, std::move(server_wrapper));
+  if (socket_factory)
+    thread->task_runner()->DeleteSoon(FROM_HERE, std::move(socket_factory));
   if (thread) {
-    BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, thread);
+    base::PostTaskWithTraits(
+        FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
+        BindOnce([](std::unique_ptr<base::Thread>) {}, std::move(thread)));
   }
 }
 
@@ -194,28 +193,33 @@
                        std::unique_ptr<net::IPEndPoint> ip_address) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (handler && thread && server_wrapper) {
-    handler->ServerStarted(thread, server_wrapper, socket_factory,
-                           std::move(ip_address));
+    handler->ServerStarted(
+        std::unique_ptr<base::Thread>(thread),
+        std::unique_ptr<ServerWrapper>(server_wrapper),
+        std::unique_ptr<DevToolsSocketFactory>(socket_factory),
+        std::move(ip_address));
   } else {
-    TerminateOnUI(thread, server_wrapper, socket_factory);
+    TerminateOnUI(std::unique_ptr<base::Thread>(thread),
+                  std::unique_ptr<ServerWrapper>(server_wrapper),
+                  std::unique_ptr<DevToolsSocketFactory>(socket_factory));
   }
 }
 
 void StartServerOnHandlerThread(
     base::WeakPtr<DevToolsHttpHandler> handler,
-    base::Thread* thread,
-    DevToolsSocketFactory* socket_factory,
+    std::unique_ptr<base::Thread> thread,
+    std::unique_ptr<DevToolsSocketFactory> socket_factory,
     const base::FilePath& output_directory,
     const base::FilePath& frontend_dir,
     bool bundles_resources) {
   DCHECK(thread->task_runner()->BelongsToCurrentThread());
-  ServerWrapper* server_wrapper = nullptr;
+  std::unique_ptr<ServerWrapper> server_wrapper;
   std::unique_ptr<net::ServerSocket> server_socket =
       socket_factory->CreateForHttpServer();
   std::unique_ptr<net::IPEndPoint> ip_address(new net::IPEndPoint);
   if (server_socket) {
-    server_wrapper = new ServerWrapper(handler, std::move(server_socket),
-                                       frontend_dir, bundles_resources);
+    server_wrapper.reset(new ServerWrapper(handler, std::move(server_socket),
+                                           frontend_dir, bundles_resources));
     if (!output_directory.empty())
       server_wrapper->WriteActivePortToUserProfile(output_directory);
 
@@ -225,34 +229,11 @@
     ip_address.reset();
     LOG(ERROR) << "Cannot start http server for devtools. Stop devtools.";
   }
-  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
-      base::Bind(&ServerStartedOnUI,
-                 handler,
-                 thread,
-                 server_wrapper,
-                 socket_factory,
-                 base::Passed(&ip_address)));
-}
-
-void StartServerOnFile(
-    base::WeakPtr<DevToolsHttpHandler> handler,
-    DevToolsSocketFactory* socket_factory,
-    const base::FilePath& output_directory,
-    const base::FilePath& frontend_dir,
-    bool bundles_resources) {
-  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
-  std::unique_ptr<base::Thread> thread(
-      new base::Thread(kDevToolsHandlerThreadName));
-  base::Thread::Options options;
-  options.message_loop_type = base::MessageLoop::TYPE_IO;
-  if (thread->StartWithOptions(options)) {
-    base::MessageLoop* message_loop = thread->message_loop();
-    message_loop->task_runner()->PostTask(
-        FROM_HERE,
-        base::Bind(&StartServerOnHandlerThread, handler,
-                   base::Unretained(thread.release()), socket_factory,
-                   output_directory, frontend_dir, bundles_resources));
-  }
+  BrowserThread::PostTask(
+      BrowserThread::UI, FROM_HERE,
+      base::BindOnce(&ServerStartedOnUI, std::move(handler), thread.release(),
+                     server_wrapper.release(), socket_factory.release(),
+                     std::move(ip_address)));
 }
 
 // DevToolsAgentHostClientImpl -----------------------------------------------
@@ -328,7 +309,8 @@
 // DevToolsHttpHandler -------------------------------------------------------
 
 DevToolsHttpHandler::~DevToolsHttpHandler() {
-  TerminateOnUI(thread_, server_wrapper_, socket_factory_);
+  TerminateOnUI(std::move(thread_), std::move(server_wrapper_),
+                std::move(socket_factory_));
 }
 
 static std::string PathWithoutParams(const std::string& path) {
@@ -653,9 +635,9 @@
   response.SetBody(json_protocol, "application/json; charset=UTF-8");
 
   thread_->task_runner()->PostTask(
-      FROM_HERE,
-      base::Bind(&ServerWrapper::SendResponse,
-                 base::Unretained(server_wrapper_), connection_id, response));
+      FROM_HERE, base::BindOnce(&ServerWrapper::SendResponse,
+                                base::Unretained(server_wrapper_.get()),
+                                connection_id, response));
 }
 
 void DevToolsHttpHandler::RespondToJsonList(
@@ -695,9 +677,9 @@
         DevToolsAgentHost::CreateForBrowser(
             thread_->task_runner(),
             base::Bind(&DevToolsSocketFactory::CreateForTethering,
-                       base::Unretained(socket_factory_)));
+                       base::Unretained(socket_factory_.get())));
     connection_to_client_[connection_id].reset(new DevToolsAgentHostClientImpl(
-        thread_->message_loop(), server_wrapper_, connection_id,
+        thread_->message_loop(), server_wrapper_.get(), connection_id,
         browser_agent));
     AcceptWebSocket(connection_id, request);
     return;
@@ -724,7 +706,7 @@
   }
 
   connection_to_client_[connection_id].reset(new DevToolsAgentHostClientImpl(
-      thread_->message_loop(), server_wrapper_, connection_id, agent));
+      thread_->message_loop(), server_wrapper_.get(), connection_id, agent));
 
   AcceptWebSocket(connection_id, request);
 }
@@ -750,37 +732,38 @@
     const base::FilePath& debug_frontend_dir,
     const std::string& product_name,
     const std::string& user_agent)
-    : thread_(nullptr),
-      frontend_url_(frontend_url),
+    : frontend_url_(frontend_url),
       product_name_(product_name),
       user_agent_(user_agent),
-      server_wrapper_(nullptr),
       delegate_(delegate),
-      socket_factory_(nullptr),
       weak_factory_(this) {
   bool bundles_resources = frontend_url_.empty();
   if (frontend_url_.empty())
     frontend_url_ = "/devtools/inspector.html";
 
-  BrowserThread::PostTask(
-      BrowserThread::FILE, FROM_HERE,
-      base::Bind(&StartServerOnFile,
-                 weak_factory_.GetWeakPtr(),
-                 socket_factory.release(),
-                 output_directory,
-                 debug_frontend_dir,
-                 bundles_resources));
+  std::unique_ptr<base::Thread> thread(
+      new base::Thread(kDevToolsHandlerThreadName));
+  base::Thread::Options options;
+  options.message_loop_type = base::MessageLoop::TYPE_IO;
+  if (thread->StartWithOptions(options)) {
+    base::TaskRunner* task_runner = thread->task_runner().get();
+    task_runner->PostTask(
+        FROM_HERE, base::BindOnce(&StartServerOnHandlerThread,
+                                  weak_factory_.GetWeakPtr(), std::move(thread),
+                                  std::move(socket_factory), output_directory,
+                                  debug_frontend_dir, bundles_resources));
+  }
 }
 
 void DevToolsHttpHandler::ServerStarted(
-    base::Thread* thread,
-    ServerWrapper* server_wrapper,
-    DevToolsSocketFactory* socket_factory,
+    std::unique_ptr<base::Thread> thread,
+    std::unique_ptr<ServerWrapper> server_wrapper,
+    std::unique_ptr<DevToolsSocketFactory> socket_factory,
     std::unique_ptr<net::IPEndPoint> ip_address) {
-  thread_ = thread;
-  server_wrapper_ = server_wrapper;
-  socket_factory_ = socket_factory;
-  server_ip_address_.swap(ip_address);
+  thread_ = std::move(thread);
+  server_wrapper_ = std::move(server_wrapper);
+  socket_factory_ = std::move(socket_factory);
+  server_ip_address_ = std::move(ip_address);
 }
 
 void ServerWrapper::WriteActivePortToUserProfile(
@@ -824,9 +807,9 @@
   response.SetBody(json_value + message, "application/json; charset=UTF-8");
 
   thread_->task_runner()->PostTask(
-      FROM_HERE,
-      base::Bind(&ServerWrapper::SendResponse,
-                 base::Unretained(server_wrapper_), connection_id, response));
+      FROM_HERE, base::Bind(&ServerWrapper::SendResponse,
+                            base::Unretained(server_wrapper_.get()),
+                            connection_id, response));
 }
 
 void DevToolsHttpHandler::Send200(int connection_id,
@@ -835,17 +818,18 @@
   if (!thread_)
     return;
   thread_->task_runner()->PostTask(
-      FROM_HERE,
-      base::Bind(&ServerWrapper::Send200, base::Unretained(server_wrapper_),
-                 connection_id, data, mime_type));
+      FROM_HERE, base::BindOnce(&ServerWrapper::Send200,
+                                base::Unretained(server_wrapper_.get()),
+                                connection_id, data, mime_type));
 }
 
 void DevToolsHttpHandler::Send404(int connection_id) {
   if (!thread_)
     return;
   thread_->task_runner()->PostTask(
-      FROM_HERE, base::Bind(&ServerWrapper::Send404,
-                            base::Unretained(server_wrapper_), connection_id));
+      FROM_HERE,
+      base::BindOnce(&ServerWrapper::Send404,
+                     base::Unretained(server_wrapper_.get()), connection_id));
 }
 
 void DevToolsHttpHandler::Send500(int connection_id,
@@ -853,9 +837,9 @@
   if (!thread_)
     return;
   thread_->task_runner()->PostTask(
-      FROM_HERE,
-      base::Bind(&ServerWrapper::Send500, base::Unretained(server_wrapper_),
-                 connection_id, message));
+      FROM_HERE, base::BindOnce(&ServerWrapper::Send500,
+                                base::Unretained(server_wrapper_.get()),
+                                connection_id, message));
 }
 
 void DevToolsHttpHandler::AcceptWebSocket(
@@ -864,9 +848,9 @@
   if (!thread_)
     return;
   thread_->task_runner()->PostTask(
-      FROM_HERE,
-      base::Bind(&ServerWrapper::AcceptWebSocket,
-                 base::Unretained(server_wrapper_), connection_id, request));
+      FROM_HERE, base::BindOnce(&ServerWrapper::AcceptWebSocket,
+                                base::Unretained(server_wrapper_.get()),
+                                connection_id, request));
 }
 
 std::unique_ptr<base::DictionaryValue> DevToolsHttpHandler::SerializeDescriptor(
diff --git a/content/browser/devtools/devtools_http_handler.h b/content/browser/devtools/devtools_http_handler.h
index 72836212..4ef44136 100644
--- a/content/browser/devtools/devtools_http_handler.h
+++ b/content/browser/devtools/devtools_http_handler.h
@@ -80,9 +80,9 @@
   void OnWebSocketMessage(int connection_id, const std::string& data);
   void OnClose(int connection_id);
 
-  void ServerStarted(base::Thread* thread,
-                     ServerWrapper* server_wrapper,
-                     DevToolsSocketFactory* socket_factory,
+  void ServerStarted(std::unique_ptr<base::Thread> thread,
+                     std::unique_ptr<ServerWrapper> server_wrapper,
+                     std::unique_ptr<DevToolsSocketFactory> socket_factory,
                      std::unique_ptr<net::IPEndPoint> ip_address);
 
   void SendJson(int connection_id,
@@ -109,17 +109,17 @@
       const std::string& host);
 
   // The thread used by the devtools handler to run server socket.
-  base::Thread* thread_;
+  std::unique_ptr<base::Thread> thread_;
   std::string frontend_url_;
   std::string product_name_;
   std::string user_agent_;
-  ServerWrapper* server_wrapper_;
+  std::unique_ptr<ServerWrapper> server_wrapper_;
   std::unique_ptr<net::IPEndPoint> server_ip_address_;
   using ConnectionToClientMap =
       std::map<int, std::unique_ptr<DevToolsAgentHostClientImpl>>;
   ConnectionToClientMap connection_to_client_;
   DevToolsManagerDelegate* delegate_;
-  DevToolsSocketFactory* socket_factory_;
+  std::unique_ptr<DevToolsSocketFactory> socket_factory_;
   base::WeakPtrFactory<DevToolsHttpHandler> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(DevToolsHttpHandler);
diff --git a/content/browser/devtools/devtools_http_handler_unittest.cc b/content/browser/devtools/devtools_http_handler_unittest.cc
index a4ad827e..7232833 100644
--- a/content/browser/devtools/devtools_http_handler_unittest.cc
+++ b/content/browser/devtools/devtools_http_handler_unittest.cc
@@ -158,10 +158,8 @@
   // Our dummy socket factory will post a quit message once the server will
   // become ready.
   run_loop.Run();
-  for (int i = 0; i < 5; i++) {
+  for (int i = 0; i < 5; i++)
     RunAllPendingInMessageLoop(BrowserThread::UI);
-    RunAllPendingInMessageLoop(BrowserThread::FILE);
-  }
   DevToolsAgentHost::StopRemoteDebuggingServer();
   // Make sure the handler actually stops.
   run_loop_2.Run();
diff --git a/content/browser/renderer_host/render_widget_host_view_android.cc b/content/browser/renderer_host/render_widget_host_view_android.cc
index 77970a32..81331a5 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.cc
+++ b/content/browser/renderer_host/render_widget_host_view_android.cc
@@ -1470,8 +1470,8 @@
   UpdateBackgroundColor(is_transparent ? SK_ColorTRANSPARENT
                                        : frame_metadata.root_background_color);
 
-  view_.set_content_offset(top_content_offset);
-  view_.set_viewport_size(frame_metadata.scrollable_viewport_size);
+  view_.UpdateFrameInfo({frame_metadata.scrollable_viewport_size,
+                         frame_metadata.page_scale_factor, top_content_offset});
 
   bool top_changed = !FloatEquals(top_shown_pix, prev_top_shown_pix_);
   if (top_changed) {
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 62ea43e..7f74100 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -39,6 +39,13 @@
 const base::Feature kBrowserSideNavigation{"browser-side-navigation",
                                            base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Toggles whether the buggy RSA parser is used.
+//
+// TODO(davidben): Remove this after Chrome 61 is released to
+// stable. https://crbug.com/735616.
+const base::Feature kBuggyRSAParser{"BuggyRSAParser",
+                                    base::FEATURE_DISABLED_BY_DEFAULT};
+
 // If Canvas2D Image Chromium is allowed, this feature controls whether it is
 // enabled.
 const base::Feature kCanvas2DImageChromium{"Canvas2DImageChromium",
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index 6627473..0417803 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -23,6 +23,7 @@
 CONTENT_EXPORT extern const base::Feature kBlockCredentialedSubresources;
 CONTENT_EXPORT extern const base::Feature kBrotliEncoding;
 CONTENT_EXPORT extern const base::Feature kBrowserSideNavigation;
+CONTENT_EXPORT extern const base::Feature kBuggyRSAParser;
 CONTENT_EXPORT extern const base::Feature kCanvas2DImageChromium;
 CONTENT_EXPORT extern const base::Feature kCheckerImaging;
 CONTENT_EXPORT extern const base::Feature kCompositeOpaqueFixedPosition;
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index 1cae4a69..1058781 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -173,6 +173,7 @@
 #include "third_party/WebKit/public/web/WebScriptController.h"
 #include "third_party/WebKit/public/web/WebSecurityPolicy.h"
 #include "third_party/WebKit/public/web/WebView.h"
+#include "third_party/boringssl/src/include/openssl/evp.h"
 #include "third_party/skia/include/core/SkGraphics.h"
 #include "ui/base/layout.h"
 #include "ui/base/ui_base_switches.h"
@@ -945,6 +946,9 @@
   if (!command_line.HasSwitch(switches::kSingleProcess))
     base::SequencedWorkerPool::EnableForProcess();
 
+  EVP_set_buggy_rsa_parser(
+      base::FeatureList::IsEnabled(features::kBuggyRSAParser));
+
   GetConnector()->BindInterface(mojom::kBrowserServiceName,
                                 mojo::MakeRequest(&frame_sink_provider_));
 }
diff --git a/content/shell/android/BUILD.gn b/content/shell/android/BUILD.gn
index eceffc1d..2a0443ee 100644
--- a/content/shell/android/BUILD.gn
+++ b/content/shell/android/BUILD.gn
@@ -15,22 +15,10 @@
   ]
 }
 
-generate_jni_registration("content_shell_jni_registration") {
-  testonly = true
-  target = ":content_shell_apk"
-  output = "$root_gen_dir/content/shell/android/${target_name}.h"
-  exception_files = [
-    "//base/android/java/src/org/chromium/base/library_loader/LegacyLinker.java",
-    "//base/android/java/src/org/chromium/base/library_loader/Linker.java",
-    "//base/android/java/src/org/chromium/base/library_loader/ModernLinker.java",
-  ]
-}
-
 shared_library("libcontent_shell_content_view") {
   testonly = true
   deps = [
     ":content_shell_jni_headers",
-    ":content_shell_jni_registration",
     "//build/config:exe_and_shlib_deps",
     "//components/crash/content/browser",
     "//content/shell:content_shell_lib",
@@ -38,17 +26,6 @@
     "//media",
     "//skia",
   ]
-
-  # Explicit dependency required for JNI registration to be able to
-  # find the native side functions.
-  if (is_component_build) {
-    deps += [
-      "//device/gamepad",
-      "//device/generic_sensor",
-      "//device/sensors",
-      "//media/midi",
-    ]
-  }
   sources = [
     "shell_library_loader.cc",
   ]
@@ -265,7 +242,6 @@
 
     deps = [
       ":linker_test_jni_headers",
-      ":linker_test_jni_registration",
       "//build/config:exe_and_shlib_deps",
       "//content/shell:content_shell_lib",
 
@@ -274,17 +250,6 @@
       "//skia",
       "//third_party/re2",
     ]
-
-    # Explicit dependency required for JNI registration to be able to
-    # find the native side functions.
-    if (is_component_build) {
-      deps += [
-        "//device/gamepad",
-        "//device/generic_sensor",
-        "//device/sensors",
-        "//media/midi",
-      ]
-    }
   }
 
   generate_jni("linker_test_jni_headers") {
@@ -294,18 +259,6 @@
       "linker_test_apk/src/org/chromium/chromium_linker_test_apk/LinkerTests.java",
     ]
   }
-
-  generate_jni_registration("linker_test_jni_registration") {
-    testonly = true
-    target = ":chromium_linker_test_apk__apk"
-    output =
-        "$root_gen_dir/content/shell/android/linker_test_apk/${target_name}.h"
-    exception_files = [
-      "//base/android/java/src/org/chromium/base/library_loader/LegacyLinker.java",
-      "//base/android/java/src/org/chromium/base/library_loader/Linker.java",
-      "//base/android/java/src/org/chromium/base/library_loader/ModernLinker.java",
-    ]
-  }
 }
 
 android_library("content_shell_browsertests_java") {
diff --git a/content/shell/android/linker_test_apk/chromium_linker_test_android.cc b/content/shell/android/linker_test_apk/chromium_linker_test_android.cc
index 310bb2a..19f9c47 100644
--- a/content/shell/android/linker_test_apk/chromium_linker_test_android.cc
+++ b/content/shell/android/linker_test_apk/chromium_linker_test_android.cc
@@ -9,7 +9,6 @@
 #include "content/public/app/content_main.h"
 #include "content/public/browser/android/compositor.h"
 #include "content/shell/android/linker_test_apk/chromium_linker_test_linker_tests.h"
-#include "content/shell/android/linker_test_apk/linker_test_jni_registration.h"
 #include "content/shell/android/shell_jni_registrar.h"
 #include "content/shell/app/shell_main_delegate.h"
 
@@ -41,11 +40,6 @@
 JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
   base::android::InitVM(vm);
   JNIEnv* env = base::android::AttachCurrentThread();
-  if (!RegisterMainDexNatives(env) || !RegisterNonMainDexNatives(env)) {
-    return -1;
-  }
-  // TODO(agrieve): Delete this block, this is a no-op now.
-  // https://crbug.com/683256.
   if (!content::android::OnJNIOnLoadRegisterJNI(env) || !RegisterJNI(env) ||
       !NativeInit()) {
     return -1;
diff --git a/content/shell/android/shell_library_loader.cc b/content/shell/android/shell_library_loader.cc
index 8cd2e99..dc88bd67 100644
--- a/content/shell/android/shell_library_loader.cc
+++ b/content/shell/android/shell_library_loader.cc
@@ -8,7 +8,6 @@
 #include "content/public/app/content_jni_onload.h"
 #include "content/public/app/content_main.h"
 #include "content/public/browser/android/compositor.h"
-#include "content/shell/android/content_shell_jni_registration.h"
 #include "content/shell/android/shell_jni_registrar.h"
 #include "content/shell/app/shell_main_delegate.h"
 
@@ -34,12 +33,6 @@
 JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
   base::android::InitVM(vm);
   JNIEnv* env = base::android::AttachCurrentThread();
-  if (!RegisterMainDexNatives(env) || !RegisterNonMainDexNatives(env)) {
-    return -1;
-  }
-
-  // TODO(agrieve): Delete this block, this is a no-op now.
-  // https://crbug.com/683256.
   if (!content::android::OnJNIOnLoadRegisterJNI(env) || !RegisterJNI(env) ||
       !NativeInit()) {
     return -1;
diff --git a/extensions/common/url_pattern_unittest.cc b/extensions/common/url_pattern_unittest.cc
index bd60583..f504a8d8 100644
--- a/extensions/common/url_pattern_unittest.cc
+++ b/extensions/common/url_pattern_unittest.cc
@@ -9,6 +9,7 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "extensions/common/constants.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
@@ -258,7 +259,7 @@
   EXPECT_TRUE(pattern.MatchesScheme("https"));
   EXPECT_TRUE(pattern.MatchesScheme("file"));
   EXPECT_TRUE(pattern.MatchesScheme("filesystem"));
-  EXPECT_TRUE(pattern.MatchesScheme("chrome-extension"));
+  EXPECT_TRUE(pattern.MatchesScheme(extensions::kExtensionScheme));
   EXPECT_TRUE(pattern.match_subdomains());
   EXPECT_TRUE(pattern.match_effective_tld());
   EXPECT_TRUE(pattern.match_all_urls());
@@ -294,7 +295,7 @@
   EXPECT_TRUE(pattern.MatchesScheme("javascript"));
   EXPECT_TRUE(pattern.MatchesScheme("data"));
   EXPECT_TRUE(pattern.MatchesScheme("about"));
-  EXPECT_TRUE(pattern.MatchesScheme("chrome-extension"));
+  EXPECT_TRUE(pattern.MatchesScheme(extensions::kExtensionScheme));
   EXPECT_TRUE(pattern.match_subdomains());
   EXPECT_TRUE(pattern.match_effective_tld());
   EXPECT_TRUE(pattern.match_all_urls());
@@ -435,7 +436,7 @@
   URLPattern pattern(URLPattern::SCHEME_EXTENSION);
   EXPECT_EQ(URLPattern::PARSE_SUCCESS,
             pattern.Parse("chrome-extension://ftw/*"));
-  EXPECT_EQ("chrome-extension", pattern.scheme());
+  EXPECT_EQ(extensions::kExtensionScheme, pattern.scheme());
   EXPECT_EQ("ftw", pattern.host());
   EXPECT_FALSE(pattern.match_subdomains());
   EXPECT_FALSE(pattern.match_all_urls());
diff --git a/ios/chrome/browser/payments/test_payment_request.h b/ios/chrome/browser/payments/test_payment_request.h
index abfbca5..1ced6fb 100644
--- a/ios/chrome/browser/payments/test_payment_request.h
+++ b/ios/chrome/browser/payments/test_payment_request.h
@@ -28,7 +28,9 @@
   // |personal_data_manager| should not be null and should outlive this object.
   TestPaymentRequest(const web::PaymentRequest& web_payment_request,
                      autofill::PersonalDataManager* personal_data_manager)
-      : PaymentRequest(web_payment_request, personal_data_manager) {}
+      : PaymentRequest(web_payment_request, personal_data_manager),
+        region_data_loader_(nullptr),
+        profile_comparator_(nullptr) {}
 
   ~TestPaymentRequest() override {}
 
diff --git a/ios/chrome/browser/payments/test_payment_request.mm b/ios/chrome/browser/payments/test_payment_request.mm
index 336d45f..be46d8a 100644
--- a/ios/chrome/browser/payments/test_payment_request.mm
+++ b/ios/chrome/browser/payments/test_payment_request.mm
@@ -26,9 +26,13 @@
 }
 
 autofill::RegionDataLoader* TestPaymentRequest::GetRegionDataLoader() {
-  return region_data_loader_;
+  if (region_data_loader_)
+    return region_data_loader_;
+  return PaymentRequest::GetRegionDataLoader();
 }
 
 payments::PaymentsProfileComparator* TestPaymentRequest::profile_comparator() {
-  return profile_comparator_;
+  if (profile_comparator_)
+    return profile_comparator_;
+  return PaymentRequest::profile_comparator();
 }
diff --git a/ios/chrome/browser/ui/contextual_search/contextual_search_js_unittest.mm b/ios/chrome/browser/ui/contextual_search/contextual_search_js_unittest.mm
index 25c87728..ca00dd30 100644
--- a/ios/chrome/browser/ui/contextual_search/contextual_search_js_unittest.mm
+++ b/ios/chrome/browser/ui/contextual_search/contextual_search_js_unittest.mm
@@ -379,6 +379,9 @@
 
 // Test that related text DOM mutation prevents contextual search.
 TEST_F(ContextualSearchJsTest, TestHighlightRelatedDOMMutationText) {
+  // TODO(crbug.com/736989): Test failures in GetMutatedNodeCount.
+  if (base::ios::IsRunningOnIOS11OrLater())
+    return;
   LoadHtml(kHTMLWithRelatedTextMutation);
   ExecuteJavaScript(@"document.getElementById('test').innerText = 'mutated';");
   ASSERT_EQ(1, GetMutatedNodeCount());
diff --git a/ios/chrome/browser/ui/payments/billing_address_selection_coordinator_unittest.mm b/ios/chrome/browser/ui/payments/billing_address_selection_coordinator_unittest.mm
index 0abd0549..15c77b4 100644
--- a/ios/chrome/browser/ui/payments/billing_address_selection_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/payments/billing_address_selection_coordinator_unittest.mm
@@ -9,9 +9,13 @@
 #include "base/test/ios/wait_util.h"
 #include "components/autofill/core/browser/autofill_profile.h"
 #include "components/autofill/core/browser/autofill_test_utils.h"
+#include "components/autofill/core/browser/autofill_type.h"
+#include "components/autofill/core/browser/field_types.h"
 #include "components/autofill/core/browser/test_personal_data_manager.h"
-#include "ios/chrome/browser/payments/payment_request.h"
+#include "components/autofill/core/browser/test_region_data_loader.h"
+#include "components/prefs/pref_service.h"
 #include "ios/chrome/browser/payments/payment_request_test_util.h"
+#include "ios/chrome/browser/payments/test_payment_request.h"
 #import "ios/chrome/browser/ui/payments/payment_request_selector_view_controller.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
@@ -27,13 +31,24 @@
  protected:
   PaymentRequestBillingAddressSelectionCoordinatorTest()
       : autofill_profile1_(autofill::test::GetFullProfile()),
-        autofill_profile2_(autofill::test::GetFullProfile2()) {
-    // Add testing profiles to autofill::TestPersonalDataManager.
+        autofill_profile2_(autofill::test::GetFullProfile2()),
+        pref_service_(autofill::test::PrefServiceForTesting()) {
+    personal_data_manager_.SetTestingPrefService(pref_service_.get());
+    // Add testing profiles to autofill::TestPersonalDataManager. Make the less
+    // frequently used one incomplete.
+    autofill_profile1_.set_use_count(10U);
     personal_data_manager_.AddTestingProfile(&autofill_profile1_);
+    autofill_profile2_.set_use_count(5U);
+    autofill_profile2_.SetInfo(
+        autofill::AutofillType(autofill::PHONE_HOME_WHOLE_NUMBER),
+        base::string16(), "en-US");
     personal_data_manager_.AddTestingProfile(&autofill_profile2_);
-    payment_request_ = base::MakeUnique<PaymentRequest>(
+    payment_request_ = base::MakeUnique<TestPaymentRequest>(
         payment_request_test_util::CreateTestWebPaymentRequest(),
         &personal_data_manager_);
+
+    test_region_data_loader_.set_synchronous_callback(true);
+    payment_request_->SetRegionDataLoader(&test_region_data_loader_);
   }
 
   void SetUp() override {
@@ -46,6 +61,10 @@
     [coordinator_ setPaymentRequest:payment_request_.get()];
   }
 
+  void TearDown() override {
+    personal_data_manager_.SetTestingPrefService(nullptr);
+  }
+
   UINavigationController* GetNavigationController() {
     return navigation_controller_;
   }
@@ -57,8 +76,10 @@
 
   autofill::AutofillProfile autofill_profile1_;
   autofill::AutofillProfile autofill_profile2_;
+  std::unique_ptr<PrefService> pref_service_;
   autofill::TestPersonalDataManager personal_data_manager_;
-  std::unique_ptr<PaymentRequest> payment_request_;
+  autofill::TestRegionDataLoader test_region_data_loader_;
+  std::unique_ptr<TestPaymentRequest> payment_request_;
 };
 
 // Tests that invoking start and stop on the coordinator presents and dismisses
@@ -84,15 +105,20 @@
 
 // Tests that calling the view controller delegate method which notifies the
 // delegate about selection of a billing address invokes the corresponding
-// coordinator delegate method.
+// coordinator delegate method, only if the payment method is complete.
 TEST_F(PaymentRequestBillingAddressSelectionCoordinatorTest,
        SelectedBillingAddress) {
   // Mock the coordinator delegate.
   id delegate = [OCMockObject
       mockForProtocol:@protocol(BillingAddressSelectionCoordinatorDelegate)];
-  autofill::AutofillProfile* profile = payment_request_->billing_profiles()[1];
-  [[delegate expect] billingAddressSelectionCoordinator:GetCoordinator()
-                                didSelectBillingAddress:profile];
+  [[delegate expect]
+      billingAddressSelectionCoordinator:GetCoordinator()
+                 didSelectBillingAddress:payment_request_
+                                             ->billing_profiles()[0]];
+  [[delegate reject]
+      billingAddressSelectionCoordinator:GetCoordinator()
+                 didSelectBillingAddress:payment_request_
+                                             ->billing_profiles()[1]];
   [GetCoordinator() setDelegate:delegate];
 
   EXPECT_EQ(1u, GetNavigationController().viewControllers.count);
@@ -102,13 +128,16 @@
   base::test::ios::SpinRunLoopWithMaxDelay(base::TimeDelta::FromSecondsD(1.0));
   EXPECT_EQ(2u, GetNavigationController().viewControllers.count);
 
-  // Call the controller delegate method.
+  // Call the controller delegate method for both selectable items.
   PaymentRequestSelectorViewController* view_controller =
       base::mac::ObjCCastStrict<PaymentRequestSelectorViewController>(
           GetNavigationController().visibleViewController);
   [GetCoordinator() paymentRequestSelectorViewController:view_controller
+                                    didSelectItemAtIndex:0];
+  // Wait for the coordinator delegate to be notified.
+  base::test::ios::SpinRunLoopWithMinDelay(base::TimeDelta::FromSecondsD(0.5));
+  [GetCoordinator() paymentRequestSelectorViewController:view_controller
                                     didSelectItemAtIndex:1];
-
   // Wait for the coordinator delegate to be notified.
   base::test::ios::SpinRunLoopWithMinDelay(base::TimeDelta::FromSecondsD(0.5));
 
diff --git a/ios/chrome/browser/ui/payments/shipping_address_selection_coordinator_unittest.mm b/ios/chrome/browser/ui/payments/shipping_address_selection_coordinator_unittest.mm
index 3eb3d51..a9c3bdb 100644
--- a/ios/chrome/browser/ui/payments/shipping_address_selection_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/payments/shipping_address_selection_coordinator_unittest.mm
@@ -9,9 +9,13 @@
 #include "base/test/ios/wait_util.h"
 #include "components/autofill/core/browser/autofill_profile.h"
 #include "components/autofill/core/browser/autofill_test_utils.h"
+#include "components/autofill/core/browser/autofill_type.h"
+#include "components/autofill/core/browser/field_types.h"
 #include "components/autofill/core/browser/test_personal_data_manager.h"
-#include "ios/chrome/browser/payments/payment_request.h"
+#include "components/autofill/core/browser/test_region_data_loader.h"
+#include "components/prefs/pref_service.h"
 #include "ios/chrome/browser/payments/payment_request_test_util.h"
+#include "ios/chrome/browser/payments/test_payment_request.h"
 #import "ios/chrome/browser/ui/payments/payment_request_selector_view_controller.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
@@ -27,19 +31,36 @@
  protected:
   PaymentRequestShippingAddressSelectionCoordinatorTest()
       : autofill_profile1_(autofill::test::GetFullProfile()),
-        autofill_profile2_(autofill::test::GetFullProfile2()) {
-    // Add testing profiles to autofill::TestPersonalDataManager.
+        autofill_profile2_(autofill::test::GetFullProfile2()),
+        pref_service_(autofill::test::PrefServiceForTesting()) {
+    personal_data_manager_.SetTestingPrefService(pref_service_.get());
+    // Add testing profiles to autofill::TestPersonalDataManager. Make the less
+    // frequently used one incomplete.
+    autofill_profile1_.set_use_count(10U);
     personal_data_manager_.AddTestingProfile(&autofill_profile1_);
+    autofill_profile2_.set_use_count(5U);
+    autofill_profile2_.SetInfo(
+        autofill::AutofillType(autofill::PHONE_HOME_WHOLE_NUMBER),
+        base::string16(), "en-US");
     personal_data_manager_.AddTestingProfile(&autofill_profile2_);
-    payment_request_ = base::MakeUnique<PaymentRequest>(
+    payment_request_ = base::MakeUnique<TestPaymentRequest>(
         payment_request_test_util::CreateTestWebPaymentRequest(),
         &personal_data_manager_);
+
+    test_region_data_loader_.set_synchronous_callback(true);
+    payment_request_->SetRegionDataLoader(&test_region_data_loader_);
+  }
+
+  void TearDown() override {
+    personal_data_manager_.SetTestingPrefService(nullptr);
   }
 
   autofill::AutofillProfile autofill_profile1_;
   autofill::AutofillProfile autofill_profile2_;
+  std::unique_ptr<PrefService> pref_service_;
   autofill::TestPersonalDataManager personal_data_manager_;
-  std::unique_ptr<PaymentRequest> payment_request_;
+  autofill::TestRegionDataLoader test_region_data_loader_;
+  std::unique_ptr<TestPaymentRequest> payment_request_;
 };
 
 // Tests that invoking start and stop on the coordinator presents and dismisses
@@ -75,7 +96,7 @@
 
 // Tests that calling the view controller delegate method which notifies the
 // delegate about selection of a shipping address invokes the corresponding
-// coordinator delegate method.
+// coordinator delegate method, only if the payment method is complete.
 TEST_F(PaymentRequestShippingAddressSelectionCoordinatorTest,
        SelectedShippingAddress) {
   UIViewController* base_view_controller = [[UIViewController alloc] init];
@@ -91,9 +112,14 @@
   // Mock the coordinator delegate.
   id delegate = [OCMockObject
       mockForProtocol:@protocol(ShippingAddressSelectionCoordinatorDelegate)];
-  autofill::AutofillProfile* profile = payment_request_->shipping_profiles()[1];
-  [[delegate expect] shippingAddressSelectionCoordinator:coordinator
-                                didSelectShippingAddress:profile];
+  [[delegate expect]
+      shippingAddressSelectionCoordinator:coordinator
+                 didSelectShippingAddress:payment_request_
+                                              ->shipping_profiles()[0]];
+  [[delegate reject]
+      shippingAddressSelectionCoordinator:coordinator
+                 didSelectShippingAddress:payment_request_
+                                              ->shipping_profiles()[1]];
   [coordinator setDelegate:delegate];
 
   EXPECT_EQ(1u, navigation_controller.viewControllers.count);
@@ -103,13 +129,16 @@
   base::test::ios::SpinRunLoopWithMaxDelay(base::TimeDelta::FromSecondsD(1.0));
   EXPECT_EQ(2u, navigation_controller.viewControllers.count);
 
-  // Call the controller delegate method.
+  // Call the controller delegate method for both selectable items.
   PaymentRequestSelectorViewController* view_controller =
       base::mac::ObjCCastStrict<PaymentRequestSelectorViewController>(
           navigation_controller.visibleViewController);
   [coordinator paymentRequestSelectorViewController:view_controller
+                               didSelectItemAtIndex:0];
+  // Wait for the coordinator delegate to be notified.
+  base::test::ios::SpinRunLoopWithMinDelay(base::TimeDelta::FromSecondsD(0.5));
+  [coordinator paymentRequestSelectorViewController:view_controller
                                didSelectItemAtIndex:1];
-
   // Wait for the coordinator delegate to be notified.
   base::test::ios::SpinRunLoopWithMinDelay(base::TimeDelta::FromSecondsD(0.5));
 
diff --git a/media/audio/audio_system_impl.cc b/media/audio/audio_system_impl.cc
index 42d0bb7..87d39a90 100644
--- a/media/audio/audio_system_impl.cc
+++ b/media/audio/audio_system_impl.cc
@@ -163,9 +163,17 @@
 
   // TODO(olka): remove this when AudioManager::GetInputStreamParameters()
   // returns invalid parameters if the device is not found.
-  if (!audio_manager->HasAudioInputDevices())
-    return AudioParameters();
-
+  if (device_id.compare(AudioDeviceDescription::kLoopbackInputDeviceId) == 0 ||
+      device_id.compare(AudioDeviceDescription::kLoopbackWithMuteDeviceId) ==
+          0) {
+    // For system audio capture, we need an output device (namely speaker)
+    // instead of an input device (namely microphone) to work.
+    if (!audio_manager->HasAudioOutputDevices())
+      return AudioParameters();
+  } else {
+    if (!audio_manager->HasAudioInputDevices())
+      return AudioParameters();
+  }
   return audio_manager->GetInputStreamParameters(device_id);
 }
 
diff --git a/media/capture/BUILD.gn b/media/capture/BUILD.gn
index d83849f78..79e561da 100644
--- a/media/capture/BUILD.gn
+++ b/media/capture/BUILD.gn
@@ -192,6 +192,7 @@
       "video/chromeos/camera_device_context.cc",
       "video/chromeos/camera_device_delegate.cc",
       "video/chromeos/camera_hal_delegate.cc",
+      "video/chromeos/camera_hal_dispatcher_impl.cc",
       "video/chromeos/camera_metadata_utils.cc",
       "video/chromeos/display_rotation_observer.cc",
       "video/chromeos/pixel_format_utils.cc",
@@ -249,6 +250,7 @@
     sources += [
       "video/chromeos/camera_device_delegate_unittest.cc",
       "video/chromeos/camera_hal_delegate_unittest.cc",
+      "video/chromeos/camera_hal_dispatcher_impl_unittest.cc",
       "video/chromeos/mock_camera_module.cc",
       "video/chromeos/mock_video_capture_client.cc",
       "video/chromeos/stream_buffer_manager_unittest.cc",
diff --git a/media/capture/video/chromeos/camera_device_delegate_unittest.cc b/media/capture/video/chromeos/camera_device_delegate_unittest.cc
index e31963dc..46aaade 100644
--- a/media/capture/video/chromeos/camera_device_delegate_unittest.cc
+++ b/media/capture/video/chromeos/camera_device_delegate_unittest.cc
@@ -126,7 +126,7 @@
     hal_delegate_thread_.Start();
     camera_hal_delegate_ =
         new CameraHalDelegate(hal_delegate_thread_.task_runner());
-    camera_hal_delegate_->StartForTesting(
+    camera_hal_delegate_->SetCameraModule(
         mock_camera_module_.GetInterfacePtrInfo());
 
     ResetCaptureClient();
diff --git a/media/capture/video/chromeos/camera_hal_delegate.cc b/media/capture/video/chromeos/camera_hal_delegate.cc
index 6f050c1..b4932c9 100644
--- a/media/capture/video/chromeos/camera_hal_delegate.cc
+++ b/media/capture/video/chromeos/camera_hal_delegate.cc
@@ -13,16 +13,10 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/strings/string_piece.h"
+#include "media/capture/video/chromeos/camera_hal_dispatcher_impl.h"
 #include "media/capture/video/chromeos/camera_metadata_utils.h"
 #include "media/capture/video/chromeos/pixel_format_utils.h"
 #include "media/capture/video/chromeos/video_capture_device_arc_chromeos.h"
-#include "mojo/edk/embedder/embedder.h"
-#include "mojo/edk/embedder/incoming_broker_client_invitation.h"
-#include "mojo/edk/embedder/named_platform_handle.h"
-#include "mojo/edk/embedder/named_platform_handle_utils.h"
-#include "mojo/edk/embedder/platform_channel_pair.h"
-#include "mojo/edk/embedder/platform_channel_utils_posix.h"
-#include "mojo/edk/embedder/platform_handle_vector.h"
 
 namespace media {
 
@@ -33,11 +27,29 @@
 const base::TimeDelta kEventWaitTimeoutMs =
     base::TimeDelta::FromMilliseconds(3000);
 
+class LocalCameraClientObserver : public CameraClientObserver {
+ public:
+  explicit LocalCameraClientObserver(
+      scoped_refptr<CameraHalDelegate> camera_hal_delegate)
+      : camera_hal_delegate_(std::move(camera_hal_delegate)) {}
+
+  void OnChannelCreated(arc::mojom::CameraModulePtr camera_module) override {
+    camera_hal_delegate_->SetCameraModule(camera_module.PassInterface());
+  }
+
+ private:
+  scoped_refptr<CameraHalDelegate> camera_hal_delegate_;
+  DISALLOW_IMPLICIT_CONSTRUCTORS(LocalCameraClientObserver);
+};
+
 }  // namespace
 
 CameraHalDelegate::CameraHalDelegate(
     scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner)
-    : builtin_camera_info_updated_(
+    : camera_module_has_been_set_(
+          base::WaitableEvent::ResetPolicy::MANUAL,
+          base::WaitableEvent::InitialState::NOT_SIGNALED),
+      builtin_camera_info_updated_(
           base::WaitableEvent::ResetPolicy::MANUAL,
           base::WaitableEvent::InitialState::NOT_SIGNALED),
       num_builtin_cameras_(0),
@@ -48,62 +60,16 @@
 
 CameraHalDelegate::~CameraHalDelegate() {}
 
-bool CameraHalDelegate::StartCameraModuleIpc() {
-  // Non-blocking socket handle.
-  mojo::edk::ScopedPlatformHandle socket_handle = mojo::edk::CreateClientHandle(
-      mojo::edk::NamedPlatformHandle(kArcCamera3SocketPath));
+void CameraHalDelegate::RegisterCameraClient() {
+  CameraHalDispatcherImpl::GetInstance()->AddClientObserver(
+      base::MakeUnique<LocalCameraClientObserver>(this));
+}
 
-  // Set socket to blocking
-  int flags = HANDLE_EINTR(fcntl(socket_handle.get().handle, F_GETFL));
-  if (flags == -1) {
-    PLOG(ERROR) << "fcntl(F_GETFL) failed: ";
-    return false;
-  }
-  if (HANDLE_EINTR(fcntl(socket_handle.get().handle, F_SETFL,
-                         flags & ~O_NONBLOCK)) == -1) {
-    PLOG(ERROR) << "fcntl(F_SETFL) failed: ";
-    return false;
-  }
-
-  const size_t kTokenSize = 32;
-  char token[kTokenSize] = {};
-  std::deque<mojo::edk::PlatformHandle> platform_handles;
-  ssize_t ret = mojo::edk::PlatformChannelRecvmsg(
-      socket_handle.get(), token, sizeof(token), &platform_handles, true);
-  if (ret == -1) {
-    PLOG(ERROR) << "PlatformChannelRecvmsg failed: ";
-    return false;
-  }
-  if (platform_handles.size() != 1) {
-    LOG(ERROR) << "Unexpected number of handles received, expected 1: "
-               << platform_handles.size();
-    return false;
-  }
-  mojo::edk::ScopedPlatformHandle parent_pipe(platform_handles.back());
-  platform_handles.pop_back();
-  if (!parent_pipe.is_valid()) {
-    LOG(ERROR) << "Invalid parent pipe";
-    return false;
-  }
-  std::unique_ptr<mojo::edk::IncomingBrokerClientInvitation> invitation =
-      mojo::edk::IncomingBrokerClientInvitation::Accept(
-          mojo::edk::ConnectionParams(mojo::edk::TransportProtocol::kLegacy,
-                                      std::move(parent_pipe)));
-  mojo::ScopedMessagePipeHandle child_pipe =
-      invitation->ExtractMessagePipe(token);
-  if (!child_pipe.is_valid()) {
-    LOG(ERROR) << "Invalid child pipe";
-    return false;
-  }
-
-  camera_module_ =
-      mojo::MakeProxy(mojo::InterfacePtrInfo<arc::mojom::CameraModule>(
-                          std::move(child_pipe), 0u),
-                      ipc_task_runner_);
-
-  VLOG(1) << "Camera module IPC connection established";
-
-  return true;
+void CameraHalDelegate::SetCameraModule(
+    arc::mojom::CameraModulePtrInfo camera_module_ptr_info) {
+  ipc_task_runner_->PostTask(
+      FROM_HERE, base::Bind(&CameraHalDelegate::SetCameraModuleOnIpcThread,
+                            this, base::Passed(&camera_module_ptr_info)));
 }
 
 void CameraHalDelegate::Reset() {
@@ -233,8 +199,10 @@
 
 void CameraHalDelegate::GetCameraInfo(int32_t camera_id,
                                       const GetCameraInfoCallback& callback) {
-  // This method may be called on any thread.  Currently this method is used by
-  // CameraDeviceDelegate to query camera info.
+  DCHECK(!ipc_task_runner_->BelongsToCurrentThread());
+  // This method may be called on any thread except |ipc_task_runner_|.
+  // Currently this method is used by CameraDeviceDelegate to query camera info.
+  camera_module_has_been_set_.Wait();
   ipc_task_runner_->PostTask(
       FROM_HERE, base::Bind(&CameraHalDelegate::GetCameraInfoOnIpcThread, this,
                             camera_id, callback));
@@ -244,16 +212,28 @@
     int32_t camera_id,
     arc::mojom::Camera3DeviceOpsRequest device_ops_request,
     const OpenDeviceCallback& callback) {
-  // This method may be called on any thread.  Currently this method is used by
-  // CameraDeviceDelegate to open a camera device.
+  DCHECK(!ipc_task_runner_->BelongsToCurrentThread());
+  // This method may be called on any thread except |ipc_task_runner_|.
+  // Currently this method is used by CameraDeviceDelegate to open a camera
+  // device.
+  camera_module_has_been_set_.Wait();
   ipc_task_runner_->PostTask(
       FROM_HERE,
       base::Bind(&CameraHalDelegate::OpenDeviceOnIpcThread, this, camera_id,
                  base::Passed(&device_ops_request), callback));
 }
 
-void CameraHalDelegate::StartForTesting(arc::mojom::CameraModulePtrInfo info) {
-  camera_module_.Bind(std::move(info), ipc_task_runner_);
+void CameraHalDelegate::SetCameraModuleOnIpcThread(
+    arc::mojom::CameraModulePtrInfo camera_module_ptr_info) {
+  DCHECK(ipc_task_runner_->BelongsToCurrentThread());
+  if (camera_module_.is_bound()) {
+    LOG(ERROR) << "CameraModule is already bound";
+    return;
+  }
+  camera_module_ = mojo::MakeProxy(std::move(camera_module_ptr_info));
+  camera_module_.set_connection_error_handler(
+      base::Bind(&CameraHalDelegate::ResetMojoInterfaceOnIpcThread, this));
+  camera_module_has_been_set_.Signal();
 }
 
 void CameraHalDelegate::ResetMojoInterfaceOnIpcThread() {
@@ -262,10 +242,15 @@
   if (camera_module_callbacks_.is_bound()) {
     camera_module_callbacks_.Close();
   }
+  builtin_camera_info_updated_.Reset();
+  camera_module_has_been_set_.Reset();
 }
 
 bool CameraHalDelegate::UpdateBuiltInCameraInfo() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(!ipc_task_runner_->BelongsToCurrentThread());
+
+  camera_module_has_been_set_.Wait();
   if (builtin_camera_info_updated_.IsSignaled()) {
     return true;
   }
diff --git a/media/capture/video/chromeos/camera_hal_delegate.h b/media/capture/video/chromeos/camera_hal_delegate.h
index 15637825..44f10ec 100644
--- a/media/capture/video/chromeos/camera_hal_delegate.h
+++ b/media/capture/video/chromeos/camera_hal_delegate.h
@@ -36,9 +36,10 @@
   explicit CameraHalDelegate(
       scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner);
 
-  // Establishes the Mojo IPC channel to the camera HAL adapter.  This method
-  // should be called before any other methods of CameraHalDelegate is called.
-  bool StartCameraModuleIpc();
+  // Registers the camera client observer to the CameraHalDispatcher instance.
+  void RegisterCameraClient();
+
+  void SetCameraModule(arc::mojom::CameraModulePtrInfo camera_module_ptr_info);
 
   // Resets |camera_module_| and |camera_module_callbacks_|.
   void Reset();
@@ -75,9 +76,8 @@
 
   ~CameraHalDelegate() final;
 
-  friend class CameraHalDelegateTest;
-  friend class CameraDeviceDelegateTest;
-  void StartForTesting(arc::mojom::CameraModulePtrInfo info);
+  void SetCameraModuleOnIpcThread(
+      arc::mojom::CameraModulePtrInfo camera_module_ptr_info);
 
   // Resets the Mojo interface and bindings.
   void ResetMojoInterfaceOnIpcThread();
@@ -112,6 +112,8 @@
       int32_t camera_id,
       arc::mojom::CameraDeviceStatus new_status) final;
 
+  base::WaitableEvent camera_module_has_been_set_;
+
   // Signaled when |num_builtin_cameras_| and |camera_info_| are updated.
   // Queried and waited by UpdateBuiltInCameraInfo, signaled by
   // OnGotCameraInfoOnIpcThread.
diff --git a/media/capture/video/chromeos/camera_hal_delegate_unittest.cc b/media/capture/video/chromeos/camera_hal_delegate_unittest.cc
index 242182bef6..06f4e61 100644
--- a/media/capture/video/chromeos/camera_hal_delegate_unittest.cc
+++ b/media/capture/video/chromeos/camera_hal_delegate_unittest.cc
@@ -33,7 +33,7 @@
     hal_delegate_thread_.Start();
     camera_hal_delegate_ =
         new CameraHalDelegate(hal_delegate_thread_.task_runner());
-    camera_hal_delegate_->StartForTesting(
+    camera_hal_delegate_->SetCameraModule(
         mock_camera_module_.GetInterfacePtrInfo());
   }
 
diff --git a/media/capture/video/chromeos/camera_hal_dispatcher_impl.cc b/media/capture/video/chromeos/camera_hal_dispatcher_impl.cc
new file mode 100644
index 0000000..06bdfb3f
--- /dev/null
+++ b/media/capture/video/chromeos/camera_hal_dispatcher_impl.cc
@@ -0,0 +1,361 @@
+// 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 "media/capture/video/chromeos/camera_hal_dispatcher_impl.h"
+
+#include <fcntl.h>
+#include <grp.h>
+#include <poll.h>
+#include <sys/uio.h>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/single_thread_task_runner.h"
+#include "base/synchronization/waitable_event.h"
+#include "mojo/edk/embedder/embedder.h"
+#include "mojo/edk/embedder/named_platform_handle.h"
+#include "mojo/edk/embedder/named_platform_handle_utils.h"
+#include "mojo/edk/embedder/outgoing_broker_client_invitation.h"
+#include "mojo/edk/embedder/platform_channel_pair.h"
+#include "mojo/edk/embedder/platform_channel_utils_posix.h"
+#include "mojo/edk/embedder/platform_handle_vector.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+
+namespace media {
+
+namespace {
+
+const base::FilePath::CharType kArcCamera3SocketPath[] =
+    "/var/run/camera/camera3.sock";
+const char kArcCameraGroup[] = "arc-camera";
+
+// Creates a pipe. Returns true on success, otherwise false.
+// On success, |read_fd| will be set to the fd of the read side, and
+// |write_fd| will be set to the one of write side.
+bool CreatePipe(base::ScopedFD* read_fd, base::ScopedFD* write_fd) {
+  int fds[2];
+  if (pipe2(fds, O_NONBLOCK | O_CLOEXEC) < 0) {
+    PLOG(ERROR) << "pipe2()";
+    return false;
+  }
+
+  read_fd->reset(fds[0]);
+  write_fd->reset(fds[1]);
+  return true;
+}
+
+// Waits until |raw_socket_fd| is readable.  We signal |raw_cancel_fd| when we
+// want to cancel the blocking wait and stop serving connections on
+// |raw_socket_fd|.  To notify such a situation, |raw_cancel_fd| is also passed
+// to here, and the write side will be closed in such a case.
+bool WaitForSocketReadable(int raw_socket_fd, int raw_cancel_fd) {
+  struct pollfd fds[2] = {
+      {raw_socket_fd, POLLIN, 0}, {raw_cancel_fd, POLLIN, 0},
+  };
+
+  if (HANDLE_EINTR(poll(fds, arraysize(fds), -1)) <= 0) {
+    PLOG(ERROR) << "poll()";
+    return false;
+  }
+
+  if (fds[1].revents) {
+    VLOG(1) << "Stop() was called";
+    return false;
+  }
+
+  DCHECK(fds[0].revents);
+  return true;
+}
+
+class MojoCameraClientObserver : public CameraClientObserver {
+ public:
+  explicit MojoCameraClientObserver(arc::mojom::CameraHalClientPtr client)
+      : client_(std::move(client)) {}
+
+  void OnChannelCreated(arc::mojom::CameraModulePtr camera_module) override {
+    client_->SetUpChannel(std::move(camera_module));
+  }
+
+  arc::mojom::CameraHalClientPtr& client() { return client_; }
+
+ private:
+  arc::mojom::CameraHalClientPtr client_;
+  DISALLOW_IMPLICIT_CONSTRUCTORS(MojoCameraClientObserver);
+};
+
+}  // namespace
+
+CameraClientObserver::~CameraClientObserver() {}
+
+// static
+CameraHalDispatcherImpl* CameraHalDispatcherImpl::GetInstance() {
+  return base::Singleton<CameraHalDispatcherImpl>::get();
+}
+
+bool CameraHalDispatcherImpl::StartThreads() {
+  DCHECK(!proxy_thread_.IsRunning());
+  DCHECK(!blocking_io_thread_.IsRunning());
+
+  if (!proxy_thread_.Start()) {
+    LOG(ERROR) << "Failed to start proxy thread";
+    return false;
+  }
+  if (!blocking_io_thread_.Start()) {
+    LOG(ERROR) << "Failed to start blocking IO thread";
+    proxy_thread_.Stop();
+    return false;
+  }
+  proxy_task_runner_ = proxy_thread_.task_runner();
+  blocking_io_task_runner_ = blocking_io_thread_.task_runner();
+  return true;
+}
+
+bool CameraHalDispatcherImpl::Start() {
+  if (!StartThreads()) {
+    return false;
+  }
+  base::WaitableEvent started(base::WaitableEvent::ResetPolicy::MANUAL,
+                              base::WaitableEvent::InitialState::NOT_SIGNALED);
+  blocking_io_task_runner_->PostTask(
+      FROM_HERE,
+      base::Bind(&CameraHalDispatcherImpl::CreateSocket, base::Unretained(this),
+                 base::Unretained(&started)));
+  started.Wait();
+  return IsStarted();
+}
+
+void CameraHalDispatcherImpl::AddClientObserver(
+    std::unique_ptr<CameraClientObserver> observer) {
+  // If |proxy_thread_| fails to start in Start() then CameraHalDelegate will
+  // not be created, and this function will not be called.
+  DCHECK(proxy_thread_.IsRunning());
+  proxy_thread_.task_runner()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&CameraHalDispatcherImpl::AddClientObserverOnProxyThread,
+                     base::Unretained(this), base::Passed(&observer)));
+}
+
+bool CameraHalDispatcherImpl::IsStarted() {
+  return proxy_thread_.IsRunning() && blocking_io_thread_.IsRunning() &&
+         proxy_fd_.is_valid();
+}
+
+CameraHalDispatcherImpl::CameraHalDispatcherImpl()
+    : proxy_thread_("CameraProxyThread"),
+      blocking_io_thread_("CameraBlockingIOThread") {}
+
+CameraHalDispatcherImpl::~CameraHalDispatcherImpl() {
+  VLOG(1) << "Stopping CameraHalDispatcherImpl...";
+  if (proxy_thread_.IsRunning()) {
+    proxy_thread_.task_runner()->PostTask(
+        FROM_HERE, base::Bind(&CameraHalDispatcherImpl::StopOnProxyThread,
+                              base::Unretained(this)));
+    proxy_thread_.Stop();
+  }
+  blocking_io_thread_.Stop();
+  VLOG(1) << "CameraHalDispatcherImpl stopped";
+}
+
+void CameraHalDispatcherImpl::RegisterServer(
+    arc::mojom::CameraHalServerPtr camera_hal_server) {
+  DCHECK(proxy_task_runner_->BelongsToCurrentThread());
+
+  if (camera_hal_server_) {
+    LOG(ERROR) << "Camera HAL server is already registered";
+    return;
+  }
+  camera_hal_server.set_connection_error_handler(
+      base::Bind(&CameraHalDispatcherImpl::OnCameraHalServerConnectionError,
+                 base::Unretained(this)));
+  camera_hal_server_ = std::move(camera_hal_server);
+  VLOG(1) << "Camera HAL server registered";
+
+  // Set up the Mojo channels for clients which registered before the server
+  // registers.
+  for (auto& client_observer : client_observers_) {
+    EstablishMojoChannel(client_observer.get());
+  }
+}
+
+void CameraHalDispatcherImpl::RegisterClient(
+    arc::mojom::CameraHalClientPtr client) {
+  DCHECK(proxy_task_runner_->BelongsToCurrentThread());
+  auto client_observer =
+      base::MakeUnique<MojoCameraClientObserver>(std::move(client));
+  client_observer->client().set_connection_error_handler(base::Bind(
+      &CameraHalDispatcherImpl::OnCameraHalClientConnectionError,
+      base::Unretained(this), base::Unretained(client_observer.get())));
+  AddClientObserver(std::move(client_observer));
+  VLOG(1) << "Camera HAL client registered";
+}
+
+void CameraHalDispatcherImpl::CreateSocket(base::WaitableEvent* started) {
+  DCHECK(blocking_io_task_runner_->BelongsToCurrentThread());
+
+  base::FilePath socket_path(kArcCamera3SocketPath);
+  mojo::edk::ScopedPlatformHandle socket_fd = mojo::edk::CreateServerHandle(
+      mojo::edk::NamedPlatformHandle(socket_path.value()));
+  if (!socket_fd.is_valid()) {
+    LOG(ERROR) << "Failed to create the socket file: " << kArcCamera3SocketPath;
+    started->Signal();
+    return;
+  }
+
+  // Change permissions on the socket.
+  struct group arc_camera_group;
+  struct group* result = nullptr;
+  char buf[1024];
+  if (HANDLE_EINTR(getgrnam_r(kArcCameraGroup, &arc_camera_group, buf,
+                              sizeof(buf), &result)) < 0) {
+    PLOG(ERROR) << "getgrnam_r()";
+    started->Signal();
+    return;
+  }
+
+  if (!result) {
+    LOG(ERROR) << "Group '" << kArcCameraGroup << "' not found";
+    started->Signal();
+    return;
+  }
+
+  if (HANDLE_EINTR(chown(kArcCamera3SocketPath, -1, arc_camera_group.gr_gid)) <
+      0) {
+    PLOG(ERROR) << "chown()";
+    started->Signal();
+    return;
+  }
+
+  if (!base::SetPosixFilePermissions(socket_path, 0660)) {
+    PLOG(ERROR) << "Could not set permissions: " << socket_path.value();
+    started->Signal();
+    return;
+  }
+
+  blocking_io_task_runner_->PostTask(
+      FROM_HERE, base::Bind(&CameraHalDispatcherImpl::StartServiceLoop,
+                            base::Unretained(this), base::Passed(&socket_fd),
+                            base::Unretained(started)));
+}
+
+void CameraHalDispatcherImpl::StartServiceLoop(
+    mojo::edk::ScopedPlatformHandle socket_fd,
+    base::WaitableEvent* started) {
+  DCHECK(blocking_io_task_runner_->BelongsToCurrentThread());
+  DCHECK(!proxy_fd_.is_valid());
+  DCHECK(!cancel_pipe_.is_valid());
+  DCHECK(socket_fd.is_valid());
+
+  base::ScopedFD cancel_fd;
+  if (!CreatePipe(&cancel_fd, &cancel_pipe_)) {
+    started->Signal();
+    LOG(ERROR) << "Failed to create cancel pipe";
+    return;
+  }
+
+  proxy_fd_ = std::move(socket_fd);
+  started->Signal();
+  VLOG(1) << "CameraHalDispatcherImpl started; waiting for incoming connection";
+
+  while (true) {
+    if (!WaitForSocketReadable(proxy_fd_.get().handle, cancel_fd.get())) {
+      VLOG(1) << "Quit CameraHalDispatcherImpl IO thread";
+      return;
+    }
+
+    mojo::edk::ScopedPlatformHandle accepted_fd;
+    if (mojo::edk::ServerAcceptConnection(proxy_fd_.get(), &accepted_fd,
+                                          false) &&
+        accepted_fd.is_valid()) {
+      VLOG(1) << "Accepted a connection";
+      // Hardcode pid 0 since it is unused in mojo.
+      const base::ProcessHandle kUnusedChildProcessHandle = 0;
+      mojo::edk::PlatformChannelPair channel_pair;
+      mojo::edk::OutgoingBrokerClientInvitation invitation;
+
+      std::string token = mojo::edk::GenerateRandomToken();
+      mojo::ScopedMessagePipeHandle pipe = invitation.AttachMessagePipe(token);
+
+      invitation.Send(
+          kUnusedChildProcessHandle,
+          mojo::edk::ConnectionParams(mojo::edk::TransportProtocol::kLegacy,
+                                      channel_pair.PassServerHandle()));
+
+      mojo::edk::ScopedPlatformHandleVectorPtr handles(
+          new mojo::edk::PlatformHandleVector{
+              channel_pair.PassClientHandle().release()});
+
+      struct iovec iov = {const_cast<char*>(token.c_str()), token.length()};
+      ssize_t result = mojo::edk::PlatformChannelSendmsgWithHandles(
+          accepted_fd.get(), &iov, 1, handles->data(), handles->size());
+      if (result == -1) {
+        PLOG(ERROR) << "sendmsg()";
+      } else {
+        proxy_task_runner_->PostTask(
+            FROM_HERE, base::Bind(&CameraHalDispatcherImpl::OnPeerConnected,
+                                  base::Unretained(this), base::Passed(&pipe)));
+      }
+    }
+  }
+}
+
+void CameraHalDispatcherImpl::AddClientObserverOnProxyThread(
+    std::unique_ptr<CameraClientObserver> observer) {
+  DCHECK(proxy_task_runner_->BelongsToCurrentThread());
+  if (camera_hal_server_) {
+    EstablishMojoChannel(observer.get());
+  }
+  client_observers_.insert(std::move(observer));
+}
+
+void CameraHalDispatcherImpl::EstablishMojoChannel(
+    CameraClientObserver* client_observer) {
+  DCHECK(proxy_task_runner_->BelongsToCurrentThread());
+  arc::mojom::CameraModulePtr camera_module_ptr;
+  arc::mojom::CameraModuleRequest camera_module_request =
+      mojo::MakeRequest(&camera_module_ptr);
+  camera_hal_server_->CreateChannel(std::move(camera_module_request));
+  client_observer->OnChannelCreated(std::move(camera_module_ptr));
+}
+
+void CameraHalDispatcherImpl::OnPeerConnected(
+    mojo::ScopedMessagePipeHandle message_pipe) {
+  DCHECK(proxy_task_runner_->BelongsToCurrentThread());
+  binding_set_.AddBinding(
+      this, arc::mojom::CameraHalDispatcherRequest(std::move(message_pipe)));
+  VLOG(1) << "New CameraHalDispatcher binding added";
+}
+
+void CameraHalDispatcherImpl::OnCameraHalServerConnectionError() {
+  DCHECK(proxy_task_runner_->BelongsToCurrentThread());
+  VLOG(1) << "Camera HAL server connection lost";
+  camera_hal_server_.reset();
+}
+
+void CameraHalDispatcherImpl::OnCameraHalClientConnectionError(
+    CameraClientObserver* client_observer) {
+  DCHECK(proxy_task_runner_->BelongsToCurrentThread());
+  for (auto& it : client_observers_) {
+    if (it.get() == client_observer) {
+      client_observers_.erase(it);
+      VLOG(1) << "Camera HAL client connection lost";
+      break;
+    }
+  }
+}
+
+void CameraHalDispatcherImpl::StopOnProxyThread() {
+  DCHECK(proxy_task_runner_->BelongsToCurrentThread());
+  if (!base::DeleteFile(base::FilePath(kArcCamera3SocketPath),
+                        /* recursive */ false)) {
+    LOG(ERROR) << "Failed to delete " << kArcCamera3SocketPath;
+  }
+  // Close |cancel_pipe_| to quit the loop in WaitForIncomingConnection.
+  cancel_pipe_.reset();
+  client_observers_.clear();
+  camera_hal_server_.reset();
+  binding_set_.CloseAllBindings();
+}
+
+}  // namespace media
diff --git a/media/capture/video/chromeos/camera_hal_dispatcher_impl.h b/media/capture/video/chromeos/camera_hal_dispatcher_impl.h
new file mode 100644
index 0000000..f5da5a5
--- /dev/null
+++ b/media/capture/video/chromeos/camera_hal_dispatcher_impl.h
@@ -0,0 +1,111 @@
+// 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 MEDIA_CAPTURE_VIDEO_CHROMEOS_CAMERA_HAL_DISPATCHER_IMPL_H_
+#define MEDIA_CAPTURE_VIDEO_CHROMEOS_CAMERA_HAL_DISPATCHER_IMPL_H_
+
+#include <memory>
+#include <set>
+
+#include "base/memory/singleton.h"
+#include "base/threading/thread.h"
+#include "media/capture/capture_export.h"
+#include "media/capture/video/chromeos/mojo/arc_camera3_service.mojom.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/interface_ptr_set.h"
+
+namespace base {
+
+class SingleThreadTaskRunner;
+class WaitableEvent;
+
+}  // namespace base
+
+namespace media {
+
+class CAPTURE_EXPORT CameraClientObserver {
+ public:
+  virtual ~CameraClientObserver();
+  virtual void OnChannelCreated(arc::mojom::CameraModulePtr camera_module) = 0;
+};
+
+// The CameraHalDispatcherImpl hosts and waits on the unix domain socket
+// /var/run/camera3.sock.  CameraHalServer and CameraHalClients connect to the
+// unix domain socket to create the initial Mojo connections with the
+// CameraHalDisptcherImpl, and CameraHalDispatcherImpl then creates and
+// dispaches the Mojo channels between CameraHalServer and CameraHalClients to
+// establish direct Mojo connections between the CameraHalServer and the
+// CameraHalClients.
+//
+// For general documentation about the CameraHalDispater Mojo interface see the
+// comments in mojo/arc_camera3_service.mojom.
+class CAPTURE_EXPORT CameraHalDispatcherImpl final
+    : public arc::mojom::CameraHalDispatcher {
+ public:
+  static CameraHalDispatcherImpl* GetInstance();
+
+  bool Start();
+
+  void AddClientObserver(std::unique_ptr<CameraClientObserver> observer);
+
+  bool IsStarted();
+
+  // CameraHalDispatcher implementations.
+  void RegisterServer(arc::mojom::CameraHalServerPtr server) final;
+  void RegisterClient(arc::mojom::CameraHalClientPtr client) final;
+
+ private:
+  friend struct base::DefaultSingletonTraits<CameraHalDispatcherImpl>;
+  // Allow the test to construct the class directly.
+  friend class CameraHalDispatcherImplTest;
+
+  CameraHalDispatcherImpl();
+  ~CameraHalDispatcherImpl() final;
+
+  bool StartThreads();
+
+  // Creates the unix domain socket for the camera client processes and the
+  // camera HALv3 adapter process to connect.
+  void CreateSocket(base::WaitableEvent* started);
+
+  // Waits for incoming connections (from HAL process or from client processes).
+  // Runs on |blocking_io_thread_|.
+  void StartServiceLoop(mojo::edk::ScopedPlatformHandle socket_fd,
+                        base::WaitableEvent* started);
+
+  void AddClientObserverOnProxyThread(
+      std::unique_ptr<CameraClientObserver> observer);
+
+  void EstablishMojoChannel(CameraClientObserver* client_observer);
+
+  // Handler for incoming Mojo connection on the unix domain socket.
+  void OnPeerConnected(mojo::ScopedMessagePipeHandle message_pipe);
+
+  // Mojo connection error handlers.
+  void OnCameraHalServerConnectionError();
+  void OnCameraHalClientConnectionError(CameraClientObserver* client);
+
+  void StopOnProxyThread();
+
+  mojo::edk::ScopedPlatformHandle proxy_fd_;
+  base::ScopedFD cancel_pipe_;
+
+  base::Thread proxy_thread_;
+  base::Thread blocking_io_thread_;
+  scoped_refptr<base::SingleThreadTaskRunner> proxy_task_runner_;
+  scoped_refptr<base::SingleThreadTaskRunner> blocking_io_task_runner_;
+
+  mojo::BindingSet<arc::mojom::CameraHalDispatcher> binding_set_;
+
+  arc::mojom::CameraHalServerPtr camera_hal_server_;
+
+  std::set<std::unique_ptr<CameraClientObserver>> client_observers_;
+
+  DISALLOW_COPY_AND_ASSIGN(CameraHalDispatcherImpl);
+};
+
+}  // namespace media
+
+#endif  // MEDIA_CAPTURE_VIDEO_CHROMEOS_ARC_CAMERA3_SERVICE_H_
diff --git a/media/capture/video/chromeos/camera_hal_dispatcher_impl_unittest.cc b/media/capture/video/chromeos/camera_hal_dispatcher_impl_unittest.cc
new file mode 100644
index 0000000..fa00667
--- /dev/null
+++ b/media/capture/video/chromeos/camera_hal_dispatcher_impl_unittest.cc
@@ -0,0 +1,216 @@
+// 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 "media/capture/video/chromeos/camera_hal_dispatcher_impl.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
+#include "media/capture/video/chromeos/mojo/arc_camera3_service.mojom.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::InvokeWithoutArgs;
+
+namespace media {
+namespace {
+
+class MockCameraHalServer : public arc::mojom::CameraHalServer {
+ public:
+  MockCameraHalServer() : binding_(this) {}
+
+  ~MockCameraHalServer() {}
+
+  void CreateChannel(
+      arc::mojom::CameraModuleRequest camera_module_request) override {
+    DoCreateChannel(camera_module_request);
+  }
+  MOCK_METHOD1(DoCreateChannel,
+               void(arc::mojom::CameraModuleRequest& camera_module_request));
+
+  arc::mojom::CameraHalServerPtr GetInterfacePtr(
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+    arc::mojom::CameraHalServerPtr camera_hal_server_ptr;
+    arc::mojom::CameraHalServerRequest camera_hal_server_request =
+        mojo::MakeRequest(&camera_hal_server_ptr, std::move(task_runner));
+    binding_.Bind(std::move(camera_hal_server_request));
+    return camera_hal_server_ptr;
+  }
+
+ private:
+  mojo::Binding<arc::mojom::CameraHalServer> binding_;
+  DISALLOW_COPY_AND_ASSIGN(MockCameraHalServer);
+};
+
+class MockCameraHalClient : public arc::mojom::CameraHalClient {
+ public:
+  MockCameraHalClient() : binding_(this) {}
+
+  ~MockCameraHalClient() {}
+
+  void SetUpChannel(arc::mojom::CameraModulePtr camera_module_ptr) override {
+    DoSetUpChannel(camera_module_ptr);
+  }
+  MOCK_METHOD1(DoSetUpChannel,
+               void(arc::mojom::CameraModulePtr& camera_module_ptr));
+
+  arc::mojom::CameraHalClientPtr GetInterfacePtr(
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+    arc::mojom::CameraHalClientPtr camera_hal_client_ptr;
+    arc::mojom::CameraHalClientRequest camera_hal_client_request =
+        mojo::MakeRequest(&camera_hal_client_ptr, std::move(task_runner));
+    binding_.Bind(std::move(camera_hal_client_request));
+    return camera_hal_client_ptr;
+  }
+
+ private:
+  mojo::Binding<arc::mojom::CameraHalClient> binding_;
+  DISALLOW_COPY_AND_ASSIGN(MockCameraHalClient);
+};
+
+}  // namespace
+
+class CameraHalDispatcherImplTest : public ::testing::Test {
+ public:
+  CameraHalDispatcherImplTest() {}
+
+  ~CameraHalDispatcherImplTest() override {}
+
+  void SetUp() override {
+    dispatcher_ = new CameraHalDispatcherImpl();
+    EXPECT_TRUE(dispatcher_->StartThreads());
+  }
+
+  void TearDown() override { delete dispatcher_; }
+
+  scoped_refptr<base::SingleThreadTaskRunner> GetProxyTaskRunner() {
+    return dispatcher_->proxy_task_runner_;
+  }
+
+  void DoLoop() {
+    run_loop_.reset(new base::RunLoop());
+    run_loop_->Run();
+  }
+
+  void QuitRunLoop() {
+    if (run_loop_) {
+      run_loop_->Quit();
+    }
+  }
+
+ protected:
+  // We can't use std::unique_ptr here because the constructor and destructor of
+  // CameraHalDispatcherImpl are private.
+  CameraHalDispatcherImpl* dispatcher_;
+
+ private:
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+  std::unique_ptr<base::RunLoop> run_loop_;
+  DISALLOW_COPY_AND_ASSIGN(CameraHalDispatcherImplTest);
+};
+
+// Test that the CameraHalDisptcherImpl correctly re-establishes a Mojo channel
+// for the client when the server crashes.
+TEST_F(CameraHalDispatcherImplTest, ServerConnectionError) {
+  // First verify that a the CameraHalDispatcherImpl establishes a Mojo channel
+  // between the server and the client.
+  auto mock_server = base::MakeUnique<MockCameraHalServer>();
+  auto mock_client = base::MakeUnique<MockCameraHalClient>();
+
+  EXPECT_CALL(*mock_server, DoCreateChannel(_)).Times(1);
+  EXPECT_CALL(*mock_client, DoSetUpChannel(_))
+      .Times(1)
+      .WillOnce(
+          InvokeWithoutArgs(this, &CameraHalDispatcherImplTest::QuitRunLoop));
+
+  auto server_ptr = mock_server->GetInterfacePtr(GetProxyTaskRunner());
+  GetProxyTaskRunner()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&CameraHalDispatcherImpl::RegisterServer,
+                     base::Unretained(dispatcher_), base::Passed(&server_ptr)));
+  auto client_ptr = mock_client->GetInterfacePtr(GetProxyTaskRunner());
+  GetProxyTaskRunner()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&CameraHalDispatcherImpl::RegisterClient,
+                     base::Unretained(dispatcher_), base::Passed(&client_ptr)));
+
+  // Wait until the client gets the established Mojo channel.
+  DoLoop();
+
+  // Re-create a new server to simulate a server crash.
+  mock_server = base::MakeUnique<MockCameraHalServer>();
+
+  // Make sure we creates a new Mojo channel from the new server to the same
+  // client.
+  EXPECT_CALL(*mock_server, DoCreateChannel(_)).Times(1);
+  EXPECT_CALL(*mock_client, DoSetUpChannel(_))
+      .Times(1)
+      .WillOnce(
+          InvokeWithoutArgs(this, &CameraHalDispatcherImplTest::QuitRunLoop));
+
+  server_ptr = mock_server->GetInterfacePtr(GetProxyTaskRunner());
+  GetProxyTaskRunner()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&CameraHalDispatcherImpl::RegisterServer,
+                     base::Unretained(dispatcher_), base::Passed(&server_ptr)));
+
+  // Wait until the clients gets the newly established Mojo channel.
+  DoLoop();
+};
+
+// Test that the CameraHalDisptcherImpl correctly re-establishes a Mojo channel
+// for the client when the client reconnects after crash.
+TEST_F(CameraHalDispatcherImplTest, ClientConnectionError) {
+  // First verify that a the CameraHalDispatcherImpl establishes a Mojo channel
+  // between the server and the client.
+  auto mock_server = base::MakeUnique<MockCameraHalServer>();
+  auto mock_client = base::MakeUnique<MockCameraHalClient>();
+
+  EXPECT_CALL(*mock_server, DoCreateChannel(_)).Times(1);
+  EXPECT_CALL(*mock_client, DoSetUpChannel(_))
+      .Times(1)
+      .WillOnce(
+          InvokeWithoutArgs(this, &CameraHalDispatcherImplTest::QuitRunLoop));
+
+  auto server_ptr = mock_server->GetInterfacePtr(GetProxyTaskRunner());
+  GetProxyTaskRunner()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&CameraHalDispatcherImpl::RegisterServer,
+                     base::Unretained(dispatcher_), base::Passed(&server_ptr)));
+  auto client_ptr = mock_client->GetInterfacePtr(GetProxyTaskRunner());
+  GetProxyTaskRunner()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&CameraHalDispatcherImpl::RegisterClient,
+                     base::Unretained(dispatcher_), base::Passed(&client_ptr)));
+
+  // Wait until the client gets the established Mojo channel.
+  DoLoop();
+
+  // Re-create a new server to simulate a server crash.
+  mock_client = base::MakeUnique<MockCameraHalClient>();
+
+  // Make sure we re-create the Mojo channel from the same server to the new
+  // client.
+  EXPECT_CALL(*mock_server, DoCreateChannel(_)).Times(1);
+  EXPECT_CALL(*mock_client, DoSetUpChannel(_))
+      .Times(1)
+      .WillOnce(
+          InvokeWithoutArgs(this, &CameraHalDispatcherImplTest::QuitRunLoop));
+
+  client_ptr = mock_client->GetInterfacePtr(GetProxyTaskRunner());
+  GetProxyTaskRunner()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&CameraHalDispatcherImpl::RegisterClient,
+                     base::Unretained(dispatcher_), base::Passed(&client_ptr)));
+
+  // Wait until the clients gets the newly established Mojo channel.
+  DoLoop();
+};
+
+}  // namespace media
diff --git a/media/capture/video/chromeos/mojo/BUILD.gn b/media/capture/video/chromeos/mojo/BUILD.gn
index cf4e480..78dee7c8 100644
--- a/media/capture/video/chromeos/mojo/BUILD.gn
+++ b/media/capture/video/chromeos/mojo/BUILD.gn
@@ -8,6 +8,7 @@
   sources = [
     "arc_camera3.mojom",
     "arc_camera3_metadata.mojom",
+    "arc_camera3_service.mojom",
     "camera_metadata_tags.mojom",
   ]
 }
diff --git a/media/capture/video/chromeos/mojo/arc_camera3_service.mojom b/media/capture/video/chromeos/mojo/arc_camera3_service.mojom
new file mode 100644
index 0000000..1261d3d
--- /dev/null
+++ b/media/capture/video/chromeos/mojo/arc_camera3_service.mojom
@@ -0,0 +1,48 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module arc.mojom;
+
+import "arc_camera3.mojom";
+
+// The ARC++ camera HAL v3 Mojo dispatcher.  The dispatcher acts as a proxy and
+// waits for the server and the clients to register.  There can only be one
+// server registered, with multiple clients requesting connections to the
+// server.  For each client, the dispatcher is responsible for creating a Mojo
+// channel to the server and pass the established Mojo channel to the client in
+// order to set up a Mojo channel between the client and the server.
+//
+// The CameraHalDispatcher is designed to help the server and the clients to
+// recover from errors easily.  For example, when the camera HAL process crashes
+// the CameraHalDispatcher can still hold the connections of the clients. When
+// the camera HAL reconnects the CameraHalDispatcher can then quickly restore
+// the Mojo channels between the clients and the camera HAL process by calling
+// CameraHalClient::SetUpChannel() in RegiserServer().
+interface CameraHalDispatcher {
+  // A CameraHalServer calls RegisterServer to register itself with the
+  // dispatcher.
+  RegisterServer@0(CameraHalServer server);
+
+  // A CameraHalClient calls RegisterClient to register itself with the
+  // dispatcher.
+  RegisterClient@1(CameraHalClient client);
+};
+
+// The ARC++ camera HAL v3 Mojo server.
+interface CameraHalServer {
+  // A caller calls CreateChannel to create a new Mojo channel to the camera
+  // HAL v3 adapter.  Upon successfully binding of |camera_module_request|, the
+  // caller will have a established Mojo channel to the camera HAL v3 adapter
+  // process.
+  CreateChannel@0(CameraModule& camera_module_request);
+};
+
+// The ARC++ camera HAL v3 Mojo client.
+interface CameraHalClient {
+  // A caller calls SetUpChannel to dispatch the established Mojo channel
+  // |camera_module_ptr| to the client.  The CameraHalClient can create a
+  // Mojo channel to the camera HAL v3 adapter process with |camera_module_ptr|.
+  // SetUpChannel may be called multiple times.
+  SetUpChannel@0(CameraModule camera_module_ptr);
+};
diff --git a/media/capture/video/chromeos/video_capture_device_factory_chromeos.cc b/media/capture/video/chromeos/video_capture_device_factory_chromeos.cc
index 275d98c..83e4041 100644
--- a/media/capture/video/chromeos/video_capture_device_factory_chromeos.cc
+++ b/media/capture/video/chromeos/video_capture_device_factory_chromeos.cc
@@ -6,6 +6,7 @@
 
 #include "base/files/file_util.h"
 #include "base/memory/ptr_util.h"
+#include "media/capture/video/chromeos/camera_hal_dispatcher_impl.h"
 #include "media/capture/video/linux/video_capture_device_factory_linux.h"
 
 namespace media {
@@ -25,9 +26,17 @@
     LOG(ERROR) << "Module thread failed to start";
     return false;
   }
+
+  if (!CameraHalDispatcherImpl::GetInstance()->IsStarted() &&
+      !CameraHalDispatcherImpl::GetInstance()->Start()) {
+    LOG(ERROR) << "Failed to start CameraHalDispatcherImpl";
+    return false;
+  }
+
   camera_hal_delegate_ =
       new CameraHalDelegate(camera_hal_ipc_thread_.task_runner());
-  return camera_hal_delegate_->StartCameraModuleIpc();
+  camera_hal_delegate_->RegisterCameraClient();
+  return true;
 }
 
 std::unique_ptr<VideoCaptureDevice>
diff --git a/media/capture/video/win/video_capture_device_win.cc b/media/capture/video/win/video_capture_device_win.cc
index ce27a07c..c6fb6f0 100644
--- a/media/capture/video/win/video_capture_device_win.cc
+++ b/media/capture/video/win/video_capture_device_win.cc
@@ -465,44 +465,6 @@
 
   client_->OnStarted();
   state_ = kCapturing;
-
-  base::win::ScopedComPtr<IKsTopologyInfo> info;
-  hr = capture_filter_.CopyTo(info.GetAddressOf());
-  if (FAILED(hr)) {
-    SetErrorState(FROM_HERE, "Failed to obtain the topology info.", hr);
-    return;
-  }
-
-  DWORD num_nodes = 0;
-  hr = info->get_NumNodes(&num_nodes);
-  if (FAILED(hr)) {
-    SetErrorState(FROM_HERE, "Failed to obtain the number of nodes.", hr);
-    return;
-  }
-
-  // Every UVC camera is expected to have a single ICameraControl and a single
-  // IVideoProcAmp nodes, and both are needed; ignore any unlikely later ones.
-  GUID node_type;
-  for (size_t i = 0; i < num_nodes; i++) {
-    info->get_NodeType(i, &node_type);
-    if (IsEqualGUID(node_type, KSNODETYPE_VIDEO_CAMERA_TERMINAL)) {
-      hr = info->CreateNodeInstance(i, IID_PPV_ARGS(&camera_control_));
-      if (SUCCEEDED(hr))
-        break;
-      SetErrorState(FROM_HERE, "Failed to retrieve the ICameraControl.", hr);
-      return;
-    }
-  }
-  for (size_t i = 0; i < num_nodes; i++) {
-    info->get_NodeType(i, &node_type);
-    if (IsEqualGUID(node_type, KSNODETYPE_VIDEO_PROCESSING)) {
-      hr = info->CreateNodeInstance(i, IID_PPV_ARGS(&video_control_));
-      if (SUCCEEDED(hr))
-        break;
-      SetErrorState(FROM_HERE, "Failed to retrieve the IVideoProcAmp.", hr);
-      return;
-    }
-  }
 }
 
 void VideoCaptureDeviceWin::StopAndDeAllocate() {
@@ -535,8 +497,10 @@
 void VideoCaptureDeviceWin::GetPhotoState(GetPhotoStateCallback callback) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
-  if (!camera_control_ || !video_control_)
-    return;
+  if (!camera_control_ || !video_control_) {
+    if (!InitializeVideoAndCameraControls())
+      return;
+  }
 
   auto photo_capabilities = mojom::PhotoState::New();
 
@@ -627,8 +591,10 @@
     VideoCaptureDevice::SetPhotoOptionsCallback callback) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
-  if (!camera_control_ || !video_control_)
-    return;
+  if (!camera_control_ || !video_control_) {
+    if (!InitializeVideoAndCameraControls())
+      return;
+  }
 
   HRESULT hr;
 
@@ -710,6 +676,48 @@
 
   callback.Run(true);
 }
+
+bool VideoCaptureDeviceWin::InitializeVideoAndCameraControls() {
+  base::win::ScopedComPtr<IKsTopologyInfo> info;
+  HRESULT hr = capture_filter_.CopyTo(info.GetAddressOf());
+  if (FAILED(hr)) {
+    SetErrorState(FROM_HERE, "Failed to obtain the topology info.", hr);
+    return false;
+  }
+
+  DWORD num_nodes = 0;
+  hr = info->get_NumNodes(&num_nodes);
+  if (FAILED(hr)) {
+    SetErrorState(FROM_HERE, "Failed to obtain the number of nodes.", hr);
+    return false;
+  }
+
+  // Every UVC camera is expected to have a single ICameraControl and a single
+  // IVideoProcAmp nodes, and both are needed; ignore any unlikely later ones.
+  GUID node_type;
+  for (size_t i = 0; i < num_nodes; i++) {
+    info->get_NodeType(i, &node_type);
+    if (IsEqualGUID(node_type, KSNODETYPE_VIDEO_CAMERA_TERMINAL)) {
+      hr = info->CreateNodeInstance(i, IID_PPV_ARGS(&camera_control_));
+      if (SUCCEEDED(hr))
+        break;
+      SetErrorState(FROM_HERE, "Failed to retrieve the ICameraControl.", hr);
+      return false;
+    }
+  }
+  for (size_t i = 0; i < num_nodes; i++) {
+    info->get_NodeType(i, &node_type);
+    if (IsEqualGUID(node_type, KSNODETYPE_VIDEO_PROCESSING)) {
+      hr = info->CreateNodeInstance(i, IID_PPV_ARGS(&video_control_));
+      if (SUCCEEDED(hr))
+        break;
+      SetErrorState(FROM_HERE, "Failed to retrieve the IVideoProcAmp.", hr);
+      return false;
+    }
+  }
+  return camera_control_ && video_control_;
+}
+
 // Implements SinkFilterObserver::SinkFilterObserver.
 void VideoCaptureDeviceWin::FrameReceived(const uint8_t* buffer,
                                           int length,
diff --git a/media/capture/video/win/video_capture_device_win.h b/media/capture/video/win/video_capture_device_win.h
index 8a2ed6cc..7ae6a710 100644
--- a/media/capture/video/win/video_capture_device_win.h
+++ b/media/capture/video/win/video_capture_device_win.h
@@ -89,6 +89,8 @@
                  // User needs to recover by destroying the object.
   };
 
+  bool InitializeVideoAndCameraControls();
+
   // Implements SinkFilterObserver.
   void FrameReceived(const uint8_t* buffer,
                      int length,
diff --git a/mojo/android/BUILD.gn b/mojo/android/BUILD.gn
index 179eb6f..525e7fc 100644
--- a/mojo/android/BUILD.gn
+++ b/mojo/android/BUILD.gn
@@ -140,8 +140,6 @@
     "//mojo/public/cpp/test_support:test_utils",
   ]
   defines = [ "UNIT_TEST" ]
-  configs -= [ "//build/config/android:hide_all_but_jni_onload" ]
-  configs += [ "//build/config/android:hide_all_but_jni" ]
 }
 
 instrumentation_test_apk("mojo_test_apk") {
diff --git a/net/android/BUILD.gn b/net/android/BUILD.gn
index 8ef6f7c..f855f47 100644
--- a/net/android/BUILD.gn
+++ b/net/android/BUILD.gn
@@ -109,9 +109,6 @@
     ":java_test_native_support",
     "//net:test_support",
   ]
-
-  configs -= [ "//build/config/android:hide_all_but_jni_onload" ]
-  configs += [ "//build/config/android:hide_all_but_jni" ]
 }
 
 android_apk("net_test_support_apk") {
diff --git a/net/base/network_change_notifier_mac.cc b/net/base/network_change_notifier_mac.cc
index dc6430f1a..e66d87a 100644
--- a/net/base/network_change_notifier_mac.cc
+++ b/net/base/network_change_notifier_mac.cc
@@ -8,6 +8,7 @@
 #include <resolv.h>
 
 #include "base/macros.h"
+#include "base/memory/ptr_util.h"
 #include "base/message_loop/message_loop.h"
 #include "base/threading/thread.h"
 #include "base/threading/thread_restrictions.h"
@@ -48,10 +49,10 @@
       connection_type_initialized_(false),
       initial_connection_type_cv_(&connection_type_lock_),
       forwarder_(this),
-      dns_config_service_thread_(new DnsConfigServiceThread()) {
+      dns_config_service_thread_(base::MakeUnique<DnsConfigServiceThread>()) {
   // Must be initialized after the rest of this object, as it may call back into
   // SetInitialConnectionType().
-  config_watcher_.reset(new NetworkConfigWatcherMac(&forwarder_));
+  config_watcher_ = base::MakeUnique<NetworkConfigWatcherMac>(&forwarder_);
   dns_config_service_thread_->StartWithOptions(
       base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
 }
@@ -104,16 +105,15 @@
 NetworkChangeNotifierMac::CalculateConnectionType(
     SCNetworkConnectionFlags flags) {
   bool reachable = CalculateReachability(flags);
-  if (reachable) {
-#if defined(OS_IOS)
-    return (flags & kSCNetworkReachabilityFlagsIsWWAN) ? CONNECTION_3G
-                                                       : CONNECTION_WIFI;
-#else
-    return ConnectionTypeFromInterfaces();
-#endif  // defined(OS_IOS)
-  } else {
+  if (!reachable)
     return CONNECTION_NONE;
-  }
+
+#if defined(OS_IOS)
+  return (flags & kSCNetworkReachabilityFlagsIsWWAN) ? CONNECTION_3G
+                                                     : CONNECTION_WIFI;
+#else
+  return ConnectionTypeFromInterfaces();
+#endif
 }
 
 void NetworkChangeNotifierMac::Forwarder::StartReachabilityNotifications() {
diff --git a/net/cert/x509_util_ios.cc b/net/cert/x509_util_ios.cc
index 18d33151..3f87720d5 100644
--- a/net/cert/x509_util_ios.cc
+++ b/net/cert/x509_util_ios.cc
@@ -11,25 +11,6 @@
 
 namespace x509_util {
 
-namespace {
-
-// Returns true if a given |cert_handle| is actually a valid X.509 certificate
-// handle.
-//
-// SecCertificateCreateFromData() does not always force the immediate parsing of
-// the certificate, and as such, may return a SecCertificateRef for an
-// invalid/unparsable certificate. Force parsing to occur to ensure that the
-// SecCertificateRef is correct. On later versions where
-// SecCertificateCreateFromData() immediately parses, rather than lazily, this
-// call is cheap, as the subject is cached.
-bool IsValidSecCertificate(SecCertificateRef cert_handle) {
-  base::ScopedCFTypeRef<CFStringRef> sanity_check(
-      SecCertificateCopySubjectSummary(cert_handle));
-  return sanity_check != nullptr;
-}
-
-}  // namespace
-
 base::ScopedCFTypeRef<SecCertificateRef> CreateSecCertificateFromBytes(
     const uint8_t* data,
     size_t length) {
@@ -39,14 +20,8 @@
   if (!cert_data)
     return base::ScopedCFTypeRef<SecCertificateRef>();
 
-  base::ScopedCFTypeRef<SecCertificateRef> cert_handle(
+  return base::ScopedCFTypeRef<SecCertificateRef>(
       SecCertificateCreateWithData(nullptr, cert_data));
-  if (!cert_handle)
-    return base::ScopedCFTypeRef<SecCertificateRef>();
-
-  if (!IsValidSecCertificate(cert_handle.get()))
-    return base::ScopedCFTypeRef<SecCertificateRef>();
-  return cert_handle;
 }
 
 base::ScopedCFTypeRef<SecCertificateRef>
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc
index c5a32fe..b258621 100644
--- a/net/http/http_network_transaction_unittest.cc
+++ b/net/http/http_network_transaction_unittest.cc
@@ -9237,7 +9237,7 @@
       session->http_server_properties();
   AlternativeService alternative_service(kProtoHTTP2, "", 444);
   base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
-  http_server_properties->SetAlternativeService(
+  http_server_properties->SetHttp2AlternativeService(
       url::SchemeHostPort("https", "host.with.alternate", 443),
       alternative_service, expiration);
 
@@ -10196,7 +10196,7 @@
   AlternativeService alternative_service(kProtoHTTP2, "different.example.org",
                                          444);
   base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
-  http_server_properties->SetAlternativeService(
+  http_server_properties->SetHttp2AlternativeService(
       url::SchemeHostPort(request.url), alternative_service, expiration);
 
   HttpNetworkTransaction trans(DEFAULT_PRIORITY, session.get());
@@ -10235,7 +10235,7 @@
       session->http_server_properties();
   AlternativeService alternative_service(kProtoHTTP2, "", 444);
   base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
-  http_server_properties->SetAlternativeService(
+  http_server_properties->SetHttp2AlternativeService(
       url::SchemeHostPort(request.url), alternative_service, expiration);
 
   HttpNetworkTransaction trans(DEFAULT_PRIORITY, session.get());
@@ -10254,8 +10254,9 @@
   url::SchemeHostPort test_server("https", "www.example.org", 443);
   AlternativeService alternative_service(kProtoQUIC, "", 80);
   base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
-  http_server_properties->SetAlternativeService(
-      test_server, alternative_service, expiration);
+  http_server_properties->SetQuicAlternativeService(
+      test_server, alternative_service, expiration,
+      session->params().quic_supported_versions);
   EXPECT_EQ(
       1u,
       http_server_properties->GetAlternativeServiceInfos(test_server).size());
@@ -10397,8 +10398,9 @@
       session->http_server_properties();
   AlternativeService alternative_service(kProtoQUIC, alternative);
   base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
-  http_server_properties->SetAlternativeService(server, alternative_service,
-                                                expiration);
+  http_server_properties->SetQuicAlternativeService(
+      server, alternative_service, expiration,
+      HttpNetworkSession::Params().quic_supported_versions);
   // Mark the QUIC alternative service as broken.
   http_server_properties->MarkAlternativeServiceBroken(alternative_service);
 
@@ -10458,13 +10460,15 @@
   base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
 
   AlternativeService alternative_service1(kProtoQUIC, alternative1);
-  AlternativeServiceInfo alternative_service_info1(alternative_service1,
-                                                   expiration);
-  alternative_service_info_vector.push_back(alternative_service_info1);
+  alternative_service_info_vector.push_back(
+      AlternativeServiceInfo::CreateQuicAlternativeServiceInfo(
+          alternative_service1, expiration,
+          session->params().quic_supported_versions));
   AlternativeService alternative_service2(kProtoQUIC, alternative2);
-  AlternativeServiceInfo alternative_service_info2(alternative_service2,
-                                                   expiration);
-  alternative_service_info_vector.push_back(alternative_service_info2);
+  alternative_service_info_vector.push_back(
+      AlternativeServiceInfo::CreateQuicAlternativeServiceInfo(
+          alternative_service2, expiration,
+          session->params().quic_supported_versions));
 
   http_server_properties->SetAlternativeServices(
       server, alternative_service_info_vector);
@@ -10516,12 +10520,12 @@
   const url::SchemeHostPort server(request.url);
   // Port must be < 1024, or the header will be ignored (since initial port was
   // port 80 (another restricted port).
-  const AlternativeService alternative_service(
-      kProtoHTTP2, "www.example.org",
-      666);  // Port is ignored by MockConnect anyway.
+  // Port is ignored by MockConnect anyway.
+  const AlternativeService alternative_service(kProtoHTTP2, "www.example.org",
+                                               666);
   base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
-  http_server_properties->SetAlternativeService(server, alternative_service,
-                                                expiration);
+  http_server_properties->SetHttp2AlternativeService(
+      server, alternative_service, expiration);
 
   HttpNetworkTransaction trans(DEFAULT_PRIORITY, session.get());
   TestCompletionCallback callback;
@@ -10583,7 +10587,7 @@
   AlternativeService alternative_service(kProtoHTTP2, "www.example.org",
                                          kUnrestrictedAlternatePort);
   base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
-  http_server_properties->SetAlternativeService(
+  http_server_properties->SetHttp2AlternativeService(
       url::SchemeHostPort(restricted_port_request.url), alternative_service,
       expiration);
 
@@ -10633,7 +10637,7 @@
   AlternativeService alternative_service(kProtoHTTP2, "www.example.org",
                                          kUnrestrictedAlternatePort);
   base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
-  http_server_properties->SetAlternativeService(
+  http_server_properties->SetHttp2AlternativeService(
       url::SchemeHostPort(restricted_port_request.url), alternative_service,
       expiration);
 
@@ -10682,7 +10686,7 @@
   AlternativeService alternative_service(kProtoHTTP2, "www.example.org",
                                          kRestrictedAlternatePort);
   base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
-  http_server_properties->SetAlternativeService(
+  http_server_properties->SetHttp2AlternativeService(
       url::SchemeHostPort(restricted_port_request.url), alternative_service,
       expiration);
 
@@ -10731,7 +10735,7 @@
   AlternativeService alternative_service(kProtoHTTP2, "www.example.org",
                                          kRestrictedAlternatePort);
   base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
-  http_server_properties->SetAlternativeService(
+  http_server_properties->SetHttp2AlternativeService(
       url::SchemeHostPort(unrestricted_port_request.url), alternative_service,
       expiration);
 
@@ -10780,7 +10784,7 @@
   AlternativeService alternative_service(kProtoHTTP2, "www.example.org",
                                          kUnrestrictedAlternatePort);
   base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
-  http_server_properties->SetAlternativeService(
+  http_server_properties->SetHttp2AlternativeService(
       url::SchemeHostPort(unrestricted_port_request.url), alternative_service,
       expiration);
 
@@ -10821,7 +10825,7 @@
   AlternativeService alternative_service(kProtoHTTP2, "www.example.org",
                                          kUnsafePort);
   base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
-  http_server_properties->SetAlternativeService(
+  http_server_properties->SetHttp2AlternativeService(
       url::SchemeHostPort(request.url), alternative_service, expiration);
 
   HttpNetworkTransaction trans(DEFAULT_PRIORITY, session.get());
@@ -11181,8 +11185,8 @@
   HostPortPair alternative("www.example.com", 443);
   AlternativeService alternative_service(kProtoHTTP2, alternative);
   base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
-  http_server_properties->SetAlternativeService(server, alternative_service,
-                                                expiration);
+  http_server_properties->SetHttp2AlternativeService(
+      server, alternative_service, expiration);
 
   // Non-alternative job should hang.
   MockConnect never_finishing_connect(SYNCHRONOUS, ERR_IO_PENDING);
@@ -14065,8 +14069,8 @@
       session->http_server_properties();
   AlternativeService alternative_service(kProtoHTTP2, alternative);
   base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
-  http_server_properties->SetAlternativeService(server, alternative_service,
-                                                expiration);
+  http_server_properties->SetHttp2AlternativeService(
+      server, alternative_service, expiration);
 
   HttpRequestInfo request;
   HttpNetworkTransaction trans(DEFAULT_PRIORITY, session.get());
@@ -14132,8 +14136,8 @@
       session->http_server_properties();
   AlternativeService alternative_service(kProtoHTTP2, alternative);
   base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
-  http_server_properties->SetAlternativeService(server, alternative_service,
-                                                expiration);
+  http_server_properties->SetHttp2AlternativeService(
+      server, alternative_service, expiration);
 
   HttpNetworkTransaction trans1(DEFAULT_PRIORITY, session.get());
   HttpRequestInfo request1;
@@ -14239,8 +14243,8 @@
       session->http_server_properties();
   AlternativeService alternative_service(kProtoHTTP2, alternative);
   base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
-  http_server_properties->SetAlternativeService(server, alternative_service,
-                                                expiration);
+  http_server_properties->SetHttp2AlternativeService(
+      server, alternative_service, expiration);
 
   // First transaction to alternative to open an HTTP/1.1 socket.
   HttpRequestInfo request1;
diff --git a/net/http/http_server_properties.cc b/net/http/http_server_properties.cc
index 15947b2..a1289cae 100644
--- a/net/http/http_server_properties.cc
+++ b/net/http/http_server_properties.cc
@@ -7,6 +7,7 @@
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/stringprintf.h"
+#include "net/http/http_network_session.h"
 #include "net/socket/ssl_client_socket.h"
 #include "net/ssl/ssl_config.h"
 
@@ -78,18 +79,40 @@
   return false;
 }
 
+// static
+AlternativeServiceInfo
+AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
+    const AlternativeService& alternative_service,
+    base::Time expiration) {
+  DCHECK_EQ(alternative_service.protocol, kProtoHTTP2);
+  return AlternativeServiceInfo(alternative_service, expiration,
+                                QuicVersionVector());
+}
+
+// static
+AlternativeServiceInfo AlternativeServiceInfo::CreateQuicAlternativeServiceInfo(
+    const AlternativeService& alternative_service,
+    base::Time expiration,
+    const QuicVersionVector& advertised_versions) {
+  DCHECK_EQ(alternative_service.protocol, kProtoQUIC);
+  return AlternativeServiceInfo(alternative_service, expiration,
+                                advertised_versions);
+}
+
 AlternativeServiceInfo::AlternativeServiceInfo() : alternative_service_() {}
 
+AlternativeServiceInfo::~AlternativeServiceInfo() {}
+
 AlternativeServiceInfo::AlternativeServiceInfo(
     const AlternativeService& alternative_service,
-    base::Time expiration)
-    : alternative_service_(alternative_service), expiration_(expiration) {}
-
-AlternativeServiceInfo::AlternativeServiceInfo(NextProto protocol,
-                                               const std::string& host,
-                                               uint16_t port,
-                                               base::Time expiration)
-    : alternative_service_(protocol, host, port), expiration_(expiration) {}
+    base::Time expiration,
+    const QuicVersionVector& advertised_versions)
+    : alternative_service_(alternative_service), expiration_(expiration) {
+  if (alternative_service_.protocol == kProtoQUIC) {
+    advertised_versions_ = advertised_versions;
+    std::sort(advertised_versions_.begin(), advertised_versions_.end());
+  }
+}
 
 AlternativeServiceInfo::AlternativeServiceInfo(
     const AlternativeServiceInfo& alternative_service_info) = default;
diff --git a/net/http/http_server_properties.h b/net/http/http_server_properties.h
index c521148b..a11d80222 100644
--- a/net/http/http_server_properties.h
+++ b/net/http/http_server_properties.h
@@ -20,6 +20,7 @@
 #include "net/base/net_export.h"
 #include "net/quic/core/quic_bandwidth.h"
 #include "net/quic/core/quic_server_id.h"
+#include "net/quic/core/quic_versions.h"
 #include "net/socket/next_proto.h"
 #include "net/spdy/core/spdy_framer.h"  // TODO(willchan): Reconsider this.
 #include "net/spdy/core/spdy_protocol.h"
@@ -116,24 +117,28 @@
 
 class NET_EXPORT_PRIVATE AlternativeServiceInfo {
  public:
+  static AlternativeServiceInfo CreateHttp2AlternativeServiceInfo(
+      const AlternativeService& alternative_service,
+      base::Time expiration);
+
+  static AlternativeServiceInfo CreateQuicAlternativeServiceInfo(
+      const AlternativeService& alternative_service,
+      base::Time expiration,
+      const QuicVersionVector& advertised_versions);
+
   AlternativeServiceInfo();
-
-  AlternativeServiceInfo(const AlternativeService& alternative_service,
-                         base::Time expiration);
-
-  AlternativeServiceInfo(NextProto protocol,
-                         const std::string& host,
-                         uint16_t port,
-                         base::Time expiration);
+  ~AlternativeServiceInfo();
 
   AlternativeServiceInfo(
       const AlternativeServiceInfo& alternative_service_info);
+
   AlternativeServiceInfo& operator=(
       const AlternativeServiceInfo& alternative_service_info);
 
   bool operator==(const AlternativeServiceInfo& other) const {
     return alternative_service_ == other.alternative_service() &&
-           expiration_ == other.expiration();
+           expiration_ == other.expiration() &&
+           advertised_versions_ == other.advertised_versions();
   }
 
   bool operator!=(const AlternativeServiceInfo& other) const {
@@ -158,15 +163,37 @@
     expiration_ = expiration;
   }
 
+  void set_advertised_versions(const QuicVersionVector& advertised_versions) {
+    if (alternative_service_.protocol != kProtoQUIC)
+      return;
+
+    advertised_versions_ = advertised_versions;
+    std::sort(advertised_versions_.begin(), advertised_versions_.end());
+  }
+
   const AlternativeService& alternative_service() const {
     return alternative_service_;
   }
 
   base::Time expiration() const { return expiration_; }
 
+  const QuicVersionVector& advertised_versions() const {
+    return advertised_versions_;
+  }
+
  private:
+  AlternativeServiceInfo(const AlternativeService& alternative_service,
+                         base::Time expiration,
+                         const QuicVersionVector& advertised_versions);
+
   AlternativeService alternative_service_;
   base::Time expiration_;
+
+  // Lists all the QUIC versions that are advertised by the server and supported
+  // by Chrome. If empty, defaults to versions used by the current instance of
+  // the netstack.
+  // This list MUST be sorted in ascending order.
+  QuicVersionVector advertised_versions_;
 };
 
 struct NET_EXPORT SupportsQuic {
@@ -270,15 +297,26 @@
   virtual AlternativeServiceInfoVector GetAlternativeServiceInfos(
       const url::SchemeHostPort& origin) = 0;
 
-  // Set a single alternative service for |origin|.  Previous alternative
+  // Set a single HTTP/2 alternative service for |origin|.  Previous
+  // alternative services for |origin| are discarded.
+  // |alternative_service.host| may be empty.
+  // Return true if |alternative_service_map_| has changed significantly enough
+  // that it should be persisted to disk.
+  virtual bool SetHttp2AlternativeService(
+      const url::SchemeHostPort& origin,
+      const AlternativeService& alternative_service,
+      base::Time expiration) = 0;
+
+  // Set a single QUIC alternative service for |origin|.  Previous alternative
   // services for |origin| are discarded.
   // |alternative_service.host| may be empty.
   // Return true if |alternative_service_map_| has changed significantly enough
   // that it should be persisted to disk.
-  virtual bool SetAlternativeService(
+  virtual bool SetQuicAlternativeService(
       const url::SchemeHostPort& origin,
       const AlternativeService& alternative_service,
-      base::Time expiration) = 0;
+      base::Time expiration,
+      const QuicVersionVector& advertised_versions) = 0;
 
   // Set alternative services for |origin|.  Previous alternative services for
   // |origin| are discarded.
diff --git a/net/http/http_server_properties_impl.cc b/net/http/http_server_properties_impl.cc
index b96a4b27..843a017 100644
--- a/net/http/http_server_properties_impl.cc
+++ b/net/http/http_server_properties_impl.cc
@@ -287,8 +287,16 @@
         ++it;
         continue;
       }
-      valid_alternative_service_infos.push_back(
-          AlternativeServiceInfo(alternative_service, it->expiration()));
+      if (alternative_service.protocol == kProtoQUIC) {
+        valid_alternative_service_infos.push_back(
+            AlternativeServiceInfo::CreateQuicAlternativeServiceInfo(
+                alternative_service, it->expiration(),
+                it->advertised_versions()));
+      } else {
+        valid_alternative_service_infos.push_back(
+            AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
+                alternative_service, it->expiration()));
+      }
       ++it;
     }
     if (map_it->second.empty()) {
@@ -323,8 +331,16 @@
       ++it;
       continue;
     }
-    valid_alternative_service_infos.push_back(
-        AlternativeServiceInfo(alternative_service, it->expiration()));
+    if (alternative_service.protocol == kProtoQUIC) {
+      valid_alternative_service_infos.push_back(
+          AlternativeServiceInfo::CreateQuicAlternativeServiceInfo(
+              alternative_service, it->expiration(),
+              it->advertised_versions()));
+    } else {
+      valid_alternative_service_infos.push_back(
+          AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
+              alternative_service, it->expiration()));
+    }
     ++it;
   }
   if (map_it->second.empty()) {
@@ -333,14 +349,31 @@
   return valid_alternative_service_infos;
 }
 
-bool HttpServerPropertiesImpl::SetAlternativeService(
+bool HttpServerPropertiesImpl::SetHttp2AlternativeService(
     const url::SchemeHostPort& origin,
     const AlternativeService& alternative_service,
     base::Time expiration) {
+  DCHECK_EQ(alternative_service.protocol, kProtoHTTP2);
+
   return SetAlternativeServices(
       origin,
       AlternativeServiceInfoVector(
-          /*size=*/1, AlternativeServiceInfo(alternative_service, expiration)));
+          /*size=*/1, AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
+                          alternative_service, expiration)));
+}
+
+bool HttpServerPropertiesImpl::SetQuicAlternativeService(
+    const url::SchemeHostPort& origin,
+    const AlternativeService& alternative_service,
+    base::Time expiration,
+    const QuicVersionVector& advertised_versions) {
+  DCHECK(alternative_service.protocol == kProtoQUIC);
+
+  return SetAlternativeServices(
+      origin, AlternativeServiceInfoVector(
+                  /*size=*/1,
+                  AlternativeServiceInfo::CreateQuicAlternativeServiceInfo(
+                      alternative_service, expiration, advertised_versions)));
 }
 
 bool HttpServerPropertiesImpl::SetAlternativeServices(
@@ -380,6 +413,12 @@
           changed = true;
           break;
         }
+        // Also persist to disk if new entry has a different list of advertised
+        // versions.
+        if (old.advertised_versions() != new_it->advertised_versions()) {
+          changed = true;
+          break;
+        }
         ++new_it;
       }
     }
diff --git a/net/http/http_server_properties_impl.h b/net/http/http_server_properties_impl.h
index 74516758..c8c42325 100644
--- a/net/http/http_server_properties_impl.h
+++ b/net/http/http_server_properties_impl.h
@@ -81,9 +81,14 @@
                         SSLConfig* ssl_config) override;
   AlternativeServiceInfoVector GetAlternativeServiceInfos(
       const url::SchemeHostPort& origin) override;
-  bool SetAlternativeService(const url::SchemeHostPort& origin,
-                             const AlternativeService& alternative_service,
-                             base::Time expiration) override;
+  bool SetHttp2AlternativeService(const url::SchemeHostPort& origin,
+                                  const AlternativeService& alternative_service,
+                                  base::Time expiration) override;
+  bool SetQuicAlternativeService(
+      const url::SchemeHostPort& origin,
+      const AlternativeService& alternative_service,
+      base::Time expiration,
+      const QuicVersionVector& advertised_versions) override;
   bool SetAlternativeServices(const url::SchemeHostPort& origin,
                               const AlternativeServiceInfoVector&
                                   alternative_service_info_vector) override;
diff --git a/net/http/http_server_properties_impl_unittest.cc b/net/http/http_server_properties_impl_unittest.cc
index 31a6074..163ced1 100644
--- a/net/http/http_server_properties_impl_unittest.cc
+++ b/net/http/http_server_properties_impl_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/values.h"
 #include "net/base/host_port_pair.h"
 #include "net/base/ip_address.h"
+#include "net/http/http_network_session.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
@@ -78,7 +79,14 @@
                              const AlternativeService& alternative_service) {
     const base::Time expiration =
         base::Time::Now() + base::TimeDelta::FromDays(1);
-    return impl_.SetAlternativeService(origin, alternative_service, expiration);
+    if (alternative_service.protocol == kProtoQUIC) {
+      return impl_.SetQuicAlternativeService(
+          origin, alternative_service, expiration,
+          HttpNetworkSession::Params().quic_supported_versions);
+    } else {
+      return impl_.SetHttp2AlternativeService(origin, alternative_service,
+                                              expiration);
+    }
   }
 
   void MarkBrokenAndLetExpireAlternativeServiceNTimes(
@@ -368,20 +376,25 @@
   AlternativeServiceInfoVector alternative_service_info_vector;
   base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
   // Same hostname, same port, TCP: should be ignored.
-  AlternativeServiceInfo alternative_service_info1(kProtoHTTP2, "foo", 443,
-                                                   expiration);
+  AlternativeServiceInfo alternative_service_info1 =
+      AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
+          AlternativeService(kProtoHTTP2, "foo", 443), expiration);
   alternative_service_info_vector.push_back(alternative_service_info1);
   // Different hostname: GetAlternativeServiceInfos should return this one.
-  AlternativeServiceInfo alternative_service_info2(kProtoHTTP2, "bar", 443,
-                                                   expiration);
+  AlternativeServiceInfo alternative_service_info2 =
+      AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
+          AlternativeService(kProtoHTTP2, "bar", 443), expiration);
   alternative_service_info_vector.push_back(alternative_service_info2);
   // Different port: GetAlternativeServiceInfos should return this one too.
-  AlternativeServiceInfo alternative_service_info3(kProtoHTTP2, "foo", 80,
-                                                   expiration);
+  AlternativeServiceInfo alternative_service_info3 =
+      AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
+          AlternativeService(kProtoHTTP2, "foo", 80), expiration);
   alternative_service_info_vector.push_back(alternative_service_info3);
   // QUIC: GetAlternativeServices should return this one too.
-  AlternativeServiceInfo alternative_service_info4(kProtoQUIC, "foo", 443,
-                                                   expiration);
+  AlternativeServiceInfo alternative_service_info4 =
+      AlternativeServiceInfo::CreateQuicAlternativeServiceInfo(
+          AlternativeService(kProtoQUIC, "foo", 443), expiration,
+          HttpNetworkSession::Params().quic_supported_versions);
   alternative_service_info_vector.push_back(alternative_service_info4);
 
   url::SchemeHostPort test_server("https", "foo", 443);
@@ -405,7 +418,8 @@
   const base::Time now = base::Time::Now();
   base::Time expiration1 = now + base::TimeDelta::FromDays(1);
   // 1st entry in the memory.
-  impl_.SetAlternativeService(test_server1, alternative_service1, expiration1);
+  impl_.SetHttp2AlternativeService(test_server1, alternative_service1,
+                                   expiration1);
 
   // |test_server2| has an alternative service, which will be
   // overwritten by SetAlternativeServiceServers(), because
@@ -415,7 +429,8 @@
   const AlternativeService alternative_service2(kProtoHTTP2, "bar2", 443);
   base::Time expiration2 = now + base::TimeDelta::FromDays(2);
   alternative_service_info_vector.push_back(
-      AlternativeServiceInfo(alternative_service2, expiration2));
+      AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
+          alternative_service2, expiration2));
   url::SchemeHostPort test_server2("http", "foo2", 80);
   // 0th entry in the memory.
   impl_.SetAlternativeServices(test_server2, alternative_service_info_vector);
@@ -427,8 +442,9 @@
           AlternativeServiceMap::NO_AUTO_EVICT);
   const AlternativeService alternative_service3(kProtoHTTP2, "bar3", 123);
   base::Time expiration3 = now + base::TimeDelta::FromDays(3);
-  const AlternativeServiceInfo alternative_service_info1(alternative_service3,
-                                                         expiration3);
+  const AlternativeServiceInfo alternative_service_info1 =
+      AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
+          alternative_service3, expiration3);
   // Simulate updating data for 0th entry with data from Preferences.
   alternative_service_map->Put(
       test_server2,
@@ -437,8 +453,9 @@
   url::SchemeHostPort test_server3("http", "foo3", 80);
   const AlternativeService alternative_service4(kProtoHTTP2, "bar4", 1234);
   base::Time expiration4 = now + base::TimeDelta::FromDays(4);
-  const AlternativeServiceInfo alternative_service_info2(alternative_service4,
-                                                         expiration4);
+  const AlternativeServiceInfo alternative_service_info2 =
+      AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
+          alternative_service4, expiration4);
   // Add an old entry from Preferences, this will be added to end of recency
   // list.
   alternative_service_map->Put(
@@ -502,8 +519,9 @@
   url::SchemeHostPort server("https", "foo", 443);
   const AlternativeService alternative_service(kProtoHTTP2, "bar", 443);
   base::Time expiration = base::Time::Now() - base::TimeDelta::FromDays(1);
-  const AlternativeServiceInfo alternative_service_info(alternative_service,
-                                                        expiration);
+  const AlternativeServiceInfo alternative_service_info =
+      AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
+          alternative_service, expiration);
   std::unique_ptr<AlternativeServiceMap> alternative_service_map =
       base::MakeUnique<AlternativeServiceMap>(
           AlternativeServiceMap::NO_AUTO_EVICT);
@@ -536,8 +554,9 @@
   url::SchemeHostPort canonical_server("https", "bar.c.youtube.com", 443);
   const AlternativeService alternative_service(kProtoHTTP2, "", 443);
   base::Time expiration = base::Time::Now() - base::TimeDelta::FromDays(1);
-  const AlternativeServiceInfo alternative_service_info(alternative_service,
-                                                        expiration);
+  const AlternativeServiceInfo alternative_service_info =
+      AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
+          alternative_service, expiration);
   std::unique_ptr<AlternativeServiceMap> alternative_service_map =
       base::MakeUnique<AlternativeServiceMap>(
           AlternativeServiceMap::NO_AUTO_EVICT);
@@ -571,8 +590,10 @@
   url::SchemeHostPort canonical_server("https", "bar.c.youtube.com", 443);
   const AlternativeService alternative_service(kProtoQUIC, "", 443);
   base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
-  const AlternativeServiceInfo alternative_service_info(alternative_service,
-                                                        expiration);
+  const AlternativeServiceInfo alternative_service_info =
+      AlternativeServiceInfo::CreateQuicAlternativeServiceInfo(
+          alternative_service, expiration,
+          HttpNetworkSession::Params().quic_supported_versions);
 
   impl_.SetAlternativeServices(
       canonical_server,
@@ -646,10 +667,12 @@
   AlternativeServiceInfoVector alternative_service_info_vector2;
   base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
   alternative_service_info_vector2.push_back(
-      AlternativeServiceInfo(alternative_service1, expiration));
+      AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
+          alternative_service1, expiration));
   const AlternativeService alternative_service2(kProtoHTTP2, "foo", 1234);
   alternative_service_info_vector2.push_back(
-      AlternativeServiceInfo(alternative_service2, expiration));
+      AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
+          alternative_service2, expiration));
   impl_.SetAlternativeServices(test_server, alternative_service_info_vector2);
   alternative_service_info_vector =
       impl_.GetAlternativeServiceInfos(test_server);
@@ -680,13 +703,15 @@
   // GetAlternativeServiceInfos().
   const AlternativeService alternative_service1(kProtoHTTP2, "foo", 443);
   alternative_service_info_vector.push_back(
-      AlternativeServiceInfo(alternative_service1, now - one_day));
+      AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
+          alternative_service1, now - one_day));
 
   // Second alterrnative service will expire one day from now, should be
   // returned by GetAlternativeSerices().
   const AlternativeService alternative_service2(kProtoHTTP2, "bar", 1234);
   alternative_service_info_vector.push_back(
-      AlternativeServiceInfo(alternative_service2, now + one_day));
+      AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
+          alternative_service2, now + one_day));
 
   url::SchemeHostPort test_server("http", "foo", 80);
   impl_.SetAlternativeServices(test_server, alternative_service_info_vector);
@@ -707,13 +732,15 @@
   // GetAlternativeServiceInfos().
   const AlternativeService alternative_service1(kProtoHTTP2, "foo", 443);
   alternative_service_info_vector.push_back(
-      AlternativeServiceInfo(alternative_service1, now - one_day));
+      AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
+          alternative_service1, now - one_day));
 
   // Second alterrnative service will expire one day from now, should be
   // returned by GetAlternativeSerices().
   const AlternativeService alternative_service2(kProtoHTTP2, "bar", 1234);
   alternative_service_info_vector.push_back(
-      AlternativeServiceInfo(alternative_service2, now + one_day));
+      AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
+          alternative_service2, now + one_day));
 
   url::SchemeHostPort canonical_server("https", "bar.c.youtube.com", 443);
   impl_.SetAlternativeServices(canonical_server,
@@ -732,10 +759,12 @@
   const AlternativeService alternative_service1(kProtoHTTP2, "foo", 443);
   base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
   alternative_service_info_vector.push_back(
-      AlternativeServiceInfo(alternative_service1, expiration));
+      AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
+          alternative_service1, expiration));
   const AlternativeService alternative_service2(kProtoHTTP2, "bar", 1234);
   alternative_service_info_vector.push_back(
-      AlternativeServiceInfo(alternative_service2, expiration));
+      AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
+          alternative_service2, expiration));
   // Set Alt-Svc list for |http_server|.
   url::SchemeHostPort http_server("http", "foo", 80);
   impl_.SetAlternativeServices(http_server, alternative_service_info_vector);
@@ -768,10 +797,12 @@
   const AlternativeService alternative_service1(kProtoHTTP2, "foo", 443);
   base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
   alternative_service_info_vector.push_back(
-      AlternativeServiceInfo(alternative_service1, expiration));
+      AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
+          alternative_service1, expiration));
   const AlternativeService alternative_service2(kProtoHTTP2, "bar", 1234);
   alternative_service_info_vector.push_back(
-      AlternativeServiceInfo(alternative_service2, expiration));
+      AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
+          alternative_service2, expiration));
   url::SchemeHostPort test_server("http", "foo", 80);
   impl_.SetAlternativeServices(test_server, alternative_service_info_vector);
 
@@ -857,10 +888,13 @@
       kProtoQUIC, "bar.c.youtube.com", 1234);
   base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
   alternative_service_info_vector.push_back(
-      AlternativeServiceInfo(canonical_alternative_service1, expiration));
+      AlternativeServiceInfo::CreateQuicAlternativeServiceInfo(
+          canonical_alternative_service1, expiration,
+          HttpNetworkSession::Params().quic_supported_versions));
   const AlternativeService canonical_alternative_service2(kProtoHTTP2, "", 443);
   alternative_service_info_vector.push_back(
-      AlternativeServiceInfo(canonical_alternative_service2, expiration));
+      AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
+          canonical_alternative_service2, expiration));
   impl_.SetAlternativeServices(canonical_server,
                                alternative_service_info_vector);
 
diff --git a/net/http/http_server_properties_manager.cc b/net/http/http_server_properties_manager.cc
index d348d199..00a82ed 100644
--- a/net/http/http_server_properties_manager.cc
+++ b/net/http/http_server_properties_manager.cc
@@ -62,6 +62,7 @@
 const char kHostKey[] = "host";
 const char kPortKey[] = "port";
 const char kExpirationKey[] = "expiration";
+const char kAdvertisedVersionsKey[] = "advertised_versions";
 const char kNetworkStatsKey[] = "network_stats";
 const char kSrttKey[] = "srtt";
 
@@ -211,12 +212,12 @@
   return http_server_properties_impl_->GetAlternativeServiceInfos(origin);
 }
 
-bool HttpServerPropertiesManager::SetAlternativeService(
+bool HttpServerPropertiesManager::SetHttp2AlternativeService(
     const url::SchemeHostPort& origin,
     const AlternativeService& alternative_service,
     base::Time expiration) {
   DCHECK(network_task_runner_->RunsTasksInCurrentSequence());
-  const bool changed = http_server_properties_impl_->SetAlternativeService(
+  const bool changed = http_server_properties_impl_->SetHttp2AlternativeService(
       origin, alternative_service, expiration);
   if (changed) {
     ScheduleUpdatePrefsOnNetworkSequence(SET_ALTERNATIVE_SERVICES);
@@ -224,6 +225,20 @@
   return changed;
 }
 
+bool HttpServerPropertiesManager::SetQuicAlternativeService(
+    const url::SchemeHostPort& origin,
+    const AlternativeService& alternative_service,
+    base::Time expiration,
+    const QuicVersionVector& advertised_versions) {
+  DCHECK(network_task_runner_->RunsTasksInCurrentSequence());
+  const bool changed = http_server_properties_impl_->SetQuicAlternativeService(
+      origin, alternative_service, expiration, advertised_versions);
+  if (changed) {
+    ScheduleUpdatePrefsOnNetworkSequence(SET_ALTERNATIVE_SERVICES);
+  }
+  return changed;
+}
+
 bool HttpServerPropertiesManager::SetAlternativeServices(
     const url::SchemeHostPort& origin,
     const AlternativeServiceInfoVector& alternative_service_info_vector) {
@@ -636,22 +651,48 @@
   }
 
   std::string expiration_string;
-  if (alternative_service_dict.GetStringWithoutPathExpansion(
+  if (!alternative_service_dict.GetStringWithoutPathExpansion(
           kExpirationKey, &expiration_string)) {
-    int64_t expiration_int64 = 0;
-    if (!base::StringToInt64(expiration_string, &expiration_int64)) {
-      DVLOG(1) << "Malformed alternative service expiration for server: "
+    DVLOG(1) << "Malformed alternative service expiration for server: "
+             << server_str;
+    return false;
+  }
+
+  int64_t expiration_int64 = 0;
+  if (!base::StringToInt64(expiration_string, &expiration_int64)) {
+    DVLOG(1) << "Malformed alternative service expiration for server: "
+             << server_str;
+    return false;
+  }
+  alternative_service_info->set_expiration(
+      base::Time::FromInternalValue(expiration_int64));
+
+  // Advertised versions list is optional.
+  if (!alternative_service_dict.HasKey(kAdvertisedVersionsKey))
+    return true;
+
+  const base::ListValue* versions_list = nullptr;
+  if (!alternative_service_dict.GetListWithoutPathExpansion(
+          kAdvertisedVersionsKey, &versions_list)) {
+    DVLOG(1)
+        << "Malformed alternative service advertised versions list for server: "
+        << server_str;
+    return false;
+  }
+
+  QuicVersionVector advertised_versions;
+  for (const auto& value : *versions_list) {
+    int version;
+    if (!value.GetAsInteger(&version)) {
+      DVLOG(1) << "Malformed alternative service version for server: "
                << server_str;
       return false;
     }
-    alternative_service_info->set_expiration(
-        base::Time::FromInternalValue(expiration_int64));
-    return true;
+    advertised_versions.push_back(QuicVersion(version));
   }
+  alternative_service_info->set_advertised_versions(advertised_versions);
 
-  DVLOG(1) << "Malformed alternative service expiration for server: "
-           << server_str;
-  return false;
+  return true;
 }
 
 bool HttpServerPropertiesManager::AddToAlternativeServiceMap(
@@ -1121,6 +1162,13 @@
         kExpirationKey,
         base::Int64ToString(
             alternative_service_info.expiration().ToInternalValue()));
+    std::unique_ptr<base::ListValue> advertised_versions_list =
+        base::MakeUnique<base::ListValue>();
+    for (const auto& version : alternative_service_info.advertised_versions()) {
+      advertised_versions_list->AppendInteger(version);
+    }
+    alternative_service_dict->SetList(kAdvertisedVersionsKey,
+                                      std::move(advertised_versions_list));
     alternative_service_list->Append(std::move(alternative_service_dict));
   }
   if (alternative_service_list->GetSize() == 0)
diff --git a/net/http/http_server_properties_manager.h b/net/http/http_server_properties_manager.h
index a2a753ac..bb2132f90 100644
--- a/net/http/http_server_properties_manager.h
+++ b/net/http/http_server_properties_manager.h
@@ -129,9 +129,14 @@
                         SSLConfig* ssl_config) override;
   AlternativeServiceInfoVector GetAlternativeServiceInfos(
       const url::SchemeHostPort& origin) override;
-  bool SetAlternativeService(const url::SchemeHostPort& origin,
-                             const AlternativeService& alternative_service,
-                             base::Time expiration) override;
+  bool SetHttp2AlternativeService(const url::SchemeHostPort& origin,
+                                  const AlternativeService& alternative_service,
+                                  base::Time expiration) override;
+  bool SetQuicAlternativeService(
+      const url::SchemeHostPort& origin,
+      const AlternativeService& alternative_service,
+      base::Time expiration,
+      const QuicVersionVector& advertised_versions) override;
   bool SetAlternativeServices(const url::SchemeHostPort& origin,
                               const AlternativeServiceInfoVector&
                                   alternative_service_info_vector) override;
@@ -246,6 +251,8 @@
   FRIEND_TEST_ALL_PREFIXES(HttpServerPropertiesManagerTest,
                            AddToAlternativeServiceMap);
   FRIEND_TEST_ALL_PREFIXES(HttpServerPropertiesManagerTest,
+                           ReadAdvertisedVersionsFromPref);
+  FRIEND_TEST_ALL_PREFIXES(HttpServerPropertiesManagerTest,
                            DoNotLoadAltSvcForInsecureOrigins);
   FRIEND_TEST_ALL_PREFIXES(HttpServerPropertiesManagerTest,
                            DoNotLoadExpiredAlternativeService);
diff --git a/net/http/http_server_properties_manager_unittest.cc b/net/http/http_server_properties_manager_unittest.cc
index d8dada1..7d7158a9 100644
--- a/net/http/http_server_properties_manager_unittest.cc
+++ b/net/http/http_server_properties_manager_unittest.cc
@@ -22,6 +22,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/values.h"
 #include "net/base/ip_address.h"
+#include "net/http/http_network_session.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
@@ -164,6 +165,7 @@
 
   void SetUp() override {
     one_day_from_now_ = base::Time::Now() + base::TimeDelta::FromDays(1);
+    advertised_versions_ = HttpNetworkSession::Params().quic_supported_versions;
     pref_delegate_ = new MockPrefDelegate;
     http_server_props_manager_.reset(
         new StrictMock<TestingHttpServerPropertiesManager>(
@@ -239,6 +241,7 @@
   std::unique_ptr<TestingHttpServerPropertiesManager>
       http_server_props_manager_;
   base::Time one_day_from_now_;
+  QuicVersionVector advertised_versions_;
 
   // Overrides the main thread's message loop with a mock tick clock. Making the
   // main thread the |pref_test_task_runner_| matches expectations better than
@@ -708,10 +711,10 @@
   EXPECT_FALSE(HasAlternativeService(spdy_server_mail));
   const AlternativeService alternative_service(kProtoHTTP2, "mail.google.com",
                                                443);
-  http_server_props_manager_->SetAlternativeService(
+  http_server_props_manager_->SetHttp2AlternativeService(
       spdy_server_mail, alternative_service, one_day_from_now_);
   // ExpectScheduleUpdatePrefsOnNetworkSequence() should be called only once.
-  http_server_props_manager_->SetAlternativeService(
+  http_server_props_manager_->SetHttp2AlternativeService(
       spdy_server_mail, alternative_service, one_day_from_now_);
 
   // Run the task.
@@ -742,11 +745,13 @@
   const AlternativeService alternative_service1(kProtoHTTP2, "mail.google.com",
                                                 443);
   alternative_service_info_vector.push_back(
-      AlternativeServiceInfo(alternative_service1, one_day_from_now_));
+      AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
+          alternative_service1, one_day_from_now_));
   const AlternativeService alternative_service2(kProtoQUIC, "mail.google.com",
                                                 1234);
   alternative_service_info_vector.push_back(
-      AlternativeServiceInfo(alternative_service2, one_day_from_now_));
+      AlternativeServiceInfo::CreateQuicAlternativeServiceInfo(
+          alternative_service2, one_day_from_now_, advertised_versions_));
   http_server_props_manager_->SetAlternativeServices(
       spdy_server_mail, alternative_service_info_vector);
   // ExpectScheduleUpdatePrefsOnNetworkSequence() should be called only once.
@@ -804,7 +809,7 @@
         AlternativeService(kProtoHTTP2, "mail.google.com", 443);
 
     ExpectScheduleUpdatePrefsOnNetworkSequence();
-    http_server_props_manager_->SetAlternativeService(
+    http_server_props_manager_->SetHttp2AlternativeService(
         spdy_server_mail, alternative_service, one_day_from_now_);
 
     EXPECT_FALSE(http_server_props_manager_->IsAlternativeServiceBroken(
@@ -977,7 +982,7 @@
     http_server_props_manager_->SetSupportsSpdy(spdy_server, true);
     AlternativeService alternative_service(kProtoHTTP2, "mail.google.com",
                                            1234);
-    http_server_props_manager_->SetAlternativeService(
+    http_server_props_manager_->SetHttp2AlternativeService(
         spdy_server, alternative_service, one_day_from_now_);
     http_server_props_manager_->SetSupportsQuic(true, actual_address);
     ServerNetworkStats stats;
@@ -1149,20 +1154,23 @@
   base::Time expiration1;
   ASSERT_TRUE(base::Time::FromUTCString("2036-12-01 10:00:00", &expiration1));
   alternative_service_info_vector.push_back(
-      AlternativeServiceInfo(www_alternative_service1, expiration1));
+      AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
+          www_alternative_service1, expiration1));
+
   AlternativeService www_alternative_service2(kProtoHTTP2, "www.google.com",
                                               1234);
   base::Time expiration2;
   ASSERT_TRUE(base::Time::FromUTCString("2036-12-31 10:00:00", &expiration2));
   alternative_service_info_vector.push_back(
-      AlternativeServiceInfo(www_alternative_service2, expiration2));
+      AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
+          www_alternative_service2, expiration2));
   ASSERT_TRUE(http_server_props_manager_->SetAlternativeServices(
       server_www, alternative_service_info_vector));
 
   AlternativeService mail_alternative_service(kProtoHTTP2, "foo.google.com",
                                               444);
   base::Time expiration3 = base::Time::Max();
-  ASSERT_TRUE(http_server_props_manager_->SetAlternativeService(
+  ASSERT_TRUE(http_server_props_manager_->SetHttp2AlternativeService(
       server_mail, mail_alternative_service, expiration3));
 
   // #3: Set ServerNetworkStats.
@@ -1194,17 +1202,15 @@
   const char expected_json[] =
       "{\"quic_servers\":{\"https://"
       "mail.google.com:80\":{\"server_info\":\"quic_server_info1\"}},"
-      "\"servers\":["
-      "{\"https://www.google.com:80\":{"
-      "\"alternative_service\":[{\"expiration\":\"13756212000000000\","
-      "\"port\":443,\"protocol_str\":\"h2\"},"
-      "{\"expiration\":\"13758804000000000\",\"host\":\"www.google.com\","
-      "\"port\":1234,\"protocol_str\":\"h2\"}]}},"
+      "\"servers\":[{\"https://www.google.com:80\":{"
+      "\"alternative_service\":[{\"advertised_versions\":[],\"expiration\":"
+      "\"13756212000000000\",\"port\":443,\"protocol_str\":\"h2\"},"
+      "{\"advertised_versions\":[],\"expiration\":\"13758804000000000\","
+      "\"host\":\"www.google.com\",\"port\":1234,\"protocol_str\":\"h2\"}]}},"
       "{\"https://mail.google.com:80\":{\"alternative_service\":[{"
-      "\"expiration\":\"9223372036854775807\",\"host\":\"foo.google.com\","
-      "\"port\":444,\"protocol_str\":\"h2\"}],"
-      "\"network_stats\":{\"srtt\":42}}}"
-      "],"
+      "\"advertised_versions\":[],\"expiration\":\"9223372036854775807\","
+      "\"host\":\"foo.google.com\",\"port\":444,\"protocol_str\":\"h2\"}],"
+      "\"network_stats\":{\"srtt\":42}}}],"
       "\"supports_quic\":{\"address\":\"127.0.0.1\",\"used_quic\":true},"
       "\"version\":5}";
 
@@ -1337,7 +1343,8 @@
     const base::Time time_one_day_later =
         base::Time::Now() + base::TimeDelta::FromDays(1);
     alternative_service_info_vector.push_back(
-        AlternativeServiceInfo(broken_alternative_service, time_one_day_later));
+        AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
+            broken_alternative_service, time_one_day_later));
     // #1: MarkAlternativeServiceBroken().
     http_server_props_manager_->MarkAlternativeServiceBroken(
         broken_alternative_service);
@@ -1347,15 +1354,17 @@
     const base::Time time_one_day_ago =
         base::Time::Now() - base::TimeDelta::FromDays(1);
     alternative_service_info_vector.push_back(
-        AlternativeServiceInfo(expired_alternative_service, time_one_day_ago));
+        AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
+            expired_alternative_service, time_one_day_ago));
 
     const AlternativeService valid_alternative_service(
         kProtoHTTP2, "valid.example.com", 443);
     alternative_service_info_vector.push_back(
-        AlternativeServiceInfo(valid_alternative_service, time_one_day_later));
+        AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
+            valid_alternative_service, time_one_day_later));
 
     const url::SchemeHostPort server("https", "www.example.com", 443);
-    // #2: SetAlternativeService().
+    // #2: SetAlternativeServices().
     ASSERT_TRUE(http_server_props_manager_->SetAlternativeServices(
         server, alternative_service_info_vector));
   }
@@ -1550,4 +1559,237 @@
   EXPECT_FALSE(pref_test_task_runner_->HasPendingTask());
 }
 
+TEST_P(HttpServerPropertiesManagerTest, PersistAdvertisedVersionsToPref) {
+  ExpectScheduleUpdatePrefsOnNetworkSequenceRepeatedly(5);
+
+  const url::SchemeHostPort server_www("https", "www.google.com", 80);
+  const url::SchemeHostPort server_mail("https", "mail.google.com", 80);
+
+  // #1 & #2: Set alternate protocol.
+  AlternativeServiceInfoVector alternative_service_info_vector;
+  // Quic alternative service set with two advertised QUIC versions.
+  AlternativeService quic_alternative_service1(kProtoQUIC, "", 443);
+  base::Time expiration1;
+  ASSERT_TRUE(base::Time::FromUTCString("2036-12-01 10:00:00", &expiration1));
+  QuicVersionVector advertised_versions = {QUIC_VERSION_37, QUIC_VERSION_35};
+  alternative_service_info_vector.push_back(
+      AlternativeServiceInfo::CreateQuicAlternativeServiceInfo(
+          quic_alternative_service1, expiration1, advertised_versions));
+  // HTTP/2 alternative service should not set any advertised version.
+  AlternativeService h2_alternative_service(kProtoHTTP2, "www.google.com",
+                                            1234);
+  base::Time expiration2;
+  ASSERT_TRUE(base::Time::FromUTCString("2036-12-31 10:00:00", &expiration2));
+  alternative_service_info_vector.push_back(
+      AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
+          h2_alternative_service, expiration2));
+  ASSERT_TRUE(http_server_props_manager_->SetAlternativeServices(
+      server_www, alternative_service_info_vector));
+
+  // Set another QUIC alternative service with a single advertised QUIC version.
+  AlternativeService mail_alternative_service(kProtoQUIC, "foo.google.com",
+                                              444);
+  base::Time expiration3 = base::Time::Max();
+  ASSERT_TRUE(http_server_props_manager_->SetQuicAlternativeService(
+      server_mail, mail_alternative_service, expiration3,
+      advertised_versions_));
+  // #3: Set ServerNetworkStats.
+  ServerNetworkStats stats;
+  stats.srtt = base::TimeDelta::FromInternalValue(42);
+  http_server_props_manager_->SetServerNetworkStats(server_mail, stats);
+
+  // #4: Set quic_server_info string.
+  QuicServerId mail_quic_server_id("mail.google.com", 80);
+  std::string quic_server_info1("quic_server_info1");
+  http_server_props_manager_->SetQuicServerInfo(mail_quic_server_id,
+                                                quic_server_info1);
+
+  // #5: Set SupportsQuic.
+  IPAddress actual_address(127, 0, 0, 1);
+  http_server_props_manager_->SetSupportsQuic(true, actual_address);
+
+  // Update Prefs.
+  ExpectPrefsUpdate(1);
+  EXPECT_TRUE(net_test_task_runner_->HasPendingTask());
+  EXPECT_FALSE(pref_test_task_runner_->HasPendingTask());
+  net_test_task_runner_->FastForwardUntilNoTasksRemain();
+  EXPECT_TRUE(pref_test_task_runner_->HasPendingTask());
+  pref_test_task_runner_->FastForwardUntilNoTasksRemain();
+  EXPECT_FALSE(net_test_task_runner_->HasPendingTask());
+  EXPECT_FALSE(pref_test_task_runner_->HasPendingTask());
+
+  // Verify preferences with correct advertised version field.
+  const char expected_json[] =
+      "{\"quic_servers\":{\"https://mail.google.com:80\":{"
+      "\"server_info\":\"quic_server_info1\"}},\"servers\":["
+      "{\"https://www.google.com:80\":{\"alternative_service\":[{"
+      "\"advertised_versions\":[35,37],\"expiration\":\"13756212000000000\","
+      "\"port\":443,\"protocol_str\":\"quic\"},{\"advertised_versions\":[],"
+      "\"expiration\":\"13758804000000000\",\"host\":\"www.google.com\","
+      "\"port\":1234,\"protocol_str\":\"h2\"}]}},"
+      "{\"https://mail.google.com:80\":{\"alternative_service\":[{"
+      "\"advertised_versions\":[37],\"expiration\":\"9223372036854775807\","
+      "\"host\":\"foo.google.com\",\"port\":444,\"protocol_str\":\"quic\"}],"
+      "\"network_stats\":{\"srtt\":42}}}],\"supports_quic\":{"
+      "\"address\":\"127.0.0.1\",\"used_quic\":true},\"version\":5}";
+
+  const base::Value* http_server_properties =
+      &pref_delegate_->GetServerProperties();
+  std::string preferences_json;
+  EXPECT_TRUE(
+      base::JSONWriter::Write(*http_server_properties, &preferences_json));
+  EXPECT_EQ(expected_json, preferences_json);
+}
+
+TEST_P(HttpServerPropertiesManagerTest, ReadAdvertisedVersionsFromPref) {
+  std::unique_ptr<base::Value> server_value = base::JSONReader::Read(
+      "{\"alternative_service\":["
+      "{\"port\":443,\"protocol_str\":\"quic\"},"
+      "{\"port\":123,\"protocol_str\":\"quic\","
+      "\"expiration\":\"9223372036854775807\","
+      "\"advertised_versions\":[37,35]}]}");
+  ASSERT_TRUE(server_value);
+  base::DictionaryValue* server_dict;
+  ASSERT_TRUE(server_value->GetAsDictionary(&server_dict));
+
+  const url::SchemeHostPort server("https", "example.com", 443);
+  AlternativeServiceMap alternative_service_map(/*max_size=*/5);
+  EXPECT_TRUE(http_server_props_manager_->AddToAlternativeServiceMap(
+      server, *server_dict, &alternative_service_map));
+
+  AlternativeServiceMap::iterator it = alternative_service_map.Get(server);
+  ASSERT_NE(alternative_service_map.end(), it);
+  AlternativeServiceInfoVector alternative_service_info_vector = it->second;
+  ASSERT_EQ(2u, alternative_service_info_vector.size());
+
+  // Verify the first alternative service with no advertised version listed.
+  EXPECT_EQ(kProtoQUIC,
+            alternative_service_info_vector[0].alternative_service().protocol);
+  EXPECT_EQ("", alternative_service_info_vector[0].alternative_service().host);
+  EXPECT_EQ(443, alternative_service_info_vector[0].alternative_service().port);
+  // Expiration defaults to one day from now, testing with tolerance.
+  const base::Time now = base::Time::Now();
+  const base::Time expiration = alternative_service_info_vector[0].expiration();
+  EXPECT_LE(now + base::TimeDelta::FromHours(23), expiration);
+  EXPECT_GE(now + base::TimeDelta::FromDays(1), expiration);
+  EXPECT_TRUE(alternative_service_info_vector[0].advertised_versions().empty());
+
+  // Verify the second alterntaive service with two advertised versions.
+  EXPECT_EQ(kProtoQUIC,
+            alternative_service_info_vector[1].alternative_service().protocol);
+  EXPECT_EQ("", alternative_service_info_vector[1].alternative_service().host);
+  EXPECT_EQ(123, alternative_service_info_vector[1].alternative_service().port);
+  EXPECT_EQ(base::Time::Max(), alternative_service_info_vector[1].expiration());
+  // Verify advertised versions.
+  const QuicVersionVector loaded_advertised_versions =
+      alternative_service_info_vector[1].advertised_versions();
+  EXPECT_EQ(2u, loaded_advertised_versions.size());
+  EXPECT_EQ(QUIC_VERSION_35, loaded_advertised_versions[0]);
+  EXPECT_EQ(QUIC_VERSION_37, loaded_advertised_versions[1]);
+}
+
+TEST_P(HttpServerPropertiesManagerTest,
+       UpdatePrefWhenAdvertisedVersionsChange) {
+  ExpectScheduleUpdatePrefsOnNetworkSequenceRepeatedly(4);
+
+  const url::SchemeHostPort server_www("https", "www.google.com", 80);
+
+  // #1: Set alternate protocol.
+  AlternativeServiceInfoVector alternative_service_info_vector;
+  // Quic alternative service set with a single QUIC version: QUIC_VERSION_37.
+  AlternativeService quic_alternative_service1(kProtoQUIC, "", 443);
+  base::Time expiration1;
+  ASSERT_TRUE(base::Time::FromUTCString("2036-12-01 10:00:00", &expiration1));
+  alternative_service_info_vector.push_back(
+      AlternativeServiceInfo::CreateQuicAlternativeServiceInfo(
+          quic_alternative_service1, expiration1, advertised_versions_));
+  ASSERT_TRUE(http_server_props_manager_->SetAlternativeServices(
+      server_www, alternative_service_info_vector));
+
+  // Set quic_server_info string.
+  QuicServerId mail_quic_server_id("mail.google.com", 80);
+  std::string quic_server_info1("quic_server_info1");
+  http_server_props_manager_->SetQuicServerInfo(mail_quic_server_id,
+                                                quic_server_info1);
+
+  // Set SupportsQuic.
+  IPAddress actual_address(127, 0, 0, 1);
+  http_server_props_manager_->SetSupportsQuic(true, actual_address);
+
+  // Update Prefs.
+  ExpectPrefsUpdate(1);
+  EXPECT_TRUE(net_test_task_runner_->HasPendingTask());
+  EXPECT_FALSE(pref_test_task_runner_->HasPendingTask());
+  net_test_task_runner_->FastForwardUntilNoTasksRemain();
+  EXPECT_TRUE(pref_test_task_runner_->HasPendingTask());
+  pref_test_task_runner_->FastForwardUntilNoTasksRemain();
+  EXPECT_FALSE(net_test_task_runner_->HasPendingTask());
+  EXPECT_FALSE(pref_test_task_runner_->HasPendingTask());
+
+  // Verify preferences with correct advertised version field.
+  const char expected_json[] =
+      "{\"quic_servers\":{\"https://mail.google.com:80\":"
+      "{\"server_info\":\"quic_server_info1\"}},\"servers\":["
+      "{\"https://www.google.com:80\":"
+      "{\"alternative_service\":[{\"advertised_versions\":[37],"
+      "\"expiration\":\"13756212000000000\",\"port\":443,"
+      "\"protocol_str\":\"quic\"}]}}],\"supports_quic\":"
+      "{\"address\":\"127.0.0.1\",\"used_quic\":true},\"version\":5}";
+
+  const base::Value* http_server_properties =
+      &pref_delegate_->GetServerProperties();
+  std::string preferences_json;
+  EXPECT_TRUE(
+      base::JSONWriter::Write(*http_server_properties, &preferences_json));
+  EXPECT_EQ(expected_json, preferences_json);
+
+  // #2: Set AlternativeService with different advertised_versions for the same
+  // AlternativeService.
+  AlternativeServiceInfoVector alternative_service_info_vector_2;
+  // Quic alternative service set with two advertised QUIC versions.
+  QuicVersionVector advertised_versions = {QUIC_VERSION_37, QUIC_VERSION_35};
+  alternative_service_info_vector_2.push_back(
+      AlternativeServiceInfo::CreateQuicAlternativeServiceInfo(
+          quic_alternative_service1, expiration1, advertised_versions));
+  ASSERT_TRUE(http_server_props_manager_->SetAlternativeServices(
+      server_www, alternative_service_info_vector_2));
+
+  // Update Prefs.
+  ExpectPrefsUpdate(1);
+  EXPECT_TRUE(net_test_task_runner_->HasPendingTask());
+  EXPECT_FALSE(pref_test_task_runner_->HasPendingTask());
+  net_test_task_runner_->FastForwardUntilNoTasksRemain();
+  EXPECT_TRUE(pref_test_task_runner_->HasPendingTask());
+  pref_test_task_runner_->FastForwardUntilNoTasksRemain();
+  EXPECT_FALSE(net_test_task_runner_->HasPendingTask());
+  EXPECT_FALSE(pref_test_task_runner_->HasPendingTask());
+
+  // Verify preferences updated with new advertised versions.
+  const char expected_json_updated[] =
+      "{\"quic_servers\":{\"https://mail.google.com:80\":"
+      "{\"server_info\":\"quic_server_info1\"}},\"servers\":["
+      "{\"https://www.google.com:80\":"
+      "{\"alternative_service\":[{\"advertised_versions\":[35,37],"
+      "\"expiration\":\"13756212000000000\",\"port\":443,"
+      "\"protocol_str\":\"quic\"}]}}],\"supports_quic\":"
+      "{\"address\":\"127.0.0.1\",\"used_quic\":true},\"version\":5}";
+  EXPECT_TRUE(
+      base::JSONWriter::Write(*http_server_properties, &preferences_json));
+  EXPECT_EQ(expected_json_updated, preferences_json);
+
+  // #3: Set AlternativeService with same advertised_versions.
+  AlternativeServiceInfoVector alternative_service_info_vector_3;
+  // A same set of QUIC versions but listed in a different order.
+  QuicVersionVector advertised_versions_2 = {QUIC_VERSION_35, QUIC_VERSION_37};
+  alternative_service_info_vector_3.push_back(
+      AlternativeServiceInfo::CreateQuicAlternativeServiceInfo(
+          quic_alternative_service1, expiration1, advertised_versions_2));
+  ASSERT_FALSE(http_server_props_manager_->SetAlternativeServices(
+      server_www, alternative_service_info_vector_3));
+
+  // No Prefs update.
+  EXPECT_FALSE(net_test_task_runner_->HasPendingTask());
+  EXPECT_FALSE(pref_test_task_runner_->HasPendingTask());
+}
+
 }  // namespace net
diff --git a/net/http/http_stream_factory.cc b/net/http/http_stream_factory.cc
index 2c7eed6..4c21ffce 100644
--- a/net/http/http_stream_factory.cc
+++ b/net/http/http_stream_factory.cc
@@ -51,19 +51,17 @@
         !IsPortValid(alternative_service_entry.port)) {
       continue;
     }
-    // Check if QUIC version is supported.
+    // Check if QUIC version is supported. Filter supported QUIC versions.
+    QuicVersionVector advertised_versions;
     if (protocol == kProtoQUIC && !alternative_service_entry.version.empty()) {
       bool match_found = false;
       for (QuicVersion supported : session->params().quic_supported_versions) {
         for (uint16_t advertised : alternative_service_entry.version) {
           if (supported == advertised) {
             match_found = true;
-            break;
+            advertised_versions.push_back(supported);
           }
         }
-        if (match_found) {
-          break;
-        }
       }
       if (!match_found) {
         continue;
@@ -75,8 +73,16 @@
     base::Time expiration =
         base::Time::Now() +
         base::TimeDelta::FromSeconds(alternative_service_entry.max_age);
-    AlternativeServiceInfo alternative_service_info(alternative_service,
-                                                    expiration);
+    AlternativeServiceInfo alternative_service_info;
+    if (protocol == kProtoQUIC) {
+      alternative_service_info =
+          AlternativeServiceInfo::CreateQuicAlternativeServiceInfo(
+              alternative_service, expiration, advertised_versions);
+    } else {
+      alternative_service_info =
+          AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
+              alternative_service, expiration);
+    }
     alternative_service_info_vector.push_back(alternative_service_info);
   }
 
diff --git a/net/http/http_stream_factory_impl_job_controller_unittest.cc b/net/http/http_stream_factory_impl_job_controller_unittest.cc
index 42b823e..1a46a95 100644
--- a/net/http/http_stream_factory_impl_job_controller_unittest.cc
+++ b/net/http/http_stream_factory_impl_job_controller_unittest.cc
@@ -157,10 +157,20 @@
       HttpStreamFactoryImpl::JobController* job_controller) {
     return job_controller->main_job_is_blocked_;
   }
+
   static bool main_job_is_resumed(
       HttpStreamFactoryImpl::JobController* job_controller) {
     return job_controller->main_job_is_resumed_;
   }
+
+  static AlternativeServiceInfo GetAlternativeServiceInfoFor(
+      HttpStreamFactoryImpl::JobController* job_controller,
+      const HttpRequestInfo& request_info,
+      HttpStreamRequest::Delegate* delegate,
+      HttpStreamRequest::StreamType stream_type) {
+    return job_controller->GetAlternativeServiceInfoFor(request_info, delegate,
+                                                        stream_type);
+  }
 };
 
 class HttpStreamFactoryImplJobControllerTest : public ::testing::Test {
@@ -253,8 +263,14 @@
     HostPortPair host_port_pair = HostPortPair::FromURL(request_info.url);
     url::SchemeHostPort server(request_info.url);
     base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
-    session_->http_server_properties()->SetAlternativeService(
-        server, alternative_service, expiration);
+    if (alternative_service.protocol == kProtoQUIC) {
+      session_->http_server_properties()->SetQuicAlternativeService(
+          server, alternative_service, expiration,
+          session_->params().quic_supported_versions);
+    } else {
+      session_->http_server_properties()->SetHttp2AlternativeService(
+          server, alternative_service, expiration);
+    }
   }
 
   void VerifyBrokenAlternateProtocolMapping(const HttpRequestInfo& request_info,
@@ -2069,6 +2085,57 @@
   EXPECT_TRUE(HttpStreamFactoryImplPeer::IsJobControllerDeleted(factory_));
 }
 
+// Test that GetAlternativeServiceInfoFor will include a list of advertised
+// versions. Returns an empty list if advertised versions are missing in
+// HttpServerProperties.
+TEST_F(HttpStreamFactoryImplJobControllerTest, GetAlternativeServiceInfoFor) {
+  HttpRequestInfo request_info;
+  request_info.method = "GET";
+  request_info.url = GURL("https://www.google.com");
+
+  Initialize(request_info);
+  url::SchemeHostPort server(request_info.url);
+  AlternativeService alternative_service(kProtoQUIC, server.host(), 443);
+  HostPortPair host_port_pair = HostPortPair::FromURL(request_info.url);
+  base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
+
+  // Set alternative service with no advertised version.
+  session_->http_server_properties()->SetQuicAlternativeService(
+      server, alternative_service, expiration, QuicVersionVector());
+
+  AlternativeServiceInfo alt_svc_info =
+      JobControllerPeer::GetAlternativeServiceInfoFor(
+          job_controller_, request_info, &request_delegate_,
+          HttpStreamRequest::HTTP_STREAM);
+  // Verify that JobController get an empty list of supported QUIC versions.
+  EXPECT_TRUE(alt_svc_info.advertised_versions().empty());
+
+  // Set alternative service for the same server with QUIC_VERSION_39 specified.
+  ASSERT_TRUE(session_->http_server_properties()->SetQuicAlternativeService(
+      server, alternative_service, expiration, {QUIC_VERSION_39}));
+
+  alt_svc_info = JobControllerPeer::GetAlternativeServiceInfoFor(
+      job_controller_, request_info, &request_delegate_,
+      HttpStreamRequest::HTTP_STREAM);
+  EXPECT_EQ(1u, alt_svc_info.advertised_versions().size());
+  // Verify that JobController returns the single version specified in set.
+  EXPECT_EQ(QUIC_VERSION_39, alt_svc_info.advertised_versions()[0]);
+
+  // Set alternative service for the same server with two QUIC versions:
+  // QUIC_VERSION_35, QUIC_VERSION_39.
+  ASSERT_TRUE(session_->http_server_properties()->SetQuicAlternativeService(
+      server, alternative_service, expiration,
+      {QUIC_VERSION_35, QUIC_VERSION_39}));
+
+  alt_svc_info = JobControllerPeer::GetAlternativeServiceInfoFor(
+      job_controller_, request_info, &request_delegate_,
+      HttpStreamRequest::HTTP_STREAM);
+  EXPECT_EQ(2u, alt_svc_info.advertised_versions().size());
+  // Verify that JobController returns the list of versions specified in set.
+  EXPECT_EQ(QUIC_VERSION_35, alt_svc_info.advertised_versions()[0]);
+  EXPECT_EQ(QUIC_VERSION_39, alt_svc_info.advertised_versions()[1]);
+}
+
 }  // namespace test
 
 }  // namespace net
diff --git a/net/http/http_stream_factory_impl_unittest.cc b/net/http/http_stream_factory_impl_unittest.cc
index a78660f..0941fdc9 100644
--- a/net/http/http_stream_factory_impl_unittest.cc
+++ b/net/http/http_stream_factory_impl_unittest.cc
@@ -1109,20 +1109,6 @@
   for (int num_streams = 1; num_streams < 3; ++num_streams) {
     GURL url = GURL("https://www.google.com");
 
-    // Set up QUIC as alternative_service.
-    HttpServerPropertiesImpl http_server_properties;
-    const AlternativeService alternative_service(kProtoQUIC, url.host().c_str(),
-                                                 url.IntPort());
-    AlternativeServiceInfoVector alternative_service_info_vector;
-    base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
-    alternative_service_info_vector.push_back(
-        AlternativeServiceInfo(alternative_service, expiration));
-    HostPortPair host_port_pair(alternative_service.host_port_pair());
-    url::SchemeHostPort server("https", host_port_pair.host(),
-                               host_port_pair.port());
-    http_server_properties.SetAlternativeServices(
-        server, alternative_service_info_vector);
-
     SpdySessionDependencies session_deps(
         ProxyService::CreateFixed("http_proxy"));
 
@@ -1131,6 +1117,18 @@
         SpdySessionDependencies::CreateSessionParams(&session_deps);
     session_params.enable_quic = true;
 
+    // Set up QUIC as alternative_service.
+    HttpServerPropertiesImpl http_server_properties;
+    const AlternativeService alternative_service(kProtoQUIC, url.host().c_str(),
+                                                 url.IntPort());
+    base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
+    HostPortPair host_port_pair(alternative_service.host_port_pair());
+    url::SchemeHostPort server("https", host_port_pair.host(),
+                               host_port_pair.port());
+    http_server_properties.SetQuicAlternativeService(
+        server, alternative_service, expiration,
+        session_params.quic_supported_versions);
+
     HttpNetworkSession::Context session_context =
         SpdySessionDependencies::CreateSessionContext(&session_deps);
     session_context.http_server_properties = &http_server_properties;
@@ -2272,12 +2270,10 @@
   void AddQuicAlternativeService() {
     const AlternativeService alternative_service(kProtoQUIC, "www.example.org",
                                                  443);
-    AlternativeServiceInfoVector alternative_service_info_vector;
     base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
-    alternative_service_info_vector.push_back(
-        AlternativeServiceInfo(alternative_service, expiration));
-    http_server_properties_.SetAlternativeServices(
-        url::SchemeHostPort(default_url_), alternative_service_info_vector);
+    http_server_properties_.SetQuicAlternativeService(
+        url::SchemeHostPort(default_url_), alternative_service, expiration,
+        session_->params().quic_supported_versions);
   }
 
   test::QuicTestPacketMaker& client_packet_maker() {
@@ -2352,8 +2348,8 @@
   socket_factory().AddSSLSocketDataProvider(&ssl_data);
 
   // Set up QUIC as alternative_service.
-  AddQuicAlternativeService();
   Initialize();
+  AddQuicAlternativeService();
 
   // Now request a stream.
   SSLConfig ssl_config;
@@ -2416,9 +2412,9 @@
   socket_factory().AddSSLSocketDataProvider(&ssl_data);
 
   // Set up QUIC as alternative_service.
-  AddQuicAlternativeService();
   DisableQuicBidirectionalStream();
   Initialize();
+  AddQuicAlternativeService();
 
   // Now request a stream.
   SSLConfig ssl_config;
@@ -2478,8 +2474,8 @@
   socket_factory().AddSSLSocketDataProvider(&ssl_data);
 
   // Set up QUIC as alternative_service.
-  AddQuicAlternativeService();
   Initialize();
+  AddQuicAlternativeService();
 
   // Now request a stream.
   SSLConfig ssl_config;
diff --git a/net/quic/chromium/quic_network_transaction_unittest.cc b/net/quic/chromium/quic_network_transaction_unittest.cc
index 5afabad..9548757 100644
--- a/net/quic/chromium/quic_network_transaction_unittest.cc
+++ b/net/quic/chromium/quic_network_transaction_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/run_loop.h"
 #include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "base/test/histogram_tester.h"
 #include "net/base/chunked_upload_data_stream.h"
@@ -127,6 +128,17 @@
   DestinationType destination_type;
 };
 
+std::string GenerateQuicVersionsListForAltSvcHeader(
+    const QuicVersionVector& versions) {
+  std::string result = "";
+  for (const QuicVersion& version : versions) {
+    if (!result.empty())
+      result.append(",");
+    result.append(base::IntToString(version));
+  }
+  return result;
+}
+
 std::vector<PoolingTestParams> GetPoolingTestParams() {
   std::vector<PoolingTestParams> params;
   QuicVersionVector all_supported_versions = AllSupportedVersions();
@@ -499,9 +511,9 @@
         std::move(headers), offset);
   }
 
-  void CreateSession() {
+  void CreateSession(const QuicVersionVector& supported_versions) {
     session_params_.enable_quic = true;
-    session_params_.quic_supported_versions = SupportedVersions(version_);
+    session_params_.quic_supported_versions = supported_versions;
 
     session_context_.quic_clock = &clock_;
     session_context_.quic_random = &random_generator_;
@@ -526,6 +538,8 @@
     session_->quic_stream_factory()->set_require_confirmation(false);
   }
 
+  void CreateSession() { return CreateSession(SupportedVersions(version_)); }
+
   void CheckWasQuicResponse(HttpNetworkTransaction* trans) {
     const HttpResponseInfo* response = trans->GetResponseInfo();
     ASSERT_TRUE(response != nullptr);
@@ -611,8 +625,9 @@
     url::SchemeHostPort server(request_.url);
     AlternativeService alternative_service(kProtoQUIC, server.host(), 443);
     base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
-    http_server_properties_.SetAlternativeService(server, alternative_service,
-                                                  expiration);
+    http_server_properties_.SetQuicAlternativeService(
+        server, alternative_service, expiration,
+        HttpNetworkSession::Params().quic_supported_versions);
   }
 
   void AddQuicRemoteAlternativeServiceMapping(
@@ -623,8 +638,9 @@
     AlternativeService alternative_service(kProtoQUIC, alternative.host(),
                                            alternative.port());
     base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
-    http_server_properties_.SetAlternativeService(server, alternative_service,
-                                                  expiration);
+    http_server_properties_.SetQuicAlternativeService(
+        server, alternative_service, expiration,
+        HttpNetworkSession::Params().quic_supported_versions);
   }
 
   void ExpectBrokenAlternateProtocolMapping() {
@@ -1187,8 +1203,9 @@
   AlternativeService alternative_service(kProtoQUIC, kDefaultServerHostName,
                                          443);
   base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
-  http_server_properties_.SetAlternativeService(server, alternative_service,
-                                                expiration);
+  http_server_properties_.SetQuicAlternativeService(
+      server, alternative_service, expiration,
+      HttpNetworkSession::Params().quic_supported_versions);
 
   // First try: The alternative job uses QUIC and reports an HTTP 421
   // Misdirected Request error.  The main job uses TCP, but |http_data| below is
@@ -1448,6 +1465,74 @@
   SendRequestAndExpectHttpResponse("hello world");
 }
 
+TEST_P(QuicNetworkTransactionTest,
+       StoreMutuallySupportedVersionsWhenProcessAltSvc) {
+  std::string advertised_versions_list_str =
+      GenerateQuicVersionsListForAltSvcHeader(AllSupportedVersions());
+  std::string altsvc_header =
+      base::StringPrintf("Alt-Svc: quic=\":443\"; v=\"%s\"\r\n\r\n",
+                         advertised_versions_list_str.c_str());
+  MockRead http_reads[] = {
+      MockRead("HTTP/1.1 200 OK\r\n"), MockRead(altsvc_header.c_str()),
+      MockRead("hello world"),
+      MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ),
+      MockRead(ASYNC, OK)};
+
+  StaticSocketDataProvider http_data(http_reads, arraysize(http_reads), nullptr,
+                                     0);
+  socket_factory_.AddSocketDataProvider(&http_data);
+  socket_factory_.AddSSLSocketDataProvider(&ssl_data_);
+
+  MockQuicData mock_quic_data;
+  QuicStreamOffset header_stream_offset = 0;
+  mock_quic_data.AddWrite(
+      ConstructInitialSettingsPacket(1, &header_stream_offset));
+  mock_quic_data.AddWrite(ConstructClientRequestHeadersPacket(
+      2, GetNthClientInitiatedStreamId(0), true, true,
+      GetRequestHeaders("GET", "https", "/"), &header_stream_offset));
+  mock_quic_data.AddRead(ConstructServerResponseHeadersPacket(
+      1, GetNthClientInitiatedStreamId(0), false, false,
+      GetResponseHeaders("200 OK")));
+  mock_quic_data.AddRead(ConstructServerDataPacket(
+      2, GetNthClientInitiatedStreamId(0), false, true, 0, "hello!"));
+  mock_quic_data.AddWrite(ConstructClientAckPacket(3, 2, 1, 1));
+  mock_quic_data.AddRead(ASYNC, ERR_IO_PENDING);  // No more data to read
+  mock_quic_data.AddRead(ASYNC, 0);               // EOF
+
+  mock_quic_data.AddSocketDataToFactory(&socket_factory_);
+
+  AddHangingNonAlternateProtocolSocketData();
+
+  // Generate a list of QUIC versions suppored by netstack.
+  QuicVersionVector current_supported_versions = SupportedVersions(version_);
+  if (version_ != QUIC_VERSION_40) {
+    current_supported_versions.push_back(QUIC_VERSION_40);
+  } else {
+    current_supported_versions.push_back(QUIC_VERSION_37);
+  }
+
+  CreateSession(current_supported_versions);
+
+  SendRequestAndExpectHttpResponse("hello world");
+  SendRequestAndExpectQuicResponse("hello!");
+
+  // Check alternative service is set with only mutually supported versions.
+  const url::SchemeHostPort https_server(request_.url);
+  const AlternativeServiceInfoVector alt_svc_info_vector =
+      session_->http_server_properties()->GetAlternativeServiceInfos(
+          https_server);
+  EXPECT_EQ(1u, alt_svc_info_vector.size());
+  EXPECT_EQ(kProtoQUIC, alt_svc_info_vector[0].alternative_service().protocol);
+  EXPECT_EQ(2u, alt_svc_info_vector[0].advertised_versions().size());
+  // Advertised versions will be lised in a sorted order.
+  std::sort(current_supported_versions.begin(),
+            current_supported_versions.end());
+  EXPECT_EQ(current_supported_versions[0],
+            alt_svc_info_vector[0].advertised_versions()[0]);
+  EXPECT_EQ(current_supported_versions[1],
+            alt_svc_info_vector[0].advertised_versions()[1]);
+}
+
 TEST_P(QuicNetworkTransactionTest, UseAlternativeServiceAllSupportedVersion) {
   std::string altsvc_header =
       base::StringPrintf("Alt-Svc: quic=\":443\"; v=\"%u\"\r\n\r\n", version_);
@@ -2777,15 +2862,17 @@
 
   // Set up alternative service for |origin1|.
   base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
-  http_server_properties_.SetAlternativeService(
+  http_server_properties_.SetQuicAlternativeService(
       url::SchemeHostPort(origin1),
-      AlternativeService(kProtoQUIC, "mail.example.com", 443), expiration);
+      AlternativeService(kProtoQUIC, "mail.example.com", 443), expiration,
+      HttpNetworkSession::Params().quic_supported_versions);
 
   // Set up alternative service for |origin2|.
   AlternativeServiceInfoVector alternative_services;
-  http_server_properties_.SetAlternativeService(
+  http_server_properties_.SetQuicAlternativeService(
       url::SchemeHostPort(origin2),
-      AlternativeService(kProtoQUIC, "www.example.com", 443), expiration);
+      AlternativeService(kProtoQUIC, "www.example.com", 443), expiration,
+      HttpNetworkSession::Params().quic_supported_versions);
   // First request opens connection to |destination1|
   // with QuicServerId.host() == origin1.host().
   SendRequestAndExpectQuicResponse("hello!");
@@ -3008,16 +3095,18 @@
   url::SchemeHostPort server(request_.url);
   AlternativeService alternative_service(kProtoQUIC, destination1, 443);
   base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
-  http_server_properties_.SetAlternativeService(server, alternative_service,
-                                                expiration);
+  http_server_properties_.SetQuicAlternativeService(
+      server, alternative_service, expiration,
+      HttpNetworkSession::Params().quic_supported_versions);
   // First request opens connection to |destination1|
   // with QuicServerId.host() == kDefaultServerHostName.
   SendRequestAndExpectQuicResponse("hello!");
 
   // Set up alternative service entry to a different destination.
   alternative_service = AlternativeService(kProtoQUIC, destination2, 443);
-  http_server_properties_.SetAlternativeService(server, alternative_service,
-                                                expiration);
+  http_server_properties_.SetQuicAlternativeService(
+      server, alternative_service, expiration,
+      HttpNetworkSession::Params().quic_supported_versions);
   // Second request pools to existing connection with same QuicServerId,
   // even though alternative service destination is different.
   SendRequestAndExpectQuicResponse("hello!");
@@ -3080,8 +3169,9 @@
   // Set up alternative service for |origin1|.
   AlternativeService alternative_service1(kProtoQUIC, destination1, 443);
   base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
-  http_server_properties_.SetAlternativeService(
-      url::SchemeHostPort(origin1), alternative_service1, expiration);
+  http_server_properties_.SetQuicAlternativeService(
+      url::SchemeHostPort(origin1), alternative_service1, expiration,
+      HttpNetworkSession::Params().quic_supported_versions);
 
   // Set up multiple alternative service entries for |origin2|,
   // the first one with a different destination as for |origin1|,
@@ -3090,9 +3180,13 @@
   AlternativeService alternative_service2(kProtoQUIC, destination2, 443);
   AlternativeServiceInfoVector alternative_services;
   alternative_services.push_back(
-      AlternativeServiceInfo(alternative_service2, expiration));
+      AlternativeServiceInfo::CreateQuicAlternativeServiceInfo(
+          alternative_service2, expiration,
+          session_->params().quic_supported_versions));
   alternative_services.push_back(
-      AlternativeServiceInfo(alternative_service1, expiration));
+      AlternativeServiceInfo::CreateQuicAlternativeServiceInfo(
+          alternative_service1, expiration,
+          session_->params().quic_supported_versions));
   http_server_properties_.SetAlternativeServices(url::SchemeHostPort(origin2),
                                                  alternative_services);
   // First request opens connection to |destination1|
@@ -4653,7 +4747,7 @@
     session_.reset();
   }
 
-  void SetAlternativeService(const std::string& origin) {
+  void SetQuicAlternativeService(const std::string& origin) {
     HostPortPair destination;
     switch (destination_type_) {
       case SAME_AS_FIRST:
@@ -4668,9 +4762,9 @@
     }
     AlternativeService alternative_service(kProtoQUIC, destination);
     base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
-    http_server_properties_.SetAlternativeService(
+    http_server_properties_.SetQuicAlternativeService(
         url::SchemeHostPort("https", origin, 443), alternative_service,
-        expiration);
+        expiration, session_->params().quic_supported_versions);
   }
 
   std::unique_ptr<QuicEncryptedPacket> ConstructClientRequestHeadersPacket(
@@ -4841,7 +4935,7 @@
   // is valid for the hostname of the alternative service.
   origin2_ = "mail.example.org";
 
-  SetAlternativeService(origin1_);
+  SetQuicAlternativeService(origin1_);
 
   scoped_refptr<X509Certificate> cert(
       ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem"));
@@ -4879,8 +4973,8 @@
   origin1_ = "mail.example.org";
   origin2_ = "news.example.org";
 
-  SetAlternativeService(origin1_);
-  SetAlternativeService(origin2_);
+  SetQuicAlternativeService(origin1_);
+  SetQuicAlternativeService(origin2_);
 
   scoped_refptr<X509Certificate> cert(
       ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem"));
@@ -4951,8 +5045,8 @@
   origin1_ = "news.example.org";
   origin2_ = "mail.example.com";
 
-  SetAlternativeService(origin1_);
-  SetAlternativeService(origin2_);
+  SetQuicAlternativeService(origin1_);
+  SetQuicAlternativeService(origin2_);
 
   scoped_refptr<X509Certificate> cert1(
       ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem"));
diff --git a/net/quic/chromium/quic_stream_factory_test.cc b/net/quic/chromium/quic_stream_factory_test.cc
index 8973f83..d97c7ce 100644
--- a/net/quic/chromium/quic_stream_factory_test.cc
+++ b/net/quic/chromium/quic_stream_factory_test.cc
@@ -487,7 +487,8 @@
     AlternativeServiceInfoVector alternative_service_info_vector;
     base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
     alternative_service_info_vector.push_back(
-        AlternativeServiceInfo(alternative_service1, expiration));
+        AlternativeServiceInfo::CreateQuicAlternativeServiceInfo(
+            alternative_service1, expiration, {version_}));
     http_server_properties_.SetAlternativeServices(
         url::SchemeHostPort(url_), alternative_service_info_vector);
 
@@ -497,7 +498,8 @@
         kProtoQUIC, host_port_pair2.host(), host_port_pair2.port());
     AlternativeServiceInfoVector alternative_service_info_vector2;
     alternative_service_info_vector2.push_back(
-        AlternativeServiceInfo(alternative_service2, expiration));
+        AlternativeServiceInfo::CreateQuicAlternativeServiceInfo(
+            alternative_service2, expiration, {version_}));
 
     http_server_properties_.SetAlternativeServices(
         server2, alternative_service_info_vector2);
diff --git a/net/spdy/chromium/spdy_session.cc b/net/spdy/chromium/spdy_session.cc
index 8f2921c..f8bb219 100644
--- a/net/spdy/chromium/spdy_session.cc
+++ b/net/spdy/chromium/spdy_session.cc
@@ -3024,19 +3024,17 @@
     // so that SpdySession::OnAltSvc and
     // HttpStreamFactory::ProcessAlternativeServices
     // could use the the same function.
-    // Check if QUIC version is supported.
+    // Check if QUIC version is supported. Filter supported QUIC versions.
+    QuicVersionVector advertised_versions;
     if (protocol == kProtoQUIC && !altsvc.version.empty()) {
       bool match_found = false;
       for (const QuicVersion& supported : quic_supported_versions_) {
         for (const uint16_t& advertised : altsvc.version) {
           if (supported == advertised) {
             match_found = true;
-            break;
+            advertised_versions.push_back(supported);
           }
         }
-        if (match_found) {
-          break;
-        }
       }
       if (!match_found) {
         continue;
@@ -3047,9 +3045,19 @@
                                                  altsvc.port);
     const base::Time expiration =
         now + base::TimeDelta::FromSeconds(altsvc.max_age);
-    alternative_service_info_vector.push_back(
-        AlternativeServiceInfo(alternative_service, expiration));
+    AlternativeServiceInfo alternative_service_info;
+    if (protocol == kProtoQUIC) {
+      alternative_service_info =
+          AlternativeServiceInfo::CreateQuicAlternativeServiceInfo(
+              alternative_service, expiration, advertised_versions);
+    } else {
+      alternative_service_info =
+          AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
+              alternative_service, expiration);
+    }
+    alternative_service_info_vector.push_back(alternative_service_info);
   }
+
   http_server_properties_->SetAlternativeServices(
       scheme_host_port, alternative_service_info_vector);
 }
diff --git a/net/ssl/openssl_ssl_util.cc b/net/ssl/openssl_ssl_util.cc
index 21b2b565..a51063d 100644
--- a/net/ssl/openssl_ssl_util.cc
+++ b/net/ssl/openssl_ssl_util.cc
@@ -173,26 +173,26 @@
       return ERR_FAILED;
     case SSL_ERROR_SSL:
       // Walk down the error stack to find an SSL or net error.
-      uint32_t error_code;
-      const char* file;
-      int line;
-      do {
-        error_code = ERR_get_error_line(&file, &line);
-        if (ERR_GET_LIB(error_code) == ERR_LIB_SSL) {
-          out_error_info->error_code = error_code;
-          out_error_info->file = file;
-          out_error_info->line = line;
-          return MapOpenSSLErrorSSL(error_code);
-        } else if (ERR_GET_LIB(error_code) == OpenSSLNetErrorLib()) {
-          out_error_info->error_code = error_code;
-          out_error_info->file = file;
-          out_error_info->line = line;
+      while (true) {
+        OpenSSLErrorInfo error_info;
+        error_info.error_code =
+            ERR_get_error_line(&error_info.file, &error_info.line);
+        if (error_info.error_code == 0) {
+          // Map errors to ERR_SSL_PROTOCOL_ERROR by default, reporting the most
+          // recent error in |*out_error_info|.
+          return ERR_SSL_PROTOCOL_ERROR;
+        }
+
+        *out_error_info = error_info;
+        if (ERR_GET_LIB(error_info.error_code) == ERR_LIB_SSL) {
+          return MapOpenSSLErrorSSL(error_info.error_code);
+        }
+        if (ERR_GET_LIB(error_info.error_code) == OpenSSLNetErrorLib()) {
           // Net error codes are negative but encoded in OpenSSL as positive
           // numbers.
-          return -ERR_GET_REASON(error_code);
+          return -ERR_GET_REASON(error_info.error_code);
         }
-      } while (error_code != 0);
-      return ERR_FAILED;
+      }
     default:
       // TODO(joth): Implement full mapping.
       LOG(WARNING) << "Unknown OpenSSL error " << err;
diff --git a/remoting/android/BUILD.gn b/remoting/android/BUILD.gn
index 07cbe53..248c1ea0 100644
--- a/remoting/android/BUILD.gn
+++ b/remoting/android/BUILD.gn
@@ -20,16 +20,6 @@
   jni_package = "remoting"
 }
 
-generate_jni_registration("remoting_jni_registration") {
-  target = ":remoting_apk"
-  output = "$root_gen_dir/remoting/client/${target_name}.h"
-  exception_files = [
-    "//base/android/java/src/org/chromium/base/library_loader/LegacyLinker.java",
-    "//base/android/java/src/org/chromium/base/library_loader/Linker.java",
-    "//base/android/java/src/org/chromium/base/library_loader/ModernLinker.java",
-  ]
-}
-
 _raw_resources_base_dir = "$target_gen_dir/credits_resources_raw/res"
 
 # The target is named this way, instead of "..._raw_resources", specifically
diff --git a/remoting/client/jni/BUILD.gn b/remoting/client/jni/BUILD.gn
index 6f10926..f0f8082 100644
--- a/remoting/client/jni/BUILD.gn
+++ b/remoting/client/jni/BUILD.gn
@@ -19,7 +19,6 @@
 shared_library("remoting_client_jni") {
   deps = [
     "//remoting/android:jni_headers",
-    "//remoting/android:remoting_jni_registration",
     "//remoting/base",
     "//remoting/client",
     "//remoting/client/display",
diff --git a/remoting/client/jni/remoting_jni_onload.cc b/remoting/client/jni/remoting_jni_onload.cc
index dd1a283d..9db79a8 100644
--- a/remoting/client/jni/remoting_jni_onload.cc
+++ b/remoting/client/jni/remoting_jni_onload.cc
@@ -11,7 +11,6 @@
 #include "base/macros.h"
 #include "net/android/net_jni_registrar.h"
 #include "remoting/client/jni/remoting_jni_registrar.h"
-#include "remoting/client/remoting_jni_registration.h"
 #include "ui/gfx/android/gfx_jni_registrar.h"
 
 namespace {
@@ -33,12 +32,6 @@
 JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
   base::android::InitVM(vm);
   JNIEnv* env = base::android::AttachCurrentThread();
-  if (!RegisterMainDexNatives(env) || !RegisterNonMainDexNatives(env)) {
-    return -1;
-  }
-
-  // TODO(agrieve): Delete this block, this is a no-op now.
-  // https://crbug.com/683256.
   if (!RegisterJNI(env) || !base::android::OnJNIOnLoadInit()) {
     return -1;
   }
diff --git a/services/ui/ws/BUILD.gn b/services/ui/ws/BUILD.gn
index 8aef9f1..5052a44 100644
--- a/services/ui/ws/BUILD.gn
+++ b/services/ui/ws/BUILD.gn
@@ -121,7 +121,7 @@
   ]
 
   deps = [
-    "//components/viz/common",
+    "//components/viz/host",
     "//gpu/command_buffer/client",
     "//gpu/command_buffer/client:gles2_interface",
     "//gpu/ipc/client",
diff --git a/services/ui/ws/DEPS b/services/ui/ws/DEPS
index 7d99cf9..d714e80d 100644
--- a/services/ui/ws/DEPS
+++ b/services/ui/ws/DEPS
@@ -1,5 +1,5 @@
 include_rules = [
-  "+components/viz/common",
+  "+components/viz/host",
   "+gpu/command_buffer/client",
   "+gpu/config",
   "+gpu/ipc/client",
diff --git a/services/ui/ws/gpu_client.cc b/services/ui/ws/gpu_client.cc
index 72e1e4a..94f38c42 100644
--- a/services/ui/ws/gpu_client.cc
+++ b/services/ui/ws/gpu_client.cc
@@ -4,7 +4,7 @@
 
 #include "services/ui/ws/gpu_client.h"
 
-#include "components/viz/common/server_gpu_memory_buffer_manager.h"
+#include "components/viz/host/server_gpu_memory_buffer_manager.h"
 #include "services/ui/gpu/interfaces/gpu_service.mojom.h"
 
 namespace {
diff --git a/services/ui/ws/gpu_host.cc b/services/ui/ws/gpu_host.cc
index 466c1ef0..0b6d0e6 100644
--- a/services/ui/ws/gpu_host.cc
+++ b/services/ui/ws/gpu_host.cc
@@ -9,7 +9,7 @@
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "components/viz/common/server_gpu_memory_buffer_manager.h"
+#include "components/viz/host/server_gpu_memory_buffer_manager.h"
 #include "gpu/ipc/client/gpu_channel_host.h"
 #include "gpu/ipc/client/gpu_memory_buffer_impl_shared_memory.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
diff --git a/skia/config/SkUserConfig.h b/skia/config/SkUserConfig.h
index 5783190..dcc693ff 100644
--- a/skia/config/SkUserConfig.h
+++ b/skia/config/SkUserConfig.h
@@ -198,6 +198,10 @@
 #   define SK_SUPPORT_LEGACY_ANISOTROPIC_MIPMAP_SCALE
 #endif
 
+#ifndef SK_SUPPORT_BLITV_FOR_BLUR_NINE
+#define SK_SUPPORT_BLITV_FOR_BLUR_NINE
+#endif
+
 // Remove this after we fixed all the issues related to the new SDF algorithm
 // (https://codereview.chromium.org/1643143002)
 #ifndef    SK_USE_LEGACY_DISTANCE_FIELDS
diff --git a/testing/android/native_test/java/src/org/chromium/native_test/MainRunner.java b/testing/android/native_test/java/src/org/chromium/native_test/MainRunner.java
index 94aaeef12..cbebb0f 100644
--- a/testing/android/native_test/java/src/org/chromium/native_test/MainRunner.java
+++ b/testing/android/native_test/java/src/org/chromium/native_test/MainRunner.java
@@ -14,14 +14,13 @@
  */
 @JNINamespace("testing::android")
 public final class MainRunner {
-    // Prevent instanciation.
+    // Prevents instantiation.
     private MainRunner() {
     }
 
     // Maps the file descriptors and executes the main method with the passed in command line.
-    public static int runMain(String[] commandLine, int[] fdsToMapKeys, int[] fdsToMapFds) {
-        return nativeRunMain(commandLine, fdsToMapKeys, fdsToMapFds);
+    public static int runMain(String[] commandLine) {
+        return nativeRunMain(commandLine);
     }
-    private static native int nativeRunMain(
-            String[] commandLine, int[] fdsToMapKeys, int[] fdsToMapFds);
+    private static native int nativeRunMain(String[] commandLine);
 }
\ No newline at end of file
diff --git a/testing/android/native_test/main_runner.cc b/testing/android/native_test/main_runner.cc
index 2dc41f3..fc993eb 100644
--- a/testing/android/native_test/main_runner.cc
+++ b/testing/android/native_test/main_runner.cc
@@ -6,7 +6,6 @@
 
 #include "base/android/jni_array.h"
 #include "base/logging.h"
-#include "base/posix/global_descriptors.h"
 #include "jni/MainRunner_jni.h"
 #include "testing/android/native_test/native_test_util.h"
 
@@ -22,9 +21,7 @@
 static jint RunMain(
     JNIEnv* env,
     const base::android::JavaParamRef<jclass>& jcaller,
-    const base::android::JavaParamRef<jobjectArray>& command_line,
-    const base::android::JavaParamRef<jintArray>& fds_to_map_keys,
-    const base::android::JavaParamRef<jintArray>& fds_to_map_fds) {
+    const base::android::JavaParamRef<jobjectArray>& command_line) {
   // Guards against process being reused.
   // In most cases, running main again will cause problems (static variables,
   // singletons, lazy instances won't be in the same state as a clean run).
@@ -32,16 +29,6 @@
   CHECK(!alreadyRun);
   alreadyRun = true;
 
-  std::vector<int> keys;
-  base::android::JavaIntArrayToIntVector(env, fds_to_map_keys, &keys);
-  std::vector<int> fds;
-  base::android::JavaIntArrayToIntVector(env, fds_to_map_fds, &fds);
-  CHECK_EQ(keys.size(), fds.size());
-
-  for (size_t i = 0; i < keys.size(); i++) {
-    base::GlobalDescriptors::GetInstance()->Set(keys[i], fds[i]);
-  }
-
   std::vector<std::string> cpp_command_line;
   AppendJavaStringArrayToStringVector(env, command_line, &cpp_command_line);
 
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl
index f305fcf..4b9eca3d 100644
--- a/testing/buildbot/gn_isolate_map.pyl
+++ b/testing/buildbot/gn_isolate_map.pyl
@@ -159,6 +159,15 @@
       "-v",
     ],
   },
+  "vrcore_fps_test": {
+    "label": "//chrome/test/vr/perf:vrcore_fps_test",
+    "type": "script",
+    "script": "//chrome/test/vr/perf/vrcore_fps/run_vrcore_fps_test.py",
+    "args": [
+      "--output-dir=${ISOLATED_OUTDIR}",
+      "-v",
+    ],
+  },
   "base_junit_tests": {
     "label": "//base:base_junit_tests",
     "type": "junit_test",
diff --git a/testing/buildbot/manage.py b/testing/buildbot/manage.py
index e733af78..8751b30 100755
--- a/testing/buildbot/manage.py
+++ b/testing/buildbot/manage.py
@@ -185,6 +185,7 @@
   'net_junit_tests',
   'service_junit_tests',
   'ui_junit_tests',
+  'vrcore_fps_test',
   'webapk_client_junit_tests',
   'webapk_shell_apk_junit_tests',
 
diff --git a/testing/test.gni b/testing/test.gni
index 64619b3..36e0d91 100644
--- a/testing/test.gni
+++ b/testing/test.gni
@@ -63,7 +63,7 @@
         # the default shared_library configs rather than executable configs.
         configs -= [
           "//build/config:shared_library_config",
-          "//build/config/android:hide_all_but_jni",
+          "//build/config/android:hide_all_but_jni_onload",
         ]
         configs += [ "//build/config:executable_config" ]
 
@@ -321,8 +321,6 @@
 set_defaults("test") {
   if (is_android) {
     configs = default_shared_library_configs
-    configs -= [ "//build/config/android:hide_all_but_jni_onload" ]
-    configs += [ "//build/config/android:hide_all_but_jni" ]
   } else {
     configs = default_executable_configs
   }
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
index 452e975c..b2fefadb 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
@@ -1801,7 +1801,6 @@
 crbug.com/591099 css3/flexbox/child-overflow.html [ Failure Pass ]
 crbug.com/591099 css3/flexbox/crash-removing-out-of-flow-child.html [ Failure ]
 crbug.com/591099 css3/flexbox/css-properties.html [ Failure ]
-crbug.com/591099 css3/flexbox/definite-cross-sizes.html [ Failure ]
 crbug.com/591099 css3/flexbox/display-flexbox-set-get.html [ Crash ]
 crbug.com/591099 css3/flexbox/flex-algorithm-with-margins.html [ Failure ]
 crbug.com/591099 css3/flexbox/flex-algorithm.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-network-service b/third_party/WebKit/LayoutTests/FlagExpectations/enable-network-service
index ae4c9ff..bce3d22 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-network-service
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-network-service
@@ -2666,6 +2666,7 @@
 Bug(none) inspector-protocol/accessibility/accessibility-nameSources-buttons.js [ Timeout ]
 Bug(none) inspector-protocol/css/css-get-background-colors.html [ Timeout ]
 Bug(none) inspector-protocol/css/cssom-modify-rule-and-get-rule-list.html [ Timeout ]
+Bug(none) inspector-protocol/dom/dom-request-document-with-child-nodes.js [ Failure ]
 Bug(none) inspector-protocol/heap-profiler/heap-samples-in-snapshot.html [ Failure ]
 Bug(none) inspector-protocol/heap-profiler/heap-snapshot-with-active-dom-object.html [ Failure ]
 Bug(none) inspector-protocol/heap-profiler/heap-snapshot-with-detached-dom-tree.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/fixed-element-in-isolated-composited-ancestor-expected.html b/third_party/WebKit/LayoutTests/compositing/overflow/fixed-element-in-isolated-composited-ancestor-expected.html
new file mode 100644
index 0000000..6710eba
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/compositing/overflow/fixed-element-in-isolated-composited-ancestor-expected.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<style>
+body {
+  margin: 0px;
+}
+
+.scroller {
+  overflow: scroll;
+  width: 200px;
+  height: 600px;
+}
+
+.box {
+  background: rgba(0, 255, 0, 0.9);
+  position: relative;
+  width: 185px;
+  height: 50px;
+  top: 200px;
+}
+
+.indicator {
+  position: absolute;
+  width: 185px;
+  height: 50px;
+  background: red; /* covered up by .box when working */
+}
+
+.container {
+  width: 100%;
+  height: 1000px;
+  background: grey;
+}
+</style>
+
+<div id="scroller" class="scroller">
+  <div class="container">
+    <div class="indicator"></div>
+    <div class="box"></div>
+  </div>
+</div>
+
+<script>
+  let scroller = document.getElementById('scroller');
+  scroller.scrollTop = 200;
+</script>
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/fixed-element-in-isolated-composited-ancestor.html b/third_party/WebKit/LayoutTests/compositing/overflow/fixed-element-in-isolated-composited-ancestor.html
new file mode 100644
index 0000000..e532a68
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/compositing/overflow/fixed-element-in-isolated-composited-ancestor.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<style>
+body {
+  margin: 0px;
+}
+
+.scroller {
+  overflow: scroll;
+  width: 200px;
+  height: 600px;
+}
+
+.composited {
+  backface-visibility: hidden;
+  isolation: isolate;
+}
+
+.box {
+  background: rgba(0, 255, 0, 0.9);
+  position: fixed;
+  width: 185px;
+  height: 50px;
+  top: 0px;
+}
+
+.indicator {
+  position: absolute;
+  width: 185px;
+  height: 50px;
+  background: red; /* covered up by .box when working */
+}
+
+.container {
+  width: 100%;
+  height: 1000px;
+  background: grey;
+}
+</style>
+
+<div id="scroller" class="scroller">
+  <div class="composited container">
+    <div class="indicator"></div>
+    <div class="box"></div>
+  </div>
+</div>
+
+<script>
+  if (window.testRunner)
+    testRunner.waitUntilDone()
+
+  function doTest() {
+    let scroller = document.getElementById('scroller');
+    window.requestAnimationFrame(function() {
+      scroller.scrollTop = 200;
+      if (window.testRunner)
+        testRunner.notifyDone();
+    });
+  }
+
+  window.addEventListener('load', function() {
+    window.requestAnimationFrame(function() {
+      window.requestAnimationFrame(doTest);
+    })
+  });
+</script>
+
diff --git a/third_party/WebKit/LayoutTests/css3/flexbox/definite-cross-sizes.html b/third_party/WebKit/LayoutTests/css3/flexbox/definite-cross-sizes.html
deleted file mode 100644
index 9ab6f07..0000000
--- a/third_party/WebKit/LayoutTests/css3/flexbox/definite-cross-sizes.html
+++ /dev/null
@@ -1,130 +0,0 @@
-<!DOCTYPE html>
-
-<title>CSS Flexbox: Definite cross sizes</title>
-
-<style>
-.rect {
-  width: 50px;
-  height: 50px;
-  background-color: green;
-}
-
-.flexbox {
-  width: 50px;
-  outline: 3px solid black;
-}
-
-.flexbox > div > div {
-  overflow: hidden;
-}
-</style>
-
-<link rel="stylesheet" href="resources/flexbox.css">
-<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#definite-sizes">
-<link rel="author" title="Google Inc." href="https://www.google.com/">
-
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../resources/check-layout-th.js"></script>
-
-<body onload="checkLayout('.flexbox')" style="height: 800px;">
-<div id=log></div>
-
-<p>This test verifies that we consider flex items' cross sizes to be definite
-if the align value is stretch (the default)</p>
-
-<p>Tests that we get a definite size in the simple case.</p>
-<div class="flexbox" data-expected-height="50">
-  <div data-expected-height="50">
-    <div style="height: 50%" data-expected-height="25">
-      <div class="rect" data-expected-height="50"></div>
-    </div>
-  </div>
-</div>
-
-<p>Tests that we get an definite size in a wrapping flexbox</p>
-<div class="flexbox wrap" style="height: 50px;" data-expected-height="50">
-  <div data-expected-height="50">
-    <div style="height: 50%" data-expected-height="25">
-      <div class="rect" data-expected-height="50"></div>
-    </div>
-  </div>
-</div>
-
-<p>Tests that we get an indefinite size when not stretch-aligning</p>
-<div class="flexbox wrap" style="height: 50px;" data-expected-height="50">
-  <div class="align-self-flex-start" data-expected-height="50">
-    <div style="height: 50%" data-expected-height="50">
-      <div class="rect" data-expected-height="50"></div>
-    </div>
-  </div>
-</div>
-
-<p>Tests that we get an definite size in a definite-height flexbox</p>
-<div class="flexbox" style="height: 50px;" data-expected-height="50">
-  <div data-expected-height="50">
-    <div style="height: 50%" data-expected-height="25">
-      <div class="rect" data-expected-height="50"></div>
-    </div>
-  </div>
-</div>
-
-<p>Tests that we get an definite size in a nested flexbox where only the outer one has an explicit height</p>
-<div class="flexbox" style="height: 50px;" data-expected-height="50">
-  <div class="flexbox" data-expected-height="50">
-    <div data-expected-height="50">
-      <div style="height: 50%" data-expected-height="25">
-        <div class="rect" data-expected-height="50"></div>
-      </div>
-    </div>
-  </div>
-</div>
-
-<p>Tests that we get an definite size in a nested flexbox where only the outer one has an explicit height and has an opposite direction.</p>
-<div class="flexbox" style="height: 50px;" data-expected-height="50">
-  <div class="flexbox column" data-expected-height="50">
-    <div class="flex-one" data-expected-height="50">
-      <div style="height: 50%" data-expected-height="25">
-        <div class="rect" data-expected-height="50"></div>
-      </div>
-    </div>
-  </div>
-</div>
-
-<p>Tests that we respect min-height</p>
-<div class="flexbox" style="height: 50px;" data-expected-height="50">
-  <div data-expected-height="50">
-    <div style="height: 50%; min-height: 30px;" data-expected-height="30">
-      <div class="rect" data-expected-height="50"></div>
-    </div>
-  </div>
-</div>
-
-<p>Tests that percentage sizes can also be definite</p>
-<div class="flexbox" style="height: 10%;" data-expected-height="80">
-  <div data-expected-height="80">
-    <div style="height: 50%" data-expected-height="40">
-      <div class="rect" data-expected-height="50"></div>
-    </div>
-  </div>
-</div>
-
-<p>Tests that we use a definite size even when a percentage size is not definite</p>
-<div>
-  <div class="flexbox" style="height: 10%;" data-expected-height="50">
-    <div data-expected-height="50">
-      <div style="height: 50%" data-expected-height="25">
-        <div class="rect" data-expected-height="50"></div>
-      </div>
-    </div>
-  </div>
-</div>
-
-<p>Tests that we don't mix up widths and heights</p>
-<div class="flexbox" style="height: 50px; width: 100px;" data-expected-height="50">
-  <div style="width: 100px;" data-expected-height="50" data-expected-width="100">
-    <div style="width: 50%" data-expected-width="50">
-      <div class="rect" data-expected-height="50" data-expected-width="50"></div>
-    </div>
-  </div>
-</div>
diff --git a/third_party/WebKit/Source/core/inspector/browser_protocol.json b/third_party/WebKit/Source/core/inspector/browser_protocol.json
index dc0dd2e..46c04fc7 100644
--- a/third_party/WebKit/Source/core/inspector/browser_protocol.json
+++ b/third_party/WebKit/Source/core/inspector/browser_protocol.json
@@ -2834,7 +2834,7 @@
     {
         "domain": "CSS",
         "experimental": true,
-        "description": "This domain exposes CSS read/write operations. All CSS objects (stylesheets, rules, and styles) have an associated <code>id</code> used in subsequent operations on the related object. Each object type has a specific <code>id</code> structure, and those are not interchangeable between objects of different kinds. CSS objects can be loaded using the <code>get*ForNode()</code> calls (which accept a DOM node id). A client can also discover all the existing stylesheets with the <code>getAllStyleSheets()</code> method (or keeping track of the <code>styleSheetAdded</code>/<code>styleSheetRemoved</code> events) and subsequently load the required stylesheet contents using the <code>getStyleSheet[Text]()</code> methods.",
+        "description": "This domain exposes CSS read/write operations. All CSS objects (stylesheets, rules, and styles) have an associated <code>id</code> used in subsequent operations on the related object. Each object type has a specific <code>id</code> structure, and those are not interchangeable between objects of different kinds. CSS objects can be loaded using the <code>get*ForNode()</code> calls (which accept a DOM node id). A client can also keep track of stylesheets via the <code>styleSheetAdded</code>/<code>styleSheetRemoved</code> events and subsequently load the required stylesheet contents using the <code>getStyleSheet[Text]()</code> methods.",
         "dependencies": ["DOM"],
         "types": [
             {
diff --git a/third_party/WebKit/Source/core/layout/LayoutTableCell.cpp b/third_party/WebKit/Source/core/layout/LayoutTableCell.cpp
index 77745e18..a749b9a9 100644
--- a/third_party/WebKit/Source/core/layout/LayoutTableCell.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutTableCell.cpp
@@ -1083,54 +1083,6 @@
              : LayoutBlockFlow::BorderAfter();
 }
 
-unsigned LayoutTableCell::CollapsedBorderHalfLeft(bool outer) const {
-  const ComputedStyle& style_for_cell_order = TableStyle();
-  if (style_for_cell_order.IsHorizontalWritingMode()) {
-    return style_for_cell_order.IsLeftToRightDirection()
-               ? CollapsedBorderHalfStart(outer)
-               : CollapsedBorderHalfEnd(outer);
-  }
-  return style_for_cell_order.IsFlippedBlocksWritingMode()
-             ? CollapsedBorderHalfAfter(outer)
-             : CollapsedBorderHalfBefore(outer);
-}
-
-unsigned LayoutTableCell::CollapsedBorderHalfRight(bool outer) const {
-  const ComputedStyle& style_for_cell_order = TableStyle();
-  if (style_for_cell_order.IsHorizontalWritingMode()) {
-    return style_for_cell_order.IsLeftToRightDirection()
-               ? CollapsedBorderHalfEnd(outer)
-               : CollapsedBorderHalfStart(outer);
-  }
-  return style_for_cell_order.IsFlippedBlocksWritingMode()
-             ? CollapsedBorderHalfBefore(outer)
-             : CollapsedBorderHalfAfter(outer);
-}
-
-unsigned LayoutTableCell::CollapsedBorderHalfTop(bool outer) const {
-  const ComputedStyle& style_for_cell_order = TableStyle();
-  if (style_for_cell_order.IsHorizontalWritingMode()) {
-    return style_for_cell_order.IsFlippedBlocksWritingMode()
-               ? CollapsedBorderHalfAfter(outer)
-               : CollapsedBorderHalfBefore(outer);
-  }
-  return style_for_cell_order.IsLeftToRightDirection()
-             ? CollapsedBorderHalfStart(outer)
-             : CollapsedBorderHalfEnd(outer);
-}
-
-unsigned LayoutTableCell::CollapsedBorderHalfBottom(bool outer) const {
-  const ComputedStyle& style_for_cell_order = TableStyle();
-  if (style_for_cell_order.IsHorizontalWritingMode()) {
-    return style_for_cell_order.IsFlippedBlocksWritingMode()
-               ? CollapsedBorderHalfBefore(outer)
-               : CollapsedBorderHalfAfter(outer);
-  }
-  return style_for_cell_order.IsLeftToRightDirection()
-             ? CollapsedBorderHalfEnd(outer)
-             : CollapsedBorderHalfStart(outer);
-}
-
 unsigned LayoutTableCell::CollapsedBorderHalfStart(bool outer) const {
   UpdateCollapsedBorderValues();
   const auto* collapsed_border_values = this->GetCollapsedBorderValues();
diff --git a/third_party/WebKit/Source/core/layout/LayoutTableCell.h b/third_party/WebKit/Source/core/layout/LayoutTableCell.h
index 32e7ed1..72a17e8 100644
--- a/third_party/WebKit/Source/core/layout/LayoutTableCell.h
+++ b/third_party/WebKit/Source/core/layout/LayoutTableCell.h
@@ -33,6 +33,7 @@
 #include "core/layout/LayoutTableRow.h"
 #include "core/layout/LayoutTableSection.h"
 #include "platform/LengthFunctions.h"
+#include "platform/text/WritingModeUtils.h"
 
 namespace blink {
 
@@ -359,10 +360,26 @@
   void ComputeOverflow(LayoutUnit old_client_after_edge,
                        bool recompute_floats = false) override;
 
-  unsigned CollapsedBorderHalfLeft(bool outer) const;
-  unsigned CollapsedBorderHalfRight(bool outer) const;
-  unsigned CollapsedBorderHalfTop(bool outer) const;
-  unsigned CollapsedBorderHalfBottom(bool outer) const;
+  LogicalToPhysical<unsigned> LogicalCollapsedBorderHalfToPhysical(
+      bool outer) const {
+    return LogicalToPhysical<unsigned>(
+        TableStyle().GetWritingMode(), TableStyle().Direction(),
+        CollapsedBorderHalfStart(outer), CollapsedBorderHalfEnd(outer),
+        CollapsedBorderHalfBefore(outer), CollapsedBorderHalfAfter(outer));
+  }
+
+  unsigned CollapsedBorderHalfLeft(bool outer) const {
+    return LogicalCollapsedBorderHalfToPhysical(outer).Left();
+  }
+  unsigned CollapsedBorderHalfRight(bool outer) const {
+    return LogicalCollapsedBorderHalfToPhysical(outer).Right();
+  }
+  unsigned CollapsedBorderHalfTop(bool outer) const {
+    return LogicalCollapsedBorderHalfToPhysical(outer).Top();
+  }
+  unsigned CollapsedBorderHalfBottom(bool outer) const {
+    return LogicalCollapsedBorderHalfToPhysical(outer).Bottom();
+  }
 
   // For the following methods, the 'start', 'end', 'before', 'after' directions
   // are all in the table's inline and block directions.
diff --git a/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp b/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp
index 661ba01b6..afd9277 100644
--- a/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp
+++ b/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp
@@ -877,6 +877,16 @@
     subpixel_accumulation = offset_from_composited_ancestor -
                             snapped_offset_from_composited_ancestor;
   }
+
+  // Invalidate the whole layer when subpixel accumulation changes, since
+  // the previous subpixel accumulation is baked into the dispay list.
+  // However, don't do so for directly composited layers, to avoid impacting
+  // performance.
+  if (subpixel_accumulation != owning_layer_.SubpixelAccumulation() &&
+      !(owning_layer_.GetCompositingReasons() &
+        kCompositingReasonComboAllDirectReasons))
+    SetContentsNeedDisplay();
+
   // Otherwise discard the sub-pixel remainder because paint offset can't be
   // transformed by a non-translation transform.
   owning_layer_.SetSubpixelAccumulation(subpixel_accumulation);
diff --git a/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMappingTest.cpp b/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMappingTest.cpp
index 8f6387e..5ffe801 100644
--- a/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMappingTest.cpp
+++ b/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMappingTest.cpp
@@ -74,6 +74,50 @@
 
 INSTANTIATE_TEST_CASE_P(All, CompositedLayerMappingTest, ::testing::Bool());
 
+TEST_P(CompositedLayerMappingTest, SubpixelAccumulationChange) {
+  SetBodyInnerHTML(
+      "<div id='target' style='will-change: transform; background: lightblue; "
+      "position: relative; left: 0.4px; width: 100px; height: 100px'>");
+
+  Element* target = GetDocument().getElementById("target");
+  target->SetInlineStyleProperty(CSSPropertyLeft, "0.6px");
+
+  GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint();
+
+  PaintLayer* paint_layer =
+      ToLayoutBoxModelObject(target->GetLayoutObject())->Layer();
+  // Directly composited layers are not invalidated on subpixel accumulation
+  // change.
+  EXPECT_FALSE(paint_layer->GraphicsLayerBacking()
+                   ->GetPaintController()
+                   .GetPaintArtifact()
+                   .IsEmpty());
+}
+
+TEST_P(CompositedLayerMappingTest,
+       SubpixelAccumulationChangeIndirectCompositing) {
+  SetBodyInnerHTML(
+      "<div style='position; relative; width: 100px; height: 100px;"
+      "    background: lightgray; will-change: transform'></div>"
+      "<div id='target' style='background: lightblue; "
+      "    position: relative; top: -10px; left: 0.4px; width: 100px;"
+      "    height: 100px'></div>");
+
+  Element* target = GetDocument().getElementById("target");
+  target->SetInlineStyleProperty(CSSPropertyLeft, "0.6px");
+
+  GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint();
+
+  PaintLayer* paint_layer =
+      ToLayoutBoxModelObject(target->GetLayoutObject())->Layer();
+  // The PaintArtifact should have been deleted because paint was
+  // invalidated for subpixel accumulation change.
+  EXPECT_FALSE(paint_layer->GraphicsLayerBacking()
+                   ->GetPaintController()
+                   .GetPaintArtifact()
+                   .IsEmpty());
+}
+
 TEST_P(CompositedLayerMappingTest, SimpleInterestRect) {
   SetBodyInnerHTML(
       "<div id='target' style='width: 200px; height: 200px; will-change: "
diff --git a/third_party/WebKit/Source/core/style/ComputedStyle.cpp b/third_party/WebKit/Source/core/style/ComputedStyle.cpp
index 1bb99f9..e680fee 100644
--- a/third_party/WebKit/Source/core/style/ComputedStyle.cpp
+++ b/third_party/WebKit/Source/core/style/ComputedStyle.cpp
@@ -1853,128 +1853,6 @@
                unvisited_color.Alpha());
 }
 
-BorderValue ComputedStyle::BorderBeforeUsing(const ComputedStyle& other) const {
-  switch (other.GetWritingMode()) {
-    case WritingMode::kHorizontalTb:
-      return BorderTop();
-    case WritingMode::kVerticalLr:
-      return BorderLeft();
-    case WritingMode::kVerticalRl:
-      return BorderRight();
-  }
-  NOTREACHED();
-  return BorderTop();
-}
-
-BorderValue ComputedStyle::BorderAfterUsing(const ComputedStyle& other) const {
-  switch (other.GetWritingMode()) {
-    case WritingMode::kHorizontalTb:
-      return BorderBottom();
-    case WritingMode::kVerticalLr:
-      return BorderRight();
-    case WritingMode::kVerticalRl:
-      return BorderLeft();
-  }
-  NOTREACHED();
-  return BorderBottom();
-}
-
-BorderValue ComputedStyle::BorderStartUsing(const ComputedStyle& other) const {
-  if (other.IsHorizontalWritingMode())
-    return other.IsLeftToRightDirection() ? BorderLeft() : BorderRight();
-  return other.IsLeftToRightDirection() ? BorderTop() : BorderBottom();
-}
-
-BorderValue ComputedStyle::BorderEndUsing(const ComputedStyle& other) const {
-  if (other.IsHorizontalWritingMode())
-    return other.IsLeftToRightDirection() ? BorderRight() : BorderLeft();
-  return other.IsLeftToRightDirection() ? BorderBottom() : BorderTop();
-}
-
-float ComputedStyle::BorderBeforeWidth() const {
-  switch (GetWritingMode()) {
-    case WritingMode::kHorizontalTb:
-      return BorderTopWidth();
-    case WritingMode::kVerticalLr:
-      return BorderLeftWidth();
-    case WritingMode::kVerticalRl:
-      return BorderRightWidth();
-  }
-  NOTREACHED();
-  return BorderTopWidth();
-}
-
-float ComputedStyle::BorderAfterWidth() const {
-  switch (GetWritingMode()) {
-    case WritingMode::kHorizontalTb:
-      return BorderBottomWidth();
-    case WritingMode::kVerticalLr:
-      return BorderRightWidth();
-    case WritingMode::kVerticalRl:
-      return BorderLeftWidth();
-  }
-  NOTREACHED();
-  return BorderBottomWidth();
-}
-
-float ComputedStyle::BorderStartWidth() const {
-  if (IsHorizontalWritingMode())
-    return IsLeftToRightDirection() ? BorderLeftWidth() : BorderRightWidth();
-  return IsLeftToRightDirection() ? BorderTopWidth() : BorderBottomWidth();
-}
-
-float ComputedStyle::BorderEndWidth() const {
-  if (IsHorizontalWritingMode())
-    return IsLeftToRightDirection() ? BorderRightWidth() : BorderLeftWidth();
-  return IsLeftToRightDirection() ? BorderBottomWidth() : BorderTopWidth();
-}
-
-float ComputedStyle::BorderOverWidth() const {
-  return IsHorizontalWritingMode() ? BorderTopWidth() : BorderRightWidth();
-}
-
-float ComputedStyle::BorderUnderWidth() const {
-  return IsHorizontalWritingMode() ? BorderBottomWidth() : BorderLeftWidth();
-}
-
-EBorderStyle ComputedStyle::BorderBeforeStyle() const {
-  switch (GetWritingMode()) {
-    case WritingMode::kHorizontalTb:
-      return BorderTopStyle();
-    case WritingMode::kVerticalLr:
-      return BorderLeftStyle();
-    case WritingMode::kVerticalRl:
-      return BorderRightStyle();
-  }
-  NOTREACHED();
-  return BorderTopStyle();
-}
-
-EBorderStyle ComputedStyle::BorderAfterStyle() const {
-  switch (GetWritingMode()) {
-    case WritingMode::kHorizontalTb:
-      return BorderBottomStyle();
-    case WritingMode::kVerticalLr:
-      return BorderRightStyle();
-    case WritingMode::kVerticalRl:
-      return BorderLeftStyle();
-  }
-  NOTREACHED();
-  return BorderBottomStyle();
-}
-
-EBorderStyle ComputedStyle::BorderStartStyle() const {
-  if (IsHorizontalWritingMode())
-    return IsLeftToRightDirection() ? BorderLeftStyle() : BorderRightStyle();
-  return IsLeftToRightDirection() ? BorderTopStyle() : BorderBottomStyle();
-}
-
-EBorderStyle ComputedStyle::BorderEndStyle() const {
-  if (IsHorizontalWritingMode())
-    return IsLeftToRightDirection() ? BorderRightStyle() : BorderLeftStyle();
-  return IsLeftToRightDirection() ? BorderBottomStyle() : BorderTopStyle();
-}
-
 void ComputedStyle::SetMarginStart(const Length& margin) {
   if (IsHorizontalWritingMode()) {
     if (IsLeftToRightDirection())
diff --git a/third_party/WebKit/Source/core/style/ComputedStyle.h b/third_party/WebKit/Source/core/style/ComputedStyle.h
index c553c43..9958459 100644
--- a/third_party/WebKit/Source/core/style/ComputedStyle.h
+++ b/third_party/WebKit/Source/core/style/ComputedStyle.h
@@ -75,6 +75,7 @@
 #include "platform/text/TabSize.h"
 #include "platform/text/TextDirection.h"
 #include "platform/text/UnicodeBidi.h"
+#include "platform/text/WritingModeUtils.h"
 #include "platform/transforms/TransformOperations.h"
 #include "platform/wtf/Forward.h"
 #include "platform/wtf/LeakAnnotations.h"
@@ -1552,28 +1553,22 @@
   const Length& MarginStart() const { return MarginStartUsing(this); }
   const Length& MarginEnd() const { return MarginEndUsing(this); }
   const Length& MarginOver() const {
-    return LengthBox::Over(GetWritingMode(), MarginTop(), MarginRight());
+    return PhysicalMarginToLogical(*this).Over();
   }
   const Length& MarginUnder() const {
-    return LengthBox::Under(GetWritingMode(), MarginBottom(), MarginLeft());
+    return PhysicalMarginToLogical(*this).Under();
   }
   const Length& MarginStartUsing(const ComputedStyle* other) const {
-    return LengthBox::Start(other->GetWritingMode(), other->Direction(),
-                            MarginTop(), MarginLeft(), MarginRight(),
-                            MarginBottom());
+    return PhysicalMarginToLogical(*other).Start();
   }
   const Length& MarginEndUsing(const ComputedStyle* other) const {
-    return LengthBox::End(other->GetWritingMode(), other->Direction(),
-                          MarginTop(), MarginLeft(), MarginRight(),
-                          MarginBottom());
+    return PhysicalMarginToLogical(*other).End();
   }
   const Length& MarginBeforeUsing(const ComputedStyle* other) const {
-    return LengthBox::Before(other->GetWritingMode(), MarginTop(), MarginLeft(),
-                             MarginRight());
+    return PhysicalMarginToLogical(*other).Before();
   }
   const Length& MarginAfterUsing(const ComputedStyle* other) const {
-    return LengthBox::After(other->GetWritingMode(), MarginBottom(),
-                            MarginLeft(), MarginRight());
+    return PhysicalMarginToLogical(*other).After();
   }
   void SetMarginStart(const Length&);
   void SetMarginEnd(const Length&);
@@ -1586,26 +1581,20 @@
 
   // Padding utility functions.
   const Length& PaddingBefore() const {
-    return LengthBox::Before(GetWritingMode(), PaddingTop(), PaddingLeft(),
-                             PaddingRight());
+    return PhysicalPaddingToLogical().Before();
   }
   const Length& PaddingAfter() const {
-    return LengthBox::After(GetWritingMode(), PaddingBottom(), PaddingLeft(),
-                            PaddingRight());
+    return PhysicalPaddingToLogical().After();
   }
   const Length& PaddingStart() const {
-    return LengthBox::Start(GetWritingMode(), Direction(), PaddingTop(),
-                            PaddingLeft(), PaddingRight(), PaddingBottom());
+    return PhysicalPaddingToLogical().Start();
   }
-  const Length& PaddingEnd() const {
-    return LengthBox::End(GetWritingMode(), Direction(), PaddingTop(),
-                          PaddingLeft(), PaddingRight(), PaddingBottom());
-  }
+  const Length& PaddingEnd() const { return PhysicalPaddingToLogical().End(); }
   const Length& PaddingOver() const {
-    return LengthBox::Over(GetWritingMode(), PaddingTop(), PaddingRight());
+    return PhysicalPaddingToLogical().Over();
   }
   const Length& PaddingUnder() const {
-    return LengthBox::Under(GetWritingMode(), PaddingBottom(), PaddingLeft());
+    return PhysicalPaddingToLogical().Under();
   }
   bool HasPadding() const {
     return !PaddingLeft().IsZero() || !PaddingRight().IsZero() ||
@@ -1669,27 +1658,53 @@
            BorderBottomWidthInternal() == o.BorderBottomWidthInternal();
   }
 
-  BorderValue BorderBeforeUsing(const ComputedStyle& other) const;
-  BorderValue BorderAfterUsing(const ComputedStyle& other) const;
-  BorderValue BorderStartUsing(const ComputedStyle& other) const;
-  BorderValue BorderEndUsing(const ComputedStyle& other) const;
+  BorderValue BorderBeforeUsing(const ComputedStyle& other) const {
+    return PhysicalBorderToLogical(other).Before();
+  }
+  BorderValue BorderAfterUsing(const ComputedStyle& other) const {
+    return PhysicalBorderToLogical(other).After();
+  }
+  BorderValue BorderStartUsing(const ComputedStyle& other) const {
+    return PhysicalBorderToLogical(other).Start();
+  }
+  BorderValue BorderEndUsing(const ComputedStyle& other) const {
+    return PhysicalBorderToLogical(other).End();
+  }
 
   BorderValue BorderBefore() const { return BorderBeforeUsing(*this); }
   BorderValue BorderAfter() const { return BorderAfterUsing(*this); }
   BorderValue BorderStart() const { return BorderStartUsing(*this); }
   BorderValue BorderEnd() const { return BorderEndUsing(*this); }
 
-  float BorderAfterWidth() const;
-  float BorderBeforeWidth() const;
-  float BorderEndWidth() const;
-  float BorderStartWidth() const;
-  float BorderOverWidth() const;
-  float BorderUnderWidth() const;
+  float BorderAfterWidth() const {
+    return PhysicalBorderWidthToLogical().After();
+  }
+  float BorderBeforeWidth() const {
+    return PhysicalBorderWidthToLogical().Before();
+  }
+  float BorderEndWidth() const { return PhysicalBorderWidthToLogical().End(); }
+  float BorderStartWidth() const {
+    return PhysicalBorderWidthToLogical().Start();
+  }
+  float BorderOverWidth() const {
+    return PhysicalBorderWidthToLogical().Over();
+  }
+  float BorderUnderWidth() const {
+    return PhysicalBorderWidthToLogical().Under();
+  }
 
-  EBorderStyle BorderAfterStyle() const;
-  EBorderStyle BorderBeforeStyle() const;
-  EBorderStyle BorderEndStyle() const;
-  EBorderStyle BorderStartStyle() const;
+  EBorderStyle BorderAfterStyle() const {
+    return PhysicalBorderStyleToLogical().After();
+  }
+  EBorderStyle BorderBeforeStyle() const {
+    return PhysicalBorderStyleToLogical().Before();
+  }
+  EBorderStyle BorderEndStyle() const {
+    return PhysicalBorderStyleToLogical().End();
+  }
+  EBorderStyle BorderStartStyle() const {
+    return PhysicalBorderStyleToLogical().Start();
+  }
 
   bool HasBorderFill() const {
     return BorderImage().HasImage() && BorderImage().Fill();
@@ -1950,16 +1965,16 @@
   // Offset utility functions.
   // Accessors for positioned object edges that take into account writing mode.
   const Length& LogicalLeft() const {
-    return LengthBox::LogicalLeft(GetWritingMode(), Left(), Top());
+    return PhysicalBoundsToLogical().LineLeft();
   }
   const Length& LogicalRight() const {
-    return LengthBox::LogicalRight(GetWritingMode(), Right(), Bottom());
+    return PhysicalBoundsToLogical().LineRight();
   }
   const Length& LogicalTop() const {
-    return LengthBox::Before(GetWritingMode(), Top(), Left(), Right());
+    return PhysicalBoundsToLogical().Before();
   }
   const Length& LogicalBottom() const {
-    return LengthBox::After(GetWritingMode(), Bottom(), Left(), Right());
+    return PhysicalBoundsToLogical().After();
   }
   bool OffsetEqual(const ComputedStyle& other) const {
     return Left() == other.Left() && Right() == other.Right() &&
@@ -2574,6 +2589,43 @@
   StyleInheritedVariables& MutableInheritedVariables();
   StyleNonInheritedVariables& MutableNonInheritedVariables();
 
+  PhysicalToLogical<const Length&> PhysicalMarginToLogical(
+      const ComputedStyle& other) const {
+    return PhysicalToLogical<const Length&>(
+        other.GetWritingMode(), other.Direction(), MarginTop(), MarginRight(),
+        MarginBottom(), MarginLeft());
+  }
+
+  PhysicalToLogical<const Length&> PhysicalPaddingToLogical() const {
+    return PhysicalToLogical<const Length&>(GetWritingMode(), Direction(),
+                                            PaddingTop(), PaddingRight(),
+                                            PaddingBottom(), PaddingLeft());
+  }
+
+  PhysicalToLogical<BorderValue> PhysicalBorderToLogical(
+      const ComputedStyle& other) const {
+    return PhysicalToLogical<BorderValue>(
+        other.GetWritingMode(), other.Direction(), BorderTop(), BorderRight(),
+        BorderBottom(), BorderLeft());
+  }
+
+  PhysicalToLogical<float> PhysicalBorderWidthToLogical() const {
+    return PhysicalToLogical<float>(GetWritingMode(), Direction(),
+                                    BorderTopWidth(), BorderRightWidth(),
+                                    BorderBottomWidth(), BorderLeftWidth());
+  }
+
+  PhysicalToLogical<EBorderStyle> PhysicalBorderStyleToLogical() const {
+    return PhysicalToLogical<EBorderStyle>(
+        GetWritingMode(), Direction(), BorderTopStyle(), BorderRightStyle(),
+        BorderBottomStyle(), BorderLeftStyle());
+  }
+
+  PhysicalToLogical<const Length&> PhysicalBoundsToLogical() const {
+    return PhysicalToLogical<const Length&>(GetWritingMode(), Direction(),
+                                            Top(), Right(), Bottom(), Left());
+  }
+
   FRIEND_TEST_ALL_PREFIXES(
       ComputedStyleTest,
       UpdatePropertySpecificDifferencesRespectsTransformAnimation);
diff --git a/third_party/WebKit/Source/core/xml/XSLTProcessorLibxslt.cpp b/third_party/WebKit/Source/core/xml/XSLTProcessorLibxslt.cpp
index 7a57419..92534667 100644
--- a/third_party/WebKit/Source/core/xml/XSLTProcessorLibxslt.cpp
+++ b/third_party/WebKit/Source/core/xml/XSLTProcessorLibxslt.cpp
@@ -231,9 +231,13 @@
   if (parameters.IsEmpty())
     return nullptr;
 
-  const char** parameter_array = static_cast<const char**>(
-      WTF::Partitions::FastMalloc(((parameters.size() * 2) + 1) * sizeof(char*),
-                                  WTF_HEAP_PROFILER_TYPE_NAME(XSLTProcessor)));
+  WTF::CheckedSizeT size = parameters.size();
+  size *= 2;
+  ++size;
+  size *= sizeof(char*);
+  const char** parameter_array =
+      static_cast<const char**>(WTF::Partitions::FastMalloc(
+          size.ValueOrDie(), WTF_HEAP_PROFILER_TYPE_NAME(XSLTProcessor)));
 
   unsigned index = 0;
   for (auto& parameter : parameters) {
diff --git a/third_party/WebKit/Source/platform/BUILD.gn b/third_party/WebKit/Source/platform/BUILD.gn
index c806f30..26d5a58 100644
--- a/third_party/WebKit/Source/platform/BUILD.gn
+++ b/third_party/WebKit/Source/platform/BUILD.gn
@@ -276,7 +276,6 @@
     "LayoutUnit.h",
     "Length.cpp",
     "Length.h",
-    "LengthBox.cpp",
     "LengthBox.h",
     "LengthFunctions.cpp",
     "LengthFunctions.h",
@@ -1470,6 +1469,7 @@
     "text/WebEntities.cpp",
     "text/WebEntities.h",
     "text/WritingMode.h",
+    "text/WritingModeUtils.h",
     "text/linux/HyphenationLinux.cpp",
     "text/mac/HyphenationMac.cpp",
     "text/win/HyphenationWin.cpp",
@@ -1958,6 +1958,7 @@
     "text/TextBreakIteratorTest.cpp",
     "text/TextEncodingDetectorTest.cpp",
     "text/UnicodeUtilitiesTest.cpp",
+    "text/WritingModeUtilsTest.cpp",
     "threading/BackgroundTaskRunnerTest.cpp",
     "transforms/AffineTransformTest.cpp",
     "transforms/RotationTest.cpp",
diff --git a/third_party/WebKit/Source/platform/LengthBox.cpp b/third_party/WebKit/Source/platform/LengthBox.cpp
deleted file mode 100644
index 2dede65..0000000
--- a/third_party/WebKit/Source/platform/LengthBox.cpp
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (c) 2012, Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "platform/LengthBox.h"
-
-namespace blink {
-
-const Length& LengthBox::LogicalLeft(WritingMode writing_mode,
-                                     const Length& left,
-                                     const Length& top) {
-  return IsHorizontalWritingMode(writing_mode) ? left : top;
-}
-
-const Length& LengthBox::LogicalRight(WritingMode writing_mode,
-                                      const Length& right,
-                                      const Length& bottom) {
-  return IsHorizontalWritingMode(writing_mode) ? right : bottom;
-}
-
-const Length& LengthBox::Before(WritingMode writing_mode,
-                                const Length& top,
-                                const Length& left,
-                                const Length& right) {
-  switch (writing_mode) {
-    case WritingMode::kHorizontalTb:
-      return top;
-    case WritingMode::kVerticalLr:
-      return left;
-    case WritingMode::kVerticalRl:
-      return right;
-  }
-  NOTREACHED();
-  return top;
-}
-
-const Length& LengthBox::After(WritingMode writing_mode,
-                               const Length& bottom,
-                               const Length& left,
-                               const Length& right) {
-  switch (writing_mode) {
-    case WritingMode::kHorizontalTb:
-      return bottom;
-    case WritingMode::kVerticalLr:
-      return right;
-    case WritingMode::kVerticalRl:
-      return left;
-  }
-  NOTREACHED();
-  return bottom;
-}
-
-const Length& LengthBox::Start(WritingMode writing_mode,
-                               TextDirection direction,
-                               const Length& top,
-                               const Length& left,
-                               const Length& right,
-                               const Length& bottom) {
-  if (IsHorizontalWritingMode(writing_mode))
-    return IsLtr(direction) ? left : right;
-  return IsLtr(direction) ? top : bottom;
-}
-
-const Length& LengthBox::End(WritingMode writing_mode,
-                             TextDirection direction,
-                             const Length& top,
-                             const Length& left,
-                             const Length& right,
-                             const Length& bottom) {
-  if (IsHorizontalWritingMode(writing_mode))
-    return IsLtr(direction) ? right : left;
-  return IsLtr(direction) ? bottom : top;
-}
-
-const Length& LengthBox::Over(WritingMode writing_mode,
-                              const Length& top,
-                              const Length& right) {
-  return IsHorizontalWritingMode(writing_mode) ? top : right;
-}
-
-const Length& LengthBox::Under(WritingMode writing_mode,
-                               const Length& bottom,
-                               const Length& left) {
-  return IsHorizontalWritingMode(writing_mode) ? bottom : left;
-}
-
-}  // namespace blink
diff --git a/third_party/WebKit/Source/platform/LengthBox.h b/third_party/WebKit/Source/platform/LengthBox.h
index 4fb231c..95077619 100644
--- a/third_party/WebKit/Source/platform/LengthBox.h
+++ b/third_party/WebKit/Source/platform/LengthBox.h
@@ -53,40 +53,6 @@
         top_(Length(t, kFixed)),
         bottom_(Length(b, kFixed)) {}
 
-  // For use in ComputedStyle.h
-  static const Length& LogicalLeft(WritingMode,
-                                   const Length& left,
-                                   const Length& top);
-  static const Length& LogicalRight(WritingMode,
-                                    const Length& right,
-                                    const Length& bottom);
-  static const Length& Before(WritingMode,
-                              const Length& top,
-                              const Length& left,
-                              const Length& right);
-  static const Length& After(WritingMode,
-                             const Length& bottom,
-                             const Length& left,
-                             const Length& right);
-  static const Length& Start(WritingMode,
-                             TextDirection,
-                             const Length& top,
-                             const Length& left,
-                             const Length& right,
-                             const Length& bottom);
-  static const Length& End(WritingMode,
-                           TextDirection,
-                           const Length& top,
-                           const Length& left,
-                           const Length& right,
-                           const Length& bottom);
-  static const Length& Over(WritingMode,
-                            const Length& top,
-                            const Length& right);
-  static const Length& Under(WritingMode,
-                             const Length& bottom,
-                             const Length& left);
-
   const Length& Left() const { return left_; }
   const Length& Right() const { return right_; }
   const Length& Top() const { return top_; }
diff --git a/third_party/WebKit/Source/platform/heap/TraceTraits.h b/third_party/WebKit/Source/platform/heap/TraceTraits.h
index 98273c4..28c1af7 100644
--- a/third_party/WebKit/Source/platform/heap/TraceTraits.h
+++ b/third_party/WebKit/Source/platform/heap/TraceTraits.h
@@ -204,8 +204,11 @@
 
  private:
   static const T* ToWrapperTracingType(const void* t) {
+    static_assert(sizeof(T), "type needs to be defined");
     static_assert(!NeedsAdjustAndMark<T>::value,
                   "wrapper tracing is not supported within mixins");
+    static_assert(IsGarbageCollectedType<T>::value,
+                  "only objects deriving from GarbageCollected can be used");
 #if DCHECK_IS_ON()
     HeapObjectHeader::CheckFromPayload(t);
 #endif
diff --git a/third_party/WebKit/Source/platform/text/WritingModeUtils.h b/third_party/WebKit/Source/platform/text/WritingModeUtils.h
new file mode 100644
index 0000000..f4d680e
--- /dev/null
+++ b/third_party/WebKit/Source/platform/text/WritingModeUtils.h
@@ -0,0 +1,142 @@
+// 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 WritingModeUtils_h
+#define WritingModeUtils_h
+
+#include "platform/text/TextDirection.h"
+#include "platform/text/WritingMode.h"
+#include "platform/wtf/Allocator.h"
+
+namespace blink {
+
+template <typename T>
+class PhysicalToLogical {
+  STACK_ALLOCATED();
+
+ public:
+  PhysicalToLogical(WritingMode writing_mode,
+                    TextDirection direction,
+                    T top,
+                    T right,
+                    T bottom,
+                    T left)
+      : writing_mode_(writing_mode),
+        direction_(direction),
+        top_(top),
+        right_(right),
+        bottom_(bottom),
+        left_(left) {}
+
+  T InlineStart() const {
+    if (IsHorizontalWritingMode(writing_mode_))
+      return IsLtr(direction_) ? left_ : right_;
+    return IsLtr(direction_) ? top_ : bottom_;
+  }
+
+  T InlineEnd() const {
+    if (IsHorizontalWritingMode(writing_mode_))
+      return IsLtr(direction_) ? right_ : left_;
+    return IsLtr(direction_) ? bottom_ : top_;
+  }
+
+  T BlockStart() const {
+    if (IsHorizontalWritingMode(writing_mode_))
+      return top_;
+    return IsFlippedBlocksWritingMode(writing_mode_) ? right_ : left_;
+  }
+
+  T BlockEnd() const {
+    if (IsHorizontalWritingMode(writing_mode_))
+      return bottom_;
+    return IsFlippedBlocksWritingMode(writing_mode_) ? left_ : right_;
+  }
+
+  T Over() const {
+    return IsHorizontalWritingMode(writing_mode_) ? top_ : right_;
+  }
+
+  T Under() const {
+    return IsHorizontalWritingMode(writing_mode_) ? bottom_ : left_;
+  }
+
+  T LineLeft() const {
+    return IsHorizontalWritingMode(writing_mode_) ? left_ : top_;
+  }
+
+  T LineRight() const {
+    return IsHorizontalWritingMode(writing_mode_) ? right_ : bottom_;
+  }
+
+  // Legacy logical directions.
+  T Start() const { return InlineStart(); }
+  T End() const { return InlineEnd(); }
+  T Before() const { return BlockStart(); }
+  T After() const { return BlockEnd(); }
+
+ private:
+  WritingMode writing_mode_;
+  TextDirection direction_;
+  T top_;
+  T right_;
+  T bottom_;
+  T left_;
+};
+
+template <typename T>
+class LogicalToPhysical {
+  STACK_ALLOCATED();
+
+ public:
+  LogicalToPhysical(WritingMode writing_mode,
+                    TextDirection direction,
+                    T inline_start,
+                    T inline_end,
+                    T block_start,
+                    T block_end)
+      : writing_mode_(writing_mode),
+        direction_(direction),
+        inline_start_(inline_start),
+        inline_end_(inline_end),
+        block_start_(block_start),
+        block_end_(block_end) {}
+
+  T Left() const {
+    if (IsHorizontalWritingMode(writing_mode_))
+      return IsLtr(direction_) ? inline_start_ : inline_end_;
+    return IsFlippedBlocksWritingMode(writing_mode_) ? block_end_
+                                                     : block_start_;
+  }
+
+  T Right() const {
+    if (IsHorizontalWritingMode(writing_mode_))
+      return IsLtr(direction_) ? inline_end_ : inline_start_;
+    return IsFlippedBlocksWritingMode(writing_mode_) ? block_start_
+                                                     : block_end_;
+  }
+
+  T Top() const {
+    if (IsHorizontalWritingMode(writing_mode_))
+      return block_start_;
+    return IsLtr(direction_) ? inline_start_ : inline_end_;
+  }
+
+  T Bottom() const {
+    if (IsHorizontalWritingMode(writing_mode_))
+      return block_end_;
+    return IsLtr(direction_) ? inline_end_ : inline_start_;
+  }
+
+ private:
+  WritingMode writing_mode_;
+  TextDirection direction_;
+  T inline_start_;  // a.k.a. start
+  T inline_end_;    // a.k.a. end
+  T block_start_;   // a.k.a. before
+  T block_end_;     // a.k.a. after
+};
+
+}  // namespace blink
+
+#endif  // WritingModeUtils_h
diff --git a/third_party/WebKit/Source/platform/text/WritingModeUtilsTest.cpp b/third_party/WebKit/Source/platform/text/WritingModeUtilsTest.cpp
new file mode 100644
index 0000000..6683fefc
--- /dev/null
+++ b/third_party/WebKit/Source/platform/text/WritingModeUtilsTest.cpp
@@ -0,0 +1,176 @@
+// 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 "platform/text/WritingModeUtils.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+namespace {
+
+enum { kTop, kRight, kBottom, kLeft };
+
+void CheckLegacyLogicalDirections(PhysicalToLogical<int> converter) {
+  EXPECT_EQ(converter.InlineStart(), converter.Start());
+  EXPECT_EQ(converter.InlineEnd(), converter.End());
+  EXPECT_EQ(converter.BlockStart(), converter.Before());
+  EXPECT_EQ(converter.BlockEnd(), converter.After());
+}
+
+TEST(WritingModeUtilsTest, PhysicalToLogicalHorizontalLtr) {
+  PhysicalToLogical<int> converter(WritingMode::kHorizontalTb,
+                                   TextDirection::kLtr, kTop, kRight, kBottom,
+                                   kLeft);
+  EXPECT_EQ(kLeft, converter.InlineStart());
+  EXPECT_EQ(kRight, converter.InlineEnd());
+  EXPECT_EQ(kTop, converter.BlockStart());
+  EXPECT_EQ(kBottom, converter.BlockEnd());
+  EXPECT_EQ(kLeft, converter.LineLeft());
+  EXPECT_EQ(kRight, converter.LineRight());
+  EXPECT_EQ(kTop, converter.Over());
+  EXPECT_EQ(kBottom, converter.Under());
+  CheckLegacyLogicalDirections(converter);
+}
+
+TEST(WritingModeUtilsTest, PhysicalToLogicalHorizontalRtl) {
+  PhysicalToLogical<int> converter(WritingMode::kHorizontalTb,
+                                   TextDirection::kRtl, kTop, kRight, kBottom,
+                                   kLeft);
+  EXPECT_EQ(kRight, converter.InlineStart());
+  EXPECT_EQ(kLeft, converter.InlineEnd());
+  EXPECT_EQ(kTop, converter.BlockStart());
+  EXPECT_EQ(kBottom, converter.BlockEnd());
+  EXPECT_EQ(kLeft, converter.LineLeft());
+  EXPECT_EQ(kRight, converter.LineRight());
+  EXPECT_EQ(kTop, converter.Over());
+  EXPECT_EQ(kBottom, converter.Under());
+  CheckLegacyLogicalDirections(converter);
+}
+
+TEST(WritingModeUtilsTest, PhysicalToLogicalVlrLtr) {
+  PhysicalToLogical<int> converter(WritingMode::kVerticalLr,
+                                   TextDirection::kLtr, kTop, kRight, kBottom,
+                                   kLeft);
+  EXPECT_EQ(kTop, converter.InlineStart());
+  EXPECT_EQ(kBottom, converter.InlineEnd());
+  EXPECT_EQ(kLeft, converter.BlockStart());
+  EXPECT_EQ(kRight, converter.BlockEnd());
+  EXPECT_EQ(kTop, converter.LineLeft());
+  EXPECT_EQ(kBottom, converter.LineRight());
+  EXPECT_EQ(kRight, converter.Over());
+  EXPECT_EQ(kLeft, converter.Under());
+  CheckLegacyLogicalDirections(converter);
+}
+
+TEST(WritingModeUtilsTest, PhysicalToLogicalVlrRtl) {
+  PhysicalToLogical<int> converter(WritingMode::kVerticalLr,
+                                   TextDirection::kRtl, kTop, kRight, kBottom,
+                                   kLeft);
+  EXPECT_EQ(kBottom, converter.InlineStart());
+  EXPECT_EQ(kTop, converter.InlineEnd());
+  EXPECT_EQ(kLeft, converter.BlockStart());
+  EXPECT_EQ(kRight, converter.BlockEnd());
+  EXPECT_EQ(kTop, converter.LineLeft());
+  EXPECT_EQ(kBottom, converter.LineRight());
+  EXPECT_EQ(kRight, converter.Over());
+  EXPECT_EQ(kLeft, converter.Under());
+  CheckLegacyLogicalDirections(converter);
+}
+
+TEST(WritingModeUtilsTest, PhysicalToLogicalVrlLtr) {
+  PhysicalToLogical<int> converter(WritingMode::kVerticalRl,
+                                   TextDirection::kLtr, kTop, kRight, kBottom,
+                                   kLeft);
+  EXPECT_EQ(kTop, converter.InlineStart());
+  EXPECT_EQ(kBottom, converter.InlineEnd());
+  EXPECT_EQ(kRight, converter.BlockStart());
+  EXPECT_EQ(kLeft, converter.BlockEnd());
+  EXPECT_EQ(kTop, converter.LineLeft());
+  EXPECT_EQ(kBottom, converter.LineRight());
+  EXPECT_EQ(kRight, converter.Over());
+  EXPECT_EQ(kLeft, converter.Under());
+  CheckLegacyLogicalDirections(converter);
+}
+
+TEST(WritingModeUtilsTest, PhysicalToLogicalVrlRtl) {
+  PhysicalToLogical<int> converter(WritingMode::kVerticalRl,
+                                   TextDirection::kRtl, kTop, kRight, kBottom,
+                                   kLeft);
+  EXPECT_EQ(kBottom, converter.InlineStart());
+  EXPECT_EQ(kTop, converter.InlineEnd());
+  EXPECT_EQ(kRight, converter.BlockStart());
+  EXPECT_EQ(kLeft, converter.BlockEnd());
+  EXPECT_EQ(kTop, converter.LineLeft());
+  EXPECT_EQ(kBottom, converter.LineRight());
+  EXPECT_EQ(kRight, converter.Over());
+  EXPECT_EQ(kLeft, converter.Under());
+  CheckLegacyLogicalDirections(converter);
+}
+
+enum { kInlineStart, kInlineEnd, kBlockStart, kBlockEnd };
+
+TEST(WritingModeUtilsTest, LogicalToPhysicalHorizontalLtr) {
+  LogicalToPhysical<int> converter(WritingMode::kHorizontalTb,
+                                   TextDirection::kLtr, kInlineStart,
+                                   kInlineEnd, kBlockStart, kBlockEnd);
+  EXPECT_EQ(kInlineStart, converter.Left());
+  EXPECT_EQ(kInlineEnd, converter.Right());
+  EXPECT_EQ(kBlockStart, converter.Top());
+  EXPECT_EQ(kBlockEnd, converter.Bottom());
+}
+
+TEST(WritingModeUtilsTest, LogicalToPhysicalHorizontalRtl) {
+  LogicalToPhysical<int> converter(WritingMode::kHorizontalTb,
+                                   TextDirection::kRtl, kInlineStart,
+                                   kInlineEnd, kBlockStart, kBlockEnd);
+  EXPECT_EQ(kInlineEnd, converter.Left());
+  EXPECT_EQ(kInlineStart, converter.Right());
+  EXPECT_EQ(kBlockStart, converter.Top());
+  EXPECT_EQ(kBlockEnd, converter.Bottom());
+}
+
+TEST(WritingModeUtilsTest, LogicalToPhysicalVlrLtr) {
+  LogicalToPhysical<int> converter(WritingMode::kVerticalLr,
+                                   TextDirection::kLtr, kInlineStart,
+                                   kInlineEnd, kBlockStart, kBlockEnd);
+  EXPECT_EQ(kBlockStart, converter.Left());
+  EXPECT_EQ(kBlockEnd, converter.Right());
+  EXPECT_EQ(kInlineStart, converter.Top());
+  EXPECT_EQ(kInlineEnd, converter.Bottom());
+}
+
+TEST(WritingModeUtilsTest, LogicalToPhysicalVlrRtl) {
+  LogicalToPhysical<int> converter(WritingMode::kVerticalLr,
+                                   TextDirection::kRtl, kInlineStart,
+                                   kInlineEnd, kBlockStart, kBlockEnd);
+  EXPECT_EQ(kBlockStart, converter.Left());
+  EXPECT_EQ(kBlockEnd, converter.Right());
+  EXPECT_EQ(kInlineEnd, converter.Top());
+  EXPECT_EQ(kInlineStart, converter.Bottom());
+}
+
+TEST(WritingModeUtilsTest, LogicalToPhysicalVrlLtr) {
+  LogicalToPhysical<int> converter(WritingMode::kVerticalRl,
+                                   TextDirection::kLtr, kInlineStart,
+                                   kInlineEnd, kBlockStart, kBlockEnd);
+  EXPECT_EQ(kBlockEnd, converter.Left());
+  EXPECT_EQ(kBlockStart, converter.Right());
+  EXPECT_EQ(kInlineStart, converter.Top());
+  EXPECT_EQ(kInlineEnd, converter.Bottom());
+}
+
+TEST(WritingModeUtilsTest, LogicalToPhysicalVrlRtl) {
+  LogicalToPhysical<int> converter(WritingMode::kVerticalRl,
+                                   TextDirection::kRtl, kInlineStart,
+                                   kInlineEnd, kBlockStart, kBlockEnd);
+  EXPECT_EQ(kBlockEnd, converter.Left());
+  EXPECT_EQ(kBlockStart, converter.Right());
+  EXPECT_EQ(kInlineEnd, converter.Top());
+  EXPECT_EQ(kInlineStart, converter.Bottom());
+}
+
+}  // namespace
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/platform/wtf/allocator/Partitions.h b/third_party/WebKit/Source/platform/wtf/allocator/Partitions.h
index f24d4b4..11713aa9b 100644
--- a/third_party/WebKit/Source/platform/wtf/allocator/Partitions.h
+++ b/third_party/WebKit/Source/platform/wtf/allocator/Partitions.h
@@ -173,6 +173,8 @@
 using base::PartitionBucketMemoryStats;
 using base::PartitionAllocHooks;
 
+using CheckedSizeT = base::CheckedNumeric<size_t>;
+
 }  // namespace WTF
 
 #endif  // Partitions_h
diff --git a/third_party/leveldatabase/OWNERS b/third_party/leveldatabase/OWNERS
index f0cb1cf..80cd612 100644
--- a/third_party/leveldatabase/OWNERS
+++ b/third_party/leveldatabase/OWNERS
@@ -1,7 +1,9 @@
+# Primary for bugs, reviews:
+pwnall@chromium.org
+
+# Secondary:
 cmumford@chromium.org
 jsbell@chromium.org
-kinuko@chromium.org
-michaeln@chromium.org
 
 # TEAM: storage-dev@chromium.org
 # COMPONENT: Blink>Storage
diff --git a/tools/gn/visual_studio_writer.cc b/tools/gn/visual_studio_writer.cc
index eede7f4..9f72a6f 100644
--- a/tools/gn/visual_studio_writer.cc
+++ b/tools/gn/visual_studio_writer.cc
@@ -89,6 +89,9 @@
 
 const char kConfigurationName[] = "GN";
 
+const char kCharSetUnicode[] = "_UNICODE";
+const char kCharSetMultiByte[] = "_MBCS";
+
 std::string GetWindowsKitsIncludeDirs(const std::string& win_kit) {
   std::string kits_path;
 
@@ -223,6 +226,18 @@
   return true;
 }
 
+bool UnicodeTarget(const Target* target) {
+  for (ConfigValuesIterator it(target); !it.done(); it.Next()) {
+    for (const std::string& define : it.cur().defines()) {
+      if (define == kCharSetUnicode)
+        return true;
+      if (define == kCharSetMultiByte)
+        return false;
+    }
+  }
+  return true;
+}
+
 }  // namespace
 
 VisualStudioWriter::SolutionEntry::SolutionEntry(const std::string& _name,
@@ -454,7 +469,9 @@
   {
     std::unique_ptr<XmlElementWriter> configuration = project.SubElement(
         "PropertyGroup", XmlAttributes("Label", "Configuration"));
-    configuration->SubElement("CharacterSet")->Text("Unicode");
+    bool unicode_target = UnicodeTarget(target);
+    configuration->SubElement("CharacterSet")
+        ->Text(unicode_target ? "Unicode" : "MultiByte");
     std::string configuration_type = GetConfigurationType(target, err);
     if (configuration_type.empty())
       return false;
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index afd63ffe..fa221c9 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -22024,7 +22024,7 @@
 from previous Chrome versions.
 -->
 
-  <summary>Chrome flags that lead to chrome restart on ChromeOS.</summary>
+  <summary>Chrome flags that lead to Chrome restart on Chrome OS.</summary>
   <int value="-2146613579" label="V8Future:disabled"/>
   <int value="-2143961262" label="D3DVsync:disabled"/>
   <int value="-2143328006"
@@ -22073,6 +22073,7 @@
   <int value="-2035126988" label="enabled-new-style-notification"/>
   <int value="-2033225430" label="NTPMostLikelyFaviconsFromServer:disabled"/>
   <int value="-2029912304" label="StaleWhileRevalidate2:enabled"/>
+  <int value="-2028232016" label="spurious-power-button-lid-angle-change"/>
   <int value="-2025367104" label="enable-material-design-ntp"/>
   <int value="-2020721975" label="smart-virtual-keyboard"/>
   <int value="-2020024440" label="scroll-end-effect"/>
@@ -22136,6 +22137,7 @@
   <int value="-1872867546" label="EnumerateAudioDevices:disabled"/>
   <int value="-1870961970" label="enable-filemanager-mtp"/>
   <int value="-1869845022" label="force-show-update-menu-item"/>
+  <int value="-1868978829" label="spurious-power-button-accel-count"/>
   <int value="-1867382602" label="WebRTC-H264WithOpenH264FFmpeg:enabled"/>
   <int value="-1867342522" label="MaterialDesignHistory:enabled"/>
   <int value="-1861814223" label="MidiManagerDynamicInstantiation:enabled"/>
@@ -22992,6 +22994,7 @@
   <int value="1211284676" label="V8NoTurbo:enabled"/>
   <int value="1214455758" label="VideoRotateToFullscreen:disabled"/>
   <int value="1215531732" label="OmniboxUIExperiments:disabled"/>
+  <int value="1217907443" label="spurious-power-button-keyboard-accel"/>
   <int value="1219628795" label="PrintScaling:disabled"/>
   <int value="1219826373" label="ServiceWorkerNavigationPreload:enabled"/>
   <int value="1220171692" label="SpeculativeLaunchServiceWorker:enabled"/>
@@ -23007,6 +23010,7 @@
   <int value="1250071868" label="disable-timezone-tracking-option"/>
   <int value="1253698118" label="ash-disable-stable-overview-order"/>
   <int value="1257980502" label="disable-accelerated-video-decode"/>
+  <int value="1260186484" label="spurious-power-button-screen-accel"/>
   <int value="1268470658" label="disable-android-password-link"/>
   <int value="1269940659" label="EnumerateAudioDevices:enabled"/>
   <int value="1269952439" label="AndroidAIAFetching:disabled"/>
@@ -23120,6 +23124,7 @@
   <int value="1658644418" label="disable-app-list-voice-search"/>
   <int value="1661925474" label="silent-debugger-extension-api"/>
   <int value="1664401033" label="ColorCorrectRendering:enabled"/>
+  <int value="1665349789" label="spurious-power-button-window"/>
   <int value="1668611601" label="enable-encrypted-media"/>
   <int value="1673427566" label="ChromeHomeExpandButton:disabled"/>
   <int value="1689123607" label="enable-app-link"/>
diff --git a/tools/perf/benchmarks/system_health_smoke_test.py b/tools/perf/benchmarks/system_health_smoke_test.py
index 69b96ef8..3f02a94 100644
--- a/tools/perf/benchmarks/system_health_smoke_test.py
+++ b/tools/perf/benchmarks/system_health_smoke_test.py
@@ -34,8 +34,6 @@
 _DISABLED_TESTS = frozenset({
   # crbug.com/733427
   'benchmarks.system_health_smoke_test.SystemHealthBenchmarkSmokeTest.system_health.memory_mobile.browse:news:cnn',  # pylint: disable=line-too-long
-  # cburg.com/721549
-  'benchmarks.system_health_smoke_test.SystemHealthBenchmarkSmokeTest.system_health.memory_mobile.browse:news:toi',  # pylint: disable=line-too-long
   # crbug.com/702455
   'benchmarks.system_health_smoke_test.SystemHealthBenchmarkSmokeTest.system_health.memory_desktop.browse:media:youtube',  # pylint: disable=line-too-long
   # crbug.com/637230
diff --git a/tools/perf/page_sets/data/system_health_mobile.json b/tools/perf/page_sets/data/system_health_mobile.json
index 51130058..978bf8c 100644
--- a/tools/perf/page_sets/data/system_health_mobile.json
+++ b/tools/perf/page_sets/data/system_health_mobile.json
@@ -58,7 +58,7 @@
             "DEFAULT": "system_health_mobile_027.wpr"
         },
         "browse:news:toi": {
-            "DEFAULT": "system_health_mobile_055.wpr"
+            "DEFAULT": "system_health_mobile_064.wpr"
         },
         "browse:news:washingtonpost": {
             "DEFAULT": "system_health_mobile_021.wpr"
@@ -234,4 +234,4 @@
     },
     "description": "Describes the Web Page Replay archives for a story set. Don't edit by hand! Use record_wpr for updating.",
     "platform_specific": true
-}
+}
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/system_health_mobile_064.wpr.sha1 b/tools/perf/page_sets/data/system_health_mobile_064.wpr.sha1
new file mode 100644
index 0000000..a81ab3f5
--- /dev/null
+++ b/tools/perf/page_sets/data/system_health_mobile_064.wpr.sha1
@@ -0,0 +1 @@
+bb16e24570463f2cd0077e83debd47282835d8c3
\ No newline at end of file
diff --git a/tools/perf/page_sets/system_health/expectations.py b/tools/perf/page_sets/system_health/expectations.py
index fd16b9e6..69bc896 100644
--- a/tools/perf/page_sets/system_health/expectations.py
+++ b/tools/perf/page_sets/system_health/expectations.py
@@ -82,8 +82,6 @@
                       [expectations.ALL_ANDROID], 'crbug.com/726301')
     self.DisableStory('long_running:tools:gmail-foreground',
                       [expectations.ALL_ANDROID], 'crbug.com/726301')
-    self.DisableStory('browse:news:toi', [expectations.ALL_ANDROID],
-                      'crbug.com/728081')
     self.DisableStory('browse:social:facebook_infinite_scroll',
                       [expectations.ALL], 'crbug.com/728152')
     self.DisableStory('browse:media:flickr_infinite_scroll',
@@ -118,8 +116,6 @@
                       [expectations.ALL_ANDROID], 'crbug.com/726301')
     self.DisableStory('long_running:tools:gmail-foreground',
                       [expectations.ALL_ANDROID], 'crbug.com/726301')
-    self.DisableStory('browse:news:toi', [expectations.ALL_ANDROID],
-                      'crbug.com/728081')
     self.DisableStory('browse:social:facebook_infinite_scroll',
                       [expectations.ALL], 'crbug.com/728152')
     self.DisableStory('browse:media:flickr_infinite_scroll',
@@ -199,8 +195,6 @@
                       'crbug.com/708300')
     self.DisableStory('browse:news:globo', [expectations.ALL_ANDROID],
                       'crbug.com/714650')
-    self.DisableStory('browse:news:toi', [expectations.ALL_ANDROID],
-                      'crbug.com/728081')
     self.DisableStory('browse:social:facebook_infinite_scroll',
                       [expectations.ALL], 'crbug.com/728152')
     self.DisableStory('browse:media:flickr_infinite_scroll',
diff --git a/ui/android/view_android.cc b/ui/android/view_android.cc
index 7dbe2b1c..f2d1486d 100644
--- a/ui/android/view_android.cc
+++ b/ui/android/view_android.cc
@@ -100,6 +100,10 @@
   delegate_ = JavaObjectWeakGlobalRef(env, delegate);
 }
 
+void ViewAndroid::UpdateFrameInfo(const FrameInfo& frame_info) {
+  frame_info_ = frame_info;
+}
+
 float ViewAndroid::GetDipScale() {
   return ui::GetScaleFactorForNativeView(this);
 }
diff --git a/ui/android/view_android.h b/ui/android/view_android.h
index c88d878..a988054 100644
--- a/ui/android/view_android.h
+++ b/ui/android/view_android.h
@@ -25,6 +25,16 @@
 class ViewClient;
 class WindowAndroid;
 
+// View-related parameters from frame updates.
+struct FrameInfo {
+  gfx::SizeF viewport_size;  // In CSS pixels.
+  float page_scale;
+
+  // Content offset from the top. Used to translate snapshots to
+  // the correct part of the view. In CSS pixels.
+  float content_offset;
+};
+
 // A simple container for a UI layer.
 // At the root of the hierarchy is a WindowAndroid, when attached.
 // Dispatches input/view events coming from Java layer. Hit testing against
@@ -95,12 +105,10 @@
   ViewAndroid();
   virtual ~ViewAndroid();
 
-  // The content offset is in CSS pixels, and is used to translate
-  // snapshots to the correct part of the view.
-  void set_content_offset(float content_offset) {
-    content_offset_ = content_offset;
-  }
-  float content_offset() const { return content_offset_; }
+  void UpdateFrameInfo(const FrameInfo& frame_info);
+  float content_offset() const { return frame_info_.content_offset; }
+  float page_scale() const { return frame_info_.page_scale; }
+  gfx::SizeF viewport_size() const { return frame_info_.viewport_size; }
 
   // Returns the window at the root of this hierarchy, or |null|
   // if disconnected.
@@ -141,10 +149,6 @@
   void OnBottomControlsChanged(float bottom_controls_offset,
                                float bottom_content_offset);
   int GetSystemWindowInsetBottom();
-  void set_viewport_size(const gfx::SizeF& viewport_size) {
-    viewport_size_ = viewport_size;
-  }
-  gfx::SizeF viewport_size() const { return viewport_size_; }
 
   ScopedAnchorView AcquireAnchorView();
   void SetAnchorRect(const base::android::JavaRef<jobject>& anchor,
@@ -223,9 +227,7 @@
   // In physical pixel.
   gfx::Size physical_size_;
 
-  // In CSS pixel.
-  gfx::SizeF viewport_size_;  // viewport size from frame update.
-  float content_offset_;      // y content offset from the top.
+  FrameInfo frame_info_;
 
   std::unique_ptr<EventForwarder> event_forwarder_;
 
diff --git a/ui/app_list/BUILD.gn b/ui/app_list/BUILD.gn
index 27b8fb68..f8a373b 100644
--- a/ui/app_list/BUILD.gn
+++ b/ui/app_list/BUILD.gn
@@ -165,6 +165,8 @@
       "views/speech_view.h",
       "views/start_page_view.cc",
       "views/start_page_view.h",
+      "views/suggestions_container_view.cc",
+      "views/suggestions_container_view.h",
       "views/tile_item_view.cc",
       "views/tile_item_view.h",
       "views/top_icon_animation_view.cc",
diff --git a/ui/app_list/views/apps_container_view.h b/ui/app_list/views/apps_container_view.h
index dc56ff1..c249ef6 100644
--- a/ui/app_list/views/apps_container_view.h
+++ b/ui/app_list/views/apps_container_view.h
@@ -106,8 +106,8 @@
 
   void PrepareToShowApps(AppListFolderItem* folder_item);
 
-  AppsGridView* apps_grid_view_;  // Owned by views hierarchy.
-  AppListFolderView* app_list_folder_view_;  // Owned by views hierarchy.
+  AppsGridView* apps_grid_view_;                  // Owned by views hierarchy.
+  AppListFolderView* app_list_folder_view_;       // Owned by views hierarchy.
   FolderBackgroundView* folder_background_view_;  // Owned by views hierarchy.
   ShowState show_state_;
 
diff --git a/ui/app_list/views/start_page_view.cc b/ui/app_list/views/start_page_view.cc
index ed255d7..e29b41e 100644
--- a/ui/app_list/views/start_page_view.cc
+++ b/ui/app_list/views/start_page_view.cc
@@ -27,6 +27,7 @@
 #include "ui/app_list/views/search_box_view.h"
 #include "ui/app_list/views/search_result_container_view.h"
 #include "ui/app_list/views/search_result_tile_item_view.h"
+#include "ui/app_list/views/suggestions_container_view.h"
 #include "ui/app_list/views/tile_item_view.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/canvas.h"
@@ -37,7 +38,6 @@
 #include "ui/views/controls/label.h"
 #include "ui/views/controls/textfield/textfield.h"
 #include "ui/views/layout/box_layout.h"
-#include "ui/views/layout/grid_layout.h"
 #include "ui/views/widget/widget.h"
 
 namespace app_list {
@@ -56,12 +56,6 @@
 constexpr int kWebViewWidth = 700;
 constexpr int kWebViewHeight = 224;
 
-// Tile container constants.
-constexpr int kTileSpacing = 7;
-constexpr int kNumStartPageTilesCols = 5;
-constexpr int kTilesHorizontalMarginLeft = 145;
-constexpr int kCenterColumnOfStartPageAppGrid = 3;
-
 constexpr int kLauncherPageBackgroundWidth = 400;
 
 }  // namespace
@@ -98,184 +92,6 @@
   DISALLOW_COPY_AND_ASSIGN(CustomLauncherPageBackgroundView);
 };
 
-// A container that holds the start page recommendation tiles and the all apps
-// tile.
-class StartPageView::StartPageTilesContainer
-    : public SearchResultContainerView {
- public:
-  StartPageTilesContainer(ContentsView* contents_view,
-                          AllAppsTileItemView* all_apps_button,
-                          AppListViewDelegate* view_delegate);
-  ~StartPageTilesContainer() override;
-
-  TileItemView* GetTileItemView(int index);
-
-  const std::vector<SearchResultTileItemView*>& tile_views() const {
-    return search_result_tile_views_;
-  }
-
-  AllAppsTileItemView* all_apps_button() { return all_apps_button_; }
-
-  // Overridden from SearchResultContainerView:
-  int DoUpdate() override;
-  void UpdateSelectedIndex(int old_selected, int new_selected) override;
-  void OnContainerSelected(bool from_bottom,
-                           bool directional_movement) override;
-  void NotifyFirstResultYIndex(int y_index) override;
-  int GetYSize() override;
-
- private:
-  void CreateAppsGrid(int apps_num);
-
-  ContentsView* contents_view_;
-  AppListViewDelegate* view_delegate_;
-
-  std::vector<SearchResultTileItemView*> search_result_tile_views_;
-  AllAppsTileItemView* all_apps_button_;
-
-  DISALLOW_COPY_AND_ASSIGN(StartPageTilesContainer);
-};
-
-StartPageView::StartPageTilesContainer::StartPageTilesContainer(
-    ContentsView* contents_view,
-    AllAppsTileItemView* all_apps_button,
-    AppListViewDelegate* view_delegate)
-    : contents_view_(contents_view),
-      view_delegate_(view_delegate),
-      all_apps_button_(all_apps_button) {
-  SetBackground(views::CreateSolidBackground(kLabelBackgroundColor));
-  all_apps_button_->SetHoverStyle(TileItemView::HOVER_STYLE_ANIMATE_SHADOW);
-  all_apps_button_->SetParentBackgroundColor(kLabelBackgroundColor);
-
-  CreateAppsGrid(features::IsFullscreenAppListEnabled()
-                     ? kNumStartPageTilesFullscreen
-                     : kNumStartPageTiles);
-}
-
-StartPageView::StartPageTilesContainer::~StartPageTilesContainer() {
-}
-
-TileItemView* StartPageView::StartPageTilesContainer::GetTileItemView(
-    int index) {
-  DCHECK_GT(num_results(), index);
-  if (index == num_results() - 1)
-    return all_apps_button_;
-
-  return search_result_tile_views_[index];
-}
-
-int StartPageView::StartPageTilesContainer::DoUpdate() {
-  // Ignore updates and disable buttons when transitioning to a different
-  // state.
-  if (contents_view_->GetActiveState() != AppListModel::STATE_START) {
-    for (auto* view : search_result_tile_views_)
-      view->SetEnabled(false);
-
-    return num_results();
-  }
-
-  std::vector<SearchResult*> display_results =
-      AppListModel::FilterSearchResultsByDisplayType(
-          results(), SearchResult::DISPLAY_RECOMMENDATION,
-          features::IsFullscreenAppListEnabled() ? kNumStartPageTilesFullscreen
-                                                 : kNumStartPageTiles);
-  if (display_results.size() != search_result_tile_views_.size()) {
-    // We should recreate the grid layout in this case.
-    for (size_t i = 0; i < search_result_tile_views_.size(); ++i)
-      delete search_result_tile_views_[i];
-    search_result_tile_views_.clear();
-    RemoveChildView(all_apps_button_);
-
-    CreateAppsGrid(features::IsFullscreenAppListEnabled()
-                       ? kNumStartPageTilesFullscreen
-                       : std::min(kNumStartPageTiles, display_results.size()));
-  }
-
-  // Update the tile item results.
-  for (size_t i = 0; i < search_result_tile_views_.size(); ++i) {
-    SearchResult* item = nullptr;
-    if (i < display_results.size())
-      item = display_results[i];
-    search_result_tile_views_[i]->SetSearchResult(item);
-    search_result_tile_views_[i]->SetEnabled(true);
-  }
-
-  parent()->Layout();
-  // Add 1 to the results size to account for the all apps button.
-  return display_results.size() + 1;
-}
-
-void StartPageView::StartPageTilesContainer::UpdateSelectedIndex(
-    int old_selected,
-    int new_selected) {
-  if (old_selected >= 0 && old_selected < num_results())
-    GetTileItemView(old_selected)->SetSelected(false);
-
-  if (new_selected >= 0 && new_selected < num_results())
-    GetTileItemView(new_selected)->SetSelected(true);
-}
-
-void StartPageView::StartPageTilesContainer::OnContainerSelected(
-    bool /*from_bottom*/,
-    bool /*directional_movement*/) {
-  NOTREACHED();
-}
-
-void StartPageView::StartPageTilesContainer::NotifyFirstResultYIndex(
-    int /*y_index*/) {
-  NOTREACHED();
-}
-
-int StartPageView::StartPageTilesContainer::GetYSize() {
-  NOTREACHED();
-  return 0;
-}
-
-void StartPageView::StartPageTilesContainer::CreateAppsGrid(int apps_num) {
-  DCHECK(search_result_tile_views_.empty());
-  views::GridLayout* tiles_layout_manager = new views::GridLayout(this);
-  SetLayoutManager(tiles_layout_manager);
-
-  views::ColumnSet* column_set = tiles_layout_manager->AddColumnSet(0);
-  column_set->AddPaddingColumn(0, kTilesHorizontalMarginLeft);
-  for (int col = 0; col < kNumStartPageTilesCols; ++col) {
-    column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 0,
-                          views::GridLayout::USE_PREF, 0, 0);
-    column_set->AddPaddingColumn(0, kTileSpacing);
-  }
-
-  // Add SearchResultTileItemViews to the container.
-  int i = 0;
-  search_result_tile_views_.reserve(apps_num);
-  for (; i < apps_num; ++i) {
-    SearchResultTileItemView* tile_item =
-        new SearchResultTileItemView(this, view_delegate_);
-    if (i % kNumStartPageTilesCols == 0)
-      tiles_layout_manager->StartRow(0, 0);
-    tiles_layout_manager->AddView(tile_item);
-    AddChildView(tile_item);
-    tile_item->SetParentBackgroundColor(kLabelBackgroundColor);
-    tile_item->SetHoverStyle(TileItemView::HOVER_STYLE_ANIMATE_SHADOW);
-    search_result_tile_views_.emplace_back(tile_item);
-  }
-
-  all_apps_button_->UpdateIcon();
-  if (features::IsFullscreenAppListEnabled()) {
-    // Also add a special "all apps" button to the middle of the next row of the
-    // container.
-    tiles_layout_manager->StartRow(0, 0);
-    tiles_layout_manager->SkipColumns(kCenterColumnOfStartPageAppGrid);
-  } else {
-    // Also add a special "all apps" button to the end of the next row of the
-    // container.
-    if (i % kNumStartPageTilesCols == 0)
-      tiles_layout_manager->StartRow(0, 0);
-  }
-
-  tiles_layout_manager->AddView(all_apps_button_);
-  AddChildView(all_apps_button_);
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 // StartPageView implementation:
 StartPageView::StartPageView(AppListMainView* app_list_main_view,
@@ -287,11 +103,10 @@
       instant_container_(new views::View),
       custom_launcher_page_background_(new CustomLauncherPageBackgroundView(
           view_delegate_->GetModel()->custom_launcher_page_name())),
-      tiles_container_(new StartPageTilesContainer(
+      suggestions_container_(new SuggestionsContainerView(
           app_list_main_view->contents_view(),
           new AllAppsTileItemView(app_list_main_view_->contents_view(),
-                                  app_list_view),
-          view_delegate)),
+                                  app_list_view))),
       is_fullscreen_app_list_enabled_(features::IsFullscreenAppListEnabled()) {
   search_box_spacer_view_->SetPreferredSize(gfx::Size(
       is_fullscreen_app_list_enabled_ ? kStartPageSearchBoxWidthFullscreen
@@ -309,10 +124,10 @@
   }
 
   // The view containing the start page tiles.
-  AddChildView(tiles_container_);
+  AddChildView(suggestions_container_);
   AddChildView(custom_launcher_page_background_);
 
-  tiles_container_->SetResults(view_delegate_->GetModel()->results());
+  suggestions_container_->SetResults(view_delegate_->GetModel()->results());
 }
 
 StartPageView::~StartPageView() = default;
@@ -362,20 +177,20 @@
 }
 
 void StartPageView::Reset() {
-  tiles_container_->Update();
+  suggestions_container_->Update();
 }
 
 void StartPageView::UpdateForTesting() {
-  tiles_container_->Update();
+  suggestions_container_->Update();
 }
 
 const std::vector<SearchResultTileItemView*>& StartPageView::tile_views()
     const {
-  return tiles_container_->tile_views();
+  return suggestions_container_->tile_views();
 }
 
 TileItemView* StartPageView::all_apps_button() const {
-  return tiles_container_->all_apps_button();
+  return suggestions_container_->all_apps_button();
 }
 
 void StartPageView::OnShown() {
@@ -387,8 +202,8 @@
     custom_page_view->SetVisible(
         app_list_main_view_->ShouldShowCustomLauncherPage());
   }
-  tiles_container_->ClearSelectedIndex();
-  tiles_container_->Update();
+  suggestions_container_->ClearSelectedIndex();
+  suggestions_container_->Update();
   custom_launcher_page_background_->SetSelected(false);
 }
 
@@ -422,8 +237,8 @@
     indicator_->SetBoundsRect(indicator_rect);
     bounds.Inset(0, indicator_->GetPreferredSize().height(), 0, 0);
   }
-  bounds.set_height(tiles_container_->GetHeightForWidth(bounds.width()));
-  tiles_container_->SetBoundsRect(bounds);
+  bounds.set_height(suggestions_container_->GetHeightForWidth(bounds.width()));
+  suggestions_container_->SetBoundsRect(bounds);
 
   CustomLauncherPageView* custom_launcher_page_view =
       app_list_main_view_->contents_view()->custom_page_view();
@@ -441,10 +256,10 @@
 
 bool StartPageView::OnKeyPressed(const ui::KeyEvent& event) {
   const int forward_dir = base::i18n::IsRTL() ? -1 : 1;
-  int selected_index = tiles_container_->selected_index();
+  int selected_index = suggestions_container_->selected_index();
 
   if (custom_launcher_page_background_->selected()) {
-    selected_index = tiles_container_->num_results();
+    selected_index = suggestions_container_->num_results();
     switch (event.key_code()) {
       case ui::VKEY_RETURN:
         MaybeOpenCustomLauncherPage();
@@ -453,7 +268,7 @@
         break;
     }
   } else if (selected_index >= 0 &&
-             tiles_container_->GetTileItemView(selected_index)
+             suggestions_container_->GetTileItemView(selected_index)
                  ->OnKeyPressed(event)) {
     return true;
   }
@@ -465,7 +280,7 @@
       break;
     case ui::VKEY_RIGHT:
       // Don't go to the custom launcher page from the All apps tile.
-      if (selected_index != tiles_container_->num_results() - 1)
+      if (selected_index != suggestions_container_->num_results() - 1)
         dir = forward_dir;
       break;
     case ui::VKEY_UP:
@@ -479,8 +294,8 @@
       // Down selects the first tile if nothing is selected.
       dir = 1;
       // If something is selected, select the custom launcher page.
-      if (tiles_container_->IsValidSelectionIndex(selected_index))
-        selected_index = tiles_container_->num_results() - 1;
+      if (suggestions_container_->IsValidSelectionIndex(selected_index))
+        selected_index = suggestions_container_->num_results() - 1;
       break;
     case ui::VKEY_TAB:
       dir = event.IsShiftDown() ? -1 : 1;
@@ -494,27 +309,28 @@
 
   if (selected_index == -1) {
     custom_launcher_page_background_->SetSelected(false);
-    tiles_container_->SetSelectedIndex(
-        dir == -1 ? tiles_container_->num_results() - 1 : 0);
+    suggestions_container_->SetSelectedIndex(
+        dir == -1 ? suggestions_container_->num_results() - 1 : 0);
     return true;
   }
 
   int selection_index = selected_index + dir;
-  if (tiles_container_->IsValidSelectionIndex(selection_index)) {
+  if (suggestions_container_->IsValidSelectionIndex(selection_index)) {
     custom_launcher_page_background_->SetSelected(false);
-    tiles_container_->SetSelectedIndex(selection_index);
+    suggestions_container_->SetSelectedIndex(selection_index);
     return true;
   }
 
-  if (selection_index == tiles_container_->num_results() &&
+  if (selection_index == suggestions_container_->num_results() &&
       app_list_main_view_->ShouldShowCustomLauncherPage()) {
     custom_launcher_page_background_->SetSelected(true);
-    tiles_container_->ClearSelectedIndex();
+    suggestions_container_->ClearSelectedIndex();
     return true;
   }
 
   if (event.key_code() == ui::VKEY_TAB && selection_index == -1)
-    tiles_container_->ClearSelectedIndex();  // ContentsView will handle focus.
+    suggestions_container_
+        ->ClearSelectedIndex();  // ContentsView will handle focus.
 
   return false;
 }
@@ -566,7 +382,7 @@
 }
 
 TileItemView* StartPageView::GetTileItemView(size_t index) {
-  return tiles_container_->GetTileItemView(index);
+  return suggestions_container_->GetTileItemView(index);
 }
 
 }  // namespace app_list
diff --git a/ui/app_list/views/start_page_view.h b/ui/app_list/views/start_page_view.h
index 21c51ff..0c4aef3c 100644
--- a/ui/app_list/views/start_page_view.h
+++ b/ui/app_list/views/start_page_view.h
@@ -21,6 +21,7 @@
 class CustomLauncherPageBackgroundView;
 class IndicatorChipView;
 class SearchResultTileItemView;
+class SuggestionsContainerView;
 class TileItemView;
 
 // The start page for the app list.
@@ -54,8 +55,6 @@
   void OnScrollEvent(ui::ScrollEvent* event) override;
 
  private:
-  class StartPageTilesContainer;
-
   void InitInstantContainer();
 
   void MaybeOpenCustomLauncherPage();
@@ -76,9 +75,10 @@
 
   views::View* instant_container_;  // Owned by views hierarchy.
   CustomLauncherPageBackgroundView*
-      custom_launcher_page_background_;       // Owned by views hierarchy.
-  IndicatorChipView* indicator_ = nullptr;    // Owned by views hierarchy.
-  StartPageTilesContainer* tiles_container_;  // Owned by views hierarchy.
+      custom_launcher_page_background_;     // Owned by views hierarchy.
+  IndicatorChipView* indicator_ = nullptr;  // Owned by views hierarchy.
+  SuggestionsContainerView*
+      suggestions_container_;  // Owned by views hierarchy.
 
   const bool is_fullscreen_app_list_enabled_;
 
diff --git a/ui/app_list/views/suggestions_container_view.cc b/ui/app_list/views/suggestions_container_view.cc
new file mode 100644
index 0000000..21ac77fa
--- /dev/null
+++ b/ui/app_list/views/suggestions_container_view.cc
@@ -0,0 +1,169 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/app_list/views/suggestions_container_view.h"
+
+#include "ui/app_list/app_list_constants.h"
+#include "ui/app_list/app_list_features.h"
+#include "ui/app_list/views/all_apps_tile_item_view.h"
+#include "ui/app_list/views/app_list_main_view.h"
+#include "ui/app_list/views/contents_view.h"
+#include "ui/app_list/views/search_result_tile_item_view.h"
+#include "ui/views/background.h"
+#include "ui/views/layout/grid_layout.h"
+
+namespace app_list {
+
+namespace {
+
+constexpr int kTileSpacing = 7;
+constexpr int kNumTilesCols = 5;
+constexpr int kTilesHorizontalMarginLeft = 145;
+constexpr int kCenterColumnOfStartPageAppGrid = 3;
+
+}  // namespace
+
+SuggestionsContainerView::SuggestionsContainerView(
+    ContentsView* contents_view,
+    AllAppsTileItemView* all_apps_button)
+    : contents_view_(contents_view),
+      all_apps_button_(all_apps_button),
+      is_fullscreen_app_list_enabled_(features::IsFullscreenAppListEnabled()) {
+  DCHECK(contents_view);
+  view_delegate_ = contents_view_->app_list_main_view()->view_delegate();
+  SetBackground(views::CreateSolidBackground(kLabelBackgroundColor));
+  if (all_apps_button_) {
+    all_apps_button_->SetHoverStyle(TileItemView::HOVER_STYLE_ANIMATE_SHADOW);
+    all_apps_button_->SetParentBackgroundColor(kLabelBackgroundColor);
+  }
+
+  CreateAppsGrid(is_fullscreen_app_list_enabled_ ? kNumStartPageTilesFullscreen
+                                                 : kNumStartPageTiles);
+}
+
+SuggestionsContainerView::~SuggestionsContainerView() = default;
+
+TileItemView* SuggestionsContainerView::GetTileItemView(int index) {
+  DCHECK_GT(num_results(), index);
+  if (all_apps_button_ && index == num_results() - 1)
+    return all_apps_button_;
+
+  return search_result_tile_views_[index];
+}
+
+int SuggestionsContainerView::DoUpdate() {
+  // Ignore updates and disable buttons when transitioning to a different
+  // state.
+  if (contents_view_->GetActiveState() != AppListModel::STATE_START) {
+    for (auto* view : search_result_tile_views_)
+      view->SetEnabled(false);
+
+    return num_results();
+  }
+
+  std::vector<SearchResult*> display_results =
+      AppListModel::FilterSearchResultsByDisplayType(
+          results(), SearchResult::DISPLAY_RECOMMENDATION,
+          is_fullscreen_app_list_enabled_ ? kNumStartPageTilesFullscreen
+                                          : kNumStartPageTiles);
+  if (display_results.size() != search_result_tile_views_.size()) {
+    // We should recreate the grid layout in this case.
+    for (size_t i = 0; i < search_result_tile_views_.size(); ++i)
+      delete search_result_tile_views_[i];
+    search_result_tile_views_.clear();
+    if (all_apps_button_)
+      RemoveChildView(all_apps_button_);
+
+    CreateAppsGrid(is_fullscreen_app_list_enabled_
+                       ? kNumStartPageTilesFullscreen
+                       : std::min(kNumStartPageTiles, display_results.size()));
+  }
+
+  // Update the tile item results.
+  for (size_t i = 0; i < search_result_tile_views_.size(); ++i) {
+    SearchResult* item = nullptr;
+    if (i < display_results.size())
+      item = display_results[i];
+    search_result_tile_views_[i]->SetSearchResult(item);
+    search_result_tile_views_[i]->SetEnabled(true);
+  }
+
+  parent()->Layout();
+  // Add 1 to the results size to account for the all apps button.
+  return display_results.size() + 1;
+}
+
+void SuggestionsContainerView::UpdateSelectedIndex(int old_selected,
+                                                   int new_selected) {
+  if (old_selected >= 0 && old_selected < num_results())
+    GetTileItemView(old_selected)->SetSelected(false);
+
+  if (new_selected >= 0 && new_selected < num_results())
+    GetTileItemView(new_selected)->SetSelected(true);
+}
+
+void SuggestionsContainerView::OnContainerSelected(
+    bool /*from_bottom*/,
+    bool /*directional_movement*/) {
+  NOTREACHED();
+}
+
+void SuggestionsContainerView::NotifyFirstResultYIndex(int /*y_index*/) {
+  NOTREACHED();
+}
+
+int SuggestionsContainerView::GetYSize() {
+  NOTREACHED();
+  return 0;
+}
+
+void SuggestionsContainerView::CreateAppsGrid(int apps_num) {
+  DCHECK(search_result_tile_views_.empty());
+  views::GridLayout* tiles_layout_manager = new views::GridLayout(this);
+  SetLayoutManager(tiles_layout_manager);
+
+  views::ColumnSet* column_set = tiles_layout_manager->AddColumnSet(0);
+  column_set->AddPaddingColumn(0, kTilesHorizontalMarginLeft);
+  for (int col = 0; col < kNumTilesCols; ++col) {
+    column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 0,
+                          views::GridLayout::USE_PREF, 0, 0);
+    column_set->AddPaddingColumn(0, kTileSpacing);
+  }
+
+  // Add SearchResultTileItemViews to the container.
+  int i = 0;
+  search_result_tile_views_.reserve(apps_num);
+  for (; i < apps_num; ++i) {
+    SearchResultTileItemView* tile_item =
+        new SearchResultTileItemView(this, view_delegate_);
+    if (i % kNumTilesCols == 0)
+      tiles_layout_manager->StartRow(0, 0);
+    tiles_layout_manager->AddView(tile_item);
+    AddChildView(tile_item);
+    tile_item->SetParentBackgroundColor(kLabelBackgroundColor);
+    tile_item->SetHoverStyle(TileItemView::HOVER_STYLE_ANIMATE_SHADOW);
+    search_result_tile_views_.emplace_back(tile_item);
+  }
+
+  if (all_apps_button_)
+    all_apps_button_->UpdateIcon();
+  if (is_fullscreen_app_list_enabled_) {
+    // Also add a special "all apps" button to the middle of the next row of the
+    // container.
+    tiles_layout_manager->StartRow(0, 0);
+    tiles_layout_manager->SkipColumns(kCenterColumnOfStartPageAppGrid);
+  } else {
+    // Also add a special "all apps" button to the end of the next row of the
+    // container.
+    if (i % kNumTilesCols == 0)
+      tiles_layout_manager->StartRow(0, 0);
+  }
+
+  if (all_apps_button_) {
+    tiles_layout_manager->AddView(all_apps_button_);
+    AddChildView(all_apps_button_);
+  }
+}
+
+}  // namespace app_list
diff --git a/ui/app_list/views/suggestions_container_view.h b/ui/app_list/views/suggestions_container_view.h
new file mode 100644
index 0000000..498d6b9
--- /dev/null
+++ b/ui/app_list/views/suggestions_container_view.h
@@ -0,0 +1,60 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_APP_LIST_VIEWS_SUGGESTIONS_CONTAINER_VIEW_H_
+#define UI_APP_LIST_VIEWS_SUGGESTIONS_CONTAINER_VIEW_H_
+
+#include <vector>
+
+#include "base/macros.h"
+#include "ui/app_list/views/search_result_container_view.h"
+
+namespace app_list {
+
+class AllAppsTileItemView;
+class AppListViewDelegate;
+class ContentsView;
+class SearchResultTileItemView;
+class TileItemView;
+
+// A container that holds the suggested app tiles and the all apps tile.
+class SuggestionsContainerView : public SearchResultContainerView {
+ public:
+  SuggestionsContainerView(ContentsView* contents_view,
+                           AllAppsTileItemView* all_apps_button);
+  ~SuggestionsContainerView() override;
+
+  TileItemView* GetTileItemView(int index);
+
+  const std::vector<SearchResultTileItemView*>& tile_views() const {
+    return search_result_tile_views_;
+  }
+
+  AllAppsTileItemView* all_apps_button() { return all_apps_button_; }
+
+  // Overridden from SearchResultContainerView:
+  int DoUpdate() override;
+  void UpdateSelectedIndex(int old_selected, int new_selected) override;
+  void OnContainerSelected(bool from_bottom,
+                           bool directional_movement) override;
+  void NotifyFirstResultYIndex(int y_index) override;
+  int GetYSize() override;
+
+ private:
+  void CreateAppsGrid(int apps_num);
+
+  ContentsView* contents_view_ = nullptr;
+  AppListViewDelegate* view_delegate_ = nullptr;
+
+  std::vector<SearchResultTileItemView*> search_result_tile_views_;
+  AllAppsTileItemView* all_apps_button_ = nullptr;
+
+  const bool is_fullscreen_app_list_enabled_;
+
+  DISALLOW_COPY_AND_ASSIGN(SuggestionsContainerView);
+};
+
+}  // namespace app_list
+
+#endif  // UI_APP_LIST_VIEWS_SUGGESTIONS_CONTAINER_VIEW_H_
diff --git a/ui/base/ime/chromeos/input_method_manager.h b/ui/base/ime/chromeos/input_method_manager.h
index 2ad022dd..be32e0d 100644
--- a/ui/base/ime/chromeos/input_method_manager.h
+++ b/ui/base/ime/chromeos/input_method_manager.h
@@ -19,7 +19,6 @@
 class Profile;
 
 namespace ui {
-class Accelerator;
 class IMEEngineHandlerInterface;
 }  // namespace ui
 
@@ -185,11 +184,6 @@
         const std::string& locale,
         const std::string& layout) = 0;
 
-    // Returns whether the input method (or keyboard layout) can be switched
-    // to the next or previous one. Returns false if only one input method is
-    // enabled.
-    virtual bool CanCycleInputMethod() = 0;
-
     // Switches the current input method (or keyboard layout) to the next one.
     virtual void SwitchToNextInputMethod() = 0;
 
@@ -197,14 +191,6 @@
     // one.
     virtual void SwitchToPreviousInputMethod() = 0;
 
-    // Returns true if the input method can be switched to the input method
-    // associated with |accelerator|.
-    virtual bool CanSwitchInputMethod(const ui::Accelerator& accelerator) = 0;
-
-    // Switches to an input method (or keyboard layout) which is associated with
-    // the |accelerator|.
-    virtual void SwitchInputMethod(const ui::Accelerator& accelerator) = 0;
-
     // Gets the descriptor of the input method which is currently selected.
     virtual InputMethodDescriptor GetCurrentInputMethod() const = 0;
 
diff --git a/ui/base/ime/chromeos/mock_input_method_manager.cc b/ui/base/ime/chromeos/mock_input_method_manager.cc
index a28f490e..f40d0798 100644
--- a/ui/base/ime/chromeos/mock_input_method_manager.cc
+++ b/ui/base/ime/chromeos/mock_input_method_manager.cc
@@ -71,22 +71,10 @@
     const std::string& locale,
     const std::string& layout) {}
 
-bool MockInputMethodManager::State::CanCycleInputMethod() {
-  return true;
-}
-
 void MockInputMethodManager::State::SwitchToNextInputMethod() {}
 
 void MockInputMethodManager::State::SwitchToPreviousInputMethod() {}
 
-bool MockInputMethodManager::State::CanSwitchInputMethod(
-    const ui::Accelerator& accelerator) {
-  return true;
-}
-
-void MockInputMethodManager::State::SwitchInputMethod(
-    const ui::Accelerator& accelerator) {}
-
 InputMethodDescriptor MockInputMethodManager::State::GetCurrentInputMethod()
     const {
   InputMethodDescriptor descriptor;
diff --git a/ui/base/ime/chromeos/mock_input_method_manager.h b/ui/base/ime/chromeos/mock_input_method_manager.h
index 946e129..62ae04c 100644
--- a/ui/base/ime/chromeos/mock_input_method_manager.h
+++ b/ui/base/ime/chromeos/mock_input_method_manager.h
@@ -46,11 +46,8 @@
     void SetInputMethodLoginDefault() override;
     void SetInputMethodLoginDefaultFromVPD(const std::string& locale,
                                            const std::string& layout) override;
-    bool CanCycleInputMethod() override;
     void SwitchToNextInputMethod() override;
     void SwitchToPreviousInputMethod() override;
-    bool CanSwitchInputMethod(const ui::Accelerator& accelerator) override;
-    void SwitchInputMethod(const ui::Accelerator& accelerator) override;
     InputMethodDescriptor GetCurrentInputMethod() const override;
     bool ReplaceEnabledInputMethods(
         const std::vector<std::string>& new_active_input_method_ids) override;
diff --git a/ui/display/manager/managed_display_info.cc b/ui/display/manager/managed_display_info.cc
index c1fc554..850aa54 100644
--- a/ui/display/manager/managed_display_info.cc
+++ b/ui/display/manager/managed_display_info.cc
@@ -393,6 +393,12 @@
   UpdateDisplaySize();
 }
 
+float ManagedDisplayInfo::GetDensityRatio() const {
+  if (Use125DSFForUIScaling() && device_scale_factor_ == 1.25f)
+    return 1.0f;
+  return device_scale_factor_;
+}
+
 float ManagedDisplayInfo::GetEffectiveDeviceScaleFactor() const {
   if (Use125DSFForUIScaling() && device_scale_factor_ == 1.25f)
     return (configured_ui_scale_ == 0.8f) ? 1.25f : 1.0f;
diff --git a/ui/display/manager/managed_display_info.h b/ui/display/manager/managed_display_info.h
index 0a2863a..e52e05e 100644
--- a/ui/display/manager/managed_display_info.h
+++ b/ui/display/manager/managed_display_info.h
@@ -227,6 +227,10 @@
   // Returns the rotation set by a given |source|.
   Display::Rotation GetRotation(Display::RotationSource source) const;
 
+  // Returns a measure of density relative to a display with 1.0 DSF. Unlike the
+  // effective DSF, this is independent from the UI scale.
+  float GetDensityRatio() const;
+
   // Returns the ui scale and device scale factor actually used to create
   // display that chrome sees. This can be different from one obtained
   // from dispaly or one specified by a user in following situation.