diff --git a/DEPS b/DEPS
index 982b53f..ccab5178 100644
--- a/DEPS
+++ b/DEPS
@@ -96,11 +96,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': '21ca04348bfcc7c9fed519060199ac0f7fc57e2f',
+  'skia_revision': 'cf9086ce1ebd9efa27e96cfb09d9c72aac68aca1',
   # 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': 'e0d8ba264219e3f5cef1cb0e4795bced26695657',
+  'v8_revision': 'e5033ec9d57ace1014487b96e8439ff86831c4d4',
   # 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.
@@ -108,7 +108,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': '4fef7738dbdc78e19396cd1d2f666221cf8f5d4e',
+  'angle_revision': '95fb2a1774124cd9169ed8694071ab7a96a915a4',
   # 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.
@@ -120,7 +120,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': '163b4a4117bcc0b2bd1866d32205fbfb9cc01e02',
+  'pdfium_revision': '0789714191b4b3109f7d5c415663090018e27577',
   # 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.
@@ -269,7 +269,7 @@
   },
 
   'src/ios/third_party/material_components_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + 'c94e6d2c5bdb2a1f63b835453460a35f15312505',
+      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '7e7ca3549d8e21c8749c3daf1f9a6dce5be840d3',
       'condition': 'checkout_ios',
   },
 
@@ -990,7 +990,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '198d637dd3e21d837fac6b3186cc6bc72e2f7219',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '700fc8483bc0222ce59b657a45923cae9da6d177',
+    Var('webrtc_git') + '/src.git' + '@' + '942b360d820f452707b5a496e3ee73ff48c88b1d',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index f203f46..106f34fe 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -552,6 +552,8 @@
     "system/accessibility/select_to_speak_tray.h",
     "system/audio/audio_detailed_view.cc",
     "system/audio/audio_detailed_view.h",
+    "system/audio/display_speaker_controller.cc",
+    "system/audio/display_speaker_controller.h",
     "system/audio/tray_audio.cc",
     "system/audio/tray_audio.h",
     "system/audio/unified_volume_slider_controller.cc",
@@ -1883,6 +1885,7 @@
     "wm/video_detector_unittest.cc",
     "wm/window_animations_unittest.cc",
     "wm/window_cycle_controller_unittest.cc",
+    "wm/window_dimmer_unittest.cc",
     "wm/window_manager_unittest.cc",
     "wm/window_modality_controller_unittest.cc",
     "wm/window_positioner_unittest.cc",
diff --git a/ash/accessibility/key_accessibility_enabler.cc b/ash/accessibility/key_accessibility_enabler.cc
index bd9d3371..4b8c967 100644
--- a/ash/accessibility/key_accessibility_enabler.cc
+++ b/ash/accessibility/key_accessibility_enabler.cc
@@ -6,13 +6,16 @@
 
 #include "ash/accessibility/spoken_feedback_enabler.h"
 #include "ash/shell.h"
+#include "ash/system/power/power_button_screenshot_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
+#include "ui/events/base_event_utils.h"
 #include "ui/events/event.h"
 
 namespace ash {
 
 KeyAccessibilityEnabler::KeyAccessibilityEnabler() {
-  Shell::Get()->AddPreTargetHandler(this, ui::EventTarget::Priority::kSystem);
+  Shell::Get()->AddPreTargetHandler(this,
+                                    ui::EventTarget::Priority::kAccessibility);
 }
 
 KeyAccessibilityEnabler::~KeyAccessibilityEnabler() {
@@ -31,11 +34,18 @@
     vol_down_pressed_ = event->type() == ui::ET_KEY_PRESSED;
   else if (event->key_code() == ui::VKEY_VOLUME_UP)
     vol_up_pressed_ = event->type() == ui::ET_KEY_PRESSED;
+  else
+    other_key_pressed_ = event->type() == ui::ET_KEY_PRESSED;
 
-  if (vol_down_pressed_ && vol_up_pressed_) {
-    event->StopPropagation();
-    if (!spoken_feedback_enabler_.get())
+  if (vol_down_pressed_ && vol_up_pressed_ && !other_key_pressed_) {
+    if (!spoken_feedback_enabler_.get()) {
       spoken_feedback_enabler_ = std::make_unique<SpokenFeedbackEnabler>();
+      first_time_both_volume_keys_pressed_ = ui::EventTimeForNow();
+    }
+
+    if (ui::EventTimeForNow() - first_time_both_volume_keys_pressed_ >
+        PowerButtonScreenshotController::kScreenshotChordDelay)
+      event->StopPropagation();
   } else if (spoken_feedback_enabler_.get()) {
     spoken_feedback_enabler_.reset();
   }
diff --git a/ash/accessibility/key_accessibility_enabler.h b/ash/accessibility/key_accessibility_enabler.h
index 5d4fb09..2edadd5 100644
--- a/ash/accessibility/key_accessibility_enabler.h
+++ b/ash/accessibility/key_accessibility_enabler.h
@@ -7,6 +7,7 @@
 
 #include "ash/ash_export.h"
 #include "base/macros.h"
+#include "base/time/time.h"
 #include "ui/events/event_handler.h"
 
 namespace ui {
@@ -32,6 +33,8 @@
   std::unique_ptr<SpokenFeedbackEnabler> spoken_feedback_enabler_;
   bool vol_down_pressed_ = false;
   bool vol_up_pressed_ = false;
+  bool other_key_pressed_ = false;
+  base::TimeTicks first_time_both_volume_keys_pressed_;
 
   DISALLOW_COPY_AND_ASSIGN(KeyAccessibilityEnabler);
 };
diff --git a/ash/shell.cc b/ash/shell.cc
index 1563c0c..79f46c7 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -87,6 +87,7 @@
 #include "ash/shell_state.h"
 #include "ash/shutdown_controller.h"
 #include "ash/sticky_keys/sticky_keys_controller.h"
+#include "ash/system/audio/display_speaker_controller.h"
 #include "ash/system/bluetooth/bluetooth_notification_controller.h"
 #include "ash/system/bluetooth/bluetooth_power_controller.h"
 #include "ash/system/bluetooth/tray_bluetooth_helper.h"
@@ -863,6 +864,7 @@
   voice_interaction_controller_.reset();
   key_accessibility_enabler_.reset();
 
+  display_speaker_controller_.reset();
   screen_switch_check_controller_.reset();
 
   // This also deletes all RootWindows. Note that we invoke Shutdown() on
@@ -975,6 +977,7 @@
   detachable_base_notification_controller_ =
       std::make_unique<DetachableBaseNotificationController>(
           detachable_base_handler_.get());
+  display_speaker_controller_ = std::make_unique<DisplaySpeakerController>();
   policy_recommendation_restorer_ =
       std::make_unique<PolicyRecommendationRestorer>();
   screen_switch_check_controller_ =
diff --git a/ash/shell.h b/ash/shell.h
index faeb590..afdda9b 100644
--- a/ash/shell.h
+++ b/ash/shell.h
@@ -111,6 +111,7 @@
 class DisplayErrorObserver;
 class DisplayPrefs;
 class DisplayShutdownObserver;
+class DisplaySpeakerController;
 class DockedMagnifierController;
 class DragDropController;
 class EventClientImpl;
@@ -743,6 +744,7 @@
   std::unique_ptr<DetachableBaseHandler> detachable_base_handler_;
   std::unique_ptr<DetachableBaseNotificationController>
       detachable_base_notification_controller_;
+  std::unique_ptr<DisplaySpeakerController> display_speaker_controller_;
   std::unique_ptr<DragDropController> drag_drop_controller_;
   std::unique_ptr<FirstRunHelper> first_run_helper_;
   std::unique_ptr<FocusCycler> focus_cycler_;
diff --git a/ash/system/audio/display_speaker_controller.cc b/ash/system/audio/display_speaker_controller.cc
new file mode 100644
index 0000000..421c9002
--- /dev/null
+++ b/ash/system/audio/display_speaker_controller.cc
@@ -0,0 +1,90 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/system/audio/display_speaker_controller.h"
+
+#include "ash/shell.h"
+#include "chromeos/audio/cras_audio_handler.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "ui/display/display.h"
+#include "ui/display/manager/display_manager.h"
+#include "ui/display/manager/managed_display_info.h"
+#include "ui/display/screen.h"
+
+using chromeos::CrasAudioHandler;
+using chromeos::DBusThreadManager;
+
+namespace ash {
+
+DisplaySpeakerController::DisplaySpeakerController() {
+  display::Screen::GetScreen()->AddObserver(this);
+  DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver(this);
+}
+
+DisplaySpeakerController::~DisplaySpeakerController() {
+  DBusThreadManager::Get()->GetPowerManagerClient()->RemoveObserver(this);
+  display::Screen::GetScreen()->RemoveObserver(this);
+}
+
+void DisplaySpeakerController::OnDisplayAdded(
+    const display::Display& new_display) {
+  if (!new_display.IsInternal())
+    return;
+  ChangeInternalSpeakerChannelMode();
+
+  // This event will be triggered when the lid of the device is opened to exit
+  // the docked mode, we should always start or re-start HDMI re-discovering
+  // grace period right after this event.
+  CrasAudioHandler::Get()->SetActiveHDMIOutoutRediscoveringIfNecessary(true);
+}
+
+void DisplaySpeakerController::OnDisplayRemoved(
+    const display::Display& old_display) {
+  if (!old_display.IsInternal())
+    return;
+  ChangeInternalSpeakerChannelMode();
+
+  // This event will be triggered when the lid of the device is closed to enter
+  // the docked mode, we should always start or re-start HDMI re-discovering
+  // grace period right after this event.
+  CrasAudioHandler::Get()->SetActiveHDMIOutoutRediscoveringIfNecessary(true);
+}
+
+void DisplaySpeakerController::OnDisplayMetricsChanged(
+    const display::Display& display,
+    uint32_t changed_metrics) {
+  if (!display.IsInternal())
+    return;
+
+  if (changed_metrics & display::DisplayObserver::DISPLAY_METRIC_ROTATION)
+    ChangeInternalSpeakerChannelMode();
+
+  // The event could be triggered multiple times during the HDMI display
+  // transition, we don't need to restart HDMI re-discovering grace period
+  // it is already started earlier.
+  CrasAudioHandler::Get()->SetActiveHDMIOutoutRediscoveringIfNecessary(false);
+}
+
+void DisplaySpeakerController::SuspendDone(
+    const base::TimeDelta& sleep_duration) {
+  // This event is triggered when the device resumes after earlier suspension,
+  // we should always start or re-start HDMI re-discovering
+  // grace period right after this event.
+  CrasAudioHandler::Get()->SetActiveHDMIOutoutRediscoveringIfNecessary(true);
+}
+
+void DisplaySpeakerController::ChangeInternalSpeakerChannelMode() {
+  // Swap left/right channel only if it is in Yoga mode.
+  bool swap = false;
+  if (display::Display::HasInternalDisplay()) {
+    const display::ManagedDisplayInfo& display_info =
+        Shell::Get()->display_manager()->GetDisplayInfo(
+            display::Display::InternalDisplayId());
+    if (display_info.GetActiveRotation() == display::Display::ROTATE_180)
+      swap = true;
+  }
+  CrasAudioHandler::Get()->SwapInternalSpeakerLeftRightChannel(swap);
+}
+
+}  // namespace ash
diff --git a/ash/system/audio/display_speaker_controller.h b/ash/system/audio/display_speaker_controller.h
new file mode 100644
index 0000000..51e2051
--- /dev/null
+++ b/ash/system/audio/display_speaker_controller.h
@@ -0,0 +1,39 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_SYSTEM_AUDIO_DISPLAY_SPEAKER_CONTROLLER_H_
+#define ASH_SYSTEM_AUDIO_DISPLAY_SPEAKER_CONTROLLER_H_
+
+#include "base/macros.h"
+#include "chromeos/dbus/power_manager_client.h"
+#include "ui/display/display_observer.h"
+
+namespace ash {
+
+// Controller that does HDMI display audio and yoga mode handling.
+class DisplaySpeakerController : public display::DisplayObserver,
+                                 public chromeos::PowerManagerClient::Observer {
+ public:
+  DisplaySpeakerController();
+  ~DisplaySpeakerController() override;
+
+  // display::DisplayObserver.
+  void OnDisplayAdded(const display::Display& new_display) override;
+  void OnDisplayRemoved(const display::Display& old_display) override;
+  void OnDisplayMetricsChanged(const display::Display& display,
+                               uint32_t changed_metrics) override;
+
+  // chromeos::PowerManagerClient::Observer:
+  void SuspendDone(const base::TimeDelta& sleep_duration) override;
+
+ private:
+  // Swaps the left and right channels on yoga devices based on orientation.
+  void ChangeInternalSpeakerChannelMode();
+
+  DISALLOW_COPY_AND_ASSIGN(DisplaySpeakerController);
+};
+
+}  // namespace ash
+
+#endif  // ASH_SYSTEM_AUDIO_DISPLAY_SPEAKER_CONTROLLER_H_
diff --git a/ash/system/audio/tray_audio.cc b/ash/system/audio/tray_audio.cc
index 601de55..9477d49 100644
--- a/ash/system/audio/tray_audio.cc
+++ b/ash/system/audio/tray_audio.cc
@@ -14,17 +14,11 @@
 #include "ash/system/tray/system_tray.h"
 #include "ash/system/tray/system_tray_item_detailed_view_delegate.h"
 #include "ash/system/tray/tray_constants.h"
-#include "chromeos/dbus/dbus_thread_manager.h"
-#include "ui/display/display.h"
-#include "ui/display/manager/display_manager.h"
-#include "ui/display/manager/managed_display_info.h"
-#include "ui/display/screen.h"
 #include "ui/views/view.h"
 
 namespace ash {
 
 using chromeos::CrasAudioHandler;
-using chromeos::DBusThreadManager;
 
 TrayAudio::TrayAudio(SystemTray* system_tray)
     : TrayImageItem(system_tray, kSystemTrayVolumeMuteIcon, UMA_AUDIO),
@@ -35,13 +29,9 @@
           std::make_unique<SystemTrayItemDetailedViewDelegate>(this)) {
   if (CrasAudioHandler::IsInitialized())
     CrasAudioHandler::Get()->AddAudioObserver(this);
-  display::Screen::GetScreen()->AddObserver(this);
-  DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver(this);
 }
 
 TrayAudio::~TrayAudio() {
-  DBusThreadManager::Get()->GetPowerManagerClient()->RemoveObserver(this);
-  display::Screen::GetScreen()->RemoveObserver(this);
   if (CrasAudioHandler::IsInitialized())
     CrasAudioHandler::Get()->RemoveAudioObserver(this);
 }
@@ -154,62 +144,6 @@
   Update();
 }
 
-void TrayAudio::ChangeInternalSpeakerChannelMode() {
-  // Swap left/right channel only if it is in Yoga mode.
-  bool swap = false;
-  if (display::Display::HasInternalDisplay()) {
-    const display::ManagedDisplayInfo& display_info =
-        Shell::Get()->display_manager()->GetDisplayInfo(
-            display::Display::InternalDisplayId());
-    if (display_info.GetActiveRotation() == display::Display::ROTATE_180)
-      swap = true;
-  }
-  CrasAudioHandler::Get()->SwapInternalSpeakerLeftRightChannel(swap);
-}
-
-void TrayAudio::OnDisplayAdded(const display::Display& new_display) {
-  if (!new_display.IsInternal())
-    return;
-  ChangeInternalSpeakerChannelMode();
-
-  // This event will be triggered when the lid of the device is opened to exit
-  // the docked mode, we should always start or re-start HDMI re-discovering
-  // grace period right after this event.
-  CrasAudioHandler::Get()->SetActiveHDMIOutoutRediscoveringIfNecessary(true);
-}
-
-void TrayAudio::OnDisplayRemoved(const display::Display& old_display) {
-  if (!old_display.IsInternal())
-    return;
-  ChangeInternalSpeakerChannelMode();
-
-  // This event will be triggered when the lid of the device is closed to enter
-  // the docked mode, we should always start or re-start HDMI re-discovering
-  // grace period right after this event.
-  CrasAudioHandler::Get()->SetActiveHDMIOutoutRediscoveringIfNecessary(true);
-}
-
-void TrayAudio::OnDisplayMetricsChanged(const display::Display& display,
-                                        uint32_t changed_metrics) {
-  if (!display.IsInternal())
-    return;
-
-  if (changed_metrics & display::DisplayObserver::DISPLAY_METRIC_ROTATION)
-    ChangeInternalSpeakerChannelMode();
-
-  // The event could be triggered multiple times during the HDMI display
-  // transition, we don't need to restart HDMI re-discovering grace period
-  // it is already started earlier.
-  CrasAudioHandler::Get()->SetActiveHDMIOutoutRediscoveringIfNecessary(false);
-}
-
-void TrayAudio::SuspendDone(const base::TimeDelta& sleep_duration) {
-  // This event is triggered when the device resumes after earlier suspension,
-  // we should always start or re-start HDMI re-discovering
-  // grace period right after this event.
-  CrasAudioHandler::Get()->SetActiveHDMIOutoutRediscoveringIfNecessary(true);
-}
-
 void TrayAudio::Update() {
   if (tray_view())
     tray_view()->SetVisible(GetInitialVisibility());
diff --git a/ash/system/audio/tray_audio.h b/ash/system/audio/tray_audio.h
index adf070d..c7f5838 100644
--- a/ash/system/audio/tray_audio.h
+++ b/ash/system/audio/tray_audio.h
@@ -11,8 +11,6 @@
 #include "ash/system/tray/tray_image_item.h"
 #include "base/macros.h"
 #include "chromeos/audio/cras_audio_handler.h"
-#include "chromeos/dbus/power_manager_client.h"
-#include "ui/display/display_observer.h"
 
 namespace ash {
 
@@ -25,9 +23,7 @@
 
 // The system tray item for audio input and output.
 class ASH_EXPORT TrayAudio : public TrayImageItem,
-                             public chromeos::CrasAudioHandler::AudioObserver,
-                             public display::DisplayObserver,
-                             public chromeos::PowerManagerClient::Observer {
+                             public chromeos::CrasAudioHandler::AudioObserver {
  public:
   explicit TrayAudio(SystemTray* system_tray);
   ~TrayAudio() override;
@@ -40,12 +36,6 @@
   bool pop_up_volume_view_for_testing() { return pop_up_volume_view_; }
 
  private:
-  // Overridden from display::DisplayObserver.
-  void OnDisplayAdded(const display::Display& new_display) override;
-  void OnDisplayRemoved(const display::Display& old_display) override;
-  void OnDisplayMetricsChanged(const display::Display& display,
-                               uint32_t changed_metrics) override;
-
   // Overridden from TrayImageItem.
   bool GetInitialVisibility() override;
 
@@ -64,12 +54,6 @@
   void OnActiveOutputNodeChanged() override;
   void OnActiveInputNodeChanged() override;
 
-  // Overridden from chromeos::PowerManagerClient::Observer.
-  void SuspendDone(const base::TimeDelta& sleep_duration) override;
-
-  // Swaps the left and right channels on yoga devices based on orientation.
-  void ChangeInternalSpeakerChannelMode();
-
   // Updates the UI views.
   void Update();
 
diff --git a/ash/wm/window_dimmer.cc b/ash/wm/window_dimmer.cc
index 27e7b6d9..64dcf1d 100644
--- a/ash/wm/window_dimmer.cc
+++ b/ash/wm/window_dimmer.cc
@@ -39,6 +39,11 @@
   parent->AddObserver(this);
   parent->StackChildAtTop(window_);
 
+  // The window is not fully opaque. Set the transparent bit so that it
+  // interacts properly with aura::WindowOcclusionTracker.
+  // https://crbug.com/833814
+  window_->SetTransparent(true);
+
   window_->SetBounds(gfx::Rect(parent_->bounds().size()));
 }
 
diff --git a/ash/wm/window_dimmer_unittest.cc b/ash/wm/window_dimmer_unittest.cc
new file mode 100644
index 0000000..dbddc04
--- /dev/null
+++ b/ash/wm/window_dimmer_unittest.cc
@@ -0,0 +1,30 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/wm/window_dimmer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/aura/test/aura_test_base.h"
+#include "ui/aura/test/test_windows.h"
+#include "ui/aura/window_occlusion_tracker.h"
+
+namespace ash {
+
+using WindowDimmerTest = aura::test::AuraTestBase;
+
+// Verify that a window underneath the window dimmer is not occluded.
+TEST_F(WindowDimmerTest, Occlusion) {
+  aura::Window* bottom_window = aura::test::CreateTestWindow(
+      SK_ColorWHITE, 1, root_window()->bounds(), root_window());
+  aura::WindowOcclusionTracker::Track(bottom_window);
+  WindowDimmer dimmer(root_window());
+  EXPECT_EQ(aura::Window::OcclusionState::VISIBLE,
+            bottom_window->occlusion_state());
+  // Sanity check: An opaque window on top of |bottom_window| occludes it.
+  aura::test::CreateTestWindow(SK_ColorWHITE, 2, root_window()->bounds(),
+                               root_window());
+  EXPECT_EQ(aura::Window::OcclusionState::OCCLUDED,
+            bottom_window->occlusion_state());
+}
+
+}  // namespace ash
diff --git a/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java b/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
index 6de133d..b98b4c8 100644
--- a/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
+++ b/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
@@ -55,28 +55,14 @@
     // Set to true to enable debug logs.
     private static final boolean DEBUG = false;
 
-    // Guards all access to the libraries
-    private static final Object sLock = new Object();
-
     // SharedPreferences key for "don't prefetch libraries" flag
     private static final String DONT_PREFETCH_LIBRARIES_KEY = "dont_prefetch_libraries";
 
-    // The singleton instance of NativeLibraryPreloader.
-    private static NativeLibraryPreloader sLibraryPreloader;
-    private static boolean sLibraryPreloaderCalled;
-
-    // The singleton instance of LibraryLoader.
-    private static LibraryLoader sInstance = new LibraryLoader();
-
     private static final EnumeratedHistogramSample sRelinkerCountHistogram =
             new EnumeratedHistogramSample("ChromiumAndroidLinker.RelinkerFallbackCount", 2);
 
-    // One-way switch becomes true when the libraries are loaded.
-    private boolean mLoaded;
-
-    // One-way switch becomes true when the Java command line is switched to
-    // native.
-    private boolean mCommandLineSwitched;
+    // The singleton instance of LibraryLoader. Never null (not final for tests).
+    private static LibraryLoader sInstance = new LibraryLoader();
 
     // One-way switch becomes true when the libraries are initialized (
     // by calling nativeLibraryLoaded, which forwards to LibraryLoaded(...) in
@@ -85,6 +71,23 @@
     // threads without a lock.
     private volatile boolean mInitialized;
 
+    // One-way switch that becomes true once
+    // {@link asyncPrefetchLibrariesToMemory} has been called.
+    private final AtomicBoolean mPrefetchLibraryHasBeenCalled = new AtomicBoolean();
+
+    // Guards all fields below.
+    private final Object mLock = new Object();
+
+    private NativeLibraryPreloader mLibraryPreloader;
+    private boolean mLibraryPreloaderCalled;
+
+    // One-way switch becomes true when the libraries are loaded.
+    private boolean mLoaded;
+
+    // One-way switch becomes true when the Java command line is switched to
+    // native.
+    private boolean mCommandLineSwitched;
+
     // One-way switches recording attempts to use Relro sharing in the browser.
     // The flags are used to report UMA stats later.
     private boolean mIsUsingBrowserSharedRelros;
@@ -97,10 +100,6 @@
     // The type of process the shared library is loaded in.
     private @LibraryProcessType int mLibraryProcessType;
 
-    // One-way switch that becomes true once
-    // {@link asyncPrefetchLibrariesToMemory} has been called.
-    private final AtomicBoolean mPrefetchLibraryHasBeenCalled = new AtomicBoolean();
-
     // The number of milliseconds it took to load all the native libraries, which
     // will be reported via UMA. Set once when the libraries are done loading.
     private long mLibraryLoadTimeMs;
@@ -117,10 +116,10 @@
      * @param loader the NativeLibraryPreloader, it shall only be set once and before the
      *               native library loaded.
      */
-    public static void setNativeLibraryPreloader(NativeLibraryPreloader loader) {
-        synchronized (sLock) {
-            assert sLibraryPreloader == null && !sInstance.mLoaded;
-            sLibraryPreloader = loader;
+    public void setNativeLibraryPreloader(NativeLibraryPreloader loader) {
+        synchronized (mLock) {
+            assert mLibraryPreloader == null && !mLoaded;
+            mLibraryPreloader = loader;
         }
     }
 
@@ -136,7 +135,7 @@
      * @param processType the process the shared library is loaded in.
      */
     public void ensureInitialized(@LibraryProcessType int processType) throws ProcessInitException {
-        synchronized (sLock) {
+        synchronized (mLock) {
             if (mInitialized) {
                 // Already initialized, nothing to do.
                 return;
@@ -160,7 +159,7 @@
      * Similar to {@link #preloadNow}, but allows specifying app context to use.
      */
     public void preloadNowOverrideApplicationContext(Context appContext) {
-        synchronized (sLock) {
+        synchronized (mLock) {
             if (!Linker.isUsed()) {
                 preloadAlreadyLocked(appContext);
             }
@@ -171,9 +170,9 @@
         try (TraceEvent te = TraceEvent.scoped("LibraryLoader.preloadAlreadyLocked")) {
             // Preloader uses system linker, we shouldn't preload if Chromium linker is used.
             assert !Linker.isUsed();
-            if (sLibraryPreloader != null && !sLibraryPreloaderCalled) {
-                mLibraryPreloaderStatus = sLibraryPreloader.loadLibrary(appContext);
-                sLibraryPreloaderCalled = true;
+            if (mLibraryPreloader != null && !mLibraryPreloaderCalled) {
+                mLibraryPreloaderStatus = mLibraryPreloader.loadLibrary(appContext);
+                mLibraryPreloaderCalled = true;
             }
         }
     }
@@ -181,8 +180,8 @@
     /**
      * Checks if library is fully loaded and initialized.
      */
-    public static boolean isInitialized() {
-        return sInstance != null && sInstance.mInitialized;
+    public boolean isInitialized() {
+        return mInitialized;
     }
 
     /**
@@ -207,7 +206,7 @@
      * @throws ProcessInitException if the native library failed to load with this context.
      */
     public void loadNowOverrideApplicationContext(Context appContext) throws ProcessInitException {
-        synchronized (sLock) {
+        synchronized (mLock) {
             if (mLoaded && appContext != ContextUtils.getApplicationContext()) {
                 throw new IllegalStateException("Attempt to load again from alternate context.");
             }
@@ -223,7 +222,7 @@
      * @param processType the process the shared library is loaded in.
      */
     public void initialize(@LibraryProcessType int processType) throws ProcessInitException {
-        synchronized (sLock) {
+        synchronized (mLock) {
             initializeAlreadyLocked(processType);
         }
     }
@@ -316,8 +315,9 @@
 
     // Helper for loadAlreadyLocked(). Load a native shared library with the Chromium linker.
     // Sets UMA flags depending on the results of loading.
-    private void loadLibraryWithCustomLinker(
+    private void loadLibraryWithCustomLinkerAlreadyLocked(
             Linker linker, @Nullable String zipFilePath, String libFilePath) {
+        assert Thread.holdsLock(mLock);
         if (linker.isUsingBrowserSharedRelros()) {
             // If the browser is set to attempt shared RELROs then we try first with shared
             // RELROs enabled, and if that fails then retry without.
@@ -399,13 +399,14 @@
 
                         try {
                             // Load the library using this Linker. May throw UnsatisfiedLinkError.
-                            loadLibraryWithCustomLinker(linker, zipFilePath, libFilePath);
+                            loadLibraryWithCustomLinkerAlreadyLocked(
+                                    linker, zipFilePath, libFilePath);
                             incrementRelinkerCountNotHitHistogram();
                         } catch (UnsatisfiedLinkError e) {
                             if (!Linker.isInZipFile()
                                     && ResourceExtractor
                                                .PLATFORM_REQUIRES_NATIVE_FALLBACK_EXTRACTION) {
-                                loadLibraryWithCustomLinker(
+                                loadLibraryWithCustomLinkerAlreadyLocked(
                                         linker, null, getExtractedLibraryPath(appContext, library));
                                 incrementRelinkerCountHitHistogram();
                             } else {
@@ -509,7 +510,7 @@
     // initialization is done. This is okay in the WebView's case since the
     // JNI is already loaded by this point.
     public void switchCommandLineForWebView() {
-        synchronized (sLock) {
+        synchronized (mLock) {
             ensureCommandLineSwitchedAlreadyLocked();
         }
     }
@@ -566,50 +567,41 @@
 
     // Called after all native initializations are complete.
     public void onNativeInitializationComplete() {
-        recordBrowserProcessHistogram();
+        synchronized (mLock) {
+            recordBrowserProcessHistogramAlreadyLocked();
+        }
     }
 
     // Record Chromium linker histogram state for the main browser process. Called from
     // onNativeInitializationComplete().
-    private void recordBrowserProcessHistogram() {
+    private void recordBrowserProcessHistogramAlreadyLocked() {
+        assert Thread.holdsLock(mLock);
         if (Linker.isUsed()) {
-            nativeRecordChromiumAndroidLinkerBrowserHistogram(
-                    mIsUsingBrowserSharedRelros,
+            nativeRecordChromiumAndroidLinkerBrowserHistogram(mIsUsingBrowserSharedRelros,
                     mLoadAtFixedAddressFailed,
-                    getLibraryLoadFromApkStatus(),
+                    mLibraryWasLoadedFromApk ? LibraryLoadFromApkStatusCodes.SUCCESSFUL
+                                             : LibraryLoadFromApkStatusCodes.UNKNOWN,
                     mLibraryLoadTimeMs);
         }
-        if (sLibraryPreloader != null) {
+        if (mLibraryPreloader != null) {
             nativeRecordLibraryPreloaderBrowserHistogram(mLibraryPreloaderStatus);
         }
     }
 
-    // Returns the device's status for loading a library directly from the APK file.
-    // This method can only be called when the Chromium linker is used.
-    private int getLibraryLoadFromApkStatus() {
-        assert Linker.isUsed();
-
-        if (mLibraryWasLoadedFromApk) {
-            return LibraryLoadFromApkStatusCodes.SUCCESSFUL;
-        }
-
-        // There were no libraries to be loaded directly from the APK file.
-        return LibraryLoadFromApkStatusCodes.UNKNOWN;
-    }
-
     // Register pending Chromium linker histogram state for renderer processes. This cannot be
     // recorded as a histogram immediately because histograms and IPC are not ready at the
     // time it are captured. This function stores a pending value, so that a later call to
     // RecordChromiumAndroidLinkerRendererHistogram() will record it correctly.
     public void registerRendererProcessHistogram(boolean requestedSharedRelro,
                                                  boolean loadAtFixedAddressFailed) {
-        if (Linker.isUsed()) {
-            nativeRegisterChromiumAndroidLinkerRendererHistogram(requestedSharedRelro,
-                                                                 loadAtFixedAddressFailed,
-                                                                 mLibraryLoadTimeMs);
-        }
-        if (sLibraryPreloader != null) {
-            nativeRegisterLibraryPreloaderRendererHistogram(mLibraryPreloaderStatus);
+        synchronized (mLock) {
+            if (Linker.isUsed()) {
+                nativeRegisterChromiumAndroidLinkerRendererHistogram(
+                        requestedSharedRelro, loadAtFixedAddressFailed, mLibraryLoadTimeMs);
+            }
+            if (mLibraryPreloader != null) {
+                nativeRegisterLibraryPreloaderRendererHistogram(mLibraryPreloaderStatus);
+            }
         }
     }
 
diff --git a/base/android/java/src/org/chromium/base/metrics/CachedMetrics.java b/base/android/java/src/org/chromium/base/metrics/CachedMetrics.java
index 93672af..ba03e51 100644
--- a/base/android/java/src/org/chromium/base/metrics/CachedMetrics.java
+++ b/base/android/java/src/org/chromium/base/metrics/CachedMetrics.java
@@ -67,7 +67,7 @@
 
         public void record() {
             synchronized (CachedMetric.sMetrics) {
-                if (LibraryLoader.isInitialized()) {
+                if (LibraryLoader.getInstance().isInitialized()) {
                     recordWithNative();
                 } else {
                     mCount++;
@@ -99,7 +99,7 @@
 
         public void record(int sample) {
             synchronized (CachedMetric.sMetrics) {
-                if (LibraryLoader.isInitialized()) {
+                if (LibraryLoader.getInstance().isInitialized()) {
                     recordWithNative(sample);
                 } else {
                     mSamples.add(sample);
@@ -133,7 +133,7 @@
 
         public void record(int sample) {
             synchronized (CachedMetric.sMetrics) {
-                if (LibraryLoader.isInitialized()) {
+                if (LibraryLoader.getInstance().isInitialized()) {
                     recordWithNative(sample);
                 } else {
                     mSamples.add(sample);
@@ -168,7 +168,7 @@
 
         public void record(long sample) {
             synchronized (CachedMetric.sMetrics) {
-                if (LibraryLoader.isInitialized()) {
+                if (LibraryLoader.getInstance().isInitialized()) {
                     recordWithNative(sample);
                 } else {
                     mSamples.add(sample);
@@ -200,7 +200,7 @@
 
         public void record(boolean sample) {
             synchronized (CachedMetric.sMetrics) {
-                if (LibraryLoader.isInitialized()) {
+                if (LibraryLoader.getInstance().isInitialized()) {
                     recordWithNative(sample);
                 } else {
                     mSamples.add(sample);
@@ -241,7 +241,7 @@
 
         public void record(int sample) {
             synchronized (CachedMetric.sMetrics) {
-                if (LibraryLoader.isInitialized()) {
+                if (LibraryLoader.getInstance().isInitialized()) {
                     recordWithNative(sample);
                 } else {
                     mSamples.add(sample);
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 5b65b3e..2ec8a666 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -433,6 +433,7 @@
 junit_binary("chrome_junit_tests") {
   # From java_sources.gni.
   java_files = chrome_junit_test_java_sources
+
   deps = [
     ":app_hooks_java",
     ":chrome_java",
@@ -597,6 +598,10 @@
     "//url/mojom:url_mojom_gurl_java",
   ]
 
+  if (enable_feed_in_chrome) {
+    deps += feed_conformance_test_deps
+  }
+
   deps += android_extra_test_deps
 
   data = [
diff --git a/chrome/android/feed/feed_java_sources.gni b/chrome/android/feed/feed_java_sources.gni
index 95ba969..3e80941 100644
--- a/chrome/android/feed/feed_java_sources.gni
+++ b/chrome/android/feed/feed_java_sources.gni
@@ -15,6 +15,13 @@
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java",
     "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/action/FeedActionHandler.java",
   ]
+
+  feed_conformance_test_sources = [ "//chrome/android/javatests/src/org/chromium/chrome/browser/feed/FeedNetworkBridgeConformanceTest.java" ]
+
+  feed_conformance_test_deps = [
+    "//third_party/feed:feed_lib_java",
+    "//third_party/feed:feed_conformance_test_lib_android_java",
+  ]
 } else {
   feed_deps = []
 
diff --git a/chrome/android/java/res/drawable/ic_globe_24dp.xml b/chrome/android/java/res/drawable/ic_globe_24dp.xml
index d711f08..f9faae5 100644
--- a/chrome/android/java/res/drawable/ic_globe_24dp.xml
+++ b/chrome/android/java/res/drawable/ic_globe_24dp.xml
@@ -12,10 +12,8 @@
     android:viewportHeight="24.0">
 
     <path
-        android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM11,
-        19.93c-3.95,-0.49 -7,-3.85 -7,-7.93 0,-0.62 0.08,-1.21 0.21,-1.79L9,15v1c0,1.1 0.9,2 2,
-        2v1.93zM17.9,17.39c-0.26,-0.81 -1,-1.39 -1.9,-1.39h-1v-3c0,-0.55 -0.45,-1 -1,
-        -1L8,12v-2h2c0.55,0 1,-0.45 1,-1L11,7h2c1.1,0 2,-0.9 2,-2v-0.41c2.93,1.19 5,4.06 5,7.41 0,
-        2.08 -0.8,3.97 -2.1,5.39z"
-        android:fillColor="#000000" />
+        android:pathData="M12,2C17.52,2 22,6.48 22,12C22,17.52 17.52,22 12,22C6.48,22 2,17.52 2,12C2,6.48 6.48,2 12,2ZM4,12L8.4,12C11.807,12.022 13.322,13.731 12.943,17.127L9.488,17.127L9.488,19.597C10.278,19.858 11.123,20 12,20C16.415,20 20,16.415 20,12C20,11.837 19.995,11.675 19.985,11.514C19.328,12.505 18.333,13 17,13C14.863,13 13.794,12.084 13.794,10.251L10.046,10.251C9.772,7.522 10.729,6.158 12.916,6.158C12.916,5.183 13.243,4.561 13.727,4.187C13.171,4.065 12.593,4 12,4C7.585,4 4,7.585 4,12Z"
+        android:strokeColor="#00000000"
+        android:fillColor="#000000"
+        android:strokeWidth="1"/>
 </vector>
diff --git a/chrome/android/java/res/drawable/ic_globe_36dp.xml b/chrome/android/java/res/drawable/ic_globe_36dp.xml
index 2c3fc26..875e908 100644
--- a/chrome/android/java/res/drawable/ic_globe_36dp.xml
+++ b/chrome/android/java/res/drawable/ic_globe_36dp.xml
@@ -12,10 +12,8 @@
     android:viewportHeight="24.0">
 
     <path
-        android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM11,
-        19.93c-3.95,-0.49 -7,-3.85 -7,-7.93 0,-0.62 0.08,-1.21 0.21,-1.79L9,15v1c0,1.1 0.9,2 2,
-        2v1.93zM17.9,17.39c-0.26,-0.81 -1,-1.39 -1.9,-1.39h-1v-3c0,-0.55 -0.45,-1 -1,
-        -1L8,12v-2h2c0.55,0 1,-0.45 1,-1L11,7h2c1.1,0 2,-0.9 2,-2v-0.41c2.93,1.19 5,4.06 5,7.41 0,
-        2.08 -0.8,3.97 -2.1,5.39z"
-        android:fillColor="#000000" />
+        android:pathData="M12,2C17.52,2 22,6.48 22,12C22,17.52 17.52,22 12,22C6.48,22 2,17.52 2,12C2,6.48 6.48,2 12,2ZM4,12L8.4,12C11.807,12.022 13.322,13.731 12.943,17.127L9.488,17.127L9.488,19.597C10.278,19.858 11.123,20 12,20C16.415,20 20,16.415 20,12C20,11.837 19.995,11.675 19.985,11.514C19.328,12.505 18.333,13 17,13C14.863,13 13.794,12.084 13.794,10.251L10.046,10.251C9.772,7.522 10.729,6.158 12.916,6.158C12.916,5.183 13.243,4.561 13.727,4.187C13.171,4.065 12.593,4 12,4C7.585,4 4,7.585 4,12Z"
+        android:strokeColor="#00000000"
+        android:fillColor="#000000"
+        android:strokeWidth="1"/>
 </vector>
diff --git a/chrome/android/java/res/drawable/signin_header_animation.xml b/chrome/android/java/res/drawable/signin_header_animation.xml
index 459d307f..26d58ce 100644
--- a/chrome/android/java/res/drawable/signin_header_animation.xml
+++ b/chrome/android/java/res/drawable/signin_header_animation.xml
@@ -9,20 +9,29 @@
     tools:targetApi="21">
     <aapt:attr name="android:drawable">
         <vector
-            android:width="320dp"
-            android:height="107dp"
+            android:width="360dp"
+            android:height="88dp"
             android:viewportWidth="360"
-            android:viewportHeight="120">
+            android:viewportHeight="88">
+            <!-- Background fill -->
+            <path
+                android:fillColor="#F8F9FA"
+                android:pathData="M0,0h360v88h-360z"/>
+            <!-- White and blue curves on the left side -->
+            <path
+                android:fillColor="#4285F4"
+                android:pathData="M52.768,61.116L55.813,58C62.049,64.382 68.703,67.61 75.591,67.594C86.411,67.567 94.368,59.475 94.446,59.393L97.514,62.485C97.143,62.871 88.283,71.945 75.651,72C67.558,72.035 59.859,68.373 52.768,61.116Z"/>
+            <path
+                android:fillColor="#E8E9EB"
+                android:pathData="M15.018,58.706L12,55.647C12.213,55.432 17.293,50.349 25.233,48.071C32.621,45.951 43.914,45.943 55.751,57.941L52.734,61C44.405,52.557 35.544,49.608 26.395,52.233C19.513,54.206 15.062,58.661 15.018,58.706Z"/>
             <!-- Cloud 1 -->
             <group
                 android:name="cloud1_animation_group"
                 android:translateX="0">
 
                 <path
-                    android:fillColor="#f8f9fa"
-                    android:pathData="M47.23,48.56 C39.58,48.50 38.77,57.44 38.77,57.44 C38.77,57.44 55.72,57.38 55.72,57.38 C55.72,57.38 55.15,48.62 47.23,48.56"
-                    android:strokeWidth="1"
-                    android:strokeColor="#ccced2"/>
+                    android:fillColor="#BDC0C5"
+                    android:pathData="M88.61,42L73.695,42L73.695,41.765C73.695,37.483 77.04,34 81.152,34C85.264,34 88.609,37.483 88.609,41.765L88.609,42L88.61,42ZM74.15,41.529L88.155,41.529C88.035,37.616 84.94,34.47 81.153,34.47C77.365,34.47 74.271,37.616 74.15,41.529Z"/>
             </group>
             <!-- Cloud 3 -->
             <group
@@ -30,10 +39,31 @@
                 android:translateX="0">
 
                 <path
-                    android:fillColor="#f8f9fa"
-                    android:pathData="M189.11,79.57 C180.67,79.67 176.69,84.87 176.69,84.87 C176.69,84.87 165.86,82.71 165.86,93.79 C177.94,93.79 203.86,93.79 203.86,93.79 C203.86,93.79 202.33,79.41 189.11,79.57"
-                    android:strokeWidth="1"
-                    android:strokeColor="#d7d9dd"/>
+                    android:fillColor="#E8E9EB"
+                    android:pathData="M210.791,58.542C217.505,58.542 223,63.839 223.286,70.458L206.441,70.458L197.74,70.458L189.602,70.458C189.882,66.229 193.425,62.875 197.74,62.875C198.654,62.875 199.567,63.033 200.456,63.346L200.819,63.473L201.061,63.176C203.45,60.231 206.997,58.542 210.791,58.542ZM210.791,58C206.688,58 203.03,59.887 200.637,62.836C199.73,62.516 198.758,62.333 197.74,62.333C192.935,62.333 189.04,66.213 189.04,71C191.58,71 194.569,71 197.74,71C200.557,71 203.517,71 206.441,71C211.895,71 217.92,71 223.842,71C223.842,63.821 217.999,58 210.791,58Z"/>
+            </group>
+            <!-- Laptop screen -->
+            <path
+                android:fillColor="#F4F4F4"
+                android:pathData="M190.741,75.661L116.191,75.661L116.191,26.757C116.191,24.369 118.145,22.415 120.533,22.415L186.399,22.415C188.787,22.415 190.741,24.369 190.741,26.757L190.741,75.661Z"/>
+            <path
+                android:fillColor="#BDC0C5"
+                android:pathData="M187.191,22.859C188.904,22.859 190.297,24.252 190.297,25.965L190.297,75.217L116.634,75.217L116.634,25.965C116.634,24.252 118.028,22.859 119.741,22.859L187.191,22.859ZM187.191,22.415L119.741,22.415C117.788,22.415 116.191,24.012 116.191,25.965L116.191,75.661L190.741,75.661L190.741,25.965C190.741,24.012 189.144,22.415 187.191,22.415Z"/>
+            <!-- Laptop keyboard -->
+            <path
+                android:fillColor="#BDC0C5"
+                android:pathData="M105.514,79h95.458v1h-95.458z"/>
+            <!-- Cloud 2 -->
+            <group
+                android:name="cloud2_animation_group"
+                android:translateX="0">
+
+                <path
+                    android:fillColor="#FFFFFF"
+                    android:pathData="M112.036,40.982C108.709,40.982 105.743,42.47 103.803,44.797C103.067,44.544 102.278,44.4 101.453,44.4C97.557,44.4 94.398,47.461 94.398,51.237C96.459,51.237 98.882,51.237 101.453,51.237C103.737,51.237 106.138,51.237 108.508,51.237C112.931,51.237 117.816,51.237 122.618,51.237C122.618,45.573 117.881,40.982 112.036,40.982Z"/>
+                <path
+                    android:fillColor="#BDC0C5"
+                    android:pathData="M122.839,51.45L94.178,51.45L94.178,51.236C94.178,47.348 97.442,44.187 101.453,44.187C102.219,44.187 102.983,44.308 103.729,44.546C105.791,42.143 108.81,40.768 112.035,40.768C117.992,40.768 122.838,45.464 122.838,51.236L122.838,51.45L122.839,51.45ZM94.622,51.023L122.396,51.023C122.279,45.585 117.676,41.196 112.036,41.196C108.892,41.196 105.954,42.557 103.974,44.931L103.876,45.049L103.73,44.999C102.985,44.744 102.219,44.615 101.454,44.615C97.759,44.614 94.739,47.47 94.622,51.023Z"/>
             </group>
             <!-- Cloud 4 -->
             <group
@@ -41,97 +71,58 @@
                 android:translateX="0">
 
                 <path
-                    android:fillColor="#e8e9eb"
-                    android:pathData="M208.42,60.04 C200.50,60.04 199.68,68.90 199.68,68.90 C199.68,68.90 217.03,68.90 217.03,68.90 C217.03,68.90 216.23,60.04 208.42,60.04"/>
-            </group>
-            <!-- Laptop screen -->
-            <path
-                android:fillColor="#f8f9fa"
-                android:pathData="M82.25,35.50 C80.27,35.50 79.50,37.25 79.50,38.96 C79.50,41.46 79.50,99.50 79.50,99.50 C79.50,99.50 169.50,99.50 169.50,99.50 C169.50,99.50 169.50,40.18 169.50,38.96 C169.50,36.85 168.01,35.50 165.63,35.50 C160.13,35.50 84.75,35.50 82.25,35.50"
-                android:strokeWidth="1"
-                android:strokeColor="#d8dadc"/>
-            <!-- Laptop keyboard -->
-            <path
-                android:fillColor="#f8f9fa"
-                android:pathData="M66.25,104.50 L182.25,104.50"
-                android:strokeWidth="1"
-                android:strokeColor="#d8dadc"
-                android:strokeLineCap="round"/>
-            <!-- White and blue curves on the left side -->
-            <path
-                android:pathData="M90.97,81.94 C90.97,81.94 80.00,92.52 65.22,92.52 C51.47,92.52 39.88,80.28 39.88,80.28"
-                android:strokeWidth="5"
-                android:strokeColor="#4285f4"/>
-            <path
-                android:pathData="M-11.34,78.44 C-11.34,78.44 -0.38,67.85 14.41,67.85 C28.16,67.85 39.75,80.09 39.75,80.09"
-                android:strokeWidth="5.5"
-                android:strokeColor="#e8e9eb"/>
-            <!-- Cloud 2 -->
-            <group
-                android:name="cloud2_animation_group"
-                android:translateX="0">
-
-                <path
-                    android:fillColor="#ffffff"
-                    android:pathData="M85.33,58.39 C77.75,58.39 74.58,62.75 74.58,62.75 C74.58,62.75 63.08,60.08 63.08,71.17 C75.17,71.17 97.75,71.17 97.75,71.17 C97.75,71.17 97.25,58.39 85.33,58.39"
-                    android:strokeWidth="1"
-                    android:strokeColor="#ccced2"/>
+                    android:fillColor="#E8E9EB"
+                    android:pathData="M244.723,50C239.752,50 234.64,50 230.802,50C230.802,46.134 233.918,43 237.763,43C241.607,43 244.723,46.134 244.723,50Z"/>
             </group>
             <!-- Mobile phone external edge -->
             <path
-                android:fillColor="#f8f9fa"
-                android:pathData="M248.00,104.38 C248.00,104.38 239.78,104.38 235.53,104.38 C231.28,104.38 229.03,101.44 229.03,96.94 C229.03,93.19 229.00,38.00 229.00,38.00 L228.99,33.15 C228.99,33.15 228.96,26.88 228.96,23.76 C228.96,20.63 231.38,18.55 234.50,18.55 C237.63,18.55 258.09,18.56 258.09,18.56 L269.99,18.57 C271.24,18.57 275.19,20.79 275.19,23.73 C275.19,27.20 275.23,44.86 275.23,44.86 L275.13,89.81 C275.13,89.81 275.19,96.56 275.19,98.00 C275.19,99.44 272.69,104.06 268.88,104.06 L248.00,104.38"
-                android:strokeWidth="1"
-                android:strokeColor="#d8dadc"/>
+                android:fillColor="#BDC0C5"
+                android:pathData="M255.206,44L254.667,44L254.667,15.5C254.667,11.916 257.627,9 261.266,9L280.52,9L280.52,9.531L261.266,9.531C257.924,9.531 255.206,12.209 255.206,15.5L255.206,44M270.576,78.469L261.182,78.469C257.883,78.469 255.199,75.792 255.199,72.5L255.199,44L254.667,44L254.667,72.5C254.667,76.084 257.59,79 261.182,79L270.576,79L270.576,78.469M288.475,9L288.475,9.579C291.026,10.421 292.861,12.681 292.861,15.339L292.861,45L293.446,45L293.446,15.339C293.447,12.378 291.356,9.864 288.475,9Z"/>
+            <path
+                android:fillColor="#E54440"
+                android:pathData="M291.92,67L291.92,72.514C291.92,75.798 289.236,78.47 285.937,78.47L276.542,78.47L276.542,79L285.936,79C289.528,79 292.452,76.09 292.452,72.514L292.452,67L291.92,67Z"/>
             <!-- Mobile phone internal edge -->
             <path
-                android:pathData="M232.71,38.09 C232.71,38.09 232.75,93.14 232.75,95.63 C232.75,98.11 234.32,99.53 236.93,99.53 C239.02,99.53 266.77,99.53 266.84,99.53 C269.10,99.53 270.57,97.53 270.57,95.66 C270.57,93.79 270.58,83.90 270.58,83.90 L270.35,47.41 C270.35,47.41 270.35,27.29 270.35,25.74 C270.35,23.95 268.61,22.64 267.01,22.64 C265.56,22.64 261.64,22.64 261.64,22.64 L254.46,22.36 C254.46,22.36 253.28,22.36 252.0,22.35 C247.43,22.33 237.49,22.27 236.28,22.27 C234.41,22.27 232.74,24.0 232.74,25.73 C232.74,27.72 232.74,31.55 232.74,31.55 L232.71,38.09"
-                android:strokeWidth="0.4"
-                android:strokeColor="#e8e9eb"/>
+                android:fillColor="#E8E9EB"
+                android:pathData="M285.175,76L261.944,76C259.576,76 257.65,74.079 257.65,71.718L257.65,16.282C257.65,13.921 259.576,12 261.944,12L285.175,12C287.542,12 289.469,13.921 289.469,16.282L289.469,71.718C289.469,74.079 287.542,76 285.175,76ZM261.945,12.538C259.874,12.538 258.19,14.218 258.19,16.282L258.19,71.718C258.19,73.782 259.874,75.462 261.945,75.462L285.175,75.462C287.246,75.462 288.93,73.782 288.93,71.718L288.93,16.282C288.93,14.218 287.246,12.538 285.175,12.538L261.945,12.538Z"/>
             <!-- White and yellow curves on the right side -->
             <path
-                android:pathData="M265.72,-0.09 C265.72,-0.09 265.53,16.40 254.96,26.40 C244.98,35.85 228.04,35.54 228.04,35.54"
-                android:strokeWidth="5"
-                android:strokeColor="#f7bb2a"/>
+                android:fillColor="#E8E9EB"
+                android:pathData="M226.156,51L221.853,51C221.853,50.693 221.9,43.393 225.959,36.027C229.737,29.172 237.784,21 254.667,21L254.667,25.363C242.788,25.363 234.394,29.667 229.716,38.155C226.196,44.54 226.156,50.937 226.156,51Z"/>
             <path
-                android:pathData="M191.22,68.79 C191.22,68.79 191.33,51.52 202.63,43.59 C213.88,35.69 228.04,35.54 228.04,35.54"
-                android:strokeWidth="5"
-                android:strokeColor="#e8e9eb"/>
+                android:fillColor="#F7BB2A"
+                android:pathData="M254.667,25L254.667,20.652C263.547,20.652 270.531,18.152 275.424,13.221C283.11,5.476 283.146,-5.853 283.145,-5.967L287.48,-6C287.484,-5.466 287.488,7.193 278.531,16.253C272.794,22.057 264.765,25 254.667,25Z"/>
+            <!-- Light grey circle -->
+            <path
+                android:fillColor="#E8E9EB"
+                android:pathData="M325.763,31.062C334.758,31.062 342.076,38.212 342.076,47C342.076,55.788 334.757,62.938 325.763,62.938C316.768,62.938 309.449,55.788 309.449,47C309.449,38.212 316.767,31.062 325.763,31.062ZM325.763,30C316.152,30 308.362,37.611 308.362,47C308.362,56.389 316.152,64 325.763,64C335.373,64 343.164,56.389 343.164,47C343.164,37.611 335.373,30 325.763,30Z"/>
             <!-- Green circle -->
             <path
-                android:fillColor="#34a751"
-                android:pathData="M283.21,45.08 C294.33,45.08 303.37,54.11 303.37,65.24 C303.37,76.37 294.33,85.41 283.21,85.41 C272.08,85.41 263.04,76.37 263.04,65.24 C263.04,54.11 272.08,45.08 283.21,45.08"/>
+                android:fillColor="#34A751"
+                android:pathData="M280.52,47a17.401,17 0,1 0,34.802 0a17.401,17 0,1 0,-34.802 0z"/>
             <!-- Sync arrows -->
             <group
                 android:name="sync_arrows_rotation_group"
-                android:pivotX="283.21"
-                android:pivotY="65.24"
+                android:pivotX="297.92"
+                android:pivotY="47"
                 android:rotation="0">
 
                 <path
-                    android:pathData="M278.31,62.54 C276.19,66.89 279.26,70.97 283.11,70.84 M283.21,69.82 C283.21,69.82 283.21,67.42 283.21,67.42 C283.21,67.42 286.41,70.82 286.41,70.82 C286.41,70.82 283.21,74.02 283.21,74.02 C283.21,74.02 283.21,71.72 283.21,71.72 M288.11,67.94 C290.23,63.59 287.16,59.51 283.31,59.64 M283.21,60.66 C283.21,60.66 283.21,63.06 283.21,63.06 C283.21,63.06 280.01,59.66 280.01,59.66 C280.01,59.66 283.21,56.46 283.21,56.46 C283.21,56.46 283.21,58.76 283.21,58.76"
-                    android:strokeWidth="0.6"
-                    android:strokeColor="#cbcbcb"/>
+                    android:fillColor="#FFFFFF"
+                    android:pathData="M297.722,54.149L297.722,51.997L297.981,51.997L297.981,53.479L299.925,51.391L297.981,49.303L297.981,50.785L297.722,50.785L297.722,48.632L300.291,51.391L297.722,54.149ZM297.98,45.667L295.412,42.909L297.98,40.15L297.98,42.303L297.721,42.303L297.721,40.821L295.777,42.909L297.721,44.997L297.721,43.515L297.98,43.515L297.98,45.667ZM301.395,49.392L301.173,49.251C301.521,48.617 301.705,47.89 301.705,47.15C301.705,44.868 299.976,43.011 297.852,43.011L297.852,42.733C300.119,42.733 301.964,44.715 301.964,47.149C301.963,47.939 301.767,48.715 301.395,49.392ZM297.851,51.566C295.584,51.566 293.739,49.585 293.739,47.149C293.739,46.359 293.936,45.583 294.307,44.907L294.529,45.048C294.181,45.682 293.997,46.408 293.997,47.148C293.997,49.431 295.726,51.287 297.851,51.287L297.851,51.566Z"/>
             </group>
-            <!-- Light grey circle -->
-            <path
-                android:fillColor="#f5f5f5"
-                android:pathData="M315.04,45.08 C326.16,45.08 335.20,54.11 335.20,65.24 C335.20,76.37 326.16,85.41 315.04,85.41 C303.91,85.41 294.87,76.37 294.87,65.24 C294.87,54.11 303.91,45.08 315.04,45.08"
-                android:strokeWidth="0.5"
-                android:strokeColor="#dedede"/>
             <!-- Blue dot -->
             <group
                 android:name="blue_dot_rotation_group"
-                android:pivotX="315.04"
-                android:pivotY="65.24"
+                android:pivotX="325.763"
+                android:pivotY="47"
                 android:rotation="-120">
 
                 <path
-                    android:fillColor="#4b87f1"
-                    android:pathData="M327.75,62.66 C329.17,62.66 330.33,63.82 330.33,65.24 C330.33,66.66 329.17,67.81 327.75,67.81 C326.34,67.81 325.18,66.66 325.18,65.24 C325.18,63.82 326.34,62.66 327.75,62.66"/>
+                    android:fillColor="#4B87F1"
+                    android:pathData="M334,47a1.989,2 0,1 0,3.977 0a1.989,2 0,1 0,-3.977 0z"/>
             </group>
 
-            <!-- TODO(https://crbug.com/814728): Add other parts of this image. -->
         </vector>
     </aapt:attr>
     <!-- Each animation here has a period of 10.45 seconds. Animation is restarted from the code. -->
@@ -143,12 +134,12 @@
                     android:interpolator="@anim/fast_out_slow_in_interpolator"
                     android:propertyName="translateX"
                     android:valueFrom="0"
-                    android:valueTo="-3.24"/>
+                    android:valueTo="-3.645"/>
                 <objectAnimator
                     android:duration="6550"
                     android:interpolator="@anim/fast_out_slow_in_interpolator"
                     android:propertyName="translateX"
-                    android:valueFrom="-3.24"
+                    android:valueFrom="-3.645"
                     android:valueTo="0"/>
             </set>
         </aapt:attr>
@@ -161,12 +152,12 @@
                     android:interpolator="@anim/fast_out_slow_in_interpolator"
                     android:propertyName="translateX"
                     android:valueFrom="0"
-                    android:valueTo="-2.16"/>
+                    android:valueTo="-2.43"/>
                 <objectAnimator
                     android:duration="5800"
                     android:interpolator="@anim/fast_out_slow_in_interpolator"
                     android:propertyName="translateX"
-                    android:valueFrom="-2.16"
+                    android:valueFrom="-2.43"
                     android:valueTo="0"/>
             </set>
         </aapt:attr>
@@ -179,12 +170,12 @@
                     android:interpolator="@anim/fast_out_slow_in_interpolator"
                     android:propertyName="translateX"
                     android:valueFrom="0"
-                    android:valueTo="-5.04"/>
+                    android:valueTo="-5.67"/>
                 <objectAnimator
                     android:duration="4300"
                     android:interpolator="@anim/fast_out_slow_in_interpolator"
                     android:propertyName="translateX"
-                    android:valueFrom="-5.04"
+                    android:valueFrom="-5.67"
                     android:valueTo="0"/>
             </set>
         </aapt:attr>
@@ -197,12 +188,12 @@
                     android:interpolator="@anim/fast_out_slow_in_interpolator"
                     android:propertyName="translateX"
                     android:valueFrom="0"
-                    android:valueTo="5.04"/>
+                    android:valueTo="5.67"/>
                 <objectAnimator
                     android:duration="4900"
                     android:interpolator="@anim/fast_out_slow_in_interpolator"
                     android:propertyName="translateX"
-                    android:valueFrom="5.04"
+                    android:valueFrom="5.67"
                     android:valueTo="0"/>
             </set>
         </aapt:attr>
diff --git a/chrome/android/java/res/layout/download_location_spinner_dropdown_item.xml b/chrome/android/java/res/layout/download_location_spinner_dropdown_item.xml
index ec37be8..a3e31c95 100644
--- a/chrome/android/java/res/layout/download_location_spinner_dropdown_item.xml
+++ b/chrome/android/java/res/layout/download_location_spinner_dropdown_item.xml
@@ -6,6 +6,7 @@
 <!-- Note: Nested layouts are used because the styling was being overwritten,
      likely because of the behavior of the Android Spinner class. -->
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:background="@android:color/white"
     android:layout_width="match_parent"
     android:layout_height="wrap_content" >
 
diff --git a/chrome/android/java/res/layout/signin_view.xml b/chrome/android/java/res/layout/signin_view.xml
index 08e8d31..34277f23 100644
--- a/chrome/android/java/res/layout/signin_view.xml
+++ b/chrome/android/java/res/layout/signin_view.xml
@@ -28,7 +28,7 @@
                 android:layout_alignParentTop="true"
                 android:background="@color/signin_header_animation_background"
                 android:contentDescription="@null"
-                android:scaleType="fitCenter"
+                android:scaleType="center"
                 chrome:srcCompat="@drawable/signin_header_animation"
                 tools:src="@drawable/signin_header_animation"/>
             <TextView
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
index f25c9d8..04b32215 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -1354,7 +1354,7 @@
         mNativeInitialized = true;
         OfflineContentAggregatorNotificationBridgeUiFactory.instance();
         maybeRemoveWindowBackground();
-        DownloadManagerService.getDownloadManagerService().onActivityLaunched(this);
+        DownloadManagerService.getDownloadManagerService().onActivityLaunched();
 
         if (getSavedInstanceState() == null && getIntent() != null) {
             VrShellDelegate.onNewIntentWithNative(this, getIntent());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
index b9fc945a..714241e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
@@ -42,7 +42,7 @@
      */
     public static boolean isInitialized() {
         if (sTestFeatures != null) return true;
-        if (!LibraryLoader.isInitialized()) return false;
+        if (!LibraryLoader.getInstance().isInitialized()) return false;
 
         // Even if the native library is loaded, the C++ FeatureList might not be initialized yet.
         // In that case, accessing it will not immediately fail, but instead cause a crash later
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeStrictMode.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeStrictMode.java
index 9d10374d..275e241 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeStrictMode.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeStrictMode.java
@@ -106,7 +106,7 @@
         // Delay handling StrictMode violations during initialization until the main loop is idle.
         Looper.myQueue().addIdleHandler(() -> {
             // Will retry if the native library has not been initialized.
-            if (!LibraryLoader.isInitialized()) return true;
+            if (!LibraryLoader.getInstance().isInitialized()) return true;
             // Check again next time if no more cached stack traces to upload, and we have not
             // reached the max number of uploads for this session.
             if (sCachedStackTraces.isEmpty()) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index 342308a4..3d2ed4a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -1319,7 +1319,7 @@
         // and triggers initialization of the native library. At the moment it seems safe to assume
         // that uninitialized native library is an indication of an application start that is
         // followed by navigation immediately without user input.
-        if (!LibraryLoader.isInitialized()) {
+        if (!LibraryLoader.getInstance().isInitialized()) {
             getActivityTabStartupMetricsTracker().trackStartupMetrics(STARTUP_UMA_HISTOGRAM_SUFFIX);
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/MonochromeApplication.java b/chrome/android/java/src/org/chromium/chrome/browser/MonochromeApplication.java
index 808af03..8223aae0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/MonochromeApplication.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/MonochromeApplication.java
@@ -23,7 +23,7 @@
     public void onCreate() {
         super.onCreate();
         FontPreloadingWorkaround.maybeInstallWorkaround(this);
-        LibraryLoader.setNativeLibraryPreloader(new MonochromeLibraryPreloader());
+        LibraryLoader.getInstance().setNativeLibraryPreloader(new MonochromeLibraryPreloader());
         // ChildProcessCreationParams is only needed for browser process, though it is
         // created and set in all processes.
         boolean bindToCaller = false;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/WarmupManager.java b/chrome/android/java/src/org/chromium/chrome/browser/WarmupManager.java
index 3f7bb6c3..14686b95 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/WarmupManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/WarmupManager.java
@@ -280,7 +280,7 @@
      */
     public void createSpareRenderProcessHost(Profile profile) {
         ThreadUtils.assertOnUiThread();
-        if (!LibraryLoader.isInitialized()) return;
+        if (!LibraryLoader.getInstance().isInitialized()) return;
         if (ChromeFeatureList.isEnabled(ChromeFeatureList.OMNIBOX_SPARE_RENDERER)) {
             // Spare WebContents should not be used with spare RenderProcessHosts, but if one
             // has been created, destroy it in order not to consume too many processes.
@@ -298,7 +298,7 @@
      */
     public void createSpareWebContents() {
         ThreadUtils.assertOnUiThread();
-        if (!LibraryLoader.isInitialized()) return;
+        if (!LibraryLoader.getInstance().isInitialized()) return;
         if (mSpareWebContents != null || SysUtils.isLowEndDevice()) return;
         mSpareWebContents = WebContentsFactory.createWebContentsWithWarmRenderer(
                 false /* incognito */, true /* initiallyHidden */);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ClusterList.java b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ClusterList.java
index 158a0ad..a60a130 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ClusterList.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ClusterList.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.contextual_suggestions;
 
+import org.chromium.chrome.browser.ntp.cards.ChildNode;
 import org.chromium.chrome.browser.ntp.cards.InnerNode;
 
 import java.util.List;
@@ -12,23 +13,30 @@
  * A node in a tree containing a list of {@link ContextualSuggestionsCluster}s.
  */
 class ClusterList extends InnerNode {
-    private boolean mIsDestroyed;
-
     /**
-     * Construct a new {@link ClusterList}.
-     * @param clusters The list of clusters held by this ClusterList.
+     * Replaces the list of clusters under this node with a new list. Any previous clusters will be
+     * destroyed.
+     *
+     * @param clusters The new list of clusters for this node.
      */
-    ClusterList(List<ContextualSuggestionsCluster> clusters) {
+    public void setClusters(List<ContextualSuggestionsCluster> clusters) {
+        destroyClusters();
+        removeChildren();
         for (ContextualSuggestionsCluster cluster : clusters) {
-            addChild(cluster);
+            addChildren(cluster);
         }
     }
 
-    /** Remove all clusters and detach itself from its parent. */
-    void destroy() {
-        assert !mIsDestroyed;
-        mIsDestroyed = true;
-        removeChildren();
-        detach();
+    /**
+     * Destroys all clusters under this node.
+     */
+    public void destroy() {
+        destroyClusters();
+    }
+
+    private void destroyClusters() {
+        for (ChildNode c : getChildren()) {
+            ((ContextualSuggestionsCluster) c).destroy();
+        }
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContentCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContentCoordinator.java
index 4d5d3a3..11e2cab 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContentCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContentCoordinator.java
@@ -13,7 +13,6 @@
 
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.contextual_suggestions.ContextualSuggestionsModel.ClusterListObservable;
 import org.chromium.chrome.browser.modelutil.RecyclerViewModelChangeProcessor;
 import org.chromium.chrome.browser.ntp.ContextMenuManager;
 import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder;
@@ -33,7 +32,7 @@
     private ContextualSuggestionsModel mModel;
     private WindowAndroid mWindowAndroid;
     private ContextMenuManager mContextMenuManager;
-    private RecyclerViewModelChangeProcessor<ClusterListObservable, NewTabPageViewHolder>
+    private RecyclerViewModelChangeProcessor<ClusterList, NewTabPageViewHolder>
             mModelChangeProcessor;
 
     /**
@@ -77,12 +76,13 @@
                 mRecyclerView::setTouchEnabled, closeContextMenuCallback);
         mWindowAndroid.addContextMenuCloseListener(mContextMenuManager);
 
-        ContextualSuggestionsAdapter adapter = new ContextualSuggestionsAdapter(context, profile,
-                new UiConfig(mRecyclerView), uiDelegate, mModel, mContextMenuManager);
+        ContextualSuggestionsAdapter adapter =
+                new ContextualSuggestionsAdapter(profile, new UiConfig(mRecyclerView), uiDelegate,
+                        mModel.getClusterList(), mContextMenuManager);
         mRecyclerView.setAdapter(adapter);
 
         mModelChangeProcessor = new RecyclerViewModelChangeProcessor<>(adapter);
-        mModel.mClusterListObservable.addObserver(mModelChangeProcessor);
+        mModel.getClusterList().addObserver(mModelChangeProcessor);
 
         // TODO(twellington): Should this be a proper model property, set by the mediator and bound
         // to the RecyclerView?
@@ -108,7 +108,7 @@
         // The model outlives the content sub-component. Remove the observer so that this object
         // can be garbage collected.
         if (mModelChangeProcessor != null) {
-            mModel.mClusterListObservable.removeObserver(mModelChangeProcessor);
+            mModel.getClusterList().removeObserver(mModelChangeProcessor);
         }
         if (mWindowAndroid != null) {
             mWindowAndroid.removeContextMenuCloseListener(mContextMenuManager);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsAdapter.java
index c6807e5..da48f79 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsAdapter.java
@@ -4,11 +4,9 @@
 
 package org.chromium.chrome.browser.contextual_suggestions;
 
-import android.content.Context;
 import android.support.v7.widget.RecyclerView;
 import android.view.ViewGroup;
 
-import org.chromium.chrome.browser.contextual_suggestions.ContextualSuggestionsModel.ClusterListObservable;
 import org.chromium.chrome.browser.modelutil.RecyclerViewAdapter;
 import org.chromium.chrome.browser.ntp.ContextMenuManager;
 import org.chromium.chrome.browser.ntp.cards.ItemViewType;
@@ -25,10 +23,9 @@
 /**
  * An adapter that contains the view binder for the content component.
  */
-class ContextualSuggestionsAdapter
-        extends RecyclerViewAdapter<ClusterListObservable, NewTabPageViewHolder> {
+class ContextualSuggestionsAdapter extends RecyclerViewAdapter<ClusterList, NewTabPageViewHolder> {
     private class ContextualSuggestionsViewBinder
-            implements ViewBinder<ClusterListObservable, NewTabPageViewHolder> {
+            implements ViewBinder<ClusterList, NewTabPageViewHolder> {
         @Override
         public NewTabPageViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
             switch (viewType) {
@@ -47,48 +44,46 @@
         }
 
         @Override
-        public void onBindViewHolder(
-                ClusterListObservable model, NewTabPageViewHolder holder, int position) {
-            model.mClusterList.onBindViewHolder(holder, position);
+        public void onBindViewHolder(ClusterList model, NewTabPageViewHolder holder, int position) {
+            model.onBindViewHolder(holder, position);
         }
     }
 
     private final Profile mProfile;
     private final UiConfig mUiConfig;
     private final SuggestionsUiDelegate mUiDelegate;
-    private final ContextualSuggestionsModel mModel;
+    private final ClusterList mClusterList;
     private final ContextMenuManager mContextMenuManager;
 
     private SuggestionsRecyclerView mRecyclerView;
 
     /**
      * Construct a new {@link ContextualSuggestionsAdapter}.
-     * @param context The {@link Context} used to retrieve resources.
      * @param profile The regular {@link Profile}.
      * @param uiConfig The {@link UiConfig} used to adjust view display.
      * @param uiDelegate The {@link SuggestionsUiDelegate} used to help construct items in the
      *                   content view.
-     * @param model The {@link ContextualSuggestionsModel} for the component.
+     * @param clusterList The {@link ClusterList} for the component.
      * @param contextMenuManager The {@link ContextMenuManager} used to display a context menu.
      */
-    ContextualSuggestionsAdapter(Context context, Profile profile, UiConfig uiConfig,
-            SuggestionsUiDelegate uiDelegate, ContextualSuggestionsModel model,
+    ContextualSuggestionsAdapter(Profile profile, UiConfig uiConfig,
+            SuggestionsUiDelegate uiDelegate, ClusterList clusterList,
             ContextMenuManager contextMenuManager) {
-        super(model.mClusterListObservable, null);
+        super(clusterList, null);
 
         setViewBinder(new ContextualSuggestionsViewBinder());
 
         mProfile = profile;
         mUiConfig = uiConfig;
         mUiDelegate = uiDelegate;
-        mModel = model;
+        mClusterList = clusterList;
         mContextMenuManager = contextMenuManager;
     }
 
     @Override
     @ItemViewType
     public int getItemViewType(int position) {
-        return mModel.getClusterList().getItemViewType(position);
+        return mClusterList.getItemViewType(position);
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsCluster.java b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsCluster.java
index 6c367dc..915e4271 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsCluster.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsCluster.java
@@ -63,12 +63,12 @@
     void buildChildren() {
         if (mShouldShowTitle) {
             mHeader = new SectionHeader(mTitle);
-            addChild(mHeader);
+            addChildren(mHeader);
         }
 
         mSuggestionsList = new SuggestionsList();
         mSuggestionsList.addAll(mSuggestions);
-        addChild(mSuggestionsList);
+        addChildren(mSuggestionsList);
 
         // Only add observer after suggestions have been added to the cluster node to avoid
         // OfflineModelObserver requesting a null list.
@@ -77,9 +77,8 @@
         mOfflineModelObserver.updateAllSuggestionsOfflineAvailability(false);
     }
 
-    @Override
-    public void detach() {
-        super.detach();
+    public void destroy() {
+        // TODO(bauerb): This should be part of a mediator instead of the cluster itself.
         if (mOfflineModelObserver != null) mOfflineModelObserver.onDestroy();
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsCoordinator.java
index a85795c..09d73494 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsCoordinator.java
@@ -34,11 +34,11 @@
 public class ContextualSuggestionsCoordinator {
     private static final String FEEDBACK_CONTEXT = "contextual_suggestions";
 
+    private final Profile mProfile = Profile.getLastUsedProfile().getOriginalProfile();
+    private final ContextualSuggestionsModel mModel = new ContextualSuggestionsModel();
     private final ChromeActivity mActivity;
     private final BottomSheetController mBottomSheetController;
     private final TabModelSelector mTabModelSelector;
-    private final Profile mProfile;
-    private final ContextualSuggestionsModel mModel;
     private final ContextualSuggestionsMediator mMediator;
 
     private @Nullable ToolbarCoordinator mToolbarCoordinator;
@@ -56,9 +56,7 @@
         mActivity = activity;
         mBottomSheetController = bottomSheetController;
         mTabModelSelector = tabModelSelector;
-        mProfile = Profile.getLastUsedProfile().getOriginalProfile();
 
-        mModel = new ContextualSuggestionsModel();
         mMediator = new ContextualSuggestionsMediator(mProfile, tabModelSelector,
                 activity.getFullscreenManager(), this, mModel,
                 mBottomSheetController.getBottomSheet());
@@ -66,6 +64,7 @@
 
     /** Called when the containing activity is destroyed. */
     public void destroy() {
+        mModel.getClusterList().destroy();
         mMediator.destroy();
 
         if (mToolbarCoordinator != null) mToolbarCoordinator.destroy();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsMediator.java
index 912147e..081bd5c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsMediator.java
@@ -265,12 +265,17 @@
                 }, remainingDelay);
             }
 
-            if (clusters.size() > 0 && clusters.get(0).getSuggestions().size() > 0) {
-                prepareModel(generateClusterList(clusters), suggestionsResult.getPeekText());
-                // If the controls are already off-screen, show the suggestions immediately so they
-                // are available on reverse scroll.
-                maybeShowContentInSheet();
+            if (clusters.isEmpty() || clusters.get(0).getSuggestions().isEmpty()) return;
+
+            for (ContextualSuggestionsCluster cluster : clusters) {
+                cluster.buildChildren();
             }
+
+            prepareModel(clusters, suggestionsResult.getPeekText());
+
+            // If the controls are already off-screen, show the suggestions immediately so they
+            // are available on reverse scroll.
+            maybeShowContentInSheet();
         });
     }
 
@@ -332,7 +337,7 @@
         mUpdateRemainingCountOnNextPeek = false;
         mTargetScrollPercentage = INVALID_PERCENTAGE;
         mRemainingPeekCount = 0f;
-        mModel.setClusterList(new ClusterList(Collections.emptyList()));
+        mModel.setClusterList(Collections.emptyList());
         mModel.setCloseButtonOnClickListener(null);
         mModel.setMenuButtonVisibility(false);
         if (!mModel.isSlimPeekEnabled()) {
@@ -356,7 +361,7 @@
         }
     }
 
-    private void prepareModel(ClusterList clusters, String title) {
+    private void prepareModel(List<ContextualSuggestionsCluster> clusters, String title) {
         if (mSuggestionsSource == null) return;
 
         mModel.setClusterList(clusters);
@@ -517,14 +522,6 @@
         mSuggestionsSource.reportEvent(mTabModelSelector.getCurrentTab().getWebContents(), event);
     }
 
-    private ClusterList generateClusterList(List<ContextualSuggestionsCluster> clusters) {
-        for (ContextualSuggestionsCluster cluster : clusters) {
-            cluster.buildChildren();
-        }
-
-        return new ClusterList(clusters);
-    }
-
     private void updateSlimPeekTranslation(float bottomSheetOffsetPx) {
         // When the sheet is closed, the toolbar translation is 1.0 to indicate the main
         // toolbar content is fully translated. As the bottomSheetOffsetPx increases, the
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsModel.java b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsModel.java
index ea4ce92..d9014ef 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsModel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsModel.java
@@ -4,17 +4,12 @@
 
 package org.chromium.chrome.browser.contextual_suggestions;
 
-import android.support.annotation.Nullable;
 import android.view.View.OnClickListener;
 
-import org.chromium.chrome.browser.modelutil.ListObservable;
 import org.chromium.chrome.browser.modelutil.PropertyObservable;
-import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder;
-import org.chromium.chrome.browser.ntp.cards.NodeParent;
-import org.chromium.chrome.browser.ntp.cards.TreeNode;
 import org.chromium.chrome.browser.widget.ListMenuButton;
 
-import java.util.Collections;
+import java.util.List;
 
 /** A model for the contextual suggestions UI component. */
 class ContextualSuggestionsModel
@@ -35,54 +30,8 @@
         private PropertyKey() {}
     }
 
-    /** A {@link ListObservable} containing the current cluster list. */
-    class ClusterListObservable extends ListObservable implements NodeParent {
-        ClusterList mClusterList = new ClusterList(Collections.emptyList());
+    private final ClusterList mClusterList = new ClusterList();
 
-        /** Constructor to initialize parent of cluster list. */
-        ClusterListObservable() {
-            mClusterList.setParent(this);
-        }
-
-        private void setClusterList(ClusterList clusterList) {
-            assert clusterList != null;
-
-            // Destroy the old cluster list.
-            mClusterList.destroy();
-
-            mClusterList = clusterList;
-            mClusterList.setParent(this);
-
-            if (getItemCount() != 0) notifyItemRangeInserted(0, getItemCount());
-        }
-
-        @Override
-        public int getItemCount() {
-            return mClusterList.getItemCount();
-        }
-
-        // NodeParent implementations.
-        @Override
-        public void onItemRangeChanged(TreeNode child, int index, int count,
-                @Nullable NewTabPageViewHolder.PartialBindCallback callback) {
-            assert child == mClusterList;
-            notifyItemRangeChanged(index, count, callback);
-        }
-
-        @Override
-        public void onItemRangeInserted(TreeNode child, int index, int count) {
-            assert child == mClusterList;
-            notifyItemRangeInserted(index, count);
-        }
-
-        @Override
-        public void onItemRangeRemoved(TreeNode child, int index, int count) {
-            assert child == mClusterList;
-            notifyItemRangeRemoved(index, count);
-        }
-    }
-
-    ClusterListObservable mClusterListObservable = new ClusterListObservable();
     private OnClickListener mCloseButtonOnClickListener;
     private boolean mMenuButtonVisibility;
     private float mMenuButtonAlpha = 1.f;
@@ -94,14 +43,14 @@
     private float mToolbarTranslationPercent;
     private int mToolbarArrowTintResourceId;
 
-    /** @param clusterList The current list of clusters. */
-    void setClusterList(ClusterList clusterList) {
-        mClusterListObservable.setClusterList(clusterList);
+    /** @param clusters The current list of clusters. */
+    void setClusterList(List<ContextualSuggestionsCluster> clusters) {
+        mClusterList.setClusters(clusters);
     }
 
     /** @return The current list of clusters. */
     ClusterList getClusterList() {
-        return mClusterListObservable.mClusterList;
+        return mClusterList;
     }
 
     /** @param listener The {@link OnClickListener} for the close button. */
@@ -215,7 +164,7 @@
     }
 
     /**
-     * @param return The toolbar translation percent where 1.f means the main toolbar content is
+     * @return The toolbar translation percent where 1.f means the main toolbar content is
      *               fully translated and 0.f means it is not translated at all. This is used by
      *               the slim peek UI to animate from fully translated when the sheet is closed
      *               to not at all translated when the sheet is opened.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
index fe4ebe27..17e74f88 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
@@ -914,7 +914,7 @@
 
     @Override
     protected boolean handleBackPressed() {
-        if (!LibraryLoader.isInitialized()) return false;
+        if (!LibraryLoader.getInstance().isInitialized()) return false;
 
         RecordUserAction.record("CustomTabs.SystemBack");
         if (getActivityTab() == null) return false;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadInfoBarController.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadInfoBarController.java
index 55f967d9..9310324 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadInfoBarController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadInfoBarController.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.download;
 
+import android.app.Activity;
 import android.content.Context;
 import android.os.Handler;
 import android.support.annotation.Nullable;
@@ -11,6 +12,7 @@
 import android.text.TextUtils;
 import android.text.format.Formatter;
 
+import org.chromium.base.ApplicationStatus;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.metrics.RecordHistogram;
@@ -25,8 +27,6 @@
 import org.chromium.chrome.browser.infobar.InfoBarIdentifier;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.browser.tabmodel.TabModelSelector;
-import org.chromium.chrome.browser.tabmodel.TabModelUtils;
 import org.chromium.chrome.browser.toolbar.ToolbarButtonInProductHelpController;
 import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.components.download.DownloadState;
@@ -238,9 +238,6 @@
     // Represents the currently displayed InfoBar data.
     private DownloadProgressInfoBarData mCurrentInfo;
 
-    // The primary means of getting the currently active tab.
-    private TabModelSelector mTabModelSelector;
-
     /** Constructor. */
     public DownloadInfoBarController(boolean isIncognito) {
         mIsIncognito = isIncognito;
@@ -248,14 +245,6 @@
     }
 
     /**
-     * Sets the {@link TabModelSelector} that will be used to get the currently active tab.
-     * @param selector A {@link TabModelSelector} that represents the state of the system.
-     */
-    public void setTabModelSelector(TabModelSelector selector) {
-        mTabModelSelector = selector;
-    }
-
-    /**
      * Shows the message that download has started. Unlike other methods in this class, this
      * method doesn't require an {@link OfflineItem} and is invoked by the backend to provide a
      * responsive feedback to the users even before the download has actually started.
@@ -787,8 +776,12 @@
 
     @Nullable
     private Tab getCurrentTab() {
-        if (mTabModelSelector == null) return null;
-        return TabModelUtils.getCurrentTab(mTabModelSelector.getModel(mIsIncognito));
+        if (!ApplicationStatus.hasVisibleActivities()) return null;
+        Activity activity = ApplicationStatus.getLastTrackedFocusedActivity();
+        if (!(activity instanceof ChromeTabbedActivity)) return null;
+        Tab tab = ((ChromeTabbedActivity) activity).getActivityTab();
+        if (tab.isIncognito() != mIsIncognito) return null;
+        return tab;
     }
 
     private Context getContext() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
index bfffb06..ffaa358 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
@@ -31,7 +31,6 @@
 import org.chromium.base.library_loader.LibraryProcessType;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.download.DownloadMetrics.DownloadOpenSource;
 import org.chromium.chrome.browser.download.ui.BackendProvider;
@@ -384,16 +383,12 @@
     /**
      * Called when browser activity is launched. For background resumption and cancellation, this
      * will not be called.
-     * @param activity The activity being launched.
      */
-    public void onActivityLaunched(ChromeActivity activity) {
+    public void onActivityLaunched() {
         DownloadNotificationService.clearResumptionAttemptLeft();
 
         DownloadManagerService.getDownloadManagerService().checkForExternallyRemovedDownloads(
                 /*isOffTheRecord=*/false);
-
-        mInfoBarController.setTabModelSelector(activity.getTabModelSelector());
-        mIncognitoInfoBarController.setTabModelSelector(activity.getTabModelSelector());
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationUmaHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationUmaHelper.java
index 56bf200..bda88df 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationUmaHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationUmaHelper.java
@@ -64,7 +64,7 @@
      * @param action Notification interaction that was taken (ie. pause, resume).
      */
     static void recordNotificationInteractionHistogram(String action) {
-        if (!LibraryLoader.isInitialized()) return;
+        if (!LibraryLoader.getInstance().isInitialized()) return;
         int actionType = sInteractions.indexOf(action);
         if (actionType == -1) return;
         RecordHistogram.recordEnumeratedHistogram("Android.DownloadManager.NotificationInteraction",
@@ -77,7 +77,7 @@
      * @param stopType Type of the foreground stop that is being recorded ({@link ServiceStopped}).
      */
     static void recordServiceStoppedHistogram(int stopType, boolean withForeground) {
-        if (!LibraryLoader.isInitialized()) return;
+        if (!LibraryLoader.getInstance().isInitialized()) return;
         if (withForeground) {
             RecordHistogram.recordEnumeratedHistogram(
                     "Android.DownloadManager.ServiceStopped.DownloadForeground", stopType,
@@ -95,7 +95,7 @@
      * @param lifecycleStep The lifecycle step that is being recorded ({@link ForegroundLifecycle}).
      */
     static void recordForegroundServiceLifecycleHistogram(int lifecycleStep) {
-        if (!LibraryLoader.isInitialized()) return;
+        if (!LibraryLoader.getInstance().isInitialized()) return;
         RecordHistogram.recordEnumeratedHistogram(
                 "Android.DownloadManager.ForegroundServiceLifecycle", lifecycleStep,
                 ForegroundLifecycle.MAX);
@@ -109,7 +109,7 @@
      * @param withForeground Whether this is with foreground enabled or not.
      */
     static void recordExistingNotificationsCountHistogram(int count, boolean withForeground) {
-        if (!LibraryLoader.isInitialized()) return;
+        if (!LibraryLoader.getInstance().isInitialized()) return;
         if (withForeground) {
             RecordHistogram.recordCountHistogram(
                     "Android.DownloadManager.NotificationsCount.ForegroundEnabled", count);
@@ -125,7 +125,7 @@
      * @param launchType Whether it is a launch or a relaunch ({@link LaunchType}).
      */
     static void recordNotificationFlickerCountHistogram(int launchType) {
-        if (!LibraryLoader.isInitialized()) return;
+        if (!LibraryLoader.getInstance().isInitialized()) return;
         RecordHistogram.recordEnumeratedHistogram(
                 "Android.DownloadManager.NotificationLaunch", launchType, LaunchType.MAX);
     }
@@ -137,7 +137,7 @@
      */
     static void recordStateAtCancelHistogram(boolean isDownload, int state) {
         if (state == -1) return;
-        if (!LibraryLoader.isInitialized()) return;
+        if (!LibraryLoader.getInstance().isInitialized()) return;
         if (isDownload) {
             RecordHistogram.recordEnumeratedHistogram(
                     "Android.OfflineItems.StateAtCancel.Downloads", state, StateAtCancel.MAX);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/ChromeFullscreenManager.java b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/ChromeFullscreenManager.java
index e213511..fbd4a3ba 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/ChromeFullscreenManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/ChromeFullscreenManager.java
@@ -273,7 +273,7 @@
         if (mActivity != activity) return;
         onWindowFocusChanged(hasFocus);
         // {@link ContentVideoView#getContentVideoView} requires native to have been initialized.
-        if (!LibraryLoader.isInitialized()) return;
+        if (!LibraryLoader.getInstance().isInitialized()) return;
         ContentVideoView videoView = ContentVideoView.getInstance();
         if (videoView != null) {
             videoView.onFullscreenWindowFocused();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java
index ef2b59b..dd0047a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java
@@ -131,7 +131,7 @@
     @Override
     public void preInflationStartup() {
         mIsTablet = DeviceFormFactor.isNonMultiDisplayContextOnTablet(this);
-        mHadWarmStart = LibraryLoader.isInitialized();
+        mHadWarmStart = LibraryLoader.getInstance().isInitialized();
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/locale/LocaleManager.java b/chrome/android/java/src/org/chromium/chrome/browser/locale/LocaleManager.java
index 6f9e3e42..cf54e2e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/locale/LocaleManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/locale/LocaleManager.java
@@ -234,7 +234,7 @@
      */
     public void showSearchEnginePromoIfNeeded(
             final Activity activity, final @Nullable Callback<Boolean> onSearchEngineFinalized) {
-        assert LibraryLoader.isInitialized();
+        assert LibraryLoader.getInstance().isInitialized();
         TemplateUrlService.getInstance().runWhenLoaded(new Runnable() {
             @Override
             public void run() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/PictureInPictureController.java b/chrome/android/java/src/org/chromium/chrome/browser/media/PictureInPictureController.java
index f366f35..c40ca86 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/PictureInPictureController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/PictureInPictureController.java
@@ -99,7 +99,7 @@
         if (webContents == null) return false;
 
         // Non-null WebContents implies the native library has been loaded.
-        assert LibraryLoader.isInitialized();
+        assert LibraryLoader.getInstance().isInitialized();
         if (!ChromeFeatureList.isEnabled(ChromeFeatureList.VIDEO_PERSISTENCE)) return false;
 
         // Only auto-PiP if there is a playing fullscreen video that allows PiP.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/remote/RecordCastAction.java b/chrome/android/java/src/org/chromium/chrome/browser/media/remote/RecordCastAction.java
index 65e0f1a5..2122183 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/remote/RecordCastAction.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/remote/RecordCastAction.java
@@ -36,7 +36,8 @@
     public static void remotePlaybackDeviceSelected(int playerType) {
         assert playerType >= 0
                 && playerType < RecordCastAction.DEVICE_TYPE_COUNT;
-        if (LibraryLoader.isInitialized()) nativeRecordRemotePlaybackDeviceSelected(playerType);
+        if (LibraryLoader.getInstance().isInitialized())
+            nativeRecordRemotePlaybackDeviceSelected(playerType);
     }
 
     /**
@@ -45,7 +46,7 @@
      * selecting the device initially.
      */
     public static void castPlayRequested() {
-        if (LibraryLoader.isInitialized()) nativeRecordCastPlayRequested();
+        if (LibraryLoader.getInstance().isInitialized()) nativeRecordCastPlayRequested();
     }
 
     /**
@@ -54,7 +55,8 @@
      * @param castSucceeded true if the playback succeeded, false if there was an error
      */
     public static void castDefaultPlayerResult(boolean castSucceeded) {
-        if (LibraryLoader.isInitialized()) nativeRecordCastDefaultPlayerResult(castSucceeded);
+        if (LibraryLoader.getInstance().isInitialized())
+            nativeRecordCastDefaultPlayerResult(castSucceeded);
     }
 
     /**
@@ -63,7 +65,8 @@
      * @param castSucceeded true if the playback succeeded, false if there was an error
      */
     public static void castYouTubePlayerResult(boolean castSucceeded) {
-        if (LibraryLoader.isInitialized()) nativeRecordCastYouTubePlayerResult(castSucceeded);
+        if (LibraryLoader.getInstance().isInitialized())
+            nativeRecordCastYouTubePlayerResult(castSucceeded);
     }
 
     /**
@@ -73,7 +76,7 @@
      * @param timeRemainingMs the remaining time in the video in milliseconds
      */
     public static void castEndedTimeRemaining(long videoLengthMs, long timeRemainingMs) {
-        if (LibraryLoader.isInitialized()) {
+        if (LibraryLoader.getInstance().isInitialized()) {
             nativeRecordCastEndedTimeRemaining((int) videoLengthMs, (int) timeRemainingMs);
         }
     }
@@ -85,7 +88,7 @@
      *            possible media types.
      */
     public static void castMediaType(int mediaType) {
-        if (LibraryLoader.isInitialized()) nativeRecordCastMediaType(mediaType);
+        if (LibraryLoader.getInstance().isInitialized()) nativeRecordCastMediaType(mediaType);
     }
 
     /**
@@ -95,7 +98,7 @@
      * @param isMediaElementAlive if the media element is alive.
      */
     public static void recordFullscreenControlsShown(boolean isMediaElementAlive) {
-        if (LibraryLoader.isInitialized()) {
+        if (LibraryLoader.getInstance().isInitialized()) {
             RecordHistogram.recordBooleanHistogram(
                     "Cast.Sender.MediaElementPresentWhenShowFullscreenControls",
                     isMediaElementAlive);
@@ -110,7 +113,7 @@
      * @param isMediaElementAlive if the media element is alive.
      */
     public static void recordFullscreenControlsAction(int action, boolean isMediaElementAlive) {
-        if (!LibraryLoader.isInitialized()) return;
+        if (!LibraryLoader.getInstance().isInitialized()) return;
 
         if (isMediaElementAlive) {
             RecordHistogram.recordEnumeratedHistogram(
@@ -130,7 +133,7 @@
      * @param url The frame URL to record the domain and registry of.
      */
     public static void castDomainAndRegistry(String url) {
-        if (LibraryLoader.isInitialized()) {
+        if (LibraryLoader.getInstance().isInitialized()) {
             RapporServiceBridge.sampleDomainAndRegistryFromURL("Cast.Sender.MediaFrameUrl", url);
         }
     }
@@ -143,7 +146,7 @@
      * @param percentage The ratio in percents.
      */
     public static void recordRemoteSessionTimeWithoutMediaElementPercentage(int percentage) {
-        if (LibraryLoader.isInitialized()) {
+        if (LibraryLoader.getInstance().isInitialized()) {
             RecordHistogram.recordPercentageHistogram(
                     "Cast.Sender.SessionTimeWithoutMediaElementPercentage", percentage);
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/metrics/ActivityTabStartupMetricsTracker.java b/chrome/android/java/src/org/chromium/chrome/browser/metrics/ActivityTabStartupMetricsTracker.java
index 08ac08b9..99e79f6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/metrics/ActivityTabStartupMetricsTracker.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/metrics/ActivityTabStartupMetricsTracker.java
@@ -40,6 +40,10 @@
                 .addStartupCompletedObserver(new BrowserStartupController.StartupCallback() {
                     @Override
                     public void onSuccess() {
+                        // Activity could have called finish and returned early during startup but
+                        // not have onDestroy called yet. The activity's TabModelSelector may not
+                        // have been initialized causing a crash. See https://crbug.com/847580
+                        if (mActivity.isActivityFinishing()) return;
                         registerObservers();
                     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/ListObservable.java b/chrome/android/java/src/org/chromium/chrome/browser/modelutil/ListObservable.java
index fee70784..c6e68645 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/ListObservable.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/modelutil/ListObservable.java
@@ -25,7 +25,8 @@
          * @param index The starting position of the range of added items.
          * @param count The number of added items.
          */
-        void onItemRangeInserted(ListObservable source, int index, int count);
+        default void
+            onItemRangeInserted(ListObservable source, int index, int count) {}
 
         /**
          * Notifies that {@code count} items starting at position {@code index} under the
@@ -35,7 +36,8 @@
          * @param index The starting position of the range of removed items.
          * @param count The number of removed items.
          */
-        void onItemRangeRemoved(ListObservable source, int index, int count);
+        default void
+            onItemRangeRemoved(ListObservable source, int index, int count) {}
 
         /**
          * Notifies that {@code count} items starting at position {@code index} under the
@@ -46,8 +48,9 @@
          * @param count The number of changed items.
          * @param payload Optional parameter, use {@code null} to identify a "full" update.
          */
-        void onItemRangeChanged(
-                ListObservable source, int index, int count, @Nullable Object payload);
+        default void
+            onItemRangeChanged(
+                    ListObservable source, int index, int count, @Nullable Object payload) {}
     }
 
     private final ObserverList<ListObserver> mObservers = new ObserverList<>();
@@ -59,12 +62,14 @@
      * @param observer An observer to be notified of changes to the model.
      */
     public void addObserver(ListObserver observer) {
-        mObservers.addObserver(observer);
+        boolean success = mObservers.addObserver(observer);
+        assert success;
     }
 
     /** @param observer The observer to remove. */
     public void removeObserver(ListObserver observer) {
-        mObservers.removeObserver(observer);
+        boolean success = mObservers.removeObserver(observer);
+        assert success;
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationUmaTracker.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationUmaTracker.java
index f1dc92d4..d6830b5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationUmaTracker.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationUmaTracker.java
@@ -124,7 +124,7 @@
     }
 
     private static void recordHistogram(String name, @SystemNotificationType int type) {
-        if (!LibraryLoader.isInitialized()) return;
+        if (!LibraryLoader.getInstance().isInitialized()) return;
         RecordHistogram.recordEnumeratedHistogram(name, type, SYSTEM_NOTIFICATION_TYPE_BOUNDARY);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
index cc00c107..9ecfd4ff 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
@@ -276,8 +276,8 @@
                 new SuggestionsNavigationDelegateImpl(
                         activity, profile, nativePageHost, tabModelSelector);
         mNewTabPageManager = new NewTabPageManagerImpl(suggestionsSource, eventReporter,
-                navigationDelegate, profile, nativePageHost, activity.getReferencePool(),
-                activity.getSnackbarManager());
+                navigationDelegate, profile, nativePageHost,
+                activity.getChromeApplication().getReferencePool(), activity.getSnackbarManager());
         mTileGroupDelegate = new NewTabPageTileGroupDelegate(activity, profile, navigationDelegate);
 
         mTitle = activity.getResources().getString(R.string.button_new_tab);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/ChildNode.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/ChildNode.java
index 7372e34..7d42ac6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/ChildNode.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/ChildNode.java
@@ -4,9 +4,9 @@
 
 package org.chromium.chrome.browser.ntp.cards;
 
-import android.support.annotation.CallSuper;
 import android.support.annotation.Nullable;
 
+import org.chromium.chrome.browser.modelutil.ListObservable;
 import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder.PartialBindCallback;
 
 /**
@@ -14,57 +14,45 @@
  *
  * This class mostly serves as a convenience base class for implementations of {@link TreeNode}.
  */
-public abstract class ChildNode implements TreeNode {
-    private NodeParent mParent;
+public abstract class ChildNode extends ListObservable implements TreeNode {
     private int mNumItems = 0;
 
     @Override
-    public final void setParent(NodeParent parent) {
-        assert mParent == null;
-        assert parent != null;
-        mParent = parent;
-    }
-
-    @Override
-    @CallSuper
-    public void detach() {
-        assert mParent != null;
-        mParent = null;
-    }
-
-    /** @return Whether the node is attached to a parent node. */
-    protected boolean isAttached() {
-        return mParent != null;
-    }
-
-    @Override
-    public final int getItemCount() {
-        assert mNumItems == getItemCountForDebugging();
+    public int getItemCount() {
+        assert mNumItems
+                == getItemCountForDebugging()
+            : "cached number of items: " + mNumItems + "; actual number of items: "
+              + getItemCountForDebugging();
         return mNumItems;
     }
 
-    protected void notifyItemRangeChanged(
-            int index, int count, @Nullable PartialBindCallback callback) {
+    @Override
+    protected void notifyItemRangeChanged(int index, int count, @Nullable Object payload) {
+        // TODO(bauerb): Parameterize the payload type.
         assert isRangeValid(index, count);
-        if (mParent != null) mParent.onItemRangeChanged(this, index, count, callback);
+        super.notifyItemRangeChanged(index, count, payload);
     }
 
+    // TODO(bauerb): Push these convenience methods to the base class once they're only called
+    // from subclasses.
     protected void notifyItemRangeChanged(int index, int count) {
         notifyItemRangeChanged(index, count, null);
     }
 
+    @Override
     protected void notifyItemRangeInserted(int index, int count) {
         mNumItems += count;
         assert mNumItems == getItemCountForDebugging();
         assert isRangeValid(index, count);
-        if (mParent != null) mParent.onItemRangeInserted(this, index, count);
+        super.notifyItemRangeInserted(index, count);
     }
 
+    @Override
     protected void notifyItemRangeRemoved(int index, int count) {
         assert isRangeValid(index, count);
         mNumItems -= count;
         assert mNumItems == getItemCountForDebugging();
-        if (mParent != null) mParent.onItemRangeRemoved(this, index, count);
+        super.notifyItemRangeRemoved(index, count);
     }
 
     protected void notifyItemChanged(int index, @Nullable PartialBindCallback callback) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/InnerNode.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/InnerNode.java
index 3052d0e..bfbe2b1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/InnerNode.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/InnerNode.java
@@ -8,9 +8,12 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.VisibleForTesting;
+import org.chromium.chrome.browser.modelutil.ListObservable;
+import org.chromium.chrome.browser.modelutil.ListObservable.ListObserver;
 import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder.PartialBindCallback;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
@@ -19,8 +22,8 @@
 /**
  * An inner node in the tree: the root of a subtree, with a list of child nodes.
  */
-public class InnerNode extends ChildNode implements NodeParent {
-    private final List<TreeNode> mChildren = new ArrayList<>();
+public class InnerNode extends ChildNode implements ListObserver {
+    private final List<ChildNode> mChildren = new ArrayList<>();
 
     private int getChildIndexForPosition(int position) {
         checkIndex(position);
@@ -47,17 +50,10 @@
         return offset;
     }
 
-    int getStartingOffsetForChild(TreeNode child) {
+    protected int getStartingOffsetForChild(ListObservable child) {
         return getStartingOffsetForChildIndex(mChildren.indexOf(child));
     }
 
-    /**
-     * Returns the child whose subtree contains the item at the given position.
-     */
-    TreeNode getChildForPosition(int position) {
-        return mChildren.get(getChildIndexForPosition(position));
-    }
-
     @Override
     protected int getItemCountForDebugging() {
         int numItems = 0;
@@ -107,43 +103,37 @@
 
     @Override
     public void onItemRangeChanged(
-            TreeNode child, int index, int count, @Nullable PartialBindCallback callback) {
-        notifyItemRangeChanged(getStartingOffsetForChild(child) + index, count, callback);
+            ListObservable source, int index, int count, @Nullable Object payload) {
+        notifyItemRangeChanged(
+                getStartingOffsetForChild(source) + index, count, (PartialBindCallback) payload);
     }
 
     @Override
-    public void onItemRangeInserted(TreeNode child, int index, int count) {
-        notifyItemRangeInserted(getStartingOffsetForChild(child) + index, count);
+    public void onItemRangeInserted(ListObservable source, int index, int count) {
+        notifyItemRangeInserted(getStartingOffsetForChild(source) + index, count);
     }
 
     @Override
-    public void onItemRangeRemoved(TreeNode child, int index, int count) {
-        notifyItemRangeRemoved(getStartingOffsetForChild(child) + index, count);
-    }
-
-    /**
-     * Helper method that adds a new child node and notifies about its insertion.
-     *
-     * @param child The child node to be added.
-     */
-    protected void addChild(TreeNode child) {
-        int insertedIndex = getItemCount();
-        mChildren.add(child);
-        child.setParent(this);
-
-        int count = child.getItemCount();
-        if (count > 0) notifyItemRangeInserted(insertedIndex, count);
+    public void onItemRangeRemoved(ListObservable source, int index, int count) {
+        notifyItemRangeRemoved(getStartingOffsetForChild(source) + index, count);
     }
 
     /**
      * Helper method that adds all the children and notifies about the inserted items.
      */
-    protected void addChildren(TreeNode... children) {
+    protected void addChildren(ChildNode... children) {
+        addChildren(Arrays.asList(children));
+    }
+
+    /**
+     * Helper method that adds all the children and notifies about the inserted items.
+     */
+    protected void addChildren(Iterable<ChildNode> children) {
         int initialCount = getItemCount();
         int addedCount = 0;
-        for (TreeNode child : children) {
+        for (ChildNode child : children) {
             mChildren.add(child);
-            child.setParent(this);
+            child.addObserver(this);
             addedCount += child.getItemCount();
         }
 
@@ -155,14 +145,14 @@
      *
      * @param child The child node to be removed.
      */
-    protected void removeChild(TreeNode child) {
+    protected void removeChild(ChildNode child) {
         int removedIndex = mChildren.indexOf(child);
         if (removedIndex == -1) throw new IndexOutOfBoundsException();
 
         int count = child.getItemCount();
         int childStartingOffset = getStartingOffsetForChildIndex(removedIndex);
 
-        child.detach();
+        child.removeObserver(this);
         mChildren.remove(removedIndex);
         if (count > 0) notifyItemRangeRemoved(childStartingOffset, count);
     }
@@ -172,15 +162,16 @@
      */
     protected void removeChildren() {
         int itemCount = getItemCount();
-        if (itemCount == 0) return;
 
-        for (TreeNode child : mChildren) child.detach();
+        for (ChildNode child : mChildren) {
+            child.removeObserver(this);
+        }
         mChildren.clear();
-        notifyItemRangeRemoved(0, itemCount);
+        if (itemCount > 0) notifyItemRangeRemoved(0, itemCount);
     }
 
     @VisibleForTesting
-    final List<TreeNode> getChildren() {
+    protected final List<ChildNode> getChildren() {
         return mChildren;
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java
index c07b9db..ee86975e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java
@@ -13,6 +13,7 @@
 import org.chromium.base.Callback;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.browser.ChromeFeatureList;
+import org.chromium.chrome.browser.modelutil.ListObservable;
 import org.chromium.chrome.browser.ntp.ContextMenuManager;
 import org.chromium.chrome.browser.ntp.LogoView;
 import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder.PartialBindCallback;
@@ -43,7 +44,8 @@
  * the above-the-fold view (containing the logo, search box, and most visited tiles) and subsequent
  * elements will be the cards shown to the user
  */
-public class NewTabPageAdapter extends Adapter<NewTabPageViewHolder> implements NodeParent {
+public class NewTabPageAdapter
+        extends Adapter<NewTabPageViewHolder> implements ListObservable.ListObserver {
     private final SuggestionsUiDelegate mUiDelegate;
     private final ContextMenuManager mContextMenuManager;
     private final OfflinePageBridge mOfflinePageBridge;
@@ -97,14 +99,14 @@
             mAboveTheFold = null;
         } else {
             mAboveTheFold = new AboveTheFoldItem();
-            mRoot.addChild(mAboveTheFold);
+            mRoot.addChildren(mAboveTheFold);
         }
 
         if (mLogoView == null) {
             mLogo = null;
         } else {
             mLogo = new LogoItem();
-            mRoot.addChild(mLogo);
+            mRoot.addChildren(mLogo);
         }
 
         if (tileGroupDelegate == null) {
@@ -112,15 +114,15 @@
         } else {
             mSiteSection = new SiteSection(uiDelegate, mContextMenuManager, tileGroupDelegate,
                     offlinePageBridge, uiConfig);
-            mRoot.addChild(mSiteSection);
+            mRoot.addChildren(mSiteSection);
         }
 
-        mRoot.addChild(mSections);
-        if (mSigninPromo != null) mRoot.addChild(mSigninPromo);
-        mRoot.addChild(mAllDismissed);
+        mRoot.addChildren(mSections);
+        if (mSigninPromo != null) mRoot.addChildren(mSigninPromo);
+        mRoot.addChildren(mAllDismissed);
 
         mFooter = new Footer();
-        mRoot.addChild(mFooter);
+        mRoot.addChildren(mFooter);
 
         mOfflinePageBridge = offlinePageBridge;
 
@@ -128,7 +130,7 @@
         mUiDelegate.addDestructionObserver(suggestionsObserver);
 
         updateAllDismissedVisibility();
-        mRoot.setParent(this);
+        mRoot.addObserver(this);
     }
 
     @Override
@@ -218,12 +220,6 @@
         }
     }
 
-    public int getAboveTheFoldPosition() {
-        if (mAboveTheFoldView == null) return RecyclerView.NO_POSITION;
-
-        return getChildPositionOffset(mAboveTheFold);
-    }
-
     public int getFirstHeaderPosition() {
         return getFirstPositionForType(ItemViewType.HEADER);
     }
@@ -260,14 +256,14 @@
     }
 
     @Override
-    public void onItemRangeChanged(TreeNode child, int itemPosition, int itemCount,
-            @Nullable PartialBindCallback callback) {
+    public void onItemRangeChanged(
+            ListObservable child, int itemPosition, int itemCount, @Nullable Object payload) {
         assert child == mRoot;
-        notifyItemRangeChanged(itemPosition, itemCount, callback);
+        notifyItemRangeChanged(itemPosition, itemCount, payload);
     }
 
     @Override
-    public void onItemRangeInserted(TreeNode child, int itemPosition, int itemCount) {
+    public void onItemRangeInserted(ListObservable child, int itemPosition, int itemCount) {
         assert child == mRoot;
         notifyItemRangeInserted(itemPosition, itemCount);
         if (mRecyclerView != null && FeatureUtilities.isChromeHomeEnabled()
@@ -279,7 +275,7 @@
     }
 
     @Override
-    public void onItemRangeRemoved(TreeNode child, int itemPosition, int itemCount) {
+    public void onItemRangeRemoved(ListObservable child, int itemPosition, int itemCount) {
         assert child == mRoot;
         notifyItemRangeRemoved(itemPosition, itemCount);
 
@@ -364,10 +360,6 @@
         return suggestions == null || !suggestions.hasCards();
     }
 
-    private int getChildPositionOffset(TreeNode child) {
-        return mRoot.getStartingOffsetForChild(child);
-    }
-
     @VisibleForTesting
     public int getFirstPositionForType(@ItemViewType int viewType) {
         int count = getItemCount();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NodeParent.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NodeParent.java
deleted file mode 100644
index f23e68af..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NodeParent.java
+++ /dev/null
@@ -1,47 +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.chrome.browser.ntp.cards;
-
-import android.support.annotation.Nullable;
-
-import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder.PartialBindCallback;
-
-/**
- * Interface to allow propagating change events upwards in the tree.
- */
-public interface NodeParent {
-    /**
-     * Notifies that {@code count} items starting at position {@code index} under the {@code child}
-     * have changed with an optional payload object.
-     * @param child The child whose items have changed.
-     * @param index The starting position of the range of changed items, relative to the
-     *         {@code child}.
-     * @param count The number of changed items.
-     * @param callback Optional parameter, use {@code null} to identify a "full" update.
-     * @see android.support.v7.widget.RecyclerView.Adapter#notifyItemRangeChanged(int, int, Object)
-     */
-    void onItemRangeChanged(
-            TreeNode child, int index, int count, @Nullable PartialBindCallback callback);
-
-    /**
-     * Notifies that {@code count} items starting at position {@code index} under the {@code child}
-     * have been added.
-     * @param child The child to which items have been added.
-     * @param index The starting position of the range of added items, relative to the child.
-     * @param count The number of added items.
-     * @see android.support.v7.widget.RecyclerView.Adapter#notifyItemRangeInserted(int, int)
-     */
-    void onItemRangeInserted(TreeNode child, int index, int count);
-
-    /**
-     * Notifies that {@code count} items starting at position {@code index} under the {@code child}
-     * have been removed.
-     * @param child The child from which items have been removed.
-     * @param index The starting position of the range of removed items, relative to the child.
-     * @param count The number of removed items.
-     * @see android.support.v7.widget.RecyclerView.Adapter#notifyItemRangeRemoved(int, int)
-     */
-    void onItemRangeRemoved(TreeNode child, int index, int count);
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SectionList.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SectionList.java
index 8ce5926..fae5785 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SectionList.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SectionList.java
@@ -40,6 +40,7 @@
 
     /** Maps suggestion categories to sections, with stable iteration ordering. */
     private final Map<Integer, SuggestionsSection> mSections = new LinkedHashMap<>();
+
     /** List of categories that are hidden because they have no content to show. */
     private final Set<Integer> mBlacklistedCategories = new HashSet<>();
     private final SuggestionsUiDelegate mUiDelegate;
@@ -127,7 +128,7 @@
                     this, mUiDelegate, suggestionsRanker, mOfflinePageBridge, info);
             mSections.put(category, section);
             suggestionsRanker.registerCategory(category);
-            addChild(section);
+            addChildren(section);
         } else {
             section.clearData();
         }
@@ -338,10 +339,14 @@
 
     private void removeSection(SuggestionsSection section) {
         mSections.remove(section.getCategory());
+        section.destroy();
         removeChild(section);
     }
 
     private void removeAllSections() {
+        for (SuggestionsSection section : mSections.values()) {
+            section.destroy();
+        }
         mSections.clear();
         removeChildren();
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java
index 2b8999c9..b1012ac 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java
@@ -4,7 +4,6 @@
 
 package org.chromium.chrome.browser.ntp.cards;
 
-import android.support.annotation.CallSuper;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.text.TextUtils;
@@ -12,6 +11,7 @@
 import org.chromium.base.Callback;
 import org.chromium.base.Log;
 import org.chromium.chrome.browser.ChromeFeatureList;
+import org.chromium.chrome.browser.modelutil.ListObservable;
 import org.chromium.chrome.browser.ntp.NewTabPageUma;
 import org.chromium.chrome.browser.ntp.snippets.CategoryInt;
 import org.chromium.chrome.browser.ntp.snippets.CategoryStatus;
@@ -73,6 +73,11 @@
     private boolean mHasInsertedContent;
 
     /**
+     * Whether the section has been destroyed.
+     */
+    private boolean mIsDestroyed;
+
+    /**
      * Delegate interface that allows dismissing this section without introducing
      * a circular dependency.
      */
@@ -129,6 +134,8 @@
         private final SuggestionsRanker mSuggestionsRanker;
         private final SuggestionsCategoryInfo mCategoryInfo;
 
+        private boolean mIsDestroyed;
+
         public SuggestionsList(SuggestionsSource suggestionsSource, SuggestionsRanker ranker,
                 SuggestionsCategoryInfo categoryInfo) {
             mSuggestionsSource = suggestionsSource;
@@ -214,7 +221,7 @@
         @Override
         public void dismissItem(int position, Callback<String> itemRemovedCallback) {
             checkIndex(position);
-            if (!isAttached()) {
+            if (mIsDestroyed) {
                 // It is possible for this method to be called after the NewTabPage has had
                 // destroy() called. This can happen when
                 // NewTabPageRecyclerView.dismissWithAnimation() is called and the animation ends
@@ -241,13 +248,18 @@
             if ((oldId == null) == (newId == null)) return;
             notifyItemChanged(index, SnippetArticleViewHolder::refreshOfflineBadgeVisibility);
         }
+
+        public void destroy() {
+            assert !mIsDestroyed;
+            mIsDestroyed = true;
+        }
     }
 
-    @Override
-    @CallSuper
-    public void detach() {
+    public void destroy() {
+        assert !mIsDestroyed;
         mOfflineModelObserver.onDestroy();
-        super.detach();
+        mSuggestionsList.destroy();
+        mIsDestroyed = true;
     }
 
     private void onSuggestionsListCountChanged(int oldSuggestionsCount) {
@@ -286,13 +298,13 @@
     }
 
     @Override
-    public void onItemRangeRemoved(TreeNode child, int index, int count) {
+    public void onItemRangeRemoved(ListObservable child, int index, int count) {
         super.onItemRangeRemoved(child, index, count);
         if (child == mSuggestionsList) onSuggestionsListCountChanged(getSuggestionsCount() + count);
     }
 
     @Override
-    public void onItemRangeInserted(TreeNode child, int index, int count) {
+    public void onItemRangeInserted(ListObservable child, int index, int count) {
         super.onItemRangeInserted(child, index, count);
         if (child == mSuggestionsList) {
             mHasInsertedContent = true;
@@ -533,9 +545,8 @@
 
         mMoreButton.updateState(ActionItem.State.LOADING);
         mSuggestionsSource.fetchSuggestions(mCategoryInfo.getCategory(),
-                getDisplayedSuggestionIds(),
-                suggestions -> {  /* successCallback */
-                    if (!isAttached()) return; // The section has been dismissed.
+                getDisplayedSuggestionIds(), suggestions -> { /* successCallback */
+                    if (mIsDestroyed) return; // The section has been dismissed.
 
                     mMoreButton.updateState(ActionItem.State.BUTTON);
 
@@ -544,9 +555,8 @@
                     if (onNoNewSuggestions != null && suggestions.size() == 0) {
                         onNoNewSuggestions.run();
                     }
-                },
-                () -> {  /* failureRunnable */
-                    if (!isAttached()) return; // The section has been dismissed.
+                }, () -> { /* failureRunnable */
+                    if (mIsDestroyed) return; // The section has been dismissed.
 
                     mMoreButton.updateState(ActionItem.State.BUTTON);
                     if (onFailure != null) onFailure.run();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/TreeNode.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/TreeNode.java
index d8e13b4e..529392e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/TreeNode.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/TreeNode.java
@@ -13,19 +13,6 @@
  */
 public interface TreeNode {
     /**
-     * Sets the parent of this node. This method should be called at most once. Before the parent
-     * has been set, the node will not send any notifications about changes to its subtree.
-     * @param parent the parent of this node.
-     */
-    void setParent(NodeParent parent);
-
-    /**
-     * Detaches the node from the parent so that changes in the node are no longer notified to the
-     * parent. This is needed when the parent removes this node from its children.
-     */
-    void detach();
-
-    /**
      * Returns the number of items under this subtree. This method may be called
      * before initialization.
      *
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
index 9c0b1d74..f5fa279 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
@@ -102,6 +102,7 @@
     private String mPreviousTldScrollText;
     private int mPreviousTldScrollViewWidth;
     private int mPreviousTldScrollResultXPosition;
+    private float mPreviousFontSize;
 
     private final int mDarkHintColor;
     private final int mDarkDefaultTextColor;
@@ -810,7 +811,10 @@
 
         int measuredWidth = getMeasuredWidth();
         if (TextUtils.equals(url, previousTldScrollText)
-                && measuredWidth == previousTldScrollViewWidth) {
+                && measuredWidth == previousTldScrollViewWidth
+                // Font size is float but it changes in discrete range (eg small font, big font),
+                // therefore false negative using regular equality is unlikely.
+                && getTextSize() == mPreviousFontSize) {
             scrollTo(previousTldScrollResultXPosition, getScrollY());
             return;
         }
@@ -824,7 +828,8 @@
         float scrollPos;
         if (startPointX < endPointX) {
             // LTR
-            scrollPos = Math.max(0, endPointX - measuredWidth);
+            float padding = getResources().getDimensionPixelSize(R.dimen.toolbar_edge_padding);
+            scrollPos = Math.max(0, endPointX - measuredWidth + padding);
         } else {
             float width = getLayout().getPaint().measureText(urlComponents.first);
             // RTL
@@ -838,6 +843,7 @@
 
         mPreviousTldScrollText = url.toString();
         mPreviousTldScrollViewWidth = measuredWidth;
+        mPreviousFontSize = getTextSize();
         mPreviousTldScrollResultXPosition = (int) scrollPos;
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/download/DownloadLocationPreferenceAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/download/DownloadLocationPreferenceAdapter.java
index 2ec3998..9156ea4e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/download/DownloadLocationPreferenceAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/download/DownloadLocationPreferenceAdapter.java
@@ -54,6 +54,8 @@
 
         RadioButton radioButton = view.findViewById(R.id.radio_button);
         radioButton.setChecked(getSelectedItemId() == position);
+        radioButton.setTag(position);
+        radioButton.setOnClickListener(this);
 
         view.setEnabled(isEnabled(position));
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchBoxDataProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchBoxDataProvider.java
index 0b1ee27..facd296 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchBoxDataProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchBoxDataProvider.java
@@ -21,7 +21,7 @@
      * @param tab The tab to use.
      */
     public void onNativeLibraryReady(Tab tab) {
-        assert LibraryLoader.isInitialized();
+        assert LibraryLoader.getInstance().isInitialized();
         mTab = tab;
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchWidgetProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchWidgetProvider.java
index d543745..1e69388c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchWidgetProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchWidgetProvider.java
@@ -151,7 +151,7 @@
      */
     public static void initialize() {
         ThreadUtils.assertOnUiThread();
-        assert LibraryLoader.isInitialized();
+        assert LibraryLoader.getInstance().isInitialized();
 
         // Set up an observer to monitor for changes.
         synchronized (OBSERVER_LOCK) {
@@ -310,7 +310,7 @@
     /** Attempts to update the cached search engine name. */
     public static void updateCachedEngineName() {
         ThreadUtils.assertOnUiThread();
-        if (!LibraryLoader.isInitialized()) return;
+        if (!LibraryLoader.getInstance().isInitialized()) return;
 
         // Getting an instance of the TemplateUrlService requires that the native library be
         // loaded, but the TemplateUrlService also itself needs to be initialized.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/superviseduser/SupervisedUserContentProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/superviseduser/SupervisedUserContentProvider.java
index 4b8ba08..cb58743 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/superviseduser/SupervisedUserContentProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/superviseduser/SupervisedUserContentProvider.java
@@ -65,7 +65,7 @@
         // This may lock for some time, but is always called on a background thread, and will only
         // take significant time if the Chrome process isn't already running.
         synchronized (sContentProviderLock) {
-            mChromeAlreadyStarted = LibraryLoader.isInitialized();
+            mChromeAlreadyStarted = LibraryLoader.getInstance().isInitialized();
             if (mNativeSupervisedUserContentProvider != 0) {
                 return mNativeSupervisedUserContentProvider;
             }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java
index 9f07e7f..599d73dc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java
@@ -266,7 +266,7 @@
     }
 
     private static void logExecutionTime(String name, long time) {
-        if (LibraryLoader.isInitialized()) {
+        if (LibraryLoader.getInstance().isInitialized()) {
             RecordHistogram.recordTimesHistogram("Android.StrictMode.TabPersistentStore." + name,
                     SystemClock.uptimeMillis() - time, TimeUnit.MILLISECONDS);
         }
@@ -914,7 +914,7 @@
 
         saveListToFile(getStateDirectory(), mPersistencePolicy.getStateFileName(), listData);
         mLastSavedMetadata = listData;
-        if (LibraryLoader.isInitialized()) {
+        if (LibraryLoader.getInstance().isInitialized()) {
             RecordHistogram.recordCountHistogram(
                     "Android.TabPersistentStore.MetadataFileSize", listData.length);
         }
@@ -1402,7 +1402,7 @@
                     Log.i(TAG, "State file does not exist.");
                     return null;
                 }
-                if (LibraryLoader.isInitialized()) {
+                if (LibraryLoader.getInstance().isInitialized()) {
                     RecordHistogram.recordCountHistogram(
                             "Android.TabPersistentStore.MergeStateMetadataFileSize",
                             (int) stateFile.length());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabbedModeTabPersistencePolicy.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabbedModeTabPersistencePolicy.java
index a8f4e9d2..17e244b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabbedModeTabPersistencePolicy.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabbedModeTabPersistencePolicy.java
@@ -243,7 +243,7 @@
         File oldMetadataFile = new File(stateDir, LEGACY_SAVED_STATE_FILE);
         if (newMetadataFile.exists()) {
             Log.e(TAG, "New metadata file already exists");
-            if (LibraryLoader.isInitialized()) {
+            if (LibraryLoader.getInstance().isInitialized()) {
                 RecordHistogram.recordBooleanHistogram(
                         "Android.MultiInstanceMigration.NewMetadataFileExists", true);
             }
@@ -252,7 +252,7 @@
             if (!oldMetadataFile.renameTo(newMetadataFile)) {
                 Log.e(TAG, "Failed to rename file: " + oldMetadataFile);
 
-                if (LibraryLoader.isInitialized()) {
+                if (LibraryLoader.getInstance().isInitialized()) {
                     RecordHistogram.recordBooleanHistogram(
                             "Android.MultiInstanceMigration.FailedToRenameMetadataFile", true);
                 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/CustomTabToolbar.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/CustomTabToolbar.java
index 4e17895..6a4d729 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/CustomTabToolbar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/CustomTabToolbar.java
@@ -91,7 +91,7 @@
                     new GestureDetector.SimpleOnGestureListener() {
                         @Override
                         public boolean onSingleTapConfirmed(MotionEvent e) {
-                            if (LibraryLoader.isInitialized()) {
+                            if (LibraryLoader.getInstance().isInitialized()) {
                                 RecordUserAction.record("CustomTabs.TapUrlBar");
                             }
                             return super.onSingleTapConfirmed(e);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/CustomTabToolbarAnimationDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/CustomTabToolbarAnimationDelegate.java
index 526547e..4e13200 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/CustomTabToolbarAnimationDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/CustomTabToolbarAnimationDelegate.java
@@ -14,6 +14,7 @@
 import android.widget.TextView;
 
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.omnibox.UrlBar;
 import org.chromium.chrome.browser.widget.animation.CancelAwareAnimatorListener;
 import org.chromium.ui.interpolators.BakedBezierInterpolator;
 
@@ -38,7 +39,7 @@
     private final AnimatorSet mSecurityButtonShowAnimator;
     private final AnimatorSet mSecurityButtonHideAnimator;
 
-    private TextView mUrlBar;
+    private UrlBar mUrlBar;
     private TextView mTitleBar;
     private int mSecurityButtonWidth;
     // A flag controlling whether the animation has run before.
@@ -97,7 +98,7 @@
         mShouldRunTitleAnimation = enabled;
     }
 
-    void prepareTitleAnim(TextView urlBar, TextView titleBar) {
+    void prepareTitleAnim(UrlBar urlBar, TextView titleBar) {
         mTitleBar = titleBar;
         mUrlBar = urlBar;
         mUrlBar.setPivotX(0f);
@@ -128,6 +129,9 @@
         mUrlBar.getLocationInWindow(oldLoc);
 
         mUrlBar.requestLayout();
+
+        // Recalculate proportion of shown text in the URL bar and scroll it after font size changed
+        mUrlBar.scrollDisplayText();
         mUrlBar.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
             @Override
             public void onLayoutChange(View v, int left, int top, int right, int bottom,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java
index ce05352..68c2a47 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java
@@ -729,7 +729,7 @@
     }
 
     private static VrShellDelegate getInstance(ChromeActivity activity) {
-        if (!LibraryLoader.isInitialized()) return null;
+        if (!LibraryLoader.getInstance().isInitialized()) return null;
         if (activity == null || !activitySupportsPresentation(activity)) return null;
         if (sInstance != null) return sInstance;
         VrClassesWrapper wrapper = getVrClassesWrapper();
@@ -1510,7 +1510,7 @@
     }
 
     /* package */ boolean canEnterVr() {
-        if (!LibraryLoader.isInitialized()) return false;
+        if (!LibraryLoader.getInstance().isInitialized()) return false;
         if (mVrSupportLevel == VrSupportLevel.VR_NOT_AVAILABLE || mNativeVrShellDelegate == 0)
             return false;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkActivity.java
index 11ad8678..cb19bd1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkActivity.java
@@ -132,7 +132,7 @@
     public void preInflationStartup() {
         // Decide whether to record startup UMA histograms. This is a similar check to the one done
         // in ChromeTabbedActivity.preInflationStartup refer to the comment there for why.
-        if (!LibraryLoader.isInitialized()) {
+        if (!LibraryLoader.getInstance().isInitialized()) {
             getActivityTabStartupMetricsTracker().trackStartupMetrics(STARTUP_UMA_HISTOGRAM_SUFFIX);
         }
         super.preInflationStartup();
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index 81849da..bb6829aa 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -5,6 +5,7 @@
 import("//components/feed/features.gni")
 import("//components/offline_pages/buildflags/features.gni")
 import("//device/vr/buildflags/buildflags.gni")
+import("//chrome/android/feed/feed_java_sources.gni")
 
 chrome_java_sources = [
   "java/src/com/google/android/apps/chrome/appwidget/bookmarks/BookmarkThumbnailWidgetProvider.java",
@@ -814,7 +815,6 @@
   "java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java",
   "java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerView.java",
   "java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageViewHolder.java",
-  "java/src/org/chromium/chrome/browser/ntp/cards/NodeParent.java",
   "java/src/org/chromium/chrome/browser/ntp/cards/NodeVisitor.java",
   "java/src/org/chromium/chrome/browser/ntp/cards/OptionalLeaf.java",
   "java/src/org/chromium/chrome/browser/ntp/cards/ProgressIndicatorView.java",
@@ -2163,6 +2163,7 @@
 if (enable_feed_in_chrome) {
   chrome_junit_test_java_sources +=
       [ "junit/src/org/chromium/chrome/browser/feed/FeedImageLoaderTest.java" ]
+  chrome_test_java_sources += feed_conformance_test_sources
 }
 
 # This is enable_arcore, not package_arcore because the apk merger, for
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillKeyboardAccessoryIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillKeyboardAccessoryIntegrationTest.java
index ee762d61..e88feb9 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillKeyboardAccessoryIntegrationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillKeyboardAccessoryIntegrationTest.java
@@ -199,6 +199,7 @@
     @Test
     @MediumTest
     @Feature({"keyboard-accessory"})
+    @DisabledTest(message = "crbug.com/847959")
     public void testSelectSuggestionHidesKeyboardAccessory()
             throws ExecutionException, InterruptedException, TimeoutException {
         loadTestPage(false);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsTest.java
index 1ac8319..2f5bbfc 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsTest.java
@@ -475,19 +475,11 @@
             mModel2 = mCoordinator2.getModelForTesting();
             mBottomSheet2 = activity2.getBottomSheet();
 
-            mModel2.mClusterListObservable.addObserver(new ListObserver() {
+            mModel2.getClusterList().addObserver(new ListObserver() {
                 @Override
                 public void onItemRangeInserted(ListObservable source, int index, int count) {
                     itemRangeInsertedCallback.notifyCalled();
                 }
-
-                @Override
-                public void onItemRangeRemoved(ListObservable source, int index, int count) {}
-
-                @Override
-                public void onItemRangeChanged(
-                        ListObservable source, int index, int count, Object payload) {}
-
             });
 
             mMediator2.onEnabledStateChanged(true);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/feed/FeedNetworkBridgeConformanceTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/feed/FeedNetworkBridgeConformanceTest.java
new file mode 100644
index 0000000..50e6370
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/feed/FeedNetworkBridgeConformanceTest.java
@@ -0,0 +1,132 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.feed;
+
+import static org.chromium.base.test.util.ScalableTimeout.scaleTimeout;
+
+import android.content.Context;
+import android.net.Uri;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+
+import com.google.android.libraries.feed.common.functional.Consumer;
+import com.google.android.libraries.feed.host.network.HttpRequest;
+import com.google.android.libraries.feed.host.network.HttpResponse;
+import com.google.android.libraries.feed.testing.conformance.network.NetworkClientConformanceTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.Log;
+import org.chromium.base.ThreadUtils;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.test.ChromeBrowserTestRule;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.net.test.EmbeddedTestServer;
+
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Conformance Tests for {@link FeedNetworkBridge}.
+ * The actual tests are implemented in NetworkClientConformanceTest
+ */
+
+// The @SmallTest class annotation is needed to allow
+// the inherited @Test methods to run using build/android/test_runner.py
+@SmallTest
+@RunWith(ChromeJUnit4ClassRunner.class)
+public final class FeedNetworkBridgeConformanceTest extends NetworkClientConformanceTest {
+    private static final String TAG = "FeedConformanceTest";
+    private static final long TIMEOUT = scaleTimeout(3000);
+
+    @Rule
+    public final ChromeBrowserTestRule mRule = new ChromeBrowserTestRule();
+
+    private EmbeddedTestServer mTestServer;
+
+    class TestConsumer implements Consumer<HttpResponse> {
+        private FutureTask<Void> mAcceptFuture;
+        private Consumer<HttpResponse> mConsumer;
+
+        public TestConsumer(Consumer<HttpResponse> consumer) {
+            mConsumer = consumer;
+            mAcceptFuture = new FutureTask<Void>(() -> null);
+        }
+
+        @Override
+        public void accept(HttpResponse input) {
+            mConsumer.accept(input);
+            mAcceptFuture.run();
+        }
+
+        public void waitUntilCalled(long milliSecsTimeout) {
+            try {
+                mAcceptFuture.get(milliSecsTimeout, TimeUnit.MILLISECONDS);
+            } catch (Exception e) {
+                Log.w(TAG, "Exception while waiting for accept: " + e);
+                e.printStackTrace();
+            }
+        }
+    }
+
+    class FeedTestNetworkBridge extends FeedNetworkBridge {
+        public FeedTestNetworkBridge(Profile p) {
+            super(p);
+        }
+
+        @Override
+        public void send(HttpRequest request, Consumer<HttpResponse> responseConsumer) {
+            // TODO(aluo): remove once b/79609987 is fixed
+            // The NetworkClientConformanceTest sends requests to google.com,
+            // change it to use a local URI in tests.
+            String url = mTestServer.getURL("/chrome/test/data/google/google.html");
+            Uri uri = Uri.parse(url);
+            HttpRequest testServerRequest = new HttpRequest(
+                    uri, request.getMethod(), request.getHeaders(), request.getBody());
+            TestConsumer testConsumer = new TestConsumer(responseConsumer);
+            ThreadUtils.runOnUiThreadBlocking(() -> super.send(testServerRequest, testConsumer));
+            // TODO(aluo): remove once b/79753857 is fixed
+            // Need convert the send into a sync call due to
+            // NetworkClientConformanceTest not waiting before checking that
+            // responseConsumer is accepted.
+            testConsumer.waitUntilCalled(TIMEOUT);
+        }
+    }
+
+    private void createNetworkClient() {
+        // The networkClient is declared and tested in NetworkClientConformanceTest
+        networkClient = new FeedTestNetworkBridge(Profile.getLastUsedProfile());
+    }
+
+    private void destroyNetworkClient() {
+        ((FeedTestNetworkBridge) networkClient).destroy();
+        networkClient = null;
+    }
+
+    private void createAndStartTestServer() throws InterruptedException {
+        Context c = InstrumentationRegistry.getContext();
+        mTestServer = EmbeddedTestServer.createAndStartServer(c);
+    }
+
+    private void stopAndDestroyTestServer() {
+        mTestServer.stopAndDestroyServer();
+        mTestServer = null;
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        createAndStartTestServer();
+        ThreadUtils.runOnUiThreadBlocking(() -> createNetworkClient());
+    }
+
+    @After
+    public void tearDown() {
+        ThreadUtils.runOnUiThreadBlocking(() -> destroyNetworkClient());
+        stopAndDestroyTestServer();
+    }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/incognito/IncognitoNotificationServiceTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/incognito/IncognitoNotificationServiceTest.java
index 895e777d..933a6bc 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/incognito/IncognitoNotificationServiceTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/incognito/IncognitoNotificationServiceTest.java
@@ -198,7 +198,7 @@
         ThreadUtils.runOnUiThreadBlocking(new Runnable() {
             @Override
             public void run() {
-                Assert.assertFalse(LibraryLoader.isInitialized());
+                Assert.assertFalse(LibraryLoader.getInstance().isInitialized());
             }
         });
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridgeIntentTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridgeIntentTest.java
index 493ea68d..a5e57aa 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridgeIntentTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridgeIntentTest.java
@@ -57,8 +57,8 @@
     @MediumTest
     @Feature({"Browser", "Notifications"})
     public void testLaunchNotificationPreferencesForCategory() {
-        Assert.assertFalse(
-                "The native library should not be loaded yet", LibraryLoader.isInitialized());
+        Assert.assertFalse("The native library should not be loaded yet",
+                LibraryLoader.getInstance().isInitialized());
 
         final Context context = InstrumentationRegistry.getInstrumentation()
                                         .getTargetContext()
@@ -93,8 +93,8 @@
     @MediumTest
     @Feature({"Browser", "Notifications"})
     public void testLaunchNotificationPreferencesForWebsite() {
-        Assert.assertFalse(
-                "The native library should not be loaded yet", LibraryLoader.isInitialized());
+        Assert.assertFalse("The native library should not be loaded yet",
+                LibraryLoader.getInstance().isInitialized());
 
         final Context context = InstrumentationRegistry.getInstrumentation()
                                         .getTargetContext()
@@ -135,8 +135,8 @@
     @MediumTest
     @Feature({"Browser", "Notifications"})
     public void testLaunchProcessForNotificationActivation() throws Exception {
-        Assert.assertFalse(
-                "The native library should not be loaded yet", LibraryLoader.isInitialized());
+        Assert.assertFalse("The native library should not be loaded yet",
+                LibraryLoader.getInstance().isInitialized());
         Assert.assertNull(NotificationPlatformBridge.getInstanceForTests());
 
         Context context = InstrumentationRegistry.getInstrumentation()
@@ -164,7 +164,8 @@
             }
         });
 
-        Assert.assertTrue("The native library should be loaded now", LibraryLoader.isInitialized());
+        Assert.assertTrue("The native library should be loaded now",
+                LibraryLoader.getInstance().isInitialized());
         Assert.assertNotNull(NotificationPlatformBridge.getInstanceForTests());
     }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/TileGridLayoutTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/TileGridLayoutTest.java
index 0c4d2e42..0f7a790 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/TileGridLayoutTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/TileGridLayoutTest.java
@@ -40,10 +40,9 @@
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeSwitches;
 import org.chromium.chrome.browser.UrlConstants;
+import org.chromium.chrome.browser.modelutil.ListObservable;
 import org.chromium.chrome.browser.ntp.NewTabPage;
 import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder;
-import org.chromium.chrome.browser.ntp.cards.NodeParent;
-import org.chromium.chrome.browser.ntp.cards.TreeNode;
 import org.chromium.chrome.browser.offlinepages.OfflinePageItem;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
@@ -469,18 +468,14 @@
         SiteSection siteSection =
                 new SiteSection(uiDelegate, null, delegate, offlinePageBridge, uiConfig);
 
-        siteSection.setParent(new NodeParent() {
+        siteSection.addObserver(new ListObservable.ListObserver() {
             @Override
-            public void onItemRangeChanged(TreeNode child, int index, int count,
-                    @Nullable NewTabPageViewHolder.PartialBindCallback callback) {
-                if (callback != null) callback.onResult(viewHolder);
+            public void onItemRangeChanged(
+                    ListObservable child, int index, int count, @Nullable Object payload) {
+                if (payload != null) {
+                    ((NewTabPageViewHolder.PartialBindCallback) payload).onResult(viewHolder);
+                }
             }
-
-            @Override
-            public void onItemRangeInserted(TreeNode child, int index, int count) {}
-
-            @Override
-            public void onItemRangeRemoved(TreeNode child, int index, int count) {}
         });
 
         return siteSection;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/InnerNodeTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/InnerNodeTest.java
index 530bbea1..c511973 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/InnerNodeTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/InnerNodeTest.java
@@ -26,7 +26,8 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder.PartialBindCallback;
+import org.chromium.chrome.browser.modelutil.ListObservable;
+import org.chromium.chrome.browser.modelutil.ListObservable.ListObserver;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -38,8 +39,9 @@
 @Config(manifest = Config.NONE)
 public class InnerNodeTest {
     private static final int[] ITEM_COUNTS = {1, 2, 3, 0, 3, 2, 1};
-    private final List<TreeNode> mChildren = new ArrayList<>();
-    @Mock private NodeParent mParent;
+    private final List<ChildNode> mChildren = new ArrayList<>();
+    @Mock
+    private ListObserver mObserver;
     private InnerNode mInnerNode;
 
     @Before
@@ -48,12 +50,12 @@
         mInnerNode = spy(new InnerNode());
 
         for (int childItemCount : ITEM_COUNTS) {
-            TreeNode child = makeDummyNode(childItemCount);
+            ChildNode child = makeDummyNode(childItemCount);
             mChildren.add(child);
-            mInnerNode.addChild(child);
+            mInnerNode.addChildren(child);
         }
 
-        mInnerNode.setParent(mParent);
+        mInnerNode.addObserver(mObserver);
     }
 
     @Test
@@ -92,38 +94,38 @@
     public void testAddChild() {
         final int itemCountBefore = mInnerNode.getItemCount();
 
-        TreeNode child = makeDummyNode(23);
-        mInnerNode.addChild(child);
+        ChildNode child = makeDummyNode(23);
+        mInnerNode.addChildren(child);
 
         // The child should have been initialized and the parent hierarchy notified about it.
-        verify(child).setParent(eq(mInnerNode));
-        verify(mParent).onItemRangeInserted(mInnerNode, itemCountBefore, 23);
+        verify(child).addObserver(eq(mInnerNode));
+        verify(mObserver).onItemRangeInserted(mInnerNode, itemCountBefore, 23);
 
-        TreeNode child2 = makeDummyNode(0);
-        mInnerNode.addChild(child2);
+        ChildNode child2 = makeDummyNode(0);
+        mInnerNode.addChildren(child2);
 
         // The empty child should have been initialized, but there should be no change
         // notifications.
-        verify(child2).setParent(eq(mInnerNode));
-        verifyNoMoreInteractions(mParent);
+        verify(child2).addObserver(eq(mInnerNode));
+        verifyNoMoreInteractions(mObserver);
     }
 
     @Test
     public void testRemoveChild() {
-        TreeNode child = mChildren.get(4);
+        ChildNode child = mChildren.get(4);
         mInnerNode.removeChild(child);
 
         // The parent should have been notified about the removed items.
-        verify(mParent).onItemRangeRemoved(mInnerNode, 6, 3);
-        verify(child).detach();
+        verify(mObserver).onItemRangeRemoved(mInnerNode, 6, 3);
+        verify(child).removeObserver(eq(mInnerNode));
 
-        reset(mParent); // Prepare for the #verifyNoMoreInteractions() call below.
-        TreeNode child2 = mChildren.get(3);
+        reset(mObserver); // Prepare for the #verifyNoMoreInteractions() call below.
+        ChildNode child2 = mChildren.get(3);
         mInnerNode.removeChild(child2);
-        verify(child2).detach();
+        verify(child2).removeObserver(eq(mInnerNode));
 
         // There should be no change notifications about the empty child.
-        verifyNoMoreInteractions(mParent);
+        verifyNoMoreInteractions(mObserver);
     }
 
     @Test
@@ -131,8 +133,10 @@
         mInnerNode.removeChildren();
 
         // The parent should have been notified about the removed items.
-        verify(mParent).onItemRangeRemoved(mInnerNode, 0, 12);
-        for (TreeNode child : mChildren) verify(child).detach();
+        verify(mObserver).onItemRangeRemoved(mInnerNode, 0, 12);
+        for (ChildNode child : mChildren) {
+            verify(child).removeObserver(eq(mInnerNode));
+        }
     }
 
     @Test
@@ -144,10 +148,10 @@
         mInnerNode.onItemRangeRemoved(mChildren.get(4), 0, 3);
         mInnerNode.onItemRangeChanged(mChildren.get(6), 0, 1, null);
 
-        verify(mParent).onItemRangeChanged(mInnerNode, 0, 1, null);
-        verify(mParent).onItemRangeInserted(mInnerNode, 5, 2);
-        verify(mParent).onItemRangeRemoved(mInnerNode, 8, 3);
-        verify(mParent).onItemRangeChanged(mInnerNode, 10, 1, null);
+        verify(mObserver).onItemRangeChanged(mInnerNode, 0, 1, null);
+        verify(mObserver).onItemRangeInserted(mInnerNode, 5, 2);
+        verify(mObserver).onItemRangeRemoved(mInnerNode, 8, 3);
+        verify(mObserver).onItemRangeChanged(mInnerNode, 10, 1, null);
 
         assertThat(mInnerNode.getItemCount(), is(11));
     }
@@ -158,38 +162,38 @@
     @Test
     public void testChangeNotificationsTiming() {
         // The MockModeParent will enforce a given number of items in the child when notified.
-        MockNodeParent parent = spy(new MockNodeParent());
+        MockListObserver observer = spy(new MockListObserver());
         InnerNode rootNode = new InnerNode();
 
-        TreeNode[] children = {makeDummyNode(3), makeDummyNode(5)};
+        ChildNode[] children = {makeDummyNode(3), makeDummyNode(5)};
         rootNode.addChildren(children);
-        rootNode.setParent(parent);
+        rootNode.addObserver(observer);
 
         assertThat(rootNode.getItemCount(), is(8));
-        verifyZeroInteractions(parent); // Before the parent is set, no notifications.
+        verifyZeroInteractions(observer); // Before the parent is set, no notifications.
 
-        parent.expectItemCount(24);
+        observer.expectItemCount(24);
         rootNode.addChildren(makeDummyNode(7), makeDummyNode(9)); // Should bundle the insertions.
-        verify(parent).onItemRangeInserted(eq(rootNode), eq(8), eq(16));
+        verify(observer).onItemRangeInserted(eq(rootNode), eq(8), eq(16));
 
-        parent.expectItemCount(28);
-        rootNode.addChild(makeDummyNode(4));
-        verify(parent).onItemRangeInserted(eq(rootNode), eq(24), eq(4));
+        observer.expectItemCount(28);
+        rootNode.addChildren(makeDummyNode(4));
+        verify(observer).onItemRangeInserted(eq(rootNode), eq(24), eq(4));
 
-        parent.expectItemCount(23);
+        observer.expectItemCount(23);
         rootNode.removeChild(children[1]);
-        verify(parent).onItemRangeRemoved(eq(rootNode), eq(3), eq(5));
+        verify(observer).onItemRangeRemoved(eq(rootNode), eq(3), eq(5));
 
-        parent.expectItemCount(0);
+        observer.expectItemCount(0);
         rootNode.removeChildren(); // Bundles the removals in a single change notification
-        verify(parent).onItemRangeRemoved(eq(rootNode), eq(0), eq(23));
+        verify(observer).onItemRangeRemoved(eq(rootNode), eq(0), eq(23));
     }
 
     /**
-     * Implementation of {@link NodeParent} that checks the item count from the node that
+     * Implementation of {@link ListObserver} that checks the item count from the node that
      * sends notifications against defined expectations. Fails on unexpected calls.
      */
-    private static class MockNodeParent implements NodeParent {
+    private static class MockListObserver implements ListObserver {
         @Nullable
         private Integer mNextExpectedItemCount;
 
@@ -198,30 +202,29 @@
         }
 
         @Override
-        public void onItemRangeChanged(
-                TreeNode child, int index, int count, PartialBindCallback callback) {
+        public void onItemRangeChanged(ListObservable child, int index, int count, Object payload) {
             checkCount(child);
         }
 
         @Override
-        public void onItemRangeInserted(TreeNode child, int index, int count) {
+        public void onItemRangeInserted(ListObservable child, int index, int count) {
             checkCount(child);
         }
 
         @Override
-        public void onItemRangeRemoved(TreeNode child, int index, int count) {
+        public void onItemRangeRemoved(ListObservable child, int index, int count) {
             checkCount(child);
         }
 
-        private void checkCount(TreeNode child) {
+        private void checkCount(ListObservable child) {
             if (mNextExpectedItemCount == null) fail("Unexpected call");
             assertThat(child.getItemCount(), is(mNextExpectedItemCount));
             mNextExpectedItemCount = null;
         }
     }
 
-    private static TreeNode makeDummyNode(int itemCount) {
-        TreeNode node = mock(TreeNode.class);
+    private static ChildNode makeDummyNode(int itemCount) {
+        ChildNode node = mock(ChildNode.class);
         when(node.getItemCount()).thenReturn(itemCount);
         return node;
     }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java
index e1a8b75..dd18e943 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java
@@ -804,7 +804,7 @@
         registerCategory(suggestionsSource, categories[3], 0);
         reloadNtp();
 
-        List<TreeNode> children = mAdapter.getSectionListForTesting().getChildren();
+        List<ChildNode> children = mAdapter.getSectionListForTesting().getChildren();
         assertEquals(4, children.size());
         assertEquals(SuggestionsSection.class, children.get(0).getClass());
         assertEquals(categories[0], getCategory(children.get(0)));
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSectionTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSectionTest.java
index 5a6c1dc..dc96357 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSectionTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSectionTest.java
@@ -48,6 +48,8 @@
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.DisableHistogramsRule;
+import org.chromium.chrome.browser.modelutil.ListObservable;
+import org.chromium.chrome.browser.modelutil.ListObservable.ListObserver;
 import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder.PartialBindCallback;
 import org.chromium.chrome.browser.ntp.snippets.CategoryStatus;
 import org.chromium.chrome.browser.ntp.snippets.KnownCategories;
@@ -99,7 +101,7 @@
     @Mock
     private SuggestionsSection.Delegate mDelegate;
     @Mock
-    private NodeParent mParent;
+    private ListObserver mObserver;
     @Mock
     private SuggestionsUiDelegate mUiDelegate;
     @Mock
@@ -208,15 +210,15 @@
         // Simulate initialisation by the adapter. Here we don't care about the notifications, since
         // the RecyclerView will be updated through notifyDataSetChanged.
         section.setStatus(CategoryStatus.AVAILABLE);
-        reset(mParent);
+        reset(mObserver);
 
         assertEquals(2, section.getItemCount()); // When empty, we have the header and status card.
         assertEquals(ItemViewType.STATUS, section.getItemViewType(1));
 
         section.appendSuggestions(snippets, /* keepSectionSize = */ true,
                 /* reportPrefetchedSuggestionsCount = */ false);
-        verify(mParent).onItemRangeInserted(section, 1, suggestionCount);
-        verify(mParent).onItemRangeRemoved(section, 1 + suggestionCount, 1);
+        verify(mObserver).onItemRangeInserted(section, 1, suggestionCount);
+        verify(mObserver).onItemRangeRemoved(section, 1 + suggestionCount, 1);
     }
 
     @Test
@@ -232,23 +234,23 @@
         section.setStatus(CategoryStatus.AVAILABLE);
         section.appendSuggestions(snippets, /* keepSectionSize = */ true,
                 /* reportPrefetchedSuggestionsCount = */ false);
-        reset(mParent);
+        reset(mObserver);
 
         // We don't clear suggestions when the status is AVAILABLE.
         section.setStatus(CategoryStatus.AVAILABLE);
-        verifyNoMoreInteractions(mParent);
+        verifyNoMoreInteractions(mObserver);
 
         // We clear existing suggestions when the status is not AVAILABLE, and show the status card.
         section.setStatus(CategoryStatus.CATEGORY_EXPLICITLY_DISABLED);
-        verify(mParent).onItemRangeRemoved(section, 1, suggestionCount);
-        verify(mParent).onItemRangeInserted(section, 1, 1);
+        verify(mObserver).onItemRangeRemoved(section, 1, suggestionCount);
+        verify(mObserver).onItemRangeInserted(section, 1, 1);
 
         // A loading state item triggers showing the loading item.
         section.setStatus(CategoryStatus.AVAILABLE_LOADING);
-        verify(mParent).onItemRangeInserted(section, 2, 1);
+        verify(mObserver).onItemRangeInserted(section, 2, 1);
 
         section.setStatus(CategoryStatus.AVAILABLE);
-        verify(mParent).onItemRangeRemoved(section, 2, 1);
+        verify(mObserver).onItemRangeRemoved(section, 2, 1);
     }
 
     @Test
@@ -268,17 +270,17 @@
 
         SuggestionsSection section = createSectionWithFetchAction(false);
         section.setStatus(CategoryStatus.AVAILABLE);
-        reset(mParent);
+        reset(mObserver);
 
         section.appendSuggestions(snippets, /* keepSectionSize = */ true,
                 /* reportPrefetchedSuggestionsCount = */ false);
 
         section.removeSuggestionById(snippets.get(1).mIdWithinCategory);
-        verify(mParent).onItemRangeRemoved(section, 2, 1);
+        verify(mObserver).onItemRangeRemoved(section, 2, 1);
 
         section.removeSuggestionById(snippets.get(0).mIdWithinCategory);
-        verify(mParent).onItemRangeRemoved(section, 1, 1);
-        verify(mParent).onItemRangeInserted(section, 1, 1);
+        verify(mObserver).onItemRangeRemoved(section, 1, 1);
+        verify(mObserver).onItemRangeInserted(section, 1, 1);
 
         assertEquals(2, section.getItemCount());
         assertEquals(ItemViewType.STATUS, section.getItemViewType(1));
@@ -297,7 +299,7 @@
                                                .build();
         SuggestionsSection section = createSection(info);
         section.setStatus(CategoryStatus.AVAILABLE);
-        reset(mParent);
+        reset(mObserver);
         assertEquals(3, section.getItemCount()); // We have the header and status card and a button.
 
         section.appendSuggestions(snippets, /* keepSectionSize = */ true,
@@ -305,11 +307,11 @@
         assertEquals(4, section.getItemCount());
 
         section.removeSuggestionById(snippets.get(0).mIdWithinCategory);
-        verify(mParent).onItemRangeRemoved(section, 1, 1);
+        verify(mObserver).onItemRangeRemoved(section, 1, 1);
 
         section.removeSuggestionById(snippets.get(1).mIdWithinCategory);
-        verify(mParent, times(2)).onItemRangeRemoved(section, 1, 1);
-        verify(mParent).onItemRangeInserted(section, 1, 1); // Only the status card is added.
+        verify(mObserver, times(2)).onItemRangeRemoved(section, 1, 1);
+        verify(mObserver).onItemRangeInserted(section, 1, 1); // Only the status card is added.
         assertEquals(3, section.getItemCount());
         assertEquals(ItemViewType.STATUS, section.getItemViewType(1));
         assertEquals(ItemViewType.ACTION, section.getItemViewType(2));
@@ -320,7 +322,7 @@
     public void testDismissSection() {
         SuggestionsSection section = createSectionWithFetchAction(false);
         section.setStatus(CategoryStatus.AVAILABLE);
-        reset(mParent);
+        reset(mObserver);
         assertEquals(2, section.getItemCount());
 
         @SuppressWarnings("unchecked")
@@ -344,7 +346,7 @@
         mSuggestionsSource.setStatusForCategory(KnownCategories.ARTICLES, CategoryStatus.AVAILABLE);
         section.setStatus(CategoryStatus.AVAILABLE);
 
-        reset(mParent);
+        reset(mObserver);
         assertEquals(1, section.getItemCount());
         assertEquals(ItemViewType.HEADER, section.getItemViewType(0));
 
@@ -380,7 +382,7 @@
         section.appendSuggestions(suggestions,
                 /* keepSectionSize = */ true, /* reportPrefetchedSuggestionsCount = */ false);
 
-        reset(mParent);
+        reset(mObserver);
         assertEquals(5, section.getItemCount());
         assertEquals(ItemViewType.HEADER, section.getItemViewType(0));
         assertEquals(ItemViewType.SNIPPET, section.getItemViewType(1));
@@ -442,7 +444,7 @@
 
     @Test
     @Feature({"Ntp"})
-    public void testOfflineStatusIgnoredIfDetached() {
+    public void testOfflineStatusIgnoredIfDestroyed() {
         final int suggestionCount = 2;
         final List<SnippetArticle> suggestions =
                 mSuggestionsSource.createAndSetSuggestions(suggestionCount, TEST_CATEGORY_ID);
@@ -459,7 +461,7 @@
         assertEquals(Long.valueOf(0L), suggestions.get(0).getOfflinePageOfflineId());
         assertNull(suggestions.get(1).getOfflinePageOfflineId());
 
-        section.detach();
+        section.destroy();
 
         final OfflinePageItem item1 = createOfflinePageItem(suggestions.get(1).mUrl, 1L);
         mBridge.setItems(Arrays.asList(item0, item1));
@@ -677,8 +679,8 @@
 
         mSuggestionsSource.createAndSetSuggestions(3, TEST_CATEGORY_ID);
         section.updateSuggestions();
-        verify(mParent).onItemRangeRemoved(section, 1, 4);
-        verify(mParent).onItemRangeInserted(section, 1, 3);
+        verify(mObserver).onItemRangeRemoved(section, 1, 4);
+        verify(mObserver).onItemRangeInserted(section, 1, 3);
         assertEquals(3, section.getSuggestionsCount());
 
         assertFalse(section.isDataStale());
@@ -701,8 +703,10 @@
 
         mSuggestionsSource.createAndSetSuggestions(3, TEST_CATEGORY_ID);
         section.updateSuggestions();
-        verify(mParent, never()).onItemRangeRemoved(any(TreeNode.class), anyInt(), anyInt());
-        verify(mParent, never()).onItemRangeInserted(any(TreeNode.class), anyInt(), anyInt());
+        verify(mObserver, never())
+                .onItemRangeRemoved(any(ListObservable.class), anyInt(), anyInt());
+        verify(mObserver, never())
+                .onItemRangeInserted(any(ListObservable.class), anyInt(), anyInt());
         assertEquals(4, section.getSuggestionsCount());
 
         assertTrue(section.isDataStale());
@@ -726,8 +730,8 @@
         List<SnippetArticle> newSnippets =
                 mSuggestionsSource.createAndSetSuggestions(3, TEST_CATEGORY_ID, "new");
         section.updateSuggestions();
-        verify(mParent).onItemRangeRemoved(section, 2, 3);
-        verify(mParent).onItemRangeInserted(section, 2, 2);
+        verify(mObserver).onItemRangeRemoved(section, 2, 3);
+        verify(mObserver).onItemRangeInserted(section, 2, 2);
         assertEquals(3, section.getSuggestionsCount());
         List<SnippetArticle> sectionSuggestions = getSuggestions(section);
         assertEquals(snippets.get(0), sectionSuggestions.get(0));
@@ -759,8 +763,8 @@
         List<SnippetArticle> newSnippets =
                 mSuggestionsSource.createAndSetSuggestions(3, TEST_CATEGORY_ID, "new");
         section.updateSuggestions();
-        verify(mParent).onItemRangeRemoved(section, 3, 2);
-        verify(mParent).onItemRangeInserted(section, 3, 1);
+        verify(mObserver).onItemRangeRemoved(section, 3, 2);
+        verify(mObserver).onItemRangeInserted(section, 3, 1);
         assertEquals(3, section.getSuggestionsCount());
         List<SnippetArticle> sectionSuggestions = getSuggestions(section);
         assertEquals(snippets.get(0), sectionSuggestions.get(0));
@@ -792,8 +796,9 @@
         section.updateSuggestions();
         // Even though the new list has just one suggestion, we need to keep the two seen ones
         // around.
-        verify(mParent).onItemRangeRemoved(section, 3, 2);
-        verify(mParent, never()).onItemRangeInserted(any(TreeNode.class), anyInt(), anyInt());
+        verify(mObserver).onItemRangeRemoved(section, 3, 2);
+        verify(mObserver, never())
+                .onItemRangeInserted(any(ListObservable.class), anyInt(), anyInt());
         assertEquals(2, section.getSuggestionsCount());
         List<SnippetArticle> sectionSuggestions = getSuggestions(section);
         assertEquals(snippets.get(0), sectionSuggestions.get(0));
@@ -824,7 +829,7 @@
         // Two suggestions left, one seen, one unseen.
         assertEquals(2, section.getSuggestionsCount());
 
-        reset(mParent);
+        reset(mObserver);
 
         mSuggestionsSource.setSuggestionsForCategory(
                 TEST_CATEGORY_ID, createDummySuggestions(4, TEST_CATEGORY_ID));
@@ -859,8 +864,10 @@
         mSuggestionsSource.setSuggestionsForCategory(
                 TEST_CATEGORY_ID, createDummySuggestions(3, TEST_CATEGORY_ID));
         section.updateSuggestions();
-        verify(mParent, never()).onItemRangeRemoved(any(TreeNode.class), anyInt(), anyInt());
-        verify(mParent, never()).onItemRangeInserted(any(TreeNode.class), anyInt(), anyInt());
+        verify(mObserver, never())
+                .onItemRangeRemoved(any(ListObservable.class), anyInt(), anyInt());
+        verify(mObserver, never())
+                .onItemRangeInserted(any(ListObservable.class), anyInt(), anyInt());
 
         // All old snippets should be in place.
         assertEquals(snippets, getSuggestions(section));
@@ -905,11 +912,11 @@
         SuggestionsSection section = createSectionWithFetchAction(false);
         section.appendSuggestions(suggestions, /* keepSectionSize = */ true,
                 /* reportPrefetchedSuggestionsCount = */ false);
-        reset(mParent);
+        reset(mObserver);
 
         // Remove the first card. The second one should get the update.
         section.removeSuggestionById(suggestions.get(0).mIdWithinCategory);
-        verify(mParent).onItemRangeChanged(
+        verify(mObserver).onItemRangeChanged(
                 same(section), eq(1), eq(1), any(PartialBindCallback.class));
     }
 
@@ -920,11 +927,11 @@
         SuggestionsSection section = createSectionWithFetchAction(false);
         section.appendSuggestions(suggestions, /* keepSectionSize = */ true,
                 /* reportPrefetchedSuggestionsCount = */ false);
-        reset(mParent);
+        reset(mObserver);
 
         // Remove the last card. The penultimate one should get the update.
         section.removeSuggestionById(suggestions.get(4).mIdWithinCategory);
-        verify(mParent).onItemRangeChanged(
+        verify(mObserver).onItemRangeChanged(
                 same(section), eq(4), eq(1), any(PartialBindCallback.class));
     }
 
@@ -935,11 +942,11 @@
         SuggestionsSection section = createSectionWithFetchAction(false);
         section.appendSuggestions(suggestions, /* keepSectionSize = */ true,
                 /* reportPrefetchedSuggestionsCount = */ false);
-        reset(mParent);
+        reset(mObserver);
 
         // Remove the last card. The penultimate one should get the update.
         section.removeSuggestionById(suggestions.get(1).mIdWithinCategory);
-        verify(mParent).onItemRangeChanged(
+        verify(mObserver).onItemRangeChanged(
                 same(section), eq(1), eq(1), any(PartialBindCallback.class));
     }
 
@@ -979,11 +986,11 @@
 
         section.appendSuggestions(suggestions, /* keepSectionSize = */ true,
                 /* reportPrefetchedSuggestionsCount = */ false);
-        reset(mParent);
+        reset(mObserver);
 
         section.appendSuggestions(createDummySuggestions(2, /* categoryId = */ 42, "new"),
                 /* keepSectionSize = */ false, /* reportPrefetchedSuggestionsCount = */ false);
-        verify(mParent).onItemRangeChanged(
+        verify(mObserver).onItemRangeChanged(
                 same(section), eq(5), eq(1), any(PartialBindCallback.class));
     }
 
@@ -995,7 +1002,7 @@
 
         // Reset any notification invocations on the parent from setting the initial list
         // of suggestions.
-        reset(mParent);
+        reset(mObserver);
         return section;
     }
 
@@ -1024,7 +1031,7 @@
     private SuggestionsSection createSection(SuggestionsCategoryInfo info) {
         SuggestionsSection section = new SuggestionsSection(
                 mDelegate, mUiDelegate, mock(SuggestionsRanker.class), mBridge, info);
-        section.setParent(mParent);
+        section.addObserver(mObserver);
         return section;
     }
 
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 1d2a112b..0555ebb 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -2136,6 +2136,8 @@
       "android/omnibox/omnibox_prerender.h",
       "android/oom_intervention/near_oom_monitor.cc",
       "android/oom_intervention/near_oom_monitor.h",
+      "android/oom_intervention/oom_intervention_config.cc",
+      "android/oom_intervention/oom_intervention_config.h",
       "android/oom_intervention/oom_intervention_decider.cc",
       "android/oom_intervention/oom_intervention_decider.h",
       "android/oom_intervention/oom_intervention_tab_helper.cc",
@@ -3330,15 +3332,8 @@
       "download/drag_download_item_views.cc",
       "lifetime/application_lifetime_aura.cc",
       "platform_util_aura.cc",
-      "ui/views/frame/browser_frame_mus.cc",
-      "ui/views/frame/browser_frame_mus.h",
-      "ui/views/frame/browser_non_client_frame_view_mus.cc",
-      "ui/views/frame/browser_non_client_frame_view_mus.h",
     ]
     deps += [
-      "//content/public/common",
-      "//services/ui/public/cpp",
-      "//services/ui/public/cpp/input_devices",
       "//ui/aura",
       "//ui/compositor",
       "//ui/snapshot",
diff --git a/chrome/browser/android/oom_intervention/near_oom_monitor.cc b/chrome/browser/android/oom_intervention/near_oom_monitor.cc
index d58b53fb..792e772 100644
--- a/chrome/browser/android/oom_intervention/near_oom_monitor.cc
+++ b/chrome/browser/android/oom_intervention/near_oom_monitor.cc
@@ -4,27 +4,13 @@
 
 #include "chrome/browser/android/oom_intervention/near_oom_monitor.h"
 
-#include "base/feature_list.h"
-#include "base/metrics/field_trial_params.h"
-#include "base/strings/string_number_conversions.h"
 #include "base/sys_info.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "chrome/common/chrome_features.h"
+#include "chrome/browser/android/oom_intervention/oom_intervention_config.h"
 #include "jni/NearOomMonitor_jni.h"
 
 namespace {
 
-const char kSwapFreeThresholdRatioParamName[] = "swap_free_threshold_ratio";
-const char kUseComponentCallbacks[] = "use_component_callbacks";
-const char kRendererWorkloadThresholdDeprecated[] =
-    "renderer_workload_threshold";
-const char kRendererWorkloadThresholdPercentage[] =
-    "renderer_workload_threshold_percentage";
-
-// Default SwapFree/SwapTotal ratio for detecting near-OOM situation.
-// TODO(bashi): Confirm that this is appropriate.
-const double kDefaultSwapFreeThresholdRatio = 0.45;
-
 // Default interval to check memory stats.
 constexpr base::TimeDelta kDefaultMonitoringDelta =
     base::TimeDelta::FromSeconds(1);
@@ -33,58 +19,15 @@
 constexpr base::TimeDelta kDefaultCooldownDelta =
     base::TimeDelta::FromSeconds(30);
 
-uint64_t GetRendererMemoryWorkloadThreshold() {
-  // Approximately 80MB for 512MB devices.
-  const uint64_t kDefaultMemoryWorkloadThresholdPercent = 16;
-
-  std::string threshold_str = base::GetFieldTrialParamValueByFeature(
-      features::kOomIntervention, kRendererWorkloadThresholdPercentage);
-  uint64_t threshold = 0;
-  size_t ram_size = base::SysInfo::AmountOfPhysicalMemory();
-  if (base::StringToUint64(threshold_str, &threshold)) {
-    return threshold * ram_size / 100;
-  }
-
-  // If the old trigger param is set, then enable intervention only on 512MB
-  // devices.
-  if (ram_size > 512 * 1024 * 1024)
-    return 0;
-
-  threshold_str = base::GetFieldTrialParamValueByFeature(
-      features::kOomIntervention, kRendererWorkloadThresholdDeprecated);
-  if (base::StringToUint64(threshold_str, &threshold)) {
-    return threshold;
-  }
-  return kDefaultMemoryWorkloadThresholdPercent * ram_size / 100;
-}
-
 }  // namespace
 
 // static
 NearOomMonitor* NearOomMonitor::Create() {
-  if (!base::FeatureList::IsEnabled(features::kOomIntervention))
-    return nullptr;
-
-  base::SystemMemoryInfoKB memory_info;
-  if (!base::GetSystemMemoryInfo(&memory_info))
-    return nullptr;
-
-  // If there is no swap (zram) the monitor doesn't work because we use
-  // SwapFree as the tracking metric.
-  if (memory_info.swap_total == 0)
-    return nullptr;
-
-  double threshold_ratio = base::GetFieldTrialParamByFeatureAsDouble(
-      features::kOomIntervention, kSwapFreeThresholdRatioParamName,
-      kDefaultSwapFreeThresholdRatio);
-  int64_t swapfree_threshold =
-      static_cast<int64_t>(memory_info.swap_total * threshold_ratio);
-
-  uint64_t renderer_workload_threshold = GetRendererMemoryWorkloadThreshold();
-  if (!renderer_workload_threshold)
+  auto* config = OomInterventionConfig::GetInstance();
+  if (!config->is_intervention_enabled())
     return nullptr;
   return new NearOomMonitor(base::ThreadTaskRunnerHandle::Get(),
-                            swapfree_threshold, renderer_workload_threshold);
+                            config->swapfree_threshold());
 }
 
 // static
@@ -95,19 +38,15 @@
 
 NearOomMonitor::NearOomMonitor(
     scoped_refptr<base::SequencedTaskRunner> task_runner,
-    int64_t swapfree_threshold,
-    uint64_t renderer_workload_threshold)
+    int64_t swapfree_threshold)
     : task_runner_(task_runner),
       check_callback_(
           base::Bind(&NearOomMonitor::Check, base::Unretained(this))),
       monitoring_interval_(kDefaultMonitoringDelta),
       cooldown_interval_(kDefaultCooldownDelta),
       swapfree_threshold_(swapfree_threshold),
-      renderer_workload_threshold_(renderer_workload_threshold),
       component_callback_is_enabled_(
-          base::GetFieldTrialParamByFeatureAsBool(features::kOomIntervention,
-                                                  kUseComponentCallbacks,
-                                                  true)) {
+          OomInterventionConfig::GetInstance()->use_components_callback()) {
   if (ComponentCallbackIsEnabled()) {
     j_object_.Reset(Java_NearOomMonitor_create(
         base::android::AttachCurrentThread(), reinterpret_cast<jlong>(this)));
diff --git a/chrome/browser/android/oom_intervention/near_oom_monitor.h b/chrome/browser/android/oom_intervention/near_oom_monitor.h
index 1d646af3..4aa158b 100644
--- a/chrome/browser/android/oom_intervention/near_oom_monitor.h
+++ b/chrome/browser/android/oom_intervention/near_oom_monitor.h
@@ -39,16 +39,11 @@
   void OnLowMemory(JNIEnv* env,
                    const base::android::JavaParamRef<jobject>& jcaller);
 
-  uint64_t renderer_workload_threshold() const {
-    return renderer_workload_threshold_;
-  }
-
  protected:
   static NearOomMonitor* Create();
 
   NearOomMonitor(scoped_refptr<base::SequencedTaskRunner> task_runner,
-                 int64_t swapfree_threshold,
-                 uint64_t renderer_workload_threshold);
+                 int64_t swapfree_threshold);
 
   // Gets system memory info. This is a virtual method so that we can override
   // this for testing.
@@ -77,7 +72,6 @@
   base::TimeTicks next_check_time_;
 
   int64_t swapfree_threshold_;
-  uint64_t renderer_workload_threshold_;
 
   CallbackList callbacks_;
 
diff --git a/chrome/browser/android/oom_intervention/near_oom_monitor_unittest.cc b/chrome/browser/android/oom_intervention/near_oom_monitor_unittest.cc
index 3e5a3c84..95cea25 100644
--- a/chrome/browser/android/oom_intervention/near_oom_monitor_unittest.cc
+++ b/chrome/browser/android/oom_intervention/near_oom_monitor_unittest.cc
@@ -12,16 +12,13 @@
 namespace {
 const int64_t kTestSwapTotalKB = 128 * 1024;
 const int64_t kTestSwapFreeThreshold = kTestSwapTotalKB / 4;
-const uint64_t kRendererWorkloadThreshold = 10 * 1024;
 }  // namespace
 
 class MockNearOomMonitor : public NearOomMonitor {
  public:
   explicit MockNearOomMonitor(
       scoped_refptr<base::SequencedTaskRunner> task_runner)
-      : NearOomMonitor(task_runner,
-                       kTestSwapFreeThreshold,
-                       kRendererWorkloadThreshold) {
+      : NearOomMonitor(task_runner, kTestSwapFreeThreshold) {
     // Start with 128MB swap total and 64MB swap free.
     memory_info_.swap_total = kTestSwapTotalKB;
     memory_info_.swap_free = kTestSwapTotalKB / 2;
diff --git a/chrome/browser/android/oom_intervention/oom_intervention_config.cc b/chrome/browser/android/oom_intervention/oom_intervention_config.cc
new file mode 100644
index 0000000..db4da445
--- /dev/null
+++ b/chrome/browser/android/oom_intervention/oom_intervention_config.cc
@@ -0,0 +1,111 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/android/oom_intervention/oom_intervention_config.h"
+
+#include "base/feature_list.h"
+#include "base/metrics/field_trial_params.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/sys_info.h"
+#include "chrome/common/chrome_features.h"
+
+namespace {
+
+const char kUseComponentCallbacks[] = "use_component_callbacks";
+const char kSwapFreeThresholdRatioParamName[] = "swap_free_threshold_ratio";
+const char kRendererWorkloadThresholdDeprecated[] =
+    "renderer_workload_threshold";
+const char kRendererWorkloadThresholdPercentage[] =
+    "renderer_workload_threshold_percentage";
+
+// Default SwapFree/SwapTotal ratio for detecting near-OOM situation.
+// TODO(bashi): Confirm that this is appropriate.
+const double kDefaultSwapFreeThresholdRatio = 0.45;
+
+// Field trial parameter names.
+const char kRendererPauseParamName[] = "pause_renderer";
+const char kShouldDetectInRenderer[] = "detect_in_renderer";
+
+bool GetThresholdParam(const char* param,
+                       size_t ram_size,
+                       uint64_t* threshold) {
+  std::string str =
+      base::GetFieldTrialParamValueByFeature(features::kOomIntervention, param);
+  uint64_t value = 0;
+  if (!base::StringToUint64(str, &value))
+    return false;
+  *threshold = value * ram_size / 100;
+  return true;
+}
+
+bool GetRendererMemoryThreshold(uint64_t* threshold) {
+  const uint64_t kDefaultMemoryWorkloadThresholdPercent = 16;
+  static size_t kRAMSize = base::SysInfo::AmountOfPhysicalMemory();
+  *threshold = 0;
+
+  if (GetThresholdParam(kRendererWorkloadThresholdPercentage, kRAMSize,
+                        threshold)) {
+    return true;
+  }
+
+  // If the old trigger param is set, then enable intervention only on 512MB
+  // devices.
+  if (kRAMSize > 512 * 1024 * 1024)
+    return false;
+
+  std::string threshold_str = base::GetFieldTrialParamValueByFeature(
+      features::kOomIntervention, kRendererWorkloadThresholdDeprecated);
+  if (base::StringToUint64(threshold_str, threshold))
+    return true;
+
+  *threshold = kDefaultMemoryWorkloadThresholdPercent * kRAMSize / 100;
+  return true;
+}
+
+bool GetSwapFreeThreshold(uint64_t* threshold) {
+  base::SystemMemoryInfoKB memory_info;
+  if (!base::GetSystemMemoryInfo(&memory_info))
+    return false;
+
+  // If there is no swap (zram) the monitor doesn't work because we use
+  // SwapFree as the tracking metric.
+  if (memory_info.swap_total == 0)
+    return false;
+
+  double threshold_ratio = base::GetFieldTrialParamByFeatureAsDouble(
+      features::kOomIntervention, kSwapFreeThresholdRatioParamName,
+      kDefaultSwapFreeThresholdRatio);
+  *threshold = static_cast<uint64_t>(memory_info.swap_total * threshold_ratio);
+  return true;
+}
+
+}  // namespace
+
+OomInterventionConfig::OomInterventionConfig()
+    : is_intervention_enabled_(
+          base::FeatureList::IsEnabled(features::kOomIntervention)) {
+  if (!is_intervention_enabled_)
+    return;
+
+  is_renderer_pause_enabled_ = base::GetFieldTrialParamByFeatureAsBool(
+      features::kOomIntervention, kRendererPauseParamName, false);
+  should_detect_in_renderer_ = base::GetFieldTrialParamByFeatureAsBool(
+      features::kOomIntervention, kShouldDetectInRenderer, true);
+
+  use_components_callback_ = base::GetFieldTrialParamByFeatureAsBool(
+      features::kOomIntervention, kUseComponentCallbacks, true);
+
+  // Enable intervention only if at least one threshold is set for detection
+  // in each process.
+  if (!GetSwapFreeThreshold(&swapfree_threshold_) ||
+      !GetRendererMemoryThreshold(&renderer_workload_threshold_)) {
+    is_intervention_enabled_ = false;
+  }
+}
+
+// static
+const OomInterventionConfig* OomInterventionConfig::GetInstance() {
+  static OomInterventionConfig* config = new OomInterventionConfig();
+  return config;
+}
diff --git a/chrome/browser/android/oom_intervention/oom_intervention_config.h b/chrome/browser/android/oom_intervention/oom_intervention_config.h
new file mode 100644
index 0000000..1a875a4
--- /dev/null
+++ b/chrome/browser/android/oom_intervention/oom_intervention_config.h
@@ -0,0 +1,50 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ANDROID_OOM_INTERVENTION_OOM_INTERVENTION_CONFIG_H_
+#define CHROME_BROWSER_ANDROID_OOM_INTERVENTION_OOM_INTERVENTION_CONFIG_H_
+
+#include "third_party/blink/public/platform/oom_intervention.mojom.h"
+
+// Holds the configurations provided by field trials for OOM intervention.
+class OomInterventionConfig {
+ public:
+  static const OomInterventionConfig* GetInstance();
+
+  // True when field trials enables intervention and config is valid.
+  bool is_intervention_enabled() const { return is_intervention_enabled_; }
+
+  // True if Android memory pressure signals should be monitored.
+  bool use_components_callback() const { return use_components_callback_; }
+
+  // True if on detection of near OOM condition the renderer JS should be
+  // paused.
+  bool is_renderer_pause_enabled() const { return is_renderer_pause_enabled_; }
+
+  // True if detection should be enabled on renderers.
+  bool should_detect_in_renderer() const { return should_detect_in_renderer_; }
+
+  // The threshold for swap size in the system to start monitoring.
+  uint64_t swapfree_threshold() const { return swapfree_threshold_; }
+
+  // The threshold for renderer workload to trigger intervention.
+  uint64_t renderer_workload_threshold() const {
+    return renderer_workload_threshold_;
+  }
+
+ private:
+  OomInterventionConfig();
+  ~OomInterventionConfig();
+
+  bool is_intervention_enabled_ = false;
+
+  bool use_components_callback_ = false;
+  bool is_renderer_pause_enabled_ = false;
+  bool should_detect_in_renderer_ = false;
+
+  uint64_t swapfree_threshold_ = 0;
+  uint64_t renderer_workload_threshold_ = 0;
+};
+
+#endif  // CHROME_BROWSER_ANDROID_OOM_INTERVENTION_OOM_INTERVENTION_CONFIG_H_
diff --git a/chrome/browser/android/oom_intervention/oom_intervention_tab_helper.cc b/chrome/browser/android/oom_intervention/oom_intervention_tab_helper.cc
index 9c096be..72b1882 100644
--- a/chrome/browser/android/oom_intervention/oom_intervention_tab_helper.cc
+++ b/chrome/browser/android/oom_intervention/oom_intervention_tab_helper.cc
@@ -4,11 +4,10 @@
 
 #include "chrome/browser/android/oom_intervention/oom_intervention_tab_helper.h"
 
-#include "base/metrics/field_trial_params.h"
 #include "base/metrics/histogram_macros.h"
+#include "chrome/browser/android/oom_intervention/oom_intervention_config.h"
 #include "chrome/browser/android/oom_intervention/oom_intervention_decider.h"
 #include "chrome/browser/ui/android/infobars/near_oom_infobar.h"
-#include "chrome/common/chrome_features.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/render_frame_host.h"
@@ -58,22 +57,6 @@
       "Memory.Experimental.OomIntervention.InterventionStateOnCrash", accepted);
 }
 
-// Field trial parameter names.
-const char kRendererPauseParamName[] = "pause_renderer";
-const char kShouldDetectInRenderer[] = "detect_in_renderer";
-
-bool RendererPauseIsEnabled() {
-  static bool enabled = base::GetFieldTrialParamByFeatureAsBool(
-      features::kOomIntervention, kRendererPauseParamName, false);
-  return enabled;
-}
-
-bool ShouldDetectInRenderer() {
-  static bool enabled = base::GetFieldTrialParamByFeatureAsBool(
-      features::kOomIntervention, kShouldDetectInRenderer, true);
-  return enabled;
-}
-
 }  // namespace
 
 // static
@@ -87,8 +70,6 @@
       decider_(OomInterventionDecider::GetForBrowserContext(
           web_contents->GetBrowserContext())),
       binding_(this),
-      renderer_memory_workload_threshold_(
-          NearOomMonitor::GetInstance()->renderer_workload_threshold()),
       weak_ptr_factory_(this) {
   OutOfMemoryReporter::FromWebContents(web_contents)->AddObserver(this);
   shared_metrics_buffer_ = base::UnsafeSharedMemoryRegion::Create(
@@ -269,7 +250,7 @@
   if (near_oom_detected_time_)
     return;
 
-  if (ShouldDetectInRenderer()) {
+  if (OomInterventionConfig::GetInstance()->should_detect_in_renderer()) {
     if (binding_.is_bound())
       return;
     StartDetectionInRenderer();
@@ -281,7 +262,7 @@
 }
 
 void OomInterventionTabHelper::StopMonitoring() {
-  if (ShouldDetectInRenderer()) {
+  if (OomInterventionConfig::GetInstance()->should_detect_in_renderer()) {
     ResetInterfaces();
   } else {
     subscription_.reset();
@@ -289,7 +270,8 @@
 }
 
 void OomInterventionTabHelper::StartDetectionInRenderer() {
-  bool trigger_intervention = RendererPauseIsEnabled();
+  auto* config = OomInterventionConfig::GetInstance();
+  bool trigger_intervention = config->is_renderer_pause_enabled();
   if (trigger_intervention && decider_) {
     DCHECK(!web_contents()->GetBrowserContext()->IsOffTheRecord());
     const std::string& host = web_contents()->GetVisibleURL().host();
@@ -307,11 +289,11 @@
   binding_.Bind(mojo::MakeRequest(&host));
   intervention_->StartDetection(
       std::move(host), shared_metrics_buffer_.Duplicate(),
-      renderer_memory_workload_threshold_, trigger_intervention);
+      config->renderer_workload_threshold(), trigger_intervention);
 }
 
 void OomInterventionTabHelper::OnNearOomDetected() {
-  DCHECK(!ShouldDetectInRenderer());
+  DCHECK(!OomInterventionConfig::GetInstance()->should_detect_in_renderer());
   DCHECK_EQ(web_contents()->GetVisibility(), content::Visibility::VISIBLE);
   DCHECK(!near_oom_detected_time_);
   subscription_.reset();
diff --git a/chrome/browser/android/oom_intervention/oom_intervention_tab_helper.h b/chrome/browser/android/oom_intervention/oom_intervention_tab_helper.h
index eaffbcc..7643aa8 100644
--- a/chrome/browser/android/oom_intervention/oom_intervention_tab_helper.h
+++ b/chrome/browser/android/oom_intervention/oom_intervention_tab_helper.h
@@ -112,10 +112,6 @@
   base::UnsafeSharedMemoryRegion shared_metrics_buffer_;
   base::WritableSharedMemoryMapping metrics_mapping_;
 
-  // If memory workload in renderer is above this threshold, we assume that we
-  // are in a near-OOM situation.
-  uint64_t renderer_memory_workload_threshold_;
-
   base::WeakPtrFactory<OomInterventionTabHelper> weak_ptr_factory_;
 };
 
diff --git a/chrome/browser/autofill/autofill_provider_browsertest.cc b/chrome/browser/autofill/autofill_provider_browsertest.cc
index 2bae34ec8..54fe542f 100644
--- a/chrome/browser/autofill/autofill_provider_browsertest.cc
+++ b/chrome/browser/autofill/autofill_provider_browsertest.cc
@@ -131,8 +131,8 @@
     testing::Mock::VerifyAndClearExpectations(autofill_provider_.get());
   }
 
-  content::RenderViewHost* RenderViewHost() {
-    return WebContents()->GetRenderViewHost();
+  content::RenderFrameHost* GetMainFrame() {
+    return WebContents()->GetMainFrame();
   }
 
   content::WebContents* WebContents() {
@@ -175,7 +175,7 @@
                                                 "/autofill/label_change.html"));
 
     std::string focus("document.getElementById('address').focus();");
-    ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), focus));
+    ASSERT_TRUE(content::ExecuteScript(GetMainFrame(), focus));
 
     SimulateUserTypingInFocuedField();
     while (!autofill_provider_->is_queried()) {
@@ -191,7 +191,7 @@
         "document.getElementById('submit_button').click();";
 
     ASSERT_TRUE(
-        content::ExecuteScript(RenderViewHost(), change_label_and_submit));
+        content::ExecuteScript(GetMainFrame(), change_label_and_submit));
     // Need to pay attention for a message that XHR has finished since there
     // is no navigation to wait for.
     content::DOMMessageQueue message_queue;
@@ -235,7 +235,7 @@
       "var iframe = document.getElementById('address_iframe');"
       "var frame_doc = iframe.contentDocument;"
       "frame_doc.getElementById('address_field').focus();");
-  ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), focus));
+  ASSERT_TRUE(content::ExecuteScript(GetMainFrame(), focus));
 
   SimulateUserTypingInFocuedField();
   std::string fill_and_submit =
@@ -243,7 +243,7 @@
       "var frame_doc = iframe.contentDocument;"
       "frame_doc.getElementById('submit_button').click();";
 
-  ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
+  ASSERT_TRUE(content::ExecuteScript(GetMainFrame(), fill_and_submit));
   std::string message;
   while (message_queue.WaitForMessage(&message)) {
     if (message == "\"SUBMISSION_FINISHED\"")
@@ -270,7 +270,7 @@
       "var iframe = document.getElementById('address_iframe');"
       "var frame_doc = iframe.contentDocument;"
       "frame_doc.getElementById('address_field').focus();");
-  ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), focus));
+  ASSERT_TRUE(content::ExecuteScript(GetMainFrame(), focus));
 
   SimulateUserTypingInFocuedField();
   std::string fill_and_submit =
@@ -278,7 +278,7 @@
       "var frame_doc = iframe.contentDocument;"
       "frame_doc.getElementById('submit_button').click();";
 
-  ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
+  ASSERT_TRUE(content::ExecuteScript(GetMainFrame(), fill_and_submit));
   std::string message;
   while (message_queue.WaitForMessage(&message)) {
     if (message == "\"SUBMISSION_FINISHED\"")
diff --git a/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc b/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc
index 2ffc405..8f31df3a2 100644
--- a/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc
+++ b/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc
@@ -4,12 +4,15 @@
 
 #include <stddef.h>
 #include <memory>
+#include <string>
 
 #include "base/bind.h"
 #include "base/callback.h"
+#include "base/files/file_enumerator.h"
 #include "base/files/file_path.h"
 #include "base/path_service.h"
 #include "base/run_loop.h"
+#include "base/test/bind_test_util.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/threading/thread_restrictions.h"
 #include "chrome/browser/browser_process.h"
@@ -22,6 +25,7 @@
 #include "chrome/browser/external_protocol/external_protocol_handler.h"
 #include "chrome/browser/net/system_network_context_manager.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/pref_names.h"
@@ -51,14 +55,82 @@
 #include "services/network/public/cpp/features.h"
 #include "services/network/public/cpp/simple_url_loader.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/re2/src/re2/re2.h"
 
 using content::BrowserThread;
 using content::BrowsingDataFilterBuilder;
 
 namespace {
 static const char* kExampleHost = "example.com";
+static const char* kLocalHost = "localhost";
+
+// Check if |file| matches any regex in |whitelist|.
+bool IsFileWhitelisted(const std::string& file,
+                       const std::vector<std::string>& whitelist) {
+  for (const std::string& pattern : whitelist) {
+    if (RE2::PartialMatch(file, pattern))
+      return true;
+  }
+  return false;
 }
 
+// Searches the user data directory for files that contain |hostname| in the
+// filename or as part of the content. Returns the number of files that
+// do not match any regex in |whitelist|.
+bool CheckUserDirectoryForString(const std::string& hostname,
+                                 const std::vector<std::string>& whitelist) {
+  base::FilePath user_data_dir =
+      g_browser_process->profile_manager()->user_data_dir();
+  base::ScopedAllowBlockingForTesting allow_blocking;
+  base::FileEnumerator enumerator(
+      user_data_dir, true /* recursive */,
+      base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES);
+  int found = 0;
+  for (base::FilePath path = enumerator.Next(); !path.empty();
+       path = enumerator.Next()) {
+    // Remove |user_data_dir| part from path.
+    std::string file =
+        path.NormalizePathSeparatorsTo('/').AsUTF8Unsafe().substr(
+            user_data_dir.AsUTF8Unsafe().length());
+
+    // Check file name.
+    if (file.find(hostname) != std::string::npos) {
+      if (IsFileWhitelisted(file, whitelist)) {
+        LOG(INFO) << "Whitelisted: " << file;
+      } else {
+        found++;
+        LOG(WARNING) << "Found file name: " << file;
+      }
+    }
+
+    // Check file content.
+    if (enumerator.GetInfo().IsDirectory())
+      continue;
+    std::string content;
+    if (!base::ReadFileToString(path, &content)) {
+      LOG(INFO) << "Could not read: " << file;
+      continue;
+    }
+    size_t pos = content.find(hostname);
+    if (pos != std::string::npos) {
+      if (IsFileWhitelisted(file, whitelist)) {
+        LOG(INFO) << "Whitelisted: " << file;
+        continue;
+      }
+      found++;
+      // Print surrounding text of the match.
+      std::string partial_content = content.substr(
+          pos < 30 ? 0 : pos - 30,
+          std::min(content.size() - 1, pos + hostname.size() + 30));
+      LOG(WARNING) << "Found file content: " << file << "\n"
+                   << partial_content << "\n";
+    }
+  }
+  return found;
+}
+
+}  // namespace
+
 class BrowsingDataRemoverBrowserTest : public InProcessBrowserTest {
  public:
   BrowsingDataRemoverBrowserTest() {}
@@ -177,19 +249,19 @@
   }
 
   void SetDataForType(const std::string& type) {
-    ASSERT_TRUE(RunScriptAndGetBool("set" + type + "()"));
+    ASSERT_TRUE(RunScriptAndGetBool("set" + type + "()"))
+        << "Couldn't create data for: " << type;
   }
 
   int GetSiteDataCount() {
     base::RunLoop run_loop;
     int count = -1;
-    (new SiteDataCountingHelper(
-         browser()->profile(), base::Time(),
-         base::Bind(&BrowsingDataRemoverBrowserTest::OnCookieCountResult,
-                    base::Unretained(this), base::Unretained(&run_loop),
-                    base::Unretained(&count))))
+    (new SiteDataCountingHelper(browser()->profile(), base::Time(),
+                                base::BindLambdaForTesting([&](int c) {
+                                  count = c;
+                                  run_loop.Quit();
+                                })))
         ->CountAndDestroySelfWhenFinished();
-
     run_loop.Run();
     return count;
   }
@@ -225,11 +297,6 @@
     run_loop->Quit();
   }
 
-  void OnCookieCountResult(base::RunLoop* run_loop, int* out_count, int count) {
-    *out_count = count;
-    run_loop->Quit();
-  }
-
   base::test::ScopedFeatureList feature_list_;
 };
 
@@ -465,6 +532,10 @@
   TestSiteData("Cookie");
 }
 
+IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverBrowserTest, SessionCookieDeletion) {
+  TestSiteData("SessionCookie");
+}
+
 IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverBrowserTest, LocalStorageDeletion) {
   TestSiteData("LocalStorage");
 }
@@ -520,3 +591,68 @@
 IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverBrowserTest, EmptyIndexedDb) {
   TestEmptySiteData("IndexedDb");
 }
+
+// Test that storage doesn't leave any traces on disk.
+IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverBrowserTest,
+                       PRE_PRE_StorageRemovedFromDisk) {
+  ASSERT_EQ(0, GetSiteDataCount());
+  ASSERT_EQ(0, CheckUserDirectoryForString(kLocalHost, {}));
+  // To use secure-only features on a host name, we need an https server.
+  net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
+  https_server.SetSSLConfig(
+      net::EmbeddedTestServer::CERT_COMMON_NAME_IS_DOMAIN);
+  base::FilePath path;
+  base::PathService::Get(content::DIR_TEST_DATA, &path);
+  https_server.ServeFilesFromDirectory(path);
+  ASSERT_TRUE(https_server.Start());
+
+  GURL url = https_server.GetURL(kLocalHost, "/browsing_data/site_data.html");
+  ui_test_utils::NavigateToURL(browser(), url);
+
+  const std::vector<std::string> types{
+      "Cookie",    "LocalStorage", "FileSystem",    "SessionStorage",
+      "IndexedDb", "WebSql",       "ServiceWorker", "CacheStorage",
+  };
+  for (const std::string& type : types) {
+    SetDataForType(type);
+    EXPECT_TRUE(HasDataForType(type));
+  }
+  // TODO(crbug.com/846297): Add more datatypes for testing. E.g. notifications,
+  // payment handler, content settings, autofill, ...?
+}
+
+// Restart after creating the data to ensure that everything was written to
+// disk.
+IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverBrowserTest,
+                       PRE_StorageRemovedFromDisk) {
+  EXPECT_EQ(1, GetSiteDataCount());
+  RemoveAndWait(ChromeBrowsingDataRemoverDelegate::DATA_TYPE_SITE_DATA |
+                content::BrowsingDataRemover::DATA_TYPE_CACHE |
+                ChromeBrowsingDataRemoverDelegate::DATA_TYPE_HISTORY |
+                ChromeBrowsingDataRemoverDelegate::DATA_TYPE_CONTENT_SETTINGS);
+  EXPECT_EQ(0, GetSiteDataCount());
+}
+
+// Check if any data remains after a deletion and a Chrome restart to force
+// all writes to be finished.
+IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverBrowserTest, StorageRemovedFromDisk) {
+  // Deletions should remove all traces of browsing data from disk
+  // but there are a few bugs that need to be fixed.
+  // Any addition to this list must have an associated TODO().
+  static const std::vector<std::string> whitelist = {
+    // TODO(crbug.com/823071): LevelDB logs are not deleted immediately.
+    "File System/Origins/[0-9]*.log",
+    "Local Storage/leveldb/[0-9]*.log",
+    "Service Worker/Database/[0-9]*.log",
+    "Session Storage/[0-9]*.log",
+
+#if defined(OS_CHROMEOS)
+    // TODO(crbug.com/846297): Many leveldb files remain on ChromeOS. I couldn't
+    // reproduce this in manual testing, so it might be a timing issue when
+    // Chrome is closed after the second test?
+    "[0-9]{6}",
+#endif
+  };
+  int found = CheckUserDirectoryForString(kLocalHost, whitelist);
+  EXPECT_EQ(0, found) << "A non-whitelisted file contains the hostname.";
+}
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index e47df77..c3374e7 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -2180,6 +2180,7 @@
     "//components/drive/sync/entry_update_performer_unittest.cc",
     "//components/drive/sync/remove_performer_unittest.cc",
     "//components/drive/sync_client_unittest.cc",
+    "//components/drive/team_drive_change_list_loader_unittest.cc",
     "//components/drive/team_drive_list_loader_unittest.cc",
   ]
 
diff --git a/chrome/browser/chromeos/arc/bluetooth/arc_bluetooth_bridge.cc b/chrome/browser/chromeos/arc/bluetooth/arc_bluetooth_bridge.cc
index c167b67..93e02c5 100644
--- a/chrome/browser/chromeos/arc/bluetooth/arc_bluetooth_bridge.cc
+++ b/chrome/browser/chromeos/arc/bluetooth/arc_bluetooth_bridge.cc
@@ -76,6 +76,10 @@
     BluetoothGattCharacteristic::Permission::PERMISSION_WRITE_ENCRYPTED |
     BluetoothGattCharacteristic::Permission::
         PERMISSION_WRITE_ENCRYPTED_AUTHENTICATED;
+// Bluetooth Spec Vol 3, Part G, 3.3.3.3 Client Characteristic Configuration.
+constexpr uint8_t DISABLE_NOTIFICATION_VALUE = 0;
+constexpr uint8_t ENABLE_NOTIFICATION_VALUE = 1;
+constexpr uint8_t ENABLE_INDICATION_VALUE = 2;
 constexpr int32_t kInvalidGattAttributeHandle = -1;
 constexpr int32_t kInvalidAdvertisementHandle = -1;
 // Bluetooth Specification Version 4.2 Vol 3 Part F Section 3.2.2
@@ -816,8 +820,10 @@
 void ArcBluetoothBridge::EnableAdapter(EnableAdapterCallback callback) {
   DCHECK(bluetooth_adapter_);
   if (IsPowerChangeInitiatedByLocal(AdapterPowerState::TURN_ON)) {
+    BLUETOOTH_LOG(EVENT) << "Received a request to enable adapter (local)";
     DequeueLocalPowerChange(AdapterPowerState::TURN_ON);
   } else {
+    BLUETOOTH_LOG(EVENT) << "Received a request to enable adapter (remote)";
     if (!bluetooth_adapter_->IsPowered()) {
       EnqueueRemotePowerChange(AdapterPowerState::TURN_ON, std::move(callback));
       return;
@@ -830,8 +836,10 @@
 void ArcBluetoothBridge::DisableAdapter(DisableAdapterCallback callback) {
   DCHECK(bluetooth_adapter_);
   if (IsPowerChangeInitiatedByLocal(AdapterPowerState::TURN_OFF)) {
+    BLUETOOTH_LOG(EVENT) << "Received a request to disable adapter (local)";
     DequeueLocalPowerChange(AdapterPowerState::TURN_OFF);
   } else {
+    BLUETOOTH_LOG(EVENT) << "Received a request to disable adapter (remote)";
     if (bluetooth_adapter_->IsPowered()) {
       EnqueueRemotePowerChange(AdapterPowerState::TURN_OFF,
                                std::move(callback));
@@ -1506,6 +1514,8 @@
   bool write_success = base::JSONWriter::Write(extras, &extras_json);
   DCHECK(write_success);
 
+  BLUETOOTH_LOG(EVENT) << "Sending Android intent to set power: "
+                       << (powered == AdapterPowerState::TURN_ON);
   intent_instance->SendBroadcast(
       ArcIntentHelperBridge::AppendStringToIntentHelperPackageName(
           "SET_BLUETOOTH_STATE"),
@@ -1588,26 +1598,49 @@
   DCHECK(descriptor);
   DCHECK(descriptor->GetPermissions() & kGattWritePermission);
 
-  // To register / deregister GATT notification, we need to
-  // 1) Write to CCC Descriptor to enable/disable the notification
-  // 2) Ask BT hw to register / deregister the notification
-  // The Chrome API groups both steps into one API, and does not support writing
-  // directly to the CCC Descriptor. Therefore, until we fix
-  // https://crbug.com/622832, we return successfully when we encounter this.
-  // TODO(http://crbug.com/622832)
-  if (descriptor->GetUUID() ==
-      BluetoothGattDescriptor::ClientCharacteristicConfigurationUuid()) {
-    OnGattOperationDone(std::move(callback));
-    return;
-  }
-
   // TODO(crbug.com/730593): Remove AdaptCallbackForRepeating() by updating
   // the callee interface.
   auto repeating_callback =
       base::AdaptCallbackForRepeating(std::move(callback));
-  descriptor->WriteRemoteDescriptor(
-      value->value, base::Bind(&OnGattOperationDone, repeating_callback),
-      base::Bind(&OnGattOperationError, repeating_callback));
+  if (value->value.empty()) {
+    repeating_callback.Run(mojom::BluetoothGattStatus::GATT_FAILURE);
+    return;
+  }
+
+  if (descriptor->GetUUID() !=
+      BluetoothGattDescriptor::ClientCharacteristicConfigurationUuid()) {
+    descriptor->WriteRemoteDescriptor(
+        value->value, base::Bind(&OnGattOperationDone, repeating_callback),
+        base::Bind(&OnGattOperationError, repeating_callback));
+    return;
+  }
+
+  BluetoothRemoteGattCharacteristic* characteristic =
+      descriptor->GetCharacteristic();
+  std::string char_id_str = characteristic->GetIdentifier();
+  auto it = notification_session_.find(char_id_str);
+  if (it == notification_session_.end()) {
+    repeating_callback.Run(mojom::BluetoothGattStatus::GATT_FAILURE);
+    return;
+  }
+
+  // Stop the previous session while keeping this client registered.
+  it->second.reset();
+  switch (value->value[0]) {
+    case DISABLE_NOTIFICATION_VALUE:
+      repeating_callback.Run(mojom::BluetoothGattStatus::GATT_SUCCESS);
+      return;
+    case ENABLE_NOTIFICATION_VALUE:
+    case ENABLE_INDICATION_VALUE:
+      characteristic->StartNotifySession(
+          base::Bind(&ArcBluetoothBridge::OnGattNotifyStartDone,
+                     weak_factory_.GetWeakPtr(), repeating_callback,
+                     char_id_str),
+          base::Bind(&OnGattOperationError, repeating_callback));
+      return;
+    default:
+      repeating_callback.Run(mojom::BluetoothGattStatus::GATT_FAILURE);
+  }
 }
 
 void ArcBluetoothBridge::OnGattNotifyStartDone(
@@ -1615,6 +1648,8 @@
     const std::string char_string_id,
     std::unique_ptr<BluetoothGattNotifySession> notify_session) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  // Hold on to |notify_session|. Destruction of |notify_session| is equivalent
+  // to stopping this session.
   notification_session_[char_string_id] = std::move(notify_session);
   std::move(callback).Run(mojom::BluetoothGattStatus::GATT_SUCCESS);
 }
@@ -1626,21 +1661,21 @@
     RegisterForGattNotificationCallback callback) {
   BluetoothRemoteGattCharacteristic* characteristic = FindGattCharacteristic(
       std::move(remote_addr), std::move(service_id), std::move(char_id));
-
-  if (!characteristic) {
+  if (characteristic == nullptr) {
     LOG(WARNING) << __func__ << " Characteristic is not existed.";
+    std::move(callback).Run(mojom::BluetoothGattStatus::GATT_FAILURE);
     return;
   }
 
-  // TODO(crbug.com/730593): Remove AdaptCallbackForRepeating() by updating
-  // the callee interface.
-  auto repeating_callback =
-      base::AdaptCallbackForRepeating(std::move(callback));
-  characteristic->StartNotifySession(
-      base::Bind(&ArcBluetoothBridge::OnGattNotifyStartDone,
-                 weak_factory_.GetWeakPtr(), repeating_callback,
-                 characteristic->GetIdentifier()),
-      base::Bind(&OnGattOperationError, repeating_callback));
+  std::string char_id_str = characteristic->GetIdentifier();
+  if (base::ContainsKey(notification_session_, char_id_str)) {
+    // There can be only one notification session per characteristic.
+    std::move(callback).Run(mojom::BluetoothGattStatus::GATT_FAILURE);
+    return;
+  }
+
+  notification_session_.emplace(char_id_str, nullptr);
+  std::move(callback).Run(mojom::BluetoothGattStatus::GATT_SUCCESS);
 }
 
 void ArcBluetoothBridge::DeregisterForGattNotification(
@@ -1652,23 +1687,21 @@
 
   BluetoothRemoteGattCharacteristic* characteristic = FindGattCharacteristic(
       std::move(remote_addr), std::move(service_id), std::move(char_id));
-
-  if (!characteristic) {
+  if (characteristic == nullptr) {
     LOG(WARNING) << __func__ << " Characteristic is not existed.";
+    std::move(callback).Run(mojom::BluetoothGattStatus::GATT_FAILURE);
     return;
   }
 
-  // TODO(rkc): Return an error code when failing. crbug.com/771055
   std::string char_id_str = characteristic->GetIdentifier();
   auto it = notification_session_.find(char_id_str);
   if (it == notification_session_.end()) {
     LOG(WARNING) << "Notification session not found " << char_id_str;
+    std::move(callback).Run(mojom::BluetoothGattStatus::GATT_FAILURE);
     return;
   }
-  std::unique_ptr<BluetoothGattNotifySession> notify = std::move(it->second);
   notification_session_.erase(it);
-  notify->Stop(
-      base::Bind(&OnGattOperationDone, base::Passed(std::move(callback))));
+  std::move(callback).Run(mojom::BluetoothGattStatus::GATT_SUCCESS);
 }
 
 void ArcBluetoothBridge::ReadRemoteRssi(mojom::BluetoothAddressPtr remote_addr,
diff --git a/chrome/browser/chromeos/login/lock/screen_locker_tester.cc b/chrome/browser/chromeos/login/lock/screen_locker_tester.cc
index 984dda4..3e4b629 100644
--- a/chrome/browser/chromeos/login/lock/screen_locker_tester.cc
+++ b/chrome/browser/chromeos/login/lock/screen_locker_tester.cc
@@ -18,7 +18,6 @@
 #include "chromeos/login/auth/fake_extended_authenticator.h"
 #include "chromeos/login/auth/stub_authenticator.h"
 #include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/render_view_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
 #include "content/public/test/test_utils.h"
@@ -108,7 +107,7 @@
 
   WebUIScreenLockerTester() {}
 
-  content::RenderViewHost* RenderViewHost() const;
+  content::RenderFrameHost* GetMainFrame() const;
 
   // Returns the ScreenLockerWebUI object.
   WebUIScreenLocker* webui_screen_locker() const;
@@ -120,17 +119,15 @@
 };
 
 void WebUIScreenLockerTester::SetPassword(const std::string& password) {
-  webui()->GetWebContents()->GetMainFrame()->ExecuteJavaScriptForTests(
-      base::ASCIIToUTF16(base::StringPrintf(
-          "$('pod-row').pods[0].passwordElement.value = '%s';",
-          password.c_str())));
+  GetMainFrame()->ExecuteJavaScriptForTests(base::ASCIIToUTF16(
+      base::StringPrintf("$('pod-row').pods[0].passwordElement.value = '%s';",
+                         password.c_str())));
 }
 
 std::string WebUIScreenLockerTester::GetPassword() {
   std::string result;
   std::unique_ptr<base::Value> v = content::ExecuteScriptAndGetValue(
-      RenderViewHost()->GetMainFrame(),
-      "$('pod-row').pods[0].passwordElement.value;");
+      GetMainFrame(), "$('pod-row').pods[0].passwordElement.value;");
   CHECK(v->GetAsString(&result));
   return result;
 }
@@ -144,7 +141,7 @@
 
   // Verify that "reauth" warning is hidden.
   std::unique_ptr<base::Value> v = content::ExecuteScriptAndGetValue(
-      RenderViewHost()->GetMainFrame(),
+      GetMainFrame(),
       "window.getComputedStyle("
       "    $('pod-row').pods[0].querySelector('.reauth-hint-container'))"
       "        .display == 'none'");
@@ -153,7 +150,7 @@
 
   // Attempt to sign in.
   LoginAttemptObserver login;
-  v = content::ExecuteScriptAndGetValue(RenderViewHost()->GetMainFrame(),
+  v = content::ExecuteScriptAndGetValue(GetMainFrame(),
                                         "$('pod-row').pods[0].activate();");
   ASSERT_TRUE(v->GetAsBoolean(&result));
   ASSERT_TRUE(result);
@@ -172,8 +169,8 @@
   return webui_screen_locker()->lock_window_;
 }
 
-content::RenderViewHost* WebUIScreenLockerTester::RenderViewHost() const {
-  return webui()->GetWebContents()->GetRenderViewHost();
+content::RenderFrameHost* WebUIScreenLockerTester::GetMainFrame() const {
+  return webui()->GetWebContents()->GetMainFrame();
 }
 
 WebUIScreenLocker* WebUIScreenLockerTester::webui_screen_locker() const {
diff --git a/chrome/browser/extensions/api/tabs/app_window_controller.cc b/chrome/browser/extensions/api/tabs/app_window_controller.cc
index 458fc5b..afcb3fe 100644
--- a/chrome/browser/extensions/api/tabs/app_window_controller.cc
+++ b/chrome/browser/extensions/api/tabs/app_window_controller.cc
@@ -47,17 +47,6 @@
   return true;
 }
 
-void AppWindowController::SetFullscreenMode(bool is_fullscreen,
-                                            const GURL& extension_url) const {
-  // Do nothing. Panels cannot be fullscreen.
-  if (app_window_->window_type_is_panel())
-    return;
-  // TODO(llandwerlin): should we prevent changes in fullscreen mode
-  // when the fullscreen state is FULLSCREEN_TYPE_FORCED?
-  app_window_->SetFullscreen(AppWindow::FULLSCREEN_TYPE_WINDOW_API,
-                             is_fullscreen);
-}
-
 Browser* AppWindowController::GetBrowser() const {
   return nullptr;
 }
diff --git a/chrome/browser/extensions/api/tabs/app_window_controller.h b/chrome/browser/extensions/api/tabs/app_window_controller.h
index a5fd585..e2a6b49 100644
--- a/chrome/browser/extensions/api/tabs/app_window_controller.h
+++ b/chrome/browser/extensions/api/tabs/app_window_controller.h
@@ -30,8 +30,6 @@
   int GetWindowId() const override;
   std::string GetWindowTypeText() const override;
   bool CanClose(Reason* reason) const override;
-  void SetFullscreenMode(bool is_fullscreen,
-                         const GURL& extension_url) const override;
   Browser* GetBrowser() const override;
   bool IsVisibleToTabsAPIForExtension(
       const Extension* extension,
diff --git a/chrome/browser/extensions/api/tabs/tabs_api.cc b/chrome/browser/extensions/api/tabs/tabs_api.cc
index bac262e..d0a727a 100644
--- a/chrome/browser/extensions/api/tabs/tabs_api.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_api.cc
@@ -29,6 +29,7 @@
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/extensions/api/tabs/tabs_constants.h"
 #include "chrome/browser/extensions/api/tabs/windows_util.h"
+#include "chrome/browser/extensions/browser_extension_window_controller.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/extension_tab_util.h"
 #include "chrome/browser/extensions/tab_helper.h"
diff --git a/chrome/browser/extensions/api/tabs/tabs_event_router.cc b/chrome/browser/extensions/api/tabs/tabs_event_router.cc
index 05f4274..fa3f57cb 100644
--- a/chrome/browser/extensions/api/tabs/tabs_event_router.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_event_router.cc
@@ -14,6 +14,7 @@
 #include "chrome/browser/extensions/api/tabs/tabs_constants.h"
 #include "chrome/browser/extensions/api/tabs/tabs_windows_api.h"
 #include "chrome/browser/extensions/api/tabs/windows_event_router.h"
+#include "chrome/browser/extensions/browser_extension_window_controller.h"
 #include "chrome/browser/extensions/extension_tab_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
diff --git a/chrome/browser/extensions/browser_extension_window_controller.cc b/chrome/browser/extensions/browser_extension_window_controller.cc
index bfd955c9..c1864e8 100644
--- a/chrome/browser/extensions/browser_extension_window_controller.cc
+++ b/chrome/browser/extensions/browser_extension_window_controller.cc
@@ -29,6 +29,13 @@
   WindowControllerList::GetInstance()->RemoveExtensionWindow(this);
 }
 
+void BrowserExtensionWindowController::SetFullscreenMode(
+    bool is_fullscreen,
+    const GURL& extension_url) const {
+  if (browser_->window()->IsFullscreen() != is_fullscreen)
+    browser_->ToggleFullscreenModeWithExtension(extension_url);
+}
+
 int BrowserExtensionWindowController::GetWindowId() const {
   return static_cast<int>(browser_->session_id().id());
 }
@@ -47,13 +54,6 @@
   return true;
 }
 
-void BrowserExtensionWindowController::SetFullscreenMode(
-    bool is_fullscreen,
-    const GURL& extension_url) const {
-  if (browser_->window()->IsFullscreen() != is_fullscreen)
-    browser_->ToggleFullscreenModeWithExtension(extension_url);
-}
-
 Browser* BrowserExtensionWindowController::GetBrowser() const {
   return browser_;
 }
diff --git a/chrome/browser/extensions/browser_extension_window_controller.h b/chrome/browser/extensions/browser_extension_window_controller.h
index 5f0af80..95534b6a 100644
--- a/chrome/browser/extensions/browser_extension_window_controller.h
+++ b/chrome/browser/extensions/browser_extension_window_controller.h
@@ -9,6 +9,7 @@
 #include "chrome/browser/extensions/window_controller.h"
 
 class Browser;
+class GURL;
 
 namespace extensions {
 class Extension;
@@ -18,12 +19,14 @@
   explicit BrowserExtensionWindowController(Browser* browser);
   ~BrowserExtensionWindowController() override;
 
+  // Sets the window's fullscreen state. |extension_url| provides the url
+  // associated with the extension (used by FullscreenController).
+  void SetFullscreenMode(bool is_fullscreen, const GURL& extension_url) const;
+
   // WindowController implementation.
   int GetWindowId() const override;
   std::string GetWindowTypeText() const override;
   bool CanClose(Reason* reason) const override;
-  void SetFullscreenMode(bool is_fullscreen,
-                         const GURL& extension_url) const override;
   Browser* GetBrowser() const override;
   bool IsVisibleToTabsAPIForExtension(
       const Extension* extension,
diff --git a/chrome/browser/extensions/extension_commands_global_registry_apitest.cc b/chrome/browser/extensions/extension_commands_global_registry_apitest.cc
index 145c9be..1ce7121 100644
--- a/chrome/browser/extensions/extension_commands_global_registry_apitest.cc
+++ b/chrome/browser/extensions/extension_commands_global_registry_apitest.cc
@@ -3,8 +3,8 @@
 // found in the LICENSE file.
 
 #include "build/build_config.h"
+#include "chrome/browser/extensions/browser_extension_window_controller.h"
 #include "chrome/browser/extensions/extension_apitest.h"
-#include "chrome/browser/extensions/window_controller.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/test/base/interactive_test_utils.h"
@@ -186,7 +186,7 @@
   ASSERT_TRUE(catcher.GetNextResult());
 
   Browser* incognito_browser = CreateIncognitoBrowser();  // Ditto.
-  WindowController* controller =
+  BrowserExtensionWindowController* controller =
       incognito_browser->extension_window_controller();
 
   ui_controls::SendKeyPress(controller->window()->GetNativeWindow(),
diff --git a/chrome/browser/extensions/extension_function_test_utils.cc b/chrome/browser/extensions/extension_function_test_utils.cc
index f7ef1c35..bf45cfd 100644
--- a/chrome/browser/extensions/extension_function_test_utils.cc
+++ b/chrome/browser/extensions/extension_function_test_utils.cc
@@ -12,6 +12,7 @@
 #include "base/macros.h"
 #include "base/values.h"
 #include "chrome/browser/extensions/api/tabs/tabs_constants.h"
+#include "chrome/browser/extensions/browser_extension_window_controller.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "components/crx_file/id_util.h"
diff --git a/chrome/browser/extensions/extension_tab_util.cc b/chrome/browser/extensions/extension_tab_util.cc
index fbfb3d7b..7dddcfe 100644
--- a/chrome/browser/extensions/extension_tab_util.cc
+++ b/chrome/browser/extensions/extension_tab_util.cc
@@ -13,6 +13,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/extensions/api/tabs/tabs_constants.h"
+#include "chrome/browser/extensions/browser_extension_window_controller.h"
 #include "chrome/browser/extensions/chrome_extension_function.h"
 #include "chrome/browser/extensions/chrome_extension_function_details.h"
 #include "chrome/browser/extensions/tab_helper.h"
diff --git a/chrome/browser/extensions/extension_view_host.cc b/chrome/browser/extensions/extension_view_host.cc
index d4dc62958..1ee4e41 100644
--- a/chrome/browser/extensions/extension_view_host.cc
+++ b/chrome/browser/extensions/extension_view_host.cc
@@ -8,6 +8,7 @@
 #include "base/strings/string_piece.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/extensions/browser_extension_window_controller.h"
 #include "chrome/browser/extensions/extension_view.h"
 #include "chrome/browser/extensions/window_controller.h"
 #include "chrome/browser/file_select_helper.h"
diff --git a/chrome/browser/extensions/window_controller.h b/chrome/browser/extensions/window_controller.h
index b5380f7..431d7d9 100644
--- a/chrome/browser/extensions/window_controller.h
+++ b/chrome/browser/extensions/window_controller.h
@@ -17,7 +17,6 @@
 #include "chrome/common/extensions/api/windows.h"
 
 class Browser;  // TODO(stevenjb) eliminate this dependency.
-class GURL;
 class Profile;
 
 namespace ui {
@@ -74,11 +73,6 @@
   // permitted and sets |reason| if not NULL.
   virtual bool CanClose(Reason* reason) const = 0;
 
-  // Set the window's fullscreen state. |extension_url| provides the url
-  // associated with the extension (used by FullscreenController).
-  virtual void SetFullscreenMode(bool is_fullscreen,
-                                 const GURL& extension_url) const = 0;
-
   // Returns a Browser if available. Defaults to returning NULL.
   // TODO(stevenjb): Temporary workaround. Eliminate this.
   virtual Browser* GetBrowser() const;
diff --git a/chrome/browser/infobars/infobars_browsertest.cc b/chrome/browser/infobars/infobars_browsertest.cc
index 2f606ab..8307d5cc 100644
--- a/chrome/browser/infobars/infobars_browsertest.cc
+++ b/chrome/browser/infobars/infobars_browsertest.cc
@@ -89,8 +89,9 @@
     base::FilePath path = ui_test_utils::GetTestFilePath(
         base::FilePath().AppendASCII("extensions"),
         base::FilePath().AppendASCII(filename));
-    ExtensionService* service = extensions::ExtensionSystem::Get(
-        browser()->profile())->extension_service();
+    extensions::ExtensionService* service =
+        extensions::ExtensionSystem::Get(browser()->profile())
+            ->extension_service();
 
     extensions::TestExtensionRegistryObserver observer(
         extensions::ExtensionRegistry::Get(browser()->profile()));
diff --git a/chrome/browser/media/encrypted_media_browsertest.cc b/chrome/browser/media/encrypted_media_browsertest.cc
index fb50ca98..f844546 100644
--- a/chrome/browser/media/encrypted_media_browsertest.cc
+++ b/chrome/browser/media/encrypted_media_browsertest.cc
@@ -667,7 +667,13 @@
   TestSimplePlayback("bear-640x360-v_frag-cenc.mp4", kMp4Avc1VideoOnly);
 }
 
-IN_PROC_BROWSER_TEST_P(EncryptedMediaTest, Playback_VideoOnly_MP4_MDAT) {
+// Flaky: crbug.com/847881
+#if defined(MAC_OSX)
+#define MAYBE_Playback_VideoOnly_MP4_MDAT DISABLED_Playback_VideoOnly_MP4_MDAT
+#else
+#define MAYBE_Playback_VideoOnly_MP4_MDAT Playback_VideoOnly_MP4_MDAT
+#endif
+IN_PROC_BROWSER_TEST_P(EncryptedMediaTest, MAYBE_Playback_VideoOnly_MP4_MDAT) {
   // MP4 without MSE is not support yet, http://crbug.com/170793.
   if (CurrentSourceType() != SrcType::MSE) {
     DVLOG(0) << "Skipping test; Can only play MP4 encrypted streams by MSE.";
diff --git a/chrome/browser/resource_coordinator/decision_details.cc b/chrome/browser/resource_coordinator/decision_details.cc
index cf279e8..3fe89559 100644
--- a/chrome/browser/resource_coordinator/decision_details.cc
+++ b/chrome/browser/resource_coordinator/decision_details.cc
@@ -278,7 +278,7 @@
   return reasons;
 }
 
-void DecisionDetails::ClearForTesting() {
+void DecisionDetails::Clear() {
   reasons_.clear();
   toggled_ = false;
 }
diff --git a/chrome/browser/resource_coordinator/decision_details.h b/chrome/browser/resource_coordinator/decision_details.h
index 87a496c..70a5a2a 100644
--- a/chrome/browser/resource_coordinator/decision_details.h
+++ b/chrome/browser/resource_coordinator/decision_details.h
@@ -193,7 +193,7 @@
   // success reasons.
   std::vector<std::string> GetFailureReasonStrings() const;
 
-  void ClearForTesting();
+  void Clear();
 
  private:
   bool CheckIfToggled();
diff --git a/chrome/browser/resource_coordinator/decision_details_unittest.cc b/chrome/browser/resource_coordinator/decision_details_unittest.cc
index a46a1b67..a9135f2 100644
--- a/chrome/browser/resource_coordinator/decision_details_unittest.cc
+++ b/chrome/browser/resource_coordinator/decision_details_unittest.cc
@@ -142,7 +142,7 @@
   EXPECT_TRUE(details.toggled());
 
   // Clear and go back to the initial state.
-  details.ClearForTesting();
+  details.Clear();
   EXPECT_EQ(0u, details.reasons().size());
   EXPECT_FALSE(details.IsPositive());
   EXPECT_FALSE(details.toggled());
diff --git a/chrome/browser/resource_coordinator/lifecycle_unit.h b/chrome/browser/resource_coordinator/lifecycle_unit.h
index b3fe017..0c56acc 100644
--- a/chrome/browser/resource_coordinator/lifecycle_unit.h
+++ b/chrome/browser/resource_coordinator/lifecycle_unit.h
@@ -13,12 +13,14 @@
 #include "base/process/process_handle.h"
 #include "base/strings/string16.h"
 #include "base/time/time.h"
+#include "chrome/browser/resource_coordinator/decision_details.h"
 #include "chrome/browser/resource_coordinator/discard_reason.h"
 #include "chrome/browser/resource_coordinator/lifecycle_state.h"
 #include "content/public/browser/visibility.h"
 
 namespace resource_coordinator {
 
+class DecisionDetails;
 class LifecycleUnitObserver;
 class TabLifecycleUnitExternal;
 
@@ -126,11 +128,20 @@
   // not on a LifecycleUnit. https://crbug.com/775644
   virtual bool CanPurge() const = 0;
 
-  // Returns true if this LifecycleUnit can be frozen.
-  virtual bool CanFreeze() const = 0;
+  // Returns true if this LifecycleUnit can be frozen. Full details regarding
+  // the policy decision are recorded in |decision_details|, for logging.
+  // Returning false but with an empty |decision_details| means the transition
+  // is not possible for a trivial reason that doesn't need to be reported (ie,
+  // the page is already frozen).
+  virtual bool CanFreeze(DecisionDetails* decision_details) const = 0;
 
-  // Returns true if this LifecycleUnit can be discared.
-  virtual bool CanDiscard(DiscardReason reason) const = 0;
+  // Returns true if this LifecycleUnit can be discarded. Full details regarding
+  // the policy decision are recorded in the |decision_details|, for logging.
+  // Returning false but with an empty |decision_details| means the transition
+  // is not possible for a trivial reason that doesn't need to be reported
+  // (ie, the page is already discarded).
+  virtual bool CanDiscard(DiscardReason reason,
+                          DecisionDetails* decision_details) const = 0;
 
   // Discards this LifecycleUnit.
   //
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc b/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc
index d1e732f..40daa00 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc
@@ -230,27 +230,56 @@
   return !IsMediaTab();
 }
 
-bool TabLifecycleUnitSource::TabLifecycleUnit::CanFreeze() const {
+bool TabLifecycleUnitSource::TabLifecycleUnit::CanFreeze(
+    DecisionDetails* decision_details) const {
+  DCHECK(decision_details->reasons().empty());
+
+  // Leave the |decision_details| empty and return immediately for "trivial"
+  // rejection reasons. These aren't worth reporting about, as they have nothing
+  // to do with the content itself.
+
   if (IsFrozen())
     return false;
 
-  if (GetWebContents()->GetVisibility() == content::Visibility::VISIBLE)
-    return false;
-
-  if (IsMediaTab())
-    return false;
-
   // Allow a page to load fully before freezing it.
   if (TabLoadTracker::Get()->GetLoadingState(GetWebContents()) !=
       TabLoadTracker::LoadingState::LOADED) {
     return false;
   }
 
-  return true;
+  // TODO(chrisha): Integrate local database observations into this policy
+  // decision.
+
+  // We deliberately run through all of the logic without early termination.
+  // This ensures that the decision details lists all possible reasons that the
+  // transition can be denied.
+
+  if (GetWebContents()->GetVisibility() == content::Visibility::VISIBLE)
+    decision_details->AddReason(DecisionFailureReason::LIVE_STATE_VISIBLE);
+
+  // Do not freeze tabs that are casting/mirroring/playing audio.
+  IsMediaTabImpl(decision_details);
+
+  // TODO(chrisha): Add integration of feature policy and global whitelist
+  // logic. In the meantime, the only success reason is because the heuristic
+  // deems the operation to be safe.
+  if (decision_details->reasons().empty()) {
+    decision_details->AddReason(
+        DecisionSuccessReason::HEURISTIC_OBSERVED_TO_BE_SAFE);
+    DCHECK(decision_details->IsPositive());
+  }
+  return decision_details->IsPositive();
 }
 
 bool TabLifecycleUnitSource::TabLifecycleUnit::CanDiscard(
-    DiscardReason reason) const {
+    DiscardReason reason,
+    DecisionDetails* decision_details) const {
+  DCHECK(decision_details->reasons().empty());
+
+  // Leave the |decision_details| empty and return immediately for "trivial"
+  // rejection reasons. These aren't worth reporting about, as they have nothing
+  // to do with the content itself.
+
   // Can't discard a tab that isn't in a TabStripModel.
   if (!tab_strip_model_)
     return false;
@@ -262,15 +291,6 @@
   if (GetWebContents()->IsCrashed())
     return false;
 
-#if defined(OS_CHROMEOS)
-  if (GetWebContents()->GetVisibility() == content::Visibility::VISIBLE)
-    return false;
-#else
-  // Do not discard the tab if it is currently active in its window.
-  if (tab_strip_model_->GetActiveWebContents() == GetWebContents())
-    return false;
-#endif  // defined(OS_CHROMEOS)
-
   // Do not discard tabs that don't have a valid URL (most probably they have
   // just been opened and discarding them would lose the URL).
   // TODO(fdoray): Look into a workaround to be able to kill the tab without
@@ -280,39 +300,68 @@
     return false;
   }
 
+  // Do not discard a tab that has already been discarded. Since this is being
+  // removed there is no way to communicate that to the heuristic. Treat this
+  // as a "trivial" rejection reason for now and return with an empty decision
+  // details.
+  // TODO(fdoray): Allow tabs to be discarded more than once.
+  // https://crbug.com/794622
+  if (discard_count_ > 0) {
+#if defined(OS_CHROMEOS)
+    // On ChromeOS this can be ignored for urgent discards, where running out of
+    // memory leads to a kernel panic.
+    if (reason != DiscardReason::kUrgent)
+      return false;
+#else
+    return false;
+#endif  // defined(OS_CHROMEOS)
+  }
+
+// TODO(chrisha): Integrate local database observations into this policy
+// decision.
+
+// We deliberately run through all of the logic without early termination.
+// This ensures that the decision details lists all possible reasons that the
+// transition can be denied.
+
+#if defined(OS_CHROMEOS)
+  if (GetWebContents()->GetVisibility() == content::Visibility::VISIBLE)
+    decision_details->AddReason(DecisionFailureReason::LIVE_STATE_VISIBLE);
+#else
+  // Do not discard the tab if it is currently active in its window.
+  if (tab_strip_model_->GetActiveWebContents() == GetWebContents())
+    decision_details->AddReason(DecisionFailureReason::LIVE_STATE_VISIBLE);
+#endif  // defined(OS_CHROMEOS)
+
   // Do not discard tabs in which the user has entered text in a form.
   if (GetWebContents()->GetPageImportanceSignals().had_form_interaction)
-    return false;
+    decision_details->AddReason(DecisionFailureReason::LIVE_STATE_FORM_ENTRY);
 
-  // Do not discard media tabs as it's too distruptive to the user experience.
-  if (IsMediaTab())
-    return false;
+  // Do not discard tabs that are casting/mirroring/playing audio.
+  IsMediaTabImpl(decision_details);
 
   // Do not discard PDFs as they might contain entry that is not saved and they
   // don't remember their scrolling positions. See crbug.com/547286 and
   // crbug.com/65244.
   // TODO(fdoray): Remove this workaround when the bugs are fixed.
   if (GetWebContents()->GetContentsMimeType() == "application/pdf")
-    return false;
+    decision_details->AddReason(DecisionFailureReason::LIVE_STATE_IS_PDF);
 
   // Do not discard a tab that was explicitly disallowed to.
-  if (!IsAutoDiscardable())
-    return false;
+  if (!IsAutoDiscardable()) {
+    decision_details->AddReason(
+        DecisionFailureReason::LIVE_STATE_EXTENSION_DISALLOWED);
+  }
 
-#if defined(OS_CHROMEOS)
-  // The following protections are ignored on ChromeOS during urgent discard,
-  // because running out of memory would lead to a kernel panic.
-  if (reason == DiscardReason::kUrgent)
-    return true;
-#endif  // defined(OS_CHROMEOS)
-
-  // Do not discard a tab that has already been discarded.
-  // TODO(fdoray): Allow tabs to be discarded more than once.
-  // https://crbug.com/794622
-  if (discard_count_ > 0)
-    return false;
-
-  return true;
+  // TODO(chrisha): Add integration of feature policy and global whitelist
+  // logic. In the meantime, the only success reason is because the heuristic
+  // deems the operation to be safe.
+  if (decision_details->reasons().empty()) {
+    decision_details->AddReason(
+        DecisionSuccessReason::HEURISTIC_OBSERVED_TO_BE_SAFE);
+    DCHECK(decision_details->IsPositive());
+  }
+  return decision_details->IsPositive();
 }
 
 bool TabLifecycleUnitSource::TabLifecycleUnit::Discard(
@@ -438,25 +487,7 @@
 }
 
 bool TabLifecycleUnitSource::TabLifecycleUnit::IsMediaTab() const {
-  // TODO(fdoray): Consider being notified of audible, capturing and mirrored
-  // state changes via WebContentsDelegate::NavigationStateChanged().
-  // https://crbug.com/775644
-
-  if (recently_audible_time_ == base::TimeTicks::Max() ||
-      (!recently_audible_time_.is_null() &&
-       NowTicks() - recently_audible_time_ < kTabAudioProtectionTime)) {
-    return true;
-  }
-
-  scoped_refptr<MediaStreamCaptureIndicator> media_indicator =
-      MediaCaptureDevicesDispatcher::GetInstance()
-          ->GetMediaStreamCaptureIndicator();
-  if (media_indicator->IsCapturingUserMedia(GetWebContents()) ||
-      media_indicator->IsBeingMirrored(GetWebContents())) {
-    return true;
-  }
-
-  return false;
+  return IsMediaTabImpl(nullptr);
 }
 
 bool TabLifecycleUnitSource::TabLifecycleUnit::IsAutoDiscardable() const {
@@ -502,6 +533,44 @@
   return discard_count_;
 }
 
+bool TabLifecycleUnitSource::TabLifecycleUnit::IsMediaTabImpl(
+    DecisionDetails* decision_details) const {
+  // TODO(fdoray): Consider being notified of audible, capturing and mirrored
+  // state changes via WebContentsDelegate::NavigationStateChanged() and/or
+  // WebContentsObserver::OnAudioStateChanged and/or RecentlyAudibleHelper.
+  // https://crbug.com/775644
+
+  bool is_media_tab = false;
+
+  if (recently_audible_time_ == base::TimeTicks::Max() ||
+      (!recently_audible_time_.is_null() &&
+       NowTicks() - recently_audible_time_ < kTabAudioProtectionTime)) {
+    is_media_tab = true;
+    if (decision_details) {
+      decision_details->AddReason(
+          DecisionFailureReason::LIVE_STATE_PLAYING_AUDIO);
+    }
+  }
+
+  scoped_refptr<MediaStreamCaptureIndicator> media_indicator =
+      MediaCaptureDevicesDispatcher::GetInstance()
+          ->GetMediaStreamCaptureIndicator();
+
+  if (media_indicator->IsCapturingUserMedia(GetWebContents())) {
+    is_media_tab = true;
+    if (decision_details)
+      decision_details->AddReason(DecisionFailureReason::LIVE_STATE_CAPTURING);
+  }
+
+  if (media_indicator->IsBeingMirrored(GetWebContents())) {
+    is_media_tab = true;
+    if (decision_details)
+      decision_details->AddReason(DecisionFailureReason::LIVE_STATE_MIRRORING);
+  }
+
+  return is_media_tab;
+}
+
 void TabLifecycleUnitSource::TabLifecycleUnit::OnDiscardedStateChange() {
   for (auto& observer : *observers_)
     observer.OnDiscardedStateChange(GetWebContents(), IsDiscarded());
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit.h b/chrome/browser/resource_coordinator/tab_lifecycle_unit.h
index 249612cb..10f396d 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit.h
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit.h
@@ -86,8 +86,9 @@
   bool Freeze() override;
   int GetEstimatedMemoryFreedOnDiscardKB() const override;
   bool CanPurge() const override;
-  bool CanFreeze() const override;
-  bool CanDiscard(DiscardReason reason) const override;
+  bool CanFreeze(DecisionDetails* decision_details) const override;
+  bool CanDiscard(DiscardReason reason,
+                  DecisionDetails* decision_details) const override;
   bool Discard(DiscardReason discard_reason) override;
 
   // TabLifecycleUnitExternal:
@@ -109,6 +110,10 @@
   friend class TabLifecycleUnitSource;
 
  private:
+  // Determines if the tab is a media tab, and populates an optional
+  // |decision_details| with full details.
+  bool IsMediaTabImpl(DecisionDetails* decision_details) const;
+
   // For non-urgent discarding, sends a request for freezing to occur prior to
   // discarding the tab.
   void RequestFreezeForDiscard(DiscardReason reason);
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source_unittest.cc b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source_unittest.cc
index f443c70..85f2fc2 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source_unittest.cc
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source_unittest.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/resource_coordinator/lifecycle_unit_source_observer.h"
 #include "chrome/browser/resource_coordinator/tab_lifecycle_observer.h"
 #include "chrome/browser/resource_coordinator/tab_lifecycle_unit.h"
+#include "chrome/browser/resource_coordinator/test_lifecycle_unit.h"
 #include "chrome/browser/resource_coordinator/time.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/tabs/test_tab_strip_model_delegate.h"
@@ -86,13 +87,6 @@
   return lifecycle_unit->GetLastFocusedTime() == base::TimeTicks::Max();
 }
 
-#define EXPECT_FOR_ALL_DISCARD_REASONS(lifecycle_unit, method, value)    \
-  do {                                                                   \
-    EXPECT_EQ(value, lifecycle_unit->method(DiscardReason::kExternal));  \
-    EXPECT_EQ(value, lifecycle_unit->method(DiscardReason::kProactive)); \
-    EXPECT_EQ(value, lifecycle_unit->method(DiscardReason::kUrgent));    \
-  } while (false)
-
 class TabLifecycleUnitSourceTest : public ChromeRenderViewHostTestHarness {
  protected:
   TabLifecycleUnitSourceTest()
@@ -259,10 +253,10 @@
                   &second_lifecycle_unit);
 
     // Detach the non-active tab. Verify that it can no longer be discarded.
-    EXPECT_FOR_ALL_DISCARD_REASONS(first_lifecycle_unit, CanDiscard, true);
+    ExpectCanDiscardTrueAllReasons(first_lifecycle_unit);
     std::unique_ptr<content::WebContents> owned_contents =
         tab_strip_model_->DetachWebContentsAt(0);
-    EXPECT_FOR_ALL_DISCARD_REASONS(first_lifecycle_unit, CanDiscard, false);
+    ExpectCanDiscardFalseTrivialAllReasons(first_lifecycle_unit);
 
     // Create a second tab strip.
     NoUnloadListenerTabStripModelDelegate other_tab_strip_model_delegate;
@@ -278,7 +272,7 @@
     // Insert the tab into the second tab strip without focusing it. Verify that
     // it can be discarded.
     other_tab_strip_model.AppendWebContents(std::move(owned_contents), false);
-    EXPECT_FOR_ALL_DISCARD_REASONS(first_lifecycle_unit, CanDiscard, true);
+    ExpectCanDiscardTrueAllReasons(first_lifecycle_unit);
 
     EXPECT_EQ(LifecycleState::ACTIVE, first_lifecycle_unit->GetState());
     EXPECT_CALL(tab_observer_, OnDiscardedStateChange(testing::_, true));
@@ -399,7 +393,7 @@
         tab_strip_model_->GetWebContentsAt(0);
 
     // It should be possible to discard the background tab.
-    EXPECT_FOR_ALL_DISCARD_REASONS(background_lifecycle_unit, CanDiscard, true);
+    ExpectCanDiscardTrueAllReasons(background_lifecycle_unit);
 
     // Discard the tab.
     EXPECT_EQ(LifecycleState::ACTIVE, background_lifecycle_unit->GetState());
@@ -428,14 +422,15 @@
 
     // It shouldn't be possible to discard the background tab again, except for
     // an urgent discard on ChromeOS.
-    EXPECT_FALSE(
-        background_lifecycle_unit->CanDiscard(DiscardReason::kExternal));
-    EXPECT_FALSE(
-        background_lifecycle_unit->CanDiscard(DiscardReason::kProactive));
+    ExpectCanDiscardFalseTrivial(background_lifecycle_unit,
+                                 DiscardReason::kExternal);
+    ExpectCanDiscardFalseTrivial(background_lifecycle_unit,
+                                 DiscardReason::kProactive);
 #if defined(OS_CHROMEOS)
-    EXPECT_TRUE(background_lifecycle_unit->CanDiscard(DiscardReason::kUrgent));
+    ExpectCanDiscardTrue(background_lifecycle_unit, DiscardReason::kUrgent);
 #else
-    EXPECT_FALSE(background_lifecycle_unit->CanDiscard(DiscardReason::kUrgent));
+    ExpectCanDiscardFalseTrivial(background_lifecycle_unit,
+                                 DiscardReason::kUrgent);
 #endif
   }
 
@@ -638,7 +633,7 @@
   task_runner_->FastForwardBy(kShortDelay);
 
   // It should be possible to discard the background tab.
-  EXPECT_FOR_ALL_DISCARD_REASONS(background_lifecycle_unit, CanDiscard, true);
+  ExpectCanDiscardTrueAllReasons(background_lifecycle_unit);
 
   // Discard the tab. Use DiscardReason::kUrgent to force the discard.
   EXPECT_EQ(LifecycleState::ACTIVE, background_lifecycle_unit->GetState());
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc b/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc
index 4b73fe63..858b3d8 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc
@@ -14,6 +14,7 @@
 #include "chrome/browser/resource_coordinator/tab_lifecycle_observer.h"
 #include "chrome/browser/resource_coordinator/tab_lifecycle_unit_external.h"
 #include "chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h"
+#include "chrome/browser/resource_coordinator/test_lifecycle_unit.h"
 #include "chrome/browser/resource_coordinator/time.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/tabs/test_tab_strip_model_delegate.h"
@@ -123,35 +124,27 @@
 TEST_F(TabLifecycleUnitTest, CanDiscardByDefault) {
   TabLifecycleUnit tab_lifecycle_unit(&observers_, web_contents_,
                                       tab_strip_model_.get());
-
-  EXPECT_TRUE(tab_lifecycle_unit.CanDiscard(DiscardReason::kExternal));
-  EXPECT_TRUE(tab_lifecycle_unit.CanDiscard(DiscardReason::kProactive));
-  EXPECT_TRUE(tab_lifecycle_unit.CanDiscard(DiscardReason::kUrgent));
+  ExpectCanDiscardTrueAllReasons(&tab_lifecycle_unit);
 }
 
 TEST_F(TabLifecycleUnitTest, SetFocused) {
   TabLifecycleUnit tab_lifecycle_unit(&observers_, web_contents_,
                                       tab_strip_model_.get());
   EXPECT_EQ(NowTicks(), tab_lifecycle_unit.GetLastFocusedTime());
-  EXPECT_TRUE(tab_lifecycle_unit.CanDiscard(DiscardReason::kExternal));
-  EXPECT_TRUE(tab_lifecycle_unit.CanDiscard(DiscardReason::kProactive));
-  EXPECT_TRUE(tab_lifecycle_unit.CanDiscard(DiscardReason::kUrgent));
+  ExpectCanDiscardTrueAllReasons(&tab_lifecycle_unit);
 
   tab_lifecycle_unit.SetFocused(true);
   tab_strip_model_->ActivateTabAt(0, false);
   web_contents_->WasShown();
   EXPECT_EQ(base::TimeTicks::Max(), tab_lifecycle_unit.GetLastFocusedTime());
-  EXPECT_FALSE(tab_lifecycle_unit.CanDiscard(DiscardReason::kExternal));
-  EXPECT_FALSE(tab_lifecycle_unit.CanDiscard(DiscardReason::kProactive));
-  EXPECT_FALSE(tab_lifecycle_unit.CanDiscard(DiscardReason::kUrgent));
+  ExpectCanDiscardFalseAllReasons(&tab_lifecycle_unit,
+                                  DecisionFailureReason::LIVE_STATE_VISIBLE);
 
   tab_lifecycle_unit.SetFocused(false);
   tab_strip_model_->ActivateTabAt(1, false);
   web_contents_->WasHidden();
   EXPECT_EQ(test_clock_.NowTicks(), tab_lifecycle_unit.GetLastFocusedTime());
-  EXPECT_TRUE(tab_lifecycle_unit.CanDiscard(DiscardReason::kExternal));
-  EXPECT_TRUE(tab_lifecycle_unit.CanDiscard(DiscardReason::kProactive));
-  EXPECT_TRUE(tab_lifecycle_unit.CanDiscard(DiscardReason::kUrgent));
+  ExpectCanDiscardTrueAllReasons(&tab_lifecycle_unit);
 }
 
 TEST_F(TabLifecycleUnitTest, AutoDiscardable) {
@@ -159,25 +152,21 @@
                                       tab_strip_model_.get());
 
   EXPECT_TRUE(tab_lifecycle_unit.IsAutoDiscardable());
-  EXPECT_TRUE(tab_lifecycle_unit.CanDiscard(DiscardReason::kExternal));
-  EXPECT_TRUE(tab_lifecycle_unit.CanDiscard(DiscardReason::kProactive));
-  EXPECT_TRUE(tab_lifecycle_unit.CanDiscard(DiscardReason::kUrgent));
+  ExpectCanDiscardTrueAllReasons(&tab_lifecycle_unit);
 
   EXPECT_CALL(observer_, OnAutoDiscardableStateChange(web_contents_, false));
   tab_lifecycle_unit.SetAutoDiscardable(false);
   testing::Mock::VerifyAndClear(&observer_);
   EXPECT_FALSE(tab_lifecycle_unit.IsAutoDiscardable());
-  EXPECT_FALSE(tab_lifecycle_unit.CanDiscard(DiscardReason::kExternal));
-  EXPECT_FALSE(tab_lifecycle_unit.CanDiscard(DiscardReason::kProactive));
-  EXPECT_FALSE(tab_lifecycle_unit.CanDiscard(DiscardReason::kUrgent));
+  ExpectCanDiscardFalseAllReasons(
+      &tab_lifecycle_unit,
+      DecisionFailureReason::LIVE_STATE_EXTENSION_DISALLOWED);
 
   EXPECT_CALL(observer_, OnAutoDiscardableStateChange(web_contents_, true));
   tab_lifecycle_unit.SetAutoDiscardable(true);
   testing::Mock::VerifyAndClear(&observer_);
   EXPECT_TRUE(tab_lifecycle_unit.IsAutoDiscardable());
-  EXPECT_TRUE(tab_lifecycle_unit.CanDiscard(DiscardReason::kExternal));
-  EXPECT_TRUE(tab_lifecycle_unit.CanDiscard(DiscardReason::kProactive));
-  EXPECT_TRUE(tab_lifecycle_unit.CanDiscard(DiscardReason::kUrgent));
+  ExpectCanDiscardTrueAllReasons(&tab_lifecycle_unit);
 }
 
 TEST_F(TabLifecycleUnitTest, CannotDiscardCrashed) {
@@ -185,10 +174,7 @@
                                       tab_strip_model_.get());
 
   web_contents_->SetIsCrashed(base::TERMINATION_STATUS_PROCESS_CRASHED, 0);
-
-  EXPECT_FALSE(tab_lifecycle_unit.CanDiscard(DiscardReason::kExternal));
-  EXPECT_FALSE(tab_lifecycle_unit.CanDiscard(DiscardReason::kProactive));
-  EXPECT_FALSE(tab_lifecycle_unit.CanDiscard(DiscardReason::kUrgent));
+  ExpectCanDiscardFalseTrivialAllReasons(&tab_lifecycle_unit);
 }
 
 #if !defined(OS_CHROMEOS)
@@ -197,9 +183,9 @@
                                       tab_strip_model_.get());
 
   tab_strip_model_->ActivateTabAt(0, false);
-  EXPECT_FALSE(tab_lifecycle_unit.CanDiscard(DiscardReason::kExternal));
-  EXPECT_FALSE(tab_lifecycle_unit.CanDiscard(DiscardReason::kProactive));
-  EXPECT_FALSE(tab_lifecycle_unit.CanDiscard(DiscardReason::kUrgent));
+
+  ExpectCanDiscardFalseAllReasons(&tab_lifecycle_unit,
+                                  DecisionFailureReason::LIVE_STATE_VISIBLE);
 }
 #endif  // !defined(OS_CHROMEOS)
 
@@ -209,10 +195,7 @@
 
   content::WebContentsTester::For(web_contents_)
       ->SetLastCommittedURL(GURL("invalid :)"));
-
-  EXPECT_FALSE(tab_lifecycle_unit.CanDiscard(DiscardReason::kExternal));
-  EXPECT_FALSE(tab_lifecycle_unit.CanDiscard(DiscardReason::kProactive));
-  EXPECT_FALSE(tab_lifecycle_unit.CanDiscard(DiscardReason::kUrgent));
+  ExpectCanDiscardFalseTrivialAllReasons(&tab_lifecycle_unit);
 }
 
 TEST_F(TabLifecycleUnitTest, CannotDiscardEmptyURL) {
@@ -220,9 +203,7 @@
                                       tab_strip_model_.get());
 
   content::WebContentsTester::For(web_contents_)->SetLastCommittedURL(GURL());
-  EXPECT_FALSE(tab_lifecycle_unit.CanDiscard(DiscardReason::kExternal));
-  EXPECT_FALSE(tab_lifecycle_unit.CanDiscard(DiscardReason::kProactive));
-  EXPECT_FALSE(tab_lifecycle_unit.CanDiscard(DiscardReason::kUrgent));
+  ExpectCanDiscardFalseTrivialAllReasons(&tab_lifecycle_unit);
 }
 
 TEST_F(TabLifecycleUnitTest, CannotDiscardVideoCapture) {
@@ -241,15 +222,12 @@
           web_contents_, video_devices);
   video_stream_ui->OnStarted(base::RepeatingClosure());
 
-  EXPECT_FALSE(tab_lifecycle_unit.CanDiscard(DiscardReason::kExternal));
-  EXPECT_FALSE(tab_lifecycle_unit.CanDiscard(DiscardReason::kProactive));
-  EXPECT_FALSE(tab_lifecycle_unit.CanDiscard(DiscardReason::kUrgent));
+  ExpectCanDiscardFalseAllReasons(&tab_lifecycle_unit,
+                                  DecisionFailureReason::LIVE_STATE_CAPTURING);
 
   video_stream_ui.reset();
 
-  EXPECT_TRUE(tab_lifecycle_unit.CanDiscard(DiscardReason::kExternal));
-  EXPECT_TRUE(tab_lifecycle_unit.CanDiscard(DiscardReason::kProactive));
-  EXPECT_TRUE(tab_lifecycle_unit.CanDiscard(DiscardReason::kUrgent));
+  ExpectCanDiscardTrueAllReasons(&tab_lifecycle_unit);
 }
 
 TEST_F(TabLifecycleUnitTest, CannotDiscardRecentlyAudible) {
@@ -258,37 +236,30 @@
 
   // Cannot discard when the "recently audible" bit is set.
   tab_lifecycle_unit.SetRecentlyAudible(true);
-  EXPECT_FALSE(tab_lifecycle_unit.CanDiscard(DiscardReason::kExternal));
-  EXPECT_FALSE(tab_lifecycle_unit.CanDiscard(DiscardReason::kProactive));
-  EXPECT_FALSE(tab_lifecycle_unit.CanDiscard(DiscardReason::kUrgent));
+  ExpectCanDiscardFalseAllReasons(
+      &tab_lifecycle_unit, DecisionFailureReason::LIVE_STATE_PLAYING_AUDIO);
 
   // The "recently audible" bit is still set. The tab cannot be discarded.
   test_clock_.Advance(kTabAudioProtectionTime);
-  EXPECT_FALSE(tab_lifecycle_unit.CanDiscard(DiscardReason::kExternal));
-  EXPECT_FALSE(tab_lifecycle_unit.CanDiscard(DiscardReason::kProactive));
-  EXPECT_FALSE(tab_lifecycle_unit.CanDiscard(DiscardReason::kUrgent));
+  ExpectCanDiscardFalseAllReasons(
+      &tab_lifecycle_unit, DecisionFailureReason::LIVE_STATE_PLAYING_AUDIO);
 
   // The "recently audible" bit was unset less than
   // kTabAudioProtectionTime ago. The tab cannot be discarded.
   tab_lifecycle_unit.SetRecentlyAudible(false);
   test_clock_.Advance(kShortDelay);
-  EXPECT_FALSE(tab_lifecycle_unit.CanDiscard(DiscardReason::kExternal));
-  EXPECT_FALSE(tab_lifecycle_unit.CanDiscard(DiscardReason::kProactive));
-  EXPECT_FALSE(tab_lifecycle_unit.CanDiscard(DiscardReason::kUrgent));
+  ExpectCanDiscardFalseAllReasons(
+      &tab_lifecycle_unit, DecisionFailureReason::LIVE_STATE_PLAYING_AUDIO);
 
   // The "recently audible" bit was unset kTabAudioProtectionTime ago. The tab
   // can be discarded.
   test_clock_.Advance(kTabAudioProtectionTime - kShortDelay);
-  EXPECT_TRUE(tab_lifecycle_unit.CanDiscard(DiscardReason::kExternal));
-  EXPECT_TRUE(tab_lifecycle_unit.CanDiscard(DiscardReason::kProactive));
-  EXPECT_TRUE(tab_lifecycle_unit.CanDiscard(DiscardReason::kUrgent));
+  ExpectCanDiscardTrueAllReasons(&tab_lifecycle_unit);
 
   // Calling SetRecentlyAudible(false) again does not change the fact that the
   // tab can be discarded.
   tab_lifecycle_unit.SetRecentlyAudible(false);
-  EXPECT_TRUE(tab_lifecycle_unit.CanDiscard(DiscardReason::kExternal));
-  EXPECT_TRUE(tab_lifecycle_unit.CanDiscard(DiscardReason::kProactive));
-  EXPECT_TRUE(tab_lifecycle_unit.CanDiscard(DiscardReason::kUrgent));
+  ExpectCanDiscardTrueAllReasons(&tab_lifecycle_unit);
 }
 
 TEST_F(TabLifecycleUnitTest, CanDiscardNeverAudibleTab) {
@@ -298,9 +269,7 @@
   tab_lifecycle_unit.SetRecentlyAudible(false);
   // Since the tab was never audible, it should be possible to discard it,
   // even if there was a recent call to SetRecentlyAudible(false).
-  EXPECT_TRUE(tab_lifecycle_unit.CanDiscard(DiscardReason::kExternal));
-  EXPECT_TRUE(tab_lifecycle_unit.CanDiscard(DiscardReason::kProactive));
-  EXPECT_TRUE(tab_lifecycle_unit.CanDiscard(DiscardReason::kUrgent));
+  ExpectCanDiscardTrueAllReasons(&tab_lifecycle_unit);
 }
 
 TEST_F(TabLifecycleUnitTest, CannotDiscardPDF) {
@@ -309,9 +278,8 @@
 
   content::WebContentsTester::For(web_contents_)
       ->SetMainFrameMimeType("application/pdf");
-  EXPECT_FALSE(tab_lifecycle_unit.CanDiscard(DiscardReason::kExternal));
-  EXPECT_FALSE(tab_lifecycle_unit.CanDiscard(DiscardReason::kProactive));
-  EXPECT_FALSE(tab_lifecycle_unit.CanDiscard(DiscardReason::kUrgent));
+  ExpectCanDiscardFalseAllReasons(&tab_lifecycle_unit,
+                                  DecisionFailureReason::LIVE_STATE_IS_PDF);
 }
 
 TEST_F(TabLifecycleUnitTest, NotifiedOfWebContentsVisibilityChanges) {
diff --git a/chrome/browser/resource_coordinator/tab_manager.cc b/chrome/browser/resource_coordinator/tab_manager.cc
index 52dc7f5..6ab4edc 100644
--- a/chrome/browser/resource_coordinator/tab_manager.cc
+++ b/chrome/browser/resource_coordinator/tab_manager.cc
@@ -627,7 +627,10 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   for (LifecycleUnit* lifecycle_unit : GetSortedLifecycleUnits()) {
-    if (lifecycle_unit->CanDiscard(reason) && lifecycle_unit->Discard(reason)) {
+    // TODO(chrisha): Report decision details!
+    DecisionDetails decision_details;
+    if (lifecycle_unit->CanDiscard(reason, &decision_details) &&
+        lifecycle_unit->Discard(reason)) {
       TabLifecycleUnitExternal* tab_lifecycle_unit_external =
           lifecycle_unit->AsTabLifecycleUnitExternal();
       // For now, all LifecycleUnits are TabLifecycleUnitExternals.
@@ -926,7 +929,9 @@
   for (LifecycleUnit* lifecycle_unit : lifecycle_units_) {
     // Freeze LifecycleUnits that have been in the background for more than
     // |kBackgroundTabFreezeTimeout|.
-    if (lifecycle_unit->CanFreeze()) {
+    // TODO(chrisha): Report decision details.
+    DecisionDetails freeze_details;
+    if (lifecycle_unit->CanFreeze(&freeze_details)) {
       const base::TimeDelta time_not_visible =
           now - lifecycle_unit->GetLastVisibleTime();
       const base::TimeDelta time_until_freeze =
@@ -942,7 +947,10 @@
 
     // Keep track of the discardable LifecycleUnit that has been non-visible for
     // the longest time. It might be discarded below.
-    if (lifecycle_unit->CanDiscard(DiscardReason::kProactive)) {
+    // TODO(chrisha): Report decision details.
+    DecisionDetails discard_details;
+    if (lifecycle_unit->CanDiscard(DiscardReason::kProactive,
+                                   &discard_details)) {
       if (!oldest_discardable_lifecycle_unit ||
           lifecycle_unit->GetLastVisibleTime() <
               oldest_discardable_lifecycle_unit->GetLastVisibleTime()) {
diff --git a/chrome/browser/resource_coordinator/tab_manager_delegate_chromeos.cc b/chrome/browser/resource_coordinator/tab_manager_delegate_chromeos.cc
index 1e2c508..4888710 100644
--- a/chrome/browser/resource_coordinator/tab_manager_delegate_chromeos.cc
+++ b/chrome/browser/resource_coordinator/tab_manager_delegate_chromeos.cc
@@ -143,8 +143,11 @@
   if (lifecycle_unit()) {
     if (lifecycle_unit_sort_key_.last_focused_time == base::TimeTicks::Max())
       return ProcessType::FOCUSED_TAB;
-    if (!lifecycle_unit()->CanDiscard(DiscardReason::kProactive))
+    DecisionDetails decision_details;
+    if (!lifecycle_unit()->CanDiscard(DiscardReason::kProactive,
+                                      &decision_details)) {
       return ProcessType::PROTECTED_BACKGROUND_TAB;
+    }
     return ProcessType::BACKGROUND_TAB;
   }
   NOTREACHED() << "Unexpected process type";
@@ -530,7 +533,10 @@
 
 bool TabManagerDelegate::KillTab(LifecycleUnit* lifecycle_unit,
                                  DiscardReason reason) {
-  return lifecycle_unit->CanDiscard(reason) && lifecycle_unit->Discard(reason);
+  // TODO(chrisha): Report decision details!
+  DecisionDetails decision_details;
+  return lifecycle_unit->CanDiscard(reason, &decision_details) &&
+         lifecycle_unit->Discard(reason);
 }
 
 chromeos::DebugDaemonClient* TabManagerDelegate::GetDebugDaemonClient() {
diff --git a/chrome/browser/resource_coordinator/test_lifecycle_unit.cc b/chrome/browser/resource_coordinator/test_lifecycle_unit.cc
index 00c4a96c..9d3318ac 100644
--- a/chrome/browser/resource_coordinator/test_lifecycle_unit.cc
+++ b/chrome/browser/resource_coordinator/test_lifecycle_unit.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/resource_coordinator/test_lifecycle_unit.h"
 
+#include "testing/gtest/include/gtest/gtest.h"
+
 namespace resource_coordinator {
 
 TestLifecycleUnit::TestLifecycleUnit(base::TimeTicks last_focused_time,
@@ -54,11 +56,12 @@
   return false;
 }
 
-bool TestLifecycleUnit::CanFreeze() const {
+bool TestLifecycleUnit::CanFreeze(DecisionDetails* decision_details) const {
   return false;
 }
 
-bool TestLifecycleUnit::CanDiscard(DiscardReason reason) const {
+bool TestLifecycleUnit::CanDiscard(DiscardReason reason,
+                                   DecisionDetails* decision_details) const {
   return can_discard_;
 }
 
@@ -66,4 +69,54 @@
   return false;
 }
 
+void ExpectCanDiscardTrue(const LifecycleUnit* lifecycle_unit,
+                          DiscardReason discard_reason) {
+  DecisionDetails decision_details;
+  EXPECT_TRUE(lifecycle_unit->CanDiscard(discard_reason, &decision_details));
+  EXPECT_TRUE(decision_details.IsPositive());
+  EXPECT_EQ(1u, decision_details.reasons().size());
+  EXPECT_EQ(DecisionSuccessReason::HEURISTIC_OBSERVED_TO_BE_SAFE,
+            decision_details.SuccessReason());
+}
+
+void ExpectCanDiscardTrueAllReasons(const LifecycleUnit* lifecycle_unit) {
+  ExpectCanDiscardTrue(lifecycle_unit, DiscardReason::kExternal);
+  ExpectCanDiscardTrue(lifecycle_unit, DiscardReason::kProactive);
+  ExpectCanDiscardTrue(lifecycle_unit, DiscardReason::kUrgent);
+}
+
+void ExpectCanDiscardFalse(const LifecycleUnit* lifecycle_unit,
+                           DecisionFailureReason failure_reason,
+                           DiscardReason discard_reason) {
+  DecisionDetails decision_details;
+  EXPECT_FALSE(lifecycle_unit->CanDiscard(discard_reason, &decision_details));
+  EXPECT_FALSE(decision_details.IsPositive());
+  EXPECT_EQ(1u, decision_details.reasons().size());
+  EXPECT_EQ(failure_reason, decision_details.FailureReason());
+}
+
+void ExpectCanDiscardFalseAllReasons(const LifecycleUnit* lifecycle_unit,
+                                     DecisionFailureReason failure_reason) {
+  ExpectCanDiscardFalse(lifecycle_unit, failure_reason,
+                        DiscardReason::kExternal);
+  ExpectCanDiscardFalse(lifecycle_unit, failure_reason,
+                        DiscardReason::kProactive);
+  ExpectCanDiscardFalse(lifecycle_unit, failure_reason, DiscardReason::kUrgent);
+}
+
+void ExpectCanDiscardFalseTrivial(const LifecycleUnit* lifecycle_unit,
+                                  DiscardReason discard_reason) {
+  DecisionDetails decision_details;
+  EXPECT_FALSE(lifecycle_unit->CanDiscard(discard_reason, &decision_details));
+  EXPECT_FALSE(decision_details.IsPositive());
+  EXPECT_TRUE(decision_details.reasons().empty());
+}
+
+void ExpectCanDiscardFalseTrivialAllReasons(
+    const LifecycleUnit* lifecycle_unit) {
+  ExpectCanDiscardFalseTrivial(lifecycle_unit, DiscardReason::kExternal);
+  ExpectCanDiscardFalseTrivial(lifecycle_unit, DiscardReason::kProactive);
+  ExpectCanDiscardFalseTrivial(lifecycle_unit, DiscardReason::kUrgent);
+}
+
 }  // namespace resource_coordinator
diff --git a/chrome/browser/resource_coordinator/test_lifecycle_unit.h b/chrome/browser/resource_coordinator/test_lifecycle_unit.h
index d17ec8f..10b7071f 100644
--- a/chrome/browser/resource_coordinator/test_lifecycle_unit.h
+++ b/chrome/browser/resource_coordinator/test_lifecycle_unit.h
@@ -34,8 +34,9 @@
   bool Freeze() override;
   int GetEstimatedMemoryFreedOnDiscardKB() const override;
   bool CanPurge() const override;
-  bool CanFreeze() const override;
-  bool CanDiscard(DiscardReason reason) const override;
+  bool CanFreeze(DecisionDetails* decision_details) const override;
+  bool CanDiscard(DiscardReason reason,
+                  DecisionDetails* decision_details) const override;
   bool Discard(DiscardReason discard_reason) override;
 
  private:
@@ -46,6 +47,19 @@
   DISALLOW_COPY_AND_ASSIGN(TestLifecycleUnit);
 };
 
+// Helper funtions for testing CanDiscard policy.
+void ExpectCanDiscardTrue(const LifecycleUnit* lifecycle_unit,
+                          DiscardReason discard_reason);
+void ExpectCanDiscardTrueAllReasons(const LifecycleUnit* lifecycle_unit);
+void ExpectCanDiscardFalse(const LifecycleUnit* lifecycle_unit,
+                           DecisionFailureReason failure_reason,
+                           DiscardReason discard_reason);
+void ExpectCanDiscardFalseAllReasons(const LifecycleUnit* lifecycle_unit,
+                                     DecisionFailureReason failure_reason);
+void ExpectCanDiscardFalseTrivial(const LifecycleUnit* lifecycle_unit,
+                                  DiscardReason discard_reason);
+void ExpectCanDiscardFalseTrivialAllReasons(const LifecycleUnit* lu);
+
 }  // namespace resource_coordinator
 
 #endif  // CHROME_BROWSER_RESOURCE_COORDINATOR_TEST_LIFECYCLE_UNIT_H_
diff --git a/chrome/browser/resources/md_extensions/OWNERS b/chrome/browser/resources/md_extensions/OWNERS
index 4efe53b..8483f0b4 100644
--- a/chrome/browser/resources/md_extensions/OWNERS
+++ b/chrome/browser/resources/md_extensions/OWNERS
@@ -1,5 +1,4 @@
 rdevlin.cronin@chromium.org
-scottchen@chromium.org
 
 # TEAM: extensions-dev@chromium.org
 # COMPONENT: Platform>Extensions
diff --git a/chrome/browser/resources/print_preview/new/button_css.html b/chrome/browser/resources/print_preview/new/button_css.html
index b7dff75..e4f5914b 100644
--- a/chrome/browser/resources/print_preview/new/button_css.html
+++ b/chrome/browser/resources/print_preview/new/button_css.html
@@ -10,12 +10,10 @@
         -webkit-padding-start: 10px;
       }
 
-    <if expr="not is_ios">
       button:enabled:hover {
         background-image: linear-gradient(#f0f0f0, #f0f0f0 38%, #e0e0e0);
         @apply --print-preview-hover;
       }
-    </if>
 
       button:enabled:active {
         background-image: linear-gradient(#e7e7e7, #e7e7e7 38%, #d7d7d7);
diff --git a/chrome/browser/resources/print_preview/new/checkbox_radio_css.html b/chrome/browser/resources/print_preview/new/checkbox_radio_css.html
index 159ad3c..4ab95716 100644
--- a/chrome/browser/resources/print_preview/new/checkbox_radio_css.html
+++ b/chrome/browser/resources/print_preview/new/checkbox_radio_css.html
@@ -43,14 +43,12 @@
         top: 3px;
       }
 
-    <if expr="not is_ios">
       input:enabled:hover:-webkit-any([type='checkbox'],
                                       [type='radio']) {
         background-image:
             linear-gradient(#f0f0f0, #f0f0f0 38%, #e0e0e0);
         @apply --print-preview-hover;
       }
-    </if>
 
       input:enabled:active:-webkit-any([type='checkbox'],
                                        [type='radio']) {
diff --git a/chrome/browser/resources/print_preview/new/print_preview_shared_css.html b/chrome/browser/resources/print_preview/new/print_preview_shared_css.html
index 63bcc781..13d1a3c 100644
--- a/chrome/browser/resources/print_preview/new/print_preview_shared_css.html
+++ b/chrome/browser/resources/print_preview/new/print_preview_shared_css.html
@@ -61,14 +61,13 @@
         min-height: 2em;
         outline: none;
         padding: 3px;
-      <if expr="is_win or is_macosx or is_ios">
+      <if expr="is_win or is_macosx">
         /* For better alignment between adjacent buttons and inputs. */
         padding-bottom: 4px;
       </if>
       }
 
       :root {
-      <if expr="not is_ios">
         /* Hover **************************************************************/
         --print-preview-hover: {
           border-color: rgba(0, 0, 0, 0.3);
@@ -76,7 +75,6 @@
               inset 0 1px 2px rgba(255, 255, 255, 0.95);
           color: black;
         };
-      </if>
 
         /* Active *************************************************************/
         --print-preview-active: {
diff --git a/chrome/browser/resources/print_preview/new/select_css.html b/chrome/browser/resources/print_preview/new/select_css.html
index 04838c08b..ddd678a 100644
--- a/chrome/browser/resources/print_preview/new/select_css.html
+++ b/chrome/browser/resources/print_preview/new/select_css.html
@@ -17,13 +17,11 @@
         width: 100%;
       }
 
-    <if expr="not is_ios">
       select:enabled:hover {
         background-image: url(chrome://resources/images/select.png),
             linear-gradient(#f0f0f0, #f0f0f0 38%, #e0e0e0);
         @apply --print-preview-hover;
       }
-    </if>
 
       select:enabled:active {
         background-image: url(chrome://resources/images/select.png),
diff --git a/chrome/browser/resources/print_preview/print_preview_resources.grd b/chrome/browser/resources/print_preview/print_preview_resources.grd
index b3f53d5..969dd80 100644
--- a/chrome/browser/resources/print_preview/print_preview_resources.grd
+++ b/chrome/browser/resources/print_preview/print_preview_resources.grd
@@ -356,16 +356,13 @@
                  preprocess="true" />
       <structure name="IDR_PRINT_PREVIEW_NEW_BUTTON_CSS_HTML"
                  file="new/button_css.html"
-                 type="chrome_html"
-                 preprocess="true"/>
+                 type="chrome_html" />
       <structure name="IDR_PRINT_PREVIEW_NEW_SELECT_CSS_HTML"
                  file="new/select_css.html"
-                 type="chrome_html"
-                 preprocess="true"/>
+                 type="chrome_html" />
       <structure name="IDR_PRINT_PREVIEW_NEW_CHECKBOX_RADIO_CSS_HTML"
                  file="new/checkbox_radio_css.html"
-                 type="chrome_html"
-                 preprocess="true"/>
+                 type="chrome_html" />
       <structure name="IDR_PRINT_PREVIEW_NEW_INPUT_CSS_HTML"
                  file="new/input_css.html"
                  type="chrome_html"/>
diff --git a/chrome/browser/resources/settings/OWNERS b/chrome/browser/resources/settings/OWNERS
index 387901f..61b5877 100644
--- a/chrome/browser/resources/settings/OWNERS
+++ b/chrome/browser/resources/settings/OWNERS
@@ -2,7 +2,6 @@
 dschuyler@chromium.org
 hcarmona@chromium.org
 michaelpg@chromium.org
-scottchen@chromium.org
 stevenjb@chromium.org
 tommycli@chromium.org
 
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_impl_win.cc b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_impl_win.cc
index 1fc6a4d..762093fb 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_impl_win.cc
+++ b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_impl_win.cc
@@ -38,6 +38,8 @@
 #include "chrome/installer/util/scoped_token_privilege.h"
 #include "components/chrome_cleaner/public/constants/constants.h"
 #include "components/component_updater/component_updater_service.h"
+#include "components/component_updater/pref_names.h"
+#include "components/prefs/pref_service.h"
 #include "components/safe_browsing/common/safe_browsing_prefs.h"
 #include "content/public/browser/browser_thread.h"
 #include "net/http/http_status_code.h"
@@ -533,6 +535,15 @@
   return safe_browsing::SwReporterReportingIsAllowedByPolicy();
 }
 
+bool ChromeCleanerControllerImpl::IsReportingManagedByPolicy() {
+  // Logs are considered managed if the logs themselves are managed or if the
+  // entire cleanup feature is disabled by policy.
+  PrefService* local_state = g_browser_process->local_state();
+  return !IsAllowedByPolicy() ||
+         (local_state &&
+          local_state->IsManagedPreference(prefs::kSwReporterReportingEnabled));
+}
+
 ChromeCleanerControllerImpl::ChromeCleanerControllerImpl()
     : real_delegate_(std::make_unique<ChromeCleanerControllerDelegate>()),
       delegate_(real_delegate_.get()),
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_impl_win.h b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_impl_win.h
index 1473f31..8d770b2 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_impl_win.h
+++ b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_impl_win.h
@@ -69,6 +69,7 @@
   void Reboot() override;
   bool IsAllowedByPolicy() override;
   bool IsReportingAllowedByPolicy() override;
+  bool IsReportingManagedByPolicy() override;
 
   static void ResetInstanceForTesting();
   // Passing in a nullptr as |delegate| resets the delegate to a default
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_win.h b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_win.h
index 60df854..10a110a 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_win.h
+++ b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_win.h
@@ -210,6 +210,9 @@
   // Returns true if cleaner reporting is allowed to run by enterprise policy.
   virtual bool IsReportingAllowedByPolicy() = 0;
 
+  // Returns true if cleaner reporting is managed by enterprise policy.
+  virtual bool IsReportingManagedByPolicy() = 0;
+
  protected:
   ChromeCleanerController();
   virtual ~ChromeCleanerController();
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_dialog_controller_impl_win.cc b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_dialog_controller_impl_win.cc
index e975ed47..e99ac1a 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_dialog_controller_impl_win.cc
+++ b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_dialog_controller_impl_win.cc
@@ -168,6 +168,10 @@
   return cleaner_controller_->logs_enabled();
 }
 
+bool ChromeCleanerDialogControllerImpl::LogsManaged() {
+  return cleaner_controller_->IsReportingManagedByPolicy();
+}
+
 void ChromeCleanerDialogControllerImpl::OnIdle(
     ChromeCleanerController::IdleReason idle_reason) {
   if (!dialog_shown_)
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_dialog_controller_impl_win.h b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_dialog_controller_impl_win.h
index 67b8a10e..5c1d9f6 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_dialog_controller_impl_win.h
+++ b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_dialog_controller_impl_win.h
@@ -47,6 +47,7 @@
   void DetailsButtonClicked(bool logs_enabled) override;
   void SetLogsEnabled(bool logs_enabled) override;
   bool LogsEnabled() override;
+  bool LogsManaged() override;
 
   // ChromeCleanerController::Observer overrides.
   void OnIdle(ChromeCleanerController::IdleReason idle_reason) override;
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_dialog_controller_win.h b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_dialog_controller_win.h
index f7b949d8..24f3179 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_dialog_controller_win.h
+++ b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_dialog_controller_win.h
@@ -52,6 +52,8 @@
   // Returns whether logs upload is currently enabled. Used to set the dialog's
   // default permission checkbox state.
   virtual bool LogsEnabled() = 0;
+  // Returns whether logs upload is currently managed by policy.
+  virtual bool LogsManaged() = 0;
 
  protected:
   virtual ~ChromeCleanerDialogController() {}
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/mock_chrome_cleaner_controller_win.h b/chrome/browser/safe_browsing/chrome_cleaner/mock_chrome_cleaner_controller_win.h
index 052c0a5..f1e8d41 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/mock_chrome_cleaner_controller_win.h
+++ b/chrome/browser/safe_browsing/chrome_cleaner/mock_chrome_cleaner_controller_win.h
@@ -36,6 +36,7 @@
   MOCK_METHOD0(Reboot, void());
   MOCK_METHOD0(IsAllowedByPolicy, bool());
   MOCK_METHOD0(IsReportingAllowedByPolicy, bool());
+  MOCK_METHOD0(IsReportingManagedByPolicy, bool());
 };
 
 }  // namespace safe_browsing
diff --git a/chrome/browser/ssl/ssl_error_controller_client.cc b/chrome/browser/ssl/ssl_error_controller_client.cc
index 8d584035..5368779 100644
--- a/chrome/browser/ssl/ssl_error_controller_client.cc
+++ b/chrome/browser/ssl/ssl_error_controller_client.cc
@@ -237,5 +237,5 @@
 bool SSLErrorControllerClient::HasSeenRecurrentError() {
   return HasSeenRecurrentErrorInternal(web_contents_, cert_error_) &&
          base::GetFieldTrialParamByFeatureAsBool(kRecurrentInterstitialFeature,
-                                                 "show_error_ui", false);
+                                                 "show_error_ui", true);
 }
diff --git a/chrome/browser/subresource_filter/subresource_filter_intercepting_browsertest.cc b/chrome/browser/subresource_filter/subresource_filter_intercepting_browsertest.cc
index fb8c70a..a4eaf806 100644
--- a/chrome/browser/subresource_filter/subresource_filter_intercepting_browsertest.cc
+++ b/chrome/browser/subresource_filter/subresource_filter_intercepting_browsertest.cc
@@ -23,7 +23,6 @@
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
-
 namespace subresource_filter {
 
 // This test harness intercepts URLRequests going to the SafeBrowsing V4 server.
@@ -59,6 +58,35 @@
     return threat_match;
   }
 
+  // Creates a redirect chain to the final redirect_url from the initial host
+  // where the SafeBrowsing result from the intial host is delayed. Returns
+  // the initial url.
+  GURL InitializeSafeBrowsingForOutOfOrderResponses(
+      const std::string& initial_host,
+      const GURL& redirect_url,
+      base::TimeDelta initial_delay) {
+    GURL url(embedded_test_server()->GetURL(
+        initial_host, "/server-redirect?" + redirect_url.spec()));
+
+    // Mark the prefixes as bad so that safe browsing will request full hashes
+    // from the v4 server.
+    database_helper()->LocallyMarkPrefixAsBad(
+        url, safe_browsing::GetUrlSubresourceFilterId());
+    database_helper()->LocallyMarkPrefixAsBad(
+        redirect_url, safe_browsing::GetUrlSubresourceFilterId());
+
+    // Map URLs to policies, enforce on the initial, and warn on the redirect.
+    std::map<GURL, safe_browsing::ThreatMatch> response_map{
+        {url, GetBetterAdsMatch(url, "enforce")},
+        {redirect_url, safe_browsing::ThreatMatch()}};
+    std::map<GURL, base::TimeDelta> delay_map{{url, initial_delay}};
+    // Delay the initial response , so it arrives after the final.
+    safe_browsing::StartRedirectingV4RequestsForTesting(
+        response_map, safe_browsing_test_server(), delay_map);
+    safe_browsing_test_server()->StartAcceptingConnections();
+    return url;
+  }
+
  private:
   // SubresourceFilterBrowserTest:
   std::unique_ptr<TestSafeBrowsingDatabaseHelper> CreateTestDatabase()
@@ -72,7 +100,6 @@
     ASSERT_TRUE(safe_browsing_test_server()->InitializeAndListen());
     SubresourceFilterBrowserTest::SetUp();
   }
-
   // This class needs some specific test server managing to intercept V4 hash
   // requests, so just use another server for that rather than try to use the
   // parent class' server.
@@ -127,8 +154,7 @@
 }
 
 // Verify that the navigation waits on all safebrowsing results to be retrieved,
-// and doesn't just return after the final (used) result.  Also verify that the
-// results reported are correct when messages arrive out-of-order.
+// and doesn't just return after the final (used) result.
 IN_PROC_BROWSER_TEST_F(SubresourceFilterInterceptingBrowserTest,
                        SafeBrowsingNotificationsWaitOnAllRedirects) {
   // TODO(ericrobinson): If servers are slow for this test, the test will pass
@@ -137,47 +163,52 @@
   //   it's not ideal.  Look into using a ControllableHttpResponse for each
   //   request, and completing the first after we know the second got to
   //   the activation throttle and check that it didn't call NotifyResults.
+  base::TimeDelta delay = base::TimeDelta::FromSeconds(2);
   ASSERT_NO_FATAL_FAILURE(
       SetRulesetToDisallowURLsWithPathSuffix("included_script.js"));
-  const std::string initial_host("a.com");
-  const std::string redirected_host("b.com");
-  base::TimeDelta delay = base::TimeDelta::FromSeconds(2);
-
   GURL redirect_url(embedded_test_server()->GetURL(
-      redirected_host, "/subresource_filter/frame_with_included_script.html"));
-  GURL url(embedded_test_server()->GetURL(
-      initial_host, "/server-redirect?" + redirect_url.spec()));
-
-  // Mark the prefixes as bad so that safe browsing will request full hashes
-  // from the v4 server.
-  database_helper()->LocallyMarkPrefixAsBad(
-      url, safe_browsing::GetUrlSubresourceFilterId());
-  database_helper()->LocallyMarkPrefixAsBad(
-      redirect_url, safe_browsing::GetUrlSubresourceFilterId());
-
-  // Map URLs to policies, enforce on the initial, and warn on the redirect.
-  std::map<GURL, safe_browsing::ThreatMatch> response_map{
-      {url, GetBetterAdsMatch(url, "enforce")},
-      {redirect_url, GetBetterAdsMatch(redirect_url, "warn")}};
-  // Delay the initial response , so it arrives after the final.
-  std::map<GURL, base::TimeDelta> delay_map{{url, delay}};
-  safe_browsing::StartRedirectingV4RequestsForTesting(
-      response_map, safe_browsing_test_server(), delay_map);
-  safe_browsing_test_server()->StartAcceptingConnections();
-
-  // The navigation should wait for all safebrowsing results, and not just
-  // return the last result, which is computed quickly.
-  content::ConsoleObserverDelegate warn_console_observer(
-      web_contents(), kActivationWarningConsoleMessage);
-  web_contents()->SetDelegate(&warn_console_observer);
+      "b.com", "/subresource_filter/frame_with_included_script.html"));
+  GURL url = InitializeSafeBrowsingForOutOfOrderResponses("a.com", redirect_url,
+                                                          delay);
   base::ElapsedTimer timer;
   ui_test_utils::NavigateToURL(browser(), url);
   EXPECT_GE(timer.Elapsed(), delay);
+}
 
-  // Make sure the action is the last one in the redirect chain, a warning.
-  warn_console_observer.Wait();
+// Verify that the correct safebrowsing result is reported when there is a
+// redirect chain. With kSafeBrowsingSubresourceFilterConsiderRedirects, the
+// result with the highest priority should be returned.
+IN_PROC_BROWSER_TEST_F(SubresourceFilterInterceptingBrowserTest,
+                       SafeBrowsingNotificationsCheckBest) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      kSafeBrowsingSubresourceFilterConsiderRedirects);
+  ASSERT_NO_FATAL_FAILURE(
+      SetRulesetToDisallowURLsWithPathSuffix("included_script.js"));
+  GURL redirect_url(embedded_test_server()->GetURL(
+      "b.com", "/subresource_filter/frame_with_included_script.html"));
+  GURL url = InitializeSafeBrowsingForOutOfOrderResponses(
+      "a.com", redirect_url, base::TimeDelta::FromSeconds(0));
+  ui_test_utils::NavigateToURL(browser(), url);
+  EXPECT_FALSE(WasParsedScriptElementLoaded(web_contents()->GetMainFrame()));
+}
+
+// Verify that the correct safebrowsing result is reported when there is a
+// redirect chain. Without kSafeBrowsingSubresourceFilterConsiderRedirects, the
+// last result should be used.
+IN_PROC_BROWSER_TEST_F(SubresourceFilterInterceptingBrowserTest,
+                       SafeBrowsingNotificationsCheckLastResult) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndDisableFeature(
+      kSafeBrowsingSubresourceFilterConsiderRedirects);
+  ASSERT_NO_FATAL_FAILURE(
+      SetRulesetToDisallowURLsWithPathSuffix("included_script.js"));
+  GURL redirect_url(embedded_test_server()->GetURL(
+      "b.com", "/subresource_filter/frame_with_included_script.html"));
+  GURL url = InitializeSafeBrowsingForOutOfOrderResponses(
+      "a.com", redirect_url, base::TimeDelta::FromSeconds(0));
+  ui_test_utils::NavigateToURL(browser(), url);
   EXPECT_TRUE(WasParsedScriptElementLoaded(web_contents()->GetMainFrame()));
-  EXPECT_EQ(warn_console_observer.message(), kActivationWarningConsoleMessage);
 }
 
 }  // namespace subresource_filter
diff --git a/chrome/browser/themes/theme_service.cc b/chrome/browser/themes/theme_service.cc
index 31365f29..df26535e 100644
--- a/chrome/browser/themes/theme_service.cc
+++ b/chrome/browser/themes/theme_service.cc
@@ -293,7 +293,7 @@
 
 void ThemeService::RevertToTheme(const Extension* extension) {
   DCHECK(extension->is_theme());
-  ExtensionService* service =
+  extensions::ExtensionService* service =
       extensions::ExtensionSystem::Get(profile_)->extension_service();
   DCHECK(!service->IsExtensionEnabled(extension->id()));
   // |extension| is disabled when reverting to the previous theme via an
@@ -358,7 +358,7 @@
   if (!ignore_infobars && number_of_infobars_ != 0)
     return;
 
-  ExtensionService* service =
+  extensions::ExtensionService* service =
       extensions::ExtensionSystem::Get(profile_)->extension_service();
   if (!service)
     return;
@@ -830,7 +830,7 @@
 }
 
 void ThemeService::MigrateTheme() {
-  ExtensionService* service =
+  extensions::ExtensionService* service =
       extensions::ExtensionSystem::Get(profile_)->extension_service();
   const Extension* extension =
       service ? service->GetExtensionById(GetThemeID(), false) : nullptr;
@@ -895,7 +895,7 @@
     return;
   }
 
-  ExtensionService* service =
+  extensions::ExtensionService* service =
       extensions::ExtensionSystem::Get(profile_)->extension_service();
   if (!service)
     return;
diff --git a/chrome/browser/themes/theme_syncable_service.cc b/chrome/browser/themes/theme_syncable_service.cc
index 5c3df58..a82dab9 100644
--- a/chrome/browser/themes/theme_syncable_service.cc
+++ b/chrome/browser/themes/theme_syncable_service.cc
@@ -210,7 +210,7 @@
     string id(theme_specifics.custom_theme_id());
     GURL update_url(theme_specifics.custom_theme_update_url());
     DVLOG(1) << "Applying theme " << id << " with update_url " << update_url;
-    ExtensionService* extensions_service =
+    extensions::ExtensionService* extensions_service =
         extensions::ExtensionSystem::Get(profile_)->extension_service();
     CHECK(extensions_service);
     const extensions::Extension* extension =
diff --git a/chrome/browser/themes/theme_syncable_service_unittest.cc b/chrome/browser/themes/theme_syncable_service_unittest.cc
index 5109193c..479a625 100644
--- a/chrome/browser/themes/theme_syncable_service_unittest.cc
+++ b/chrome/browser/themes/theme_syncable_service_unittest.cc
@@ -180,8 +180,9 @@
     extensions::TestExtensionSystem* test_ext_system =
         static_cast<extensions::TestExtensionSystem*>(
                 extensions::ExtensionSystem::Get(profile_.get()));
-    ExtensionService* service = test_ext_system->CreateExtensionService(
-        &command_line, base::FilePath(kExtensionFilePath), false);
+    extensions::ExtensionService* service =
+        test_ext_system->CreateExtensionService(
+            &command_line, base::FilePath(kExtensionFilePath), false);
     EXPECT_TRUE(service->extensions_enabled());
     service->Init();
     base::RunLoop().RunUntilIdle();
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 8f9b7b1..e43ea34 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1899,8 +1899,12 @@
       "views/extensions/request_file_system_dialog_view.h",
       "views/frame/browser_frame_ash.cc",
       "views/frame/browser_frame_ash.h",
+      "views/frame/browser_frame_mash.cc",
+      "views/frame/browser_frame_mash.h",
       "views/frame/browser_non_client_frame_view_ash.cc",
       "views/frame/browser_non_client_frame_view_ash.h",
+      "views/frame/browser_non_client_frame_view_mash.cc",
+      "views/frame/browser_non_client_frame_view_mash.h",
       "views/frame/immersive_context_mus.cc",
       "views/frame/immersive_context_mus.h",
       "views/frame/immersive_handler_factory_mus.cc",
diff --git a/chrome/browser/ui/ash/assistant/assistant_card_renderer.cc b/chrome/browser/ui/ash/assistant/assistant_card_renderer.cc
index f0eef52..efb89c8 100644
--- a/chrome/browser/ui/ash/assistant/assistant_card_renderer.cc
+++ b/chrome/browser/ui/ash/assistant/assistant_card_renderer.cc
@@ -100,8 +100,9 @@
     web_contents_->GetController().LoadURLWithParams(load_params);
 
     // Enable auto-resizing, respecting the specified size parameters.
+    // Dimensions should be non-zero to pass DCHECK.
     web_contents_->GetRenderWidgetHostView()->EnableAutoResize(
-        gfx::Size(params->min_width_dip, 0),
+        gfx::Size(params->min_width_dip, 1),
         gfx::Size(params->max_width_dip, INT_MAX));
   }
 
diff --git a/chrome/browser/ui/browser.h b/chrome/browser/ui/browser.h
index 265aa89c..57bbbbb2 100644
--- a/chrome/browser/ui/browser.h
+++ b/chrome/browser/ui/browser.h
@@ -84,10 +84,10 @@
 }
 
 namespace extensions {
+class BrowserExtensionWindowController;
 class HostedAppBrowserController;
 class Extension;
 class ExtensionRegistry;
-class WindowController;
 }
 
 namespace gfx {
@@ -515,7 +515,8 @@
     return exclusive_access_manager_.get();
   }
 
-  extensions::WindowController* extension_window_controller() const {
+  extensions::BrowserExtensionWindowController* extension_window_controller()
+      const {
     return extension_window_controller_.get();
   }
 
@@ -992,7 +993,8 @@
 
   std::unique_ptr<ExclusiveAccessManager> exclusive_access_manager_;
 
-  std::unique_ptr<extensions::WindowController> extension_window_controller_;
+  std::unique_ptr<extensions::BrowserExtensionWindowController>
+      extension_window_controller_;
 
   std::unique_ptr<chrome::BrowserCommandController> command_controller_;
 
diff --git a/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.mm b/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.mm
index e8d53095..2e8624d 100644
--- a/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.mm
+++ b/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.mm
@@ -5,6 +5,7 @@
 #import "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h"
 
 #include "base/bind.h"
+#include "base/bind_helpers.h"
 #import "base/mac/mac_util.h"
 #include "base/stl_util.h"
 #include "base/strings/string_util.h"
@@ -84,11 +85,6 @@
 // SkColorSetARGB(0xCC, 0xFF, 0xFF 0xFF);
 const SkColor kMaterialDarkVectorIconColor = SK_ColorWHITE;
 
-void NotReached(const gfx::Image& image) {
-  // Mac Cocoa version should not receive asynchronously delivered favicons.
-  NOTREACHED();
-}
-
 }  // namespace
 
 // TODO(shess): This code is mostly copied from the gtk
@@ -464,8 +460,10 @@
     image_skia = gfx::CreateVectorIcon(toolbar::kHttpsValidIcon,
                                        kDefaultIconSize, vector_icon_color);
   } else {
+    // The view may return an icon asynchronously when certain flags are on,
+    // but the Cocoa implementation should just ignore them.
     image_skia = omnibox_view_->GetIcon(kDefaultIconSize, vector_icon_color,
-                                        base::BindOnce(&NotReached));
+                                        base::DoNothing());
   }
 
   NSImage* image = NSImageFromImageSkiaWithColorSpace(
diff --git a/chrome/browser/ui/omnibox/omnibox_theme.cc b/chrome/browser/ui/omnibox/omnibox_theme.cc
index cb99057b6..8795c5df 100644
--- a/chrome/browser/ui/omnibox/omnibox_theme.cc
+++ b/chrome/browser/ui/omnibox/omnibox_theme.cc
@@ -185,6 +185,16 @@
 }
 
 SkColor GetSecurityChipColor(OmniboxTint tint, OmniboxPartState state) {
+  if (ui::MaterialDesignController::IsNewerMaterialUi()) {
+    if (tint == OmniboxTint::DARK)
+      return gfx::kGoogleGrey200;
+
+    if (state == OmniboxPartState::CHIP_DANGEROUS)
+      return gfx::kGoogleRed600;
+
+    return gfx::kChromeIconGrey;
+  }
+
   if (tint == OmniboxTint::DARK)
     return gfx::kGoogleGrey100;
 
diff --git a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc
index 2d7fa5d..3b4049f 100644
--- a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc
+++ b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc
@@ -51,6 +51,11 @@
 const SkColor kFooterBackgroundColor = gfx::kGoogleGrey050;
 const SkColor kSeparatorColor = gfx::kGoogleGrey200;
 
+int GetCornerRadius() {
+  return ChromeLayoutProvider::Get()->GetCornerRadiusMetric(
+      views::EMPHASIS_LOW);
+}
+
 }  // namespace
 
 namespace autofill {
@@ -99,14 +104,18 @@
   }
 
  protected:
-  AutofillPopupItemView(AutofillPopupController* controller, int line_number)
-      : AutofillPopupRowView(controller, line_number) {}
+  AutofillPopupItemView(AutofillPopupController* controller,
+                        int line_number,
+                        int extra_bottom_padding = 0)
+      : AutofillPopupRowView(controller, line_number),
+        extra_bottom_padding_(extra_bottom_padding) {}
 
   // AutofillPopupRowView:
   void CreateContent() override {
     auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
         views::BoxLayout::kHorizontal,
-        gfx::Insets(0, views::MenuConfig::instance().item_left_margin)));
+        gfx::Insets(extra_bottom_padding_,
+                    views::MenuConfig::instance().item_left_margin)));
 
     layout->set_cross_axis_alignment(
         views::BoxLayout::CrossAxisAlignment::CROSS_AXIS_ALIGNMENT_CENTER);
@@ -179,6 +188,7 @@
     layout->SetFlexForView(spacer, /*flex=*/resize ? 1 : 0);
   }
 
+  const int extra_bottom_padding_;
   views::Label* text_label_ = nullptr;
   views::Label* subtext_label_ = nullptr;
 
@@ -220,9 +230,10 @@
   ~AutofillPopupFooterView() override = default;
 
   static AutofillPopupFooterView* Create(AutofillPopupController* controller,
-                                         int line_number) {
-    AutofillPopupFooterView* result =
-        new AutofillPopupFooterView(controller, line_number);
+                                         int line_number,
+                                         int extra_bottom_padding) {
+    AutofillPopupFooterView* result = new AutofillPopupFooterView(
+        controller, line_number, extra_bottom_padding);
     result->Init();
     return result;
   }
@@ -245,8 +256,10 @@
   }
 
  private:
-  AutofillPopupFooterView(AutofillPopupController* controller, int line_number)
-      : AutofillPopupItemView(controller, line_number) {
+  AutofillPopupFooterView(AutofillPopupController* controller,
+                          int line_number,
+                          int extra_bottom_padding)
+      : AutofillPopupItemView(controller, line_number, extra_bottom_padding) {
     SetFocusBehavior(FocusBehavior::ALWAYS);
   }
 
@@ -493,20 +506,16 @@
     footer_container->SetBackground(
         views::CreateSolidBackground(kFooterBackgroundColor));
 
-    views::BoxLayout* footer_layout =
-        footer_container->SetLayoutManager(std::make_unique<views::BoxLayout>(
-            views::BoxLayout::kVertical,
-            gfx::Insets(/*top=*/0,
-                        /*left=*/0,
-                        /*bottom=*/
-                        views::MenuConfig::instance().menu_vertical_border_size,
-                        /*right=*/0)));
+    views::BoxLayout* footer_layout = footer_container->SetLayoutManager(
+        std::make_unique<views::BoxLayout>(views::BoxLayout::kVertical));
     footer_layout->set_main_axis_alignment(
         views::BoxLayout::MAIN_AXIS_ALIGNMENT_START);
 
     while (line_number < controller_->GetLineCount()) {
-      rows_.push_back(
-          AutofillPopupFooterView::Create(controller_, line_number));
+      rows_.push_back(AutofillPopupFooterView::Create(
+          controller_, line_number,
+          line_number == controller_->GetLineCount() - 1 ? GetCornerRadius()
+                                                         : 0));
       footer_container->AddChildView(rows_.back());
       line_number++;
     }
@@ -536,8 +545,7 @@
   auto border = std::make_unique<views::BubbleBorder>(
       views::BubbleBorder::NONE, views::BubbleBorder::SMALL_SHADOW,
       SK_ColorWHITE);
-  border->SetCornerRadius(
-      ChromeLayoutProvider::Get()->GetCornerRadiusMetric(views::EMPHASIS_LOW));
+  border->SetCornerRadius(GetCornerRadius());
   border->set_md_shadow_elevation(
       ChromeLayoutProvider::Get()->GetShadowElevationMetric(
           views::EMPHASIS_LOW));
diff --git a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views_unittest.cc b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views_unittest.cc
index 3dcadab..b9d1f0dc 100644
--- a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views_unittest.cc
+++ b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views_unittest.cc
@@ -11,6 +11,7 @@
 #include "chrome/browser/ui/autofill/autofill_popup_controller.h"
 #include "chrome/browser/ui/autofill/autofill_popup_layout_model.h"
 #include "chrome/browser/ui/views/autofill/autofill_popup_view_native_views.h"
+#include "chrome/browser/ui/views/harmony/chrome_layout_provider.h"
 #include "components/autofill/core/browser/popup_item_ids.h"
 #include "components/autofill/core/browser/suggestion.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -119,6 +120,11 @@
   void SetUp() override {
     views::ViewsTestBase::SetUp();
 
+    // The layout provider is meant to be a singleton, but it is not initialized
+    // for unit tests. Constructing one here makes it globally available, which
+    // is later used by the view during initialization.
+    layout_provider_ = std::make_unique<ChromeLayoutProvider>();
+
     CreateWidget();
     generator_.reset(new ui::test::EventGenerator(widget_.GetNativeWindow()));
   }
@@ -157,6 +163,7 @@
   std::unique_ptr<ui::test::EventGenerator> generator_;
 
  private:
+  std::unique_ptr<ChromeLayoutProvider> layout_provider_;
   DISALLOW_COPY_AND_ASSIGN(AutofillPopupViewNativeViewsTest);
 };
 
diff --git a/chrome/browser/ui/views/chrome_cleaner_dialog_browsertest_win.cc b/chrome/browser/ui/views/chrome_cleaner_dialog_browsertest_win.cc
index 227be34..6ac59be 100644
--- a/chrome/browser/ui/views/chrome_cleaner_dialog_browsertest_win.cc
+++ b/chrome/browser/ui/views/chrome_cleaner_dialog_browsertest_win.cc
@@ -33,6 +33,7 @@
   MOCK_METHOD1(DetailsButtonClicked, void(bool));
   MOCK_METHOD1(SetLogsEnabled, void(bool));
   MOCK_METHOD0(LogsEnabled, bool());
+  MOCK_METHOD0(LogsManaged, bool());
 };
 
 class ChromeCleanerDialogTest : public DialogBrowserTest {
diff --git a/chrome/browser/ui/views/chrome_cleaner_dialog_win.cc b/chrome/browser/ui/views/chrome_cleaner_dialog_win.cc
index a5c4e46a..b4bb77c 100644
--- a/chrome/browser/ui/views/chrome_cleaner_dialog_win.cc
+++ b/chrome/browser/ui/views/chrome_cleaner_dialog_win.cc
@@ -77,6 +77,8 @@
       l10n_util::GetStringUTF16(IDS_CHROME_CLEANUP_LOGS_PERMISSION));
   logs_permission_checkbox_->SetChecked(dialog_controller_->LogsEnabled());
   logs_permission_checkbox_->set_listener(this);
+  if (dialog_controller_->LogsManaged())
+    logs_permission_checkbox_->SetState(views::Checkbox::STATE_DISABLED);
 
   AddChildView(label);
   AddChildView(logs_permission_checkbox_);
diff --git a/chrome/browser/ui/views/frame/browser_frame_mus.cc b/chrome/browser/ui/views/frame/browser_frame_mash.cc
similarity index 84%
rename from chrome/browser/ui/views/frame/browser_frame_mus.cc
rename to chrome/browser/ui/views/frame/browser_frame_mash.cc
index 9ae16f3a..439ab1f 100644
--- a/chrome/browser/ui/views/frame/browser_frame_mus.cc
+++ b/chrome/browser/ui/views/frame/browser_frame_mash.cc
@@ -2,25 +2,12 @@
 // 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/frame/browser_frame_mus.h"
+#include "chrome/browser/ui/views/frame/browser_frame_mash.h"
 
 #include <stdint.h>
 
 #include <memory>
 
-#include "chrome/browser/ui/browser_window_state.h"
-#include "chrome/browser/ui/views/frame/browser_frame.h"
-#include "chrome/browser/ui/views/frame/browser_view.h"
-#include "chrome/common/extensions/extension_constants.h"
-#include "services/ui/public/cpp/property_type_converters.h"
-#include "services/ui/public/interfaces/window_tree.mojom.h"
-#include "ui/aura/client/aura_constants.h"
-#include "ui/aura/mus/window_tree_host_mus_init_params.h"
-#include "ui/views/mus/desktop_window_tree_host_mus.h"
-#include "ui/views/mus/mus_client.h"
-#include "ui/views/mus/window_manager_frame_values.h"
-
-#if defined(OS_CHROMEOS)
 #include "ash/public/cpp/config.h"
 #include "ash/public/cpp/shelf_types.h"
 #include "ash/public/cpp/window_properties.h"
@@ -28,26 +15,33 @@
 #include "ash/public/interfaces/window_properties.mojom.h"
 #include "ash/public/interfaces/window_style.mojom.h"
 #include "chrome/browser/chromeos/ash_config.h"
+#include "chrome/browser/ui/browser_window_state.h"
+#include "chrome/browser/ui/views/frame/browser_frame.h"
 #include "chrome/browser/ui/views/frame/browser_frame_ash.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/common/extensions/extension_constants.h"
+#include "services/ui/public/cpp/property_type_converters.h"
 #include "services/ui/public/interfaces/window_manager.mojom.h"
-#endif
+#include "services/ui/public/interfaces/window_tree.mojom.h"
+#include "ui/aura/client/aura_constants.h"
+#include "ui/aura/mus/window_tree_host_mus_init_params.h"
+#include "ui/views/mus/desktop_window_tree_host_mus.h"
+#include "ui/views/mus/mus_client.h"
+#include "ui/views/mus/window_manager_frame_values.h"
 
-BrowserFrameMus::BrowserFrameMus(BrowserFrame* browser_frame,
-                                 BrowserView* browser_view)
+BrowserFrameMash::BrowserFrameMash(BrowserFrame* browser_frame,
+                                   BrowserView* browser_view)
     : views::DesktopNativeWidgetAura(browser_frame),
       browser_frame_(browser_frame),
       browser_view_(browser_view) {
   DCHECK(browser_frame_);
   DCHECK(browser_view_);
-#if defined(OS_CHROMEOS)
-  // Not used with Mus on Chrome OS.
   DCHECK_EQ(chromeos::GetAshConfig(), ash::Config::MASH);
-#endif
 }
 
-BrowserFrameMus::~BrowserFrameMus() {}
+BrowserFrameMash::~BrowserFrameMash() {}
 
-views::Widget::InitParams BrowserFrameMus::GetWidgetParams() {
+views::Widget::InitParams BrowserFrameMash::GetWidgetParams() {
   views::Widget::InitParams params;
   params.name = "BrowserFrame";
   params.native_widget = this;
@@ -59,7 +53,7 @@
   // Indicates mash shouldn't handle immersive, rather we will.
   properties[ui::mojom::WindowManager::kDisableImmersive_InitProperty] =
       mojo::ConvertTo<std::vector<uint8_t>>(true);
-#if defined(OS_CHROMEOS)
+
   Browser* browser = browser_view_->browser();
   properties[ash::mojom::kAshWindowStyle_InitProperty] =
       mojo::ConvertTo<std::vector<uint8_t>>(
@@ -91,41 +85,38 @@
         mojo::ConvertTo<std::vector<uint8_t>>(
             static_cast<int64_t>(BrowserFrameAsh::kMdWebUiFrameColor));
   }
-#endif
+
   aura::WindowTreeHostMusInitParams window_tree_host_init_params =
       aura::CreateInitParamsForTopLevel(
           views::MusClient::Get()->window_tree_client(), std::move(properties));
   std::unique_ptr<views::DesktopWindowTreeHostMus> desktop_window_tree_host =
       std::make_unique<views::DesktopWindowTreeHostMus>(
           std::move(window_tree_host_init_params), browser_frame_, this);
-  // BrowserNonClientFrameViewMus::OnBoundsChanged() takes care of updating
+  // BrowserNonClientFrameViewMash::OnBoundsChanged() takes care of updating
   // the insets.
   desktop_window_tree_host->set_auto_update_client_area(false);
   SetDesktopWindowTreeHost(std::move(desktop_window_tree_host));
   return params;
 }
 
-bool BrowserFrameMus::UseCustomFrame() const {
+bool BrowserFrameMash::UseCustomFrame() const {
   return true;
 }
 
-bool BrowserFrameMus::UsesNativeSystemMenu() const {
+bool BrowserFrameMash::UsesNativeSystemMenu() const {
   return false;
 }
 
-bool BrowserFrameMus::ShouldSaveWindowPlacement() const {
-#if defined(OS_CHROMEOS)
+bool BrowserFrameMash::ShouldSaveWindowPlacement() const {
   return nullptr == GetWidget()->GetNativeWindow()->GetProperty(
                         ash::kRestoreBoundsOverrideKey);
-#else
-  return true;
-#endif
 }
 
-void BrowserFrameMus::GetWindowPlacement(
-    gfx::Rect* bounds, ui::WindowShowState* show_state) const {
+void BrowserFrameMash::GetWindowPlacement(
+    gfx::Rect* bounds,
+    ui::WindowShowState* show_state) const {
   DesktopNativeWidgetAura::GetWindowPlacement(bounds, show_state);
-#if defined(OS_CHROMEOS)
+
   gfx::Rect* override_bounds = GetWidget()->GetNativeWindow()->GetProperty(
       ash::kRestoreBoundsOverrideKey);
   if (override_bounds && !override_bounds->IsEmpty()) {
@@ -134,7 +125,6 @@
         ash::ToWindowShowState(GetWidget()->GetNativeWindow()->GetProperty(
             ash::kRestoreWindowStateTypeOverrideKey));
   }
-#endif
 
   // Session restore might be unable to correctly restore other states.
   // For the record, https://crbug.com/396272
@@ -144,16 +134,16 @@
   }
 }
 
-bool BrowserFrameMus::PreHandleKeyboardEvent(
+bool BrowserFrameMash::PreHandleKeyboardEvent(
     const content::NativeWebKeyboardEvent& event) {
   return false;
 }
 
-bool BrowserFrameMus::HandleKeyboardEvent(
+bool BrowserFrameMash::HandleKeyboardEvent(
     const content::NativeWebKeyboardEvent& event) {
   return false;
 }
 
-int BrowserFrameMus::GetMinimizeButtonOffset() const {
+int BrowserFrameMash::GetMinimizeButtonOffset() const {
   return 0;
 }
diff --git a/chrome/browser/ui/views/frame/browser_frame_mus.h b/chrome/browser/ui/views/frame/browser_frame_mash.h
similarity index 65%
rename from chrome/browser/ui/views/frame/browser_frame_mus.h
rename to chrome/browser/ui/views/frame/browser_frame_mash.h
index e8bbe53..eeaea897 100644
--- a/chrome/browser/ui/views/frame/browser_frame_mus.h
+++ b/chrome/browser/ui/views/frame/browser_frame_mash.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_VIEWS_FRAME_BROWSER_FRAME_MUS_H_
-#define CHROME_BROWSER_UI_VIEWS_FRAME_BROWSER_FRAME_MUS_H_
+#ifndef CHROME_BROWSER_UI_VIEWS_FRAME_BROWSER_FRAME_MASH_H_
+#define CHROME_BROWSER_UI_VIEWS_FRAME_BROWSER_FRAME_MASH_H_
 
 #include "base/macros.h"
 #include "chrome/browser/ui/views/frame/native_browser_frame.h"
@@ -13,13 +13,11 @@
 class BrowserView;
 
 // Used with mash on Chrome OS.
-// TODO(jamescook): Rename to BrowserFrameMash. Linux Ozone used to use this
-// frame but doesn't any more.
-class BrowserFrameMus : public views::DesktopNativeWidgetAura,
-                        public NativeBrowserFrame {
+class BrowserFrameMash : public views::DesktopNativeWidgetAura,
+                         public NativeBrowserFrame {
  public:
-  BrowserFrameMus(BrowserFrame* browser_frame, BrowserView* browser_view);
-  ~BrowserFrameMus() override;
+  BrowserFrameMash(BrowserFrame* browser_frame, BrowserView* browser_view);
+  ~BrowserFrameMash() override;
 
  private:
   // Overridden from NativeBrowserFrame:
@@ -38,7 +36,7 @@
   BrowserFrame* browser_frame_;
   BrowserView* browser_view_;
 
-  DISALLOW_COPY_AND_ASSIGN(BrowserFrameMus);
+  DISALLOW_COPY_AND_ASSIGN(BrowserFrameMash);
 };
 
-#endif  // CHROME_BROWSER_UI_VIEWS_FRAME_BROWSER_FRAME_MUS_H_
+#endif  // CHROME_BROWSER_UI_VIEWS_FRAME_BROWSER_FRAME_MASH_H_
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_factory_chromeos.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_factory_chromeos.cc
index cbbb0cbf..9e0a2ac 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_factory_chromeos.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_factory_chromeos.cc
@@ -6,7 +6,7 @@
 #include "build/build_config.h"
 #include "chrome/browser/chromeos/ash_config.h"
 #include "chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h"
-#include "chrome/browser/ui/views/frame/browser_non_client_frame_view_mus.h"
+#include "chrome/browser/ui/views/frame/browser_non_client_frame_view_mash.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 
 namespace chrome {
@@ -15,8 +15,8 @@
     BrowserFrame* frame,
     BrowserView* browser_view) {
   if (chromeos::GetAshConfig() == ash::Config::MASH) {
-    BrowserNonClientFrameViewMus* frame_view =
-        new BrowserNonClientFrameViewMus(frame, browser_view);
+    BrowserNonClientFrameViewMash* frame_view =
+        new BrowserNonClientFrameViewMash(frame, browser_view);
     frame_view->Init();
     return frame_view;
   }
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_factory_views.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_factory_views.cc
index ee5b465..3d7e8beb 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_factory_views.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_factory_views.cc
@@ -9,11 +9,6 @@
 #include "chrome/browser/ui/views/frame/opaque_browser_frame_view.h"
 #include "chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.h"
 
-#if defined(USE_AURA)
-#include "chrome/browser/ui/views/frame/browser_non_client_frame_view_mus.h"
-#include "ui/aura/env.h"
-#endif
-
 #if defined(OS_WIN)
 #include "chrome/browser/ui/views/frame/glass_browser_frame_view.h"
 #endif
@@ -33,14 +28,6 @@
 BrowserNonClientFrameView* CreateBrowserNonClientFrameView(
     BrowserFrame* frame,
     BrowserView* browser_view) {
-#if defined(USE_AURA)
-  if (aura::Env::GetInstance()->mode() == aura::Env::Mode::MUS) {
-    BrowserNonClientFrameViewMus* frame_view =
-        new BrowserNonClientFrameViewMus(frame, browser_view);
-    frame_view->Init();
-    return frame_view;
-  }
-#endif
 #if defined(OS_WIN)
   if (frame->ShouldUseNativeFrame())
     return new GlassBrowserFrameView(frame, browser_view);
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mus.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mash.cc
similarity index 71%
rename from chrome/browser/ui/views/frame/browser_non_client_frame_view_mus.cc
rename to chrome/browser/ui/views/frame/browser_non_client_frame_view_mash.cc
index e2de133..7abed39 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mus.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mash.cc
@@ -2,20 +2,20 @@
 // 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/frame/browser_non_client_frame_view_mus.h"
+#include "chrome/browser/ui/views/frame/browser_non_client_frame_view_mash.h"
 
 #include <algorithm>
 
-#include "chrome/browser/profiles/profiles_state.h"
+#include "ash/public/cpp/ash_layout_constants.h"
+#include "ash/public/cpp/window_properties.h"
 #include "chrome/browser/themes/theme_properties.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/layout_constants.h"
 #include "chrome/browser/ui/views/frame/browser_frame.h"
-#include "chrome/browser/ui/views/frame/browser_frame_mus.h"
+#include "chrome/browser/ui/views/frame/browser_frame_mash.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/frame/immersive_mode_controller.h"
 #include "chrome/browser/ui/views/frame/top_container_view.h"
-#include "chrome/browser/ui/views/profiles/profile_indicator_icon.h"
 #include "chrome/browser/ui/views/tab_icon_view.h"
 #include "chrome/browser/ui/views/tabs/tab_strip.h"
 #include "chrome/browser/web_applications/web_app.h"
@@ -36,36 +36,8 @@
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_delegate.h"
 
-#if defined(OS_CHROMEOS)
-#include "ash/public/cpp/ash_layout_constants.h"
-#include "ash/public/cpp/window_properties.h"
-#endif
-
-#if !defined(OS_CHROMEOS)
-#define FRAME_AVATAR_BUTTON
-#endif
-
 namespace {
 
-#if defined(FRAME_AVATAR_BUTTON)
-// Space between the new avatar button and the minimize button.
-constexpr int kAvatarButtonOffset = 5;
-#endif
-
-#if defined(FRAME_AVATAR_BUTTON)
-// Combines View::ConvertPointToTarget() and View::HitTest() for a given
-// |point|. Converts |point| from |src| to |dst| and hit tests it against |dst|.
-bool ConvertedHitTest(views::View* src,
-                      views::View* dst,
-                      const gfx::Point& point) {
-  DCHECK(src);
-  DCHECK(dst);
-  gfx::Point converted_point(point);
-  views::View::ConvertPointToTarget(src, dst, &converted_point);
-  return dst->HitTestPoint(converted_point);
-}
-#endif
-
 const views::WindowManagerFrameValues& frame_values() {
   return views::WindowManagerFrameValues::instance();
 }
@@ -73,23 +45,22 @@
 }  // namespace
 
 ///////////////////////////////////////////////////////////////////////////////
-// BrowserNonClientFrameViewMus, public:
+// BrowserNonClientFrameViewMash, public:
 
 // static
-const char BrowserNonClientFrameViewMus::kViewClassName[] =
-    "BrowserNonClientFrameViewMus";
+const char BrowserNonClientFrameViewMash::kViewClassName[] =
+    "BrowserNonClientFrameViewMash";
 
-BrowserNonClientFrameViewMus::BrowserNonClientFrameViewMus(
+BrowserNonClientFrameViewMash::BrowserNonClientFrameViewMash(
     BrowserFrame* frame,
     BrowserView* browser_view)
     : BrowserNonClientFrameView(frame, browser_view),
       window_icon_(nullptr),
-      tab_strip_(nullptr) {
-}
+      tab_strip_(nullptr) {}
 
-BrowserNonClientFrameViewMus::~BrowserNonClientFrameViewMus() {}
+BrowserNonClientFrameViewMash::~BrowserNonClientFrameViewMash() {}
 
-void BrowserNonClientFrameViewMus::Init() {
+void BrowserNonClientFrameViewMash::Init() {
   // Initializing the TabIconView is expensive, so only do it if we need to.
   if (browser_view()->ShouldShowWindowIcon()) {
     window_icon_ = new TabIconView(this, nullptr);
@@ -104,13 +75,13 @@
 ///////////////////////////////////////////////////////////////////////////////
 // BrowserNonClientFrameView:
 
-void BrowserNonClientFrameViewMus::OnBrowserViewInitViewsComplete() {
+void BrowserNonClientFrameViewMash::OnBrowserViewInitViewsComplete() {
   DCHECK(browser_view()->tabstrip());
   DCHECK(!tab_strip_);
   tab_strip_ = browser_view()->tabstrip();
 }
 
-gfx::Rect BrowserNonClientFrameViewMus::GetBoundsForTabStrip(
+gfx::Rect BrowserNonClientFrameViewMash::GetBoundsForTabStrip(
     views::View* tabstrip) const {
   if (!tabstrip)
     return gfx::Rect();
@@ -123,7 +94,7 @@
   return bounds;
 }
 
-int BrowserNonClientFrameViewMus::GetTopInset(bool restored) const {
+int BrowserNonClientFrameViewMash::GetTopInset(bool restored) const {
   if (!ShouldPaint()) {
     // When immersive fullscreen unrevealed, tabstrip is offscreen with normal
     // tapstrip bounds, the top inset should reach this topmost edge.
@@ -144,16 +115,16 @@
   return header_height;
 }
 
-int BrowserNonClientFrameViewMus::GetThemeBackgroundXInset() const {
+int BrowserNonClientFrameViewMash::GetThemeBackgroundXInset() const {
   return 5;
 }
 
-void BrowserNonClientFrameViewMus::UpdateThrobber(bool running) {
+void BrowserNonClientFrameViewMash::UpdateThrobber(bool running) {
   if (window_icon_)
     window_icon_->Update();
 }
 
-void BrowserNonClientFrameViewMus::UpdateClientArea() {
+void BrowserNonClientFrameViewMash::UpdateClientArea() {
   std::vector<gfx::Rect> additional_client_area;
   int top_container_offset = 0;
   ImmersiveModeController* immersive_mode_controller =
@@ -215,7 +186,7 @@
   }
 }
 
-void BrowserNonClientFrameViewMus::UpdateMinimumSize() {
+void BrowserNonClientFrameViewMash::UpdateMinimumSize() {
   gfx::Size min_size = GetMinimumSize();
   aura::Window* frame_window = frame()->GetNativeWindow();
   const gfx::Size* previous_min_size =
@@ -226,12 +197,12 @@
   }
 }
 
-int BrowserNonClientFrameViewMus::GetTabStripLeftInset() const {
+int BrowserNonClientFrameViewMash::GetTabStripLeftInset() const {
   return BrowserNonClientFrameView::GetTabStripLeftInset() +
          frame_values().normal_insets.left();
 }
 
-void BrowserNonClientFrameViewMus::OnTabsMaxXChanged() {
+void BrowserNonClientFrameViewMash::OnTabsMaxXChanged() {
   BrowserNonClientFrameView::OnTabsMaxXChanged();
   UpdateClientArea();
 }
@@ -239,33 +210,25 @@
 ///////////////////////////////////////////////////////////////////////////////
 // views::NonClientFrameView:
 
-gfx::Rect BrowserNonClientFrameViewMus::GetBoundsForClientView() const {
+gfx::Rect BrowserNonClientFrameViewMash::GetBoundsForClientView() const {
   // The ClientView must be flush with the top edge of the widget so that the
   // web contents can take up the entire screen in immersive fullscreen (with
   // or without the top-of-window views revealed). When in immersive fullscreen
   // and the top-of-window views are revealed, the TopContainerView paints the
   // window header by redirecting paints from its background to
-  // BrowserNonClientFrameViewMus.
+  // BrowserNonClientFrameViewMash.
   return bounds();
 }
 
-gfx::Rect BrowserNonClientFrameViewMus::GetWindowBoundsForClientBounds(
+gfx::Rect BrowserNonClientFrameViewMash::GetWindowBoundsForClientBounds(
     const gfx::Rect& client_bounds) const {
   return client_bounds;
 }
 
-int BrowserNonClientFrameViewMus::NonClientHitTest(const gfx::Point& point) {
+int BrowserNonClientFrameViewMash::NonClientHitTest(const gfx::Point& point) {
   // TODO(sky): figure out how this interaction should work.
   int hit_test = HTCLIENT;
 
-#if defined(FRAME_AVATAR_BUTTON)
-  views::View* profile_switcher_view = GetProfileSwitcherButton();
-  if (hit_test == HTCAPTION && profile_switcher_view &&
-      ConvertedHitTest(this, profile_switcher_view, point)) {
-    return HTCLIENT;
-  }
-#endif
-
   // When the window is restored we want a large click target above the tabs
   // to drag the window, so redirect clicks in the tab's shadow to caption.
   if (hit_test == HTCLIENT &&
@@ -282,26 +245,26 @@
   return hit_test;
 }
 
-void BrowserNonClientFrameViewMus::GetWindowMask(const gfx::Size& size,
-                                                 gfx::Path* window_mask) {
+void BrowserNonClientFrameViewMash::GetWindowMask(const gfx::Size& size,
+                                                  gfx::Path* window_mask) {
   // Aura does not use window masks.
 }
 
-void BrowserNonClientFrameViewMus::ResetWindowControls() {}
+void BrowserNonClientFrameViewMash::ResetWindowControls() {}
 
-void BrowserNonClientFrameViewMus::UpdateWindowIcon() {
+void BrowserNonClientFrameViewMash::UpdateWindowIcon() {
   if (window_icon_)
     window_icon_->SchedulePaint();
 }
 
-void BrowserNonClientFrameViewMus::UpdateWindowTitle() {}
+void BrowserNonClientFrameViewMash::UpdateWindowTitle() {}
 
-void BrowserNonClientFrameViewMus::SizeConstraintsChanged() {}
+void BrowserNonClientFrameViewMash::SizeConstraintsChanged() {}
 
 ///////////////////////////////////////////////////////////////////////////////
 // views::View:
 
-void BrowserNonClientFrameViewMus::OnPaint(gfx::Canvas* canvas) {
+void BrowserNonClientFrameViewMash::OnPaint(gfx::Canvas* canvas) {
   if (!ShouldPaint())
     return;
 
@@ -311,30 +274,25 @@
     PaintContentEdge(canvas);
 }
 
-void BrowserNonClientFrameViewMus::Layout() {
+void BrowserNonClientFrameViewMash::Layout() {
   if (profile_indicator_icon())
     LayoutIncognitoButton();
 
-#if defined(FRAME_AVATAR_BUTTON)
-  if (GetProfileSwitcherButton())
-    LayoutProfileSwitcher();
-#endif
-
   BrowserNonClientFrameView::Layout();
 
   UpdateClientArea();
 }
 
-const char* BrowserNonClientFrameViewMus::GetClassName() const {
+const char* BrowserNonClientFrameViewMash::GetClassName() const {
   return kViewClassName;
 }
 
-void BrowserNonClientFrameViewMus::GetAccessibleNodeData(
+void BrowserNonClientFrameViewMash::GetAccessibleNodeData(
     ui::AXNodeData* node_data) {
   node_data->role = ax::mojom::Role::kTitleBar;
 }
 
-gfx::Size BrowserNonClientFrameViewMus::GetMinimumSize() const {
+gfx::Size BrowserNonClientFrameViewMash::GetMinimumSize() const {
   gfx::Size min_client_view_size(frame()->client_view()->GetMinimumSize());
   const int min_frame_width = frame_values().max_title_bar_button_width +
                               frame_values().normal_insets.width();
@@ -351,8 +309,7 @@
   return gfx::Size(min_width, min_client_view_size.height());
 }
 
-void BrowserNonClientFrameViewMus::OnThemeChanged() {
-#if defined(OS_CHROMEOS)
+void BrowserNonClientFrameViewMash::OnThemeChanged() {
   gfx::ImageSkia active_frame_image = GetFrameImage(true);
   if (active_frame_image.isNull()) {
     frame()->GetNativeWindow()->ClearProperty(ash::kFrameImageActiveKey);
@@ -360,13 +317,12 @@
     frame()->GetNativeWindow()->SetProperty(
         ash::kFrameImageActiveKey, new gfx::ImageSkia(active_frame_image));
   }
-#endif
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 // TabIconViewModel:
 
-bool BrowserNonClientFrameViewMus::ShouldTabIconViewAnimate() const {
+bool BrowserNonClientFrameViewMash::ShouldTabIconViewAnimate() const {
   // This function is queried during the creation of the window as the
   // TabIconView we host is initialized, so we need to null check the selected
   // WebContents because in this condition there is not yet a selected tab.
@@ -374,28 +330,24 @@
   return current_tab ? current_tab->IsLoading() : false;
 }
 
-gfx::ImageSkia BrowserNonClientFrameViewMus::GetFaviconForTabIconView() {
+gfx::ImageSkia BrowserNonClientFrameViewMash::GetFaviconForTabIconView() {
   views::WidgetDelegate* delegate = frame()->widget_delegate();
   if (!delegate)
     return gfx::ImageSkia();
   return delegate->GetWindowIcon();
 }
 ///////////////////////////////////////////////////////////////////////////////
-// BrowserNonClientFrameViewMus, protected:
+// BrowserNonClientFrameViewMash, protected:
 
 // BrowserNonClientFrameView:
-AvatarButtonStyle BrowserNonClientFrameViewMus::GetAvatarButtonStyle() const {
-#if defined(FRAME_AVATAR_BUTTON)
-  return AvatarButtonStyle::NATIVE;
-#else
+AvatarButtonStyle BrowserNonClientFrameViewMash::GetAvatarButtonStyle() const {
   return AvatarButtonStyle::NONE;
-#endif
 }
 
 ///////////////////////////////////////////////////////////////////////////////
-// BrowserNonClientFrameViewMus, private:
+// BrowserNonClientFrameViewMash, private:
 
-int BrowserNonClientFrameViewMus::GetTabStripRightInset() const {
+int BrowserNonClientFrameViewMash::GetTabStripRightInset() const {
   int right_inset = frame_values().normal_insets.right() +
                     frame_values().max_title_bar_button_width;
 
@@ -407,35 +359,17 @@
   if (!MD::IsRefreshUi())
     right_inset += kTabstripRightSpacing;
 
-#if defined(FRAME_AVATAR_BUTTON)
-  views::View* profile_switcher_view = GetProfileSwitcherButton();
-  if (profile_switcher_view) {
-    right_inset +=
-        kAvatarButtonOffset + profile_switcher_view->GetPreferredSize().width();
-  }
-#endif
-
   return right_inset;
 }
 
-bool BrowserNonClientFrameViewMus::UsePackagedAppHeaderStyle() const {
+bool BrowserNonClientFrameViewMash::UsePackagedAppHeaderStyle() const {
   // Use for non tabbed trusted source windows, e.g. Settings, as well as apps.
   const Browser* const browser = browser_view()->browser();
   return (!browser->is_type_tabbed() && browser->is_trusted_source()) ||
          browser->is_app();
 }
 
-void BrowserNonClientFrameViewMus::LayoutProfileSwitcher() {
-#if defined(FRAME_AVATAR_BUTTON)
-  views::View* profile_switcher_view = GetProfileSwitcherButton();
-  gfx::Size button_size = profile_switcher_view->GetPreferredSize();
-  int button_x = width() - GetTabStripRightInset();
-  profile_switcher_view->SetBounds(button_x, 0, button_size.width(),
-                                   button_size.height());
-#endif
-}
-
-bool BrowserNonClientFrameViewMus::ShouldPaint() const {
+bool BrowserNonClientFrameViewMash::ShouldPaint() const {
   if (!frame()->IsFullscreen())
     return true;
 
@@ -447,23 +381,18 @@
          immersive_mode_controller->IsRevealed();
 }
 
-void BrowserNonClientFrameViewMus::PaintContentEdge(gfx::Canvas* canvas) {
+void BrowserNonClientFrameViewMash::PaintContentEdge(gfx::Canvas* canvas) {
   DCHECK(!UsePackagedAppHeaderStyle());
   const int bottom = frame_values().normal_insets.bottom();
-  canvas->FillRect(
-      gfx::Rect(0, bottom, width(), kClientEdgeThickness),
-      GetThemeProvider()->GetColor(
-          ThemeProperties::COLOR_TOOLBAR_BOTTOM_SEPARATOR));
+  canvas->FillRect(gfx::Rect(0, bottom, width(), kClientEdgeThickness),
+                   GetThemeProvider()->GetColor(
+                       ThemeProperties::COLOR_TOOLBAR_BOTTOM_SEPARATOR));
 }
 
-int BrowserNonClientFrameViewMus::GetHeaderHeight() const {
-#if defined(OS_CHROMEOS)
+int BrowserNonClientFrameViewMash::GetHeaderHeight() const {
   const bool restored = !frame()->IsMaximized() && !frame()->IsFullscreen();
   return GetAshLayoutSize(restored
                               ? ash::AshLayoutSize::kBrowserCaptionRestored
                               : ash::AshLayoutSize::kBrowserCaptionMaximized)
       .height();
-#else
-  return views::WindowManagerFrameValues::instance().normal_insets.top();
-#endif
 }
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mus.h b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mash.h
similarity index 85%
rename from chrome/browser/ui/views/frame/browser_non_client_frame_view_mus.h
rename to chrome/browser/ui/views/frame/browser_non_client_frame_view_mash.h
index 4ba4f05..25ff795 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mus.h
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mash.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_VIEWS_FRAME_BROWSER_NON_CLIENT_FRAME_VIEW_MUS_H_
-#define CHROME_BROWSER_UI_VIEWS_FRAME_BROWSER_NON_CLIENT_FRAME_VIEW_MUS_H_
+#ifndef CHROME_BROWSER_UI_VIEWS_FRAME_BROWSER_NON_CLIENT_FRAME_VIEW_MASH_H_
+#define CHROME_BROWSER_UI_VIEWS_FRAME_BROWSER_NON_CLIENT_FRAME_VIEW_MASH_H_
 
 #include <memory>
 
@@ -12,21 +12,17 @@
 #include "chrome/browser/ui/views/frame/browser_non_client_frame_view.h"
 #include "chrome/browser/ui/views/tab_icon_view_model.h"
 
-#if !defined(OS_CHROMEOS)
-#include "chrome/browser/ui/views/frame/avatar_button_manager.h"
-#endif
-
 class TabIconView;
 
 // TODO: Make sure caption buttons ink drop effects work with immersive
 // fullscreen mode browsers. https://crbug.com/840242.
-class BrowserNonClientFrameViewMus : public BrowserNonClientFrameView,
-                                     public TabIconViewModel {
+class BrowserNonClientFrameViewMash : public BrowserNonClientFrameView,
+                                      public TabIconViewModel {
  public:
   static const char kViewClassName[];
 
-  BrowserNonClientFrameViewMus(BrowserFrame* frame, BrowserView* browser_view);
-  ~BrowserNonClientFrameViewMus() override;
+  BrowserNonClientFrameViewMash(BrowserFrame* frame, BrowserView* browser_view);
+  ~BrowserNonClientFrameViewMash() override;
 
   void Init();
 
@@ -78,9 +74,6 @@
   // scheme than browser windows.
   bool UsePackagedAppHeaderStyle() const;
 
-  // Layout the profile switcher (if there is one).
-  void LayoutProfileSwitcher();
-
   // Returns true if there is anything to paint. Some fullscreen windows do not
   // need their frames painted.
   bool ShouldPaint() const;
@@ -99,7 +92,7 @@
 
   TabStrip* tab_strip_;
 
-  DISALLOW_COPY_AND_ASSIGN(BrowserNonClientFrameViewMus);
+  DISALLOW_COPY_AND_ASSIGN(BrowserNonClientFrameViewMash);
 };
 
-#endif  // CHROME_BROWSER_UI_VIEWS_FRAME_BROWSER_NON_CLIENT_FRAME_VIEW_MUS_H_
+#endif  // CHROME_BROWSER_UI_VIEWS_FRAME_BROWSER_NON_CLIENT_FRAME_VIEW_MASH_H_
diff --git a/chrome/browser/ui/views/frame/native_browser_frame_factory_aurawin.cc b/chrome/browser/ui/views/frame/native_browser_frame_factory_aurawin.cc
index 3a0e232..aaab285 100644
--- a/chrome/browser/ui/views/frame/native_browser_frame_factory_aurawin.cc
+++ b/chrome/browser/ui/views/frame/native_browser_frame_factory_aurawin.cc
@@ -4,14 +4,10 @@
 
 #include "chrome/browser/ui/views/frame/native_browser_frame_factory.h"
 
-#include "chrome/browser/ui/views/frame/browser_frame_mus.h"
 #include "chrome/browser/ui/views/frame/desktop_browser_frame_aura.h"
-#include "ui/aura/env.h"
 
 NativeBrowserFrame* NativeBrowserFrameFactory::Create(
     BrowserFrame* browser_frame,
     BrowserView* browser_view) {
-  if (aura::Env::GetInstance()->mode() == aura::Env::Mode::MUS)
-    return new BrowserFrameMus(browser_frame, browser_view);
   return new DesktopBrowserFrameAura(browser_frame, browser_view);
 }
diff --git a/chrome/browser/ui/views/frame/native_browser_frame_factory_aurax11.cc b/chrome/browser/ui/views/frame/native_browser_frame_factory_aurax11.cc
index ce8f6b8..21096248c 100644
--- a/chrome/browser/ui/views/frame/native_browser_frame_factory_aurax11.cc
+++ b/chrome/browser/ui/views/frame/native_browser_frame_factory_aurax11.cc
@@ -4,14 +4,10 @@
 
 #include "chrome/browser/ui/views/frame/native_browser_frame_factory.h"
 
-#include "chrome/browser/ui/views/frame/browser_frame_mus.h"
 #include "chrome/browser/ui/views/frame/desktop_browser_frame_aurax11.h"
-#include "ui/aura/env.h"
 
 NativeBrowserFrame* NativeBrowserFrameFactory::Create(
     BrowserFrame* browser_frame,
     BrowserView* browser_view) {
-  if (aura::Env::GetInstance()->mode() == aura::Env::Mode::MUS)
-    return new BrowserFrameMus(browser_frame, browser_view);
   return new DesktopBrowserFrameAuraX11(browser_frame, browser_view);
 }
diff --git a/chrome/browser/ui/views/frame/native_browser_frame_factory_chromeos.cc b/chrome/browser/ui/views/frame/native_browser_frame_factory_chromeos.cc
index 052b7090..27b59a7 100644
--- a/chrome/browser/ui/views/frame/native_browser_frame_factory_chromeos.cc
+++ b/chrome/browser/ui/views/frame/native_browser_frame_factory_chromeos.cc
@@ -6,13 +6,12 @@
 
 #include "chrome/browser/chromeos/ash_config.h"
 #include "chrome/browser/ui/views/frame/browser_frame_ash.h"
-#include "chrome/browser/ui/views/frame/browser_frame_mus.h"
-#include "services/service_manager/runner/common/client_util.h"
+#include "chrome/browser/ui/views/frame/browser_frame_mash.h"
 
 NativeBrowserFrame* NativeBrowserFrameFactory::Create(
     BrowserFrame* browser_frame,
     BrowserView* browser_view) {
   if (chromeos::GetAshConfig() == ash::Config::MASH)
-    return new BrowserFrameMus(browser_frame, browser_view);
+    return new BrowserFrameMash(browser_frame, browser_view);
   return new BrowserFrameAsh(browser_frame, browser_view);
 }
diff --git a/chrome/browser/ui/views/frame/native_browser_frame_factory_ozone.cc b/chrome/browser/ui/views/frame/native_browser_frame_factory_ozone.cc
index 9595d56..06be688c 100644
--- a/chrome/browser/ui/views/frame/native_browser_frame_factory_ozone.cc
+++ b/chrome/browser/ui/views/frame/native_browser_frame_factory_ozone.cc
@@ -4,14 +4,10 @@
 
 #include "chrome/browser/ui/views/frame/native_browser_frame_factory.h"
 
-#include "chrome/browser/ui/views/frame/browser_frame_mus.h"
 #include "chrome/browser/ui/views/frame/desktop_browser_frame_aura.h"
-#include "ui/aura/env.h"
 
 NativeBrowserFrame* NativeBrowserFrameFactory::Create(
     BrowserFrame* browser_frame,
     BrowserView* browser_view) {
-  if (aura::Env::GetInstance()->mode() == aura::Env::Mode::MUS)
-    return new BrowserFrameMus(browser_frame, browser_view);
   return new DesktopBrowserFrameAura(browser_frame, browser_view);
 }
diff --git a/chrome/browser/ui/views/harmony/material_refresh_layout_provider.cc b/chrome/browser/ui/views/harmony/material_refresh_layout_provider.cc
index a2ad98e..e01094a1 100644
--- a/chrome/browser/ui/views/harmony/material_refresh_layout_provider.cc
+++ b/chrome/browser/ui/views/harmony/material_refresh_layout_provider.cc
@@ -37,3 +37,23 @@
       return 0;
   }
 }
+
+int MaterialRefreshLayoutProvider::GetShadowElevationMetric(
+    views::EmphasisMetric emphasis_metric) const {
+  switch (emphasis_metric) {
+    case views::EMPHASIS_LOW:
+      return 1;
+    case views::EMPHASIS_MEDIUM:
+      return 3;
+    case views::EMPHASIS_HIGH:
+      return 16;
+    default:
+      NOTREACHED();
+      return 0;
+  }
+}
+
+gfx::ShadowValues MaterialRefreshLayoutProvider::MakeShadowValues(
+    int elevation) const {
+  return gfx::ShadowValue::MakeRefreshShadowValues(elevation);
+}
diff --git a/chrome/browser/ui/views/harmony/material_refresh_layout_provider.h b/chrome/browser/ui/views/harmony/material_refresh_layout_provider.h
index b796d86..5bd070c 100644
--- a/chrome/browser/ui/views/harmony/material_refresh_layout_provider.h
+++ b/chrome/browser/ui/views/harmony/material_refresh_layout_provider.h
@@ -18,6 +18,9 @@
   gfx::Insets GetInsetsMetric(int metric) const override;
   int GetCornerRadiusMetric(views::EmphasisMetric emphasis_metric,
                             const gfx::Size& size = gfx::Size()) const override;
+  int GetShadowElevationMetric(
+      views::EmphasisMetric emphasis_metric) const override;
+  gfx::ShadowValues MakeShadowValues(int elevation) const override;
 };
 
 #endif  // CHROME_BROWSER_UI_VIEWS_HARMONY_MATERIAL_REFRESH_LAYOUT_PROVIDER_H_
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.cc b/chrome/browser/ui/views/location_bar/location_bar_view.cc
index 86080e3..ee69177 100644
--- a/chrome/browser/ui/views/location_bar/location_bar_view.cc
+++ b/chrome/browser/ui/views/location_bar/location_bar_view.cc
@@ -120,12 +120,6 @@
 
 namespace {
 
-// Returns true when a views::FocusRing should be used.
-bool ShouldUseFocusRingView(bool show_focus_ring) {
-  return (show_focus_ring && LocationBarView::IsRounded()) ||
-         ChromePlatformStyle::ShouldOmniboxUseFocusRing();
-}
-
 // Helper function to create a rounded rect background (no stroke).
 std::unique_ptr<views::Background> CreateRoundRectBackground(SkColor bg_color,
                                                              float radius) {
@@ -352,21 +346,6 @@
   ime_inline_autocomplete_view_->SetVisible(!text.empty());
 }
 
-void LocationBarView::SetShowFocusRect(bool show) {
-  show_focus_rect_ = show;
-  if (ShouldUseFocusRingView(show)) {
-    focus_ring_ = views::FocusRing::Install(this);
-    focus_ring_->SetPath(GetFocusRingPath());
-    focus_ring_->SetHasFocusPredicate([](View* view) -> bool {
-      auto* v = static_cast<LocationBarView*>(view);
-      return v->omnibox_view_->HasFocus();
-    });
-  } else {
-    focus_ring_.reset();
-  }
-  SchedulePaint();
-}
-
 void LocationBarView::SelectAll() {
   omnibox_view_->SelectAll(true);
 }
@@ -751,12 +730,6 @@
   return std::max(0, GetLayoutConstant(LOCATION_BAR_HEIGHT) -
                          2 * GetTotalVerticalPadding());
 }
-SkPath LocationBarView::GetFocusRingPath() const {
-  SkPath path;
-  path.addRRect(SkRRect::MakeRectXY(RectToSkRect(GetLocalBounds()),
-                                    GetBorderRadius(), GetBorderRadius()));
-  return path;
-}
 
 // static
 int LocationBarView::GetAvailableDecorationTextHeight() {
@@ -807,9 +780,6 @@
       background_color = border_color =
           GetColor(OmniboxPart::RESULTS_BACKGROUND);
     }
-    // Remove the focus ring if the omnibox popup is open.
-    if (focus_ring_)
-      focus_ring_->SetVisible(!GetOmniboxPopupView()->IsOpen());
   }
 
   if (is_popup_mode_) {
@@ -937,6 +907,37 @@
       gfx::Insets(GetLayoutConstant(LOCATION_BAR_ICON_INTERIOR_PADDING))));
 }
 
+void LocationBarView::RefreshFocusRing() {
+  // Install a focus ring if the Omnibox is rounded, or if the system style uses
+  // a focus ring (i.e. Mac). Early exit if neither of those cases apply.
+  if (!LocationBarView::IsRounded() &&
+      !ChromePlatformStyle::ShouldOmniboxUseFocusRing())
+    return;
+
+  // We may not have a usable path during initialization before first layout.
+  // This will be called again once there is a usable path, so early exit.
+  SkPath path;
+  path.addRRect(SkRRect::MakeRectXY(RectToSkRect(GetLocalBounds()),
+                                    GetBorderRadius(), GetBorderRadius()));
+  if (!views::FocusRing::IsPathUseable(path))
+    return;
+
+  // Install a focus ring if it's not already there.
+  if (!focus_ring_) {
+    focus_ring_ = views::FocusRing::Install(this);
+    focus_ring_->SetHasFocusPredicate([](View* view) -> bool {
+      auto* v = static_cast<LocationBarView*>(view);
+
+      // Show focus ring when the Omnibox is focused and the popup is closed.
+      return v->omnibox_view_->HasFocus() &&
+             !v->GetOmniboxPopupView()->IsOpen();
+    });
+  }
+
+  // Update the focus ring path.
+  focus_ring_->SetPath(path);
+}
+
 base::string16 LocationBarView::GetLocationIconText() const {
   if (GetToolbarModel()->GetURL().SchemeIs(content::kChromeUIScheme))
     return l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME);
@@ -1121,9 +1122,7 @@
 
 void LocationBarView::OnBoundsChanged(const gfx::Rect& previous_bounds) {
   RefreshBackground();
-  // Update the focus rect if needed.
-  if (!bounds().IsEmpty())
-    SetShowFocusRect(show_focus_rect_);
+  RefreshFocusRing();
 }
 
 void LocationBarView::OnFocus() {
@@ -1202,8 +1201,10 @@
 void LocationBarView::OnInputInProgress(bool in_progress) {
   ChromeOmniboxEditController::OnInputInProgress(in_progress);
 
-  if (ui::MaterialDesignController::IsRefreshUi())
+  if (ui::MaterialDesignController::IsRefreshUi()) {
     GetOmniboxPopupView()->UpdatePopupAppearance();
+    RefreshFocusRing();
+  }
 }
 
 void LocationBarView::OnChanged() {
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.h b/chrome/browser/ui/views/location_bar/location_bar_view.h
index 6d5aea6..641aff3 100644
--- a/chrome/browser/ui/views/location_bar/location_bar_view.h
+++ b/chrome/browser/ui/views/location_bar/location_bar_view.h
@@ -189,11 +189,6 @@
   // comments on |ime_inline_autocomplete_view_|.
   void SetImeInlineAutocompletion(const base::string16& text);
 
-  // Set if we should show a focus rect while the location entry field is
-  // focused. Used when the toolbar is in full keyboard accessibility mode.
-  // Repaints if necessary.
-  virtual void SetShowFocusRect(bool show);
-
   // Select all of the text. Needed when the user tabs through controls
   // in the toolbar in full keyboard accessibility mode.
   virtual void SelectAll();
@@ -319,6 +314,9 @@
   // Updates the color of the icon for the "clear all" button.
   void RefreshClearAllButtonIcon();
 
+  // Updates the focus ring.
+  void RefreshFocusRing();
+
   // Returns text to be placed in the location icon view.
   // - For secure/insecure pages, returns text describing the URL's security
   // level.
@@ -398,9 +396,6 @@
   // which is the vertical edge thickness plus the padding next to it.
   static int GetTotalVerticalPadding();
 
-  // Returns the path to draw this LocationBarView's focus ring around.
-  SkPath GetFocusRingPath() const;
-
   // The Browser this LocationBarView is in.  Note that at least
   // chromeos::SimpleWebViewDialog uses a LocationBarView outside any browser
   // window, so this may be NULL.
diff --git a/chrome/browser/ui/views/omnibox/omnibox_result_view.cc b/chrome/browser/ui/views/omnibox/omnibox_result_view.cc
index 3fa9cde..ed91a95 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_result_view.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_result_view.cc
@@ -226,6 +226,39 @@
 ////////////////////////////////////////////////////////////////////////////////
 // OmniboxResultView, views::View overrides:
 
+void OmniboxResultView::Layout() {
+  views::View::Layout();
+  // NOTE: While animating the keyword match, both matches may be visible.
+  int suggestion_width = width();
+  AutocompleteMatch* keyword_match = match_.associated_keyword.get();
+  if (keyword_match) {
+    const int icon_width = keyword_view_->icon()->width() +
+                           GetIconAlignmentOffset() + (HorizontalPadding() * 2);
+    const int max_kw_x = width() - icon_width;
+    suggestion_width = animation_->CurrentValueBetween(max_kw_x, 0);
+  }
+  if (suggestion_tab_switch_button_) {
+    suggestion_tab_switch_button_->ProvideWidthHint(suggestion_width);
+    const gfx::Size ts_button_size =
+        suggestion_tab_switch_button_->GetPreferredSize();
+    if (ts_button_size.width() > 0) {
+      suggestion_tab_switch_button_->SetSize(ts_button_size);
+
+      // It looks nice to have the same margin on top, bottom and right side.
+      const int margin =
+          (suggestion_view_->height() - ts_button_size.height()) / 2;
+      suggestion_width -= ts_button_size.width() + margin;
+      suggestion_tab_switch_button_->SetPosition(
+          gfx::Point(suggestion_width, margin));
+      suggestion_tab_switch_button_->SetVisible(true);
+    } else {
+      suggestion_tab_switch_button_->SetVisible(false);
+    }
+  }
+  keyword_view_->SetBounds(suggestion_width, 0, width(), height());
+  suggestion_view_->SetBounds(0, 0, suggestion_width, height());
+}
+
 bool OmniboxResultView::OnMousePressed(const ui::MouseEvent& event) {
   if (event.IsOnlyLeftMouseButton())
     model_->SetSelectedLine(model_index_);
@@ -336,39 +369,6 @@
 ////////////////////////////////////////////////////////////////////////////////
 // OmniboxResultView, views::View overrides, private:
 
-void OmniboxResultView::Layout() {
-  views::View::Layout();
-  // NOTE: While animating the keyword match, both matches may be visible.
-  int suggestion_width = width();
-  AutocompleteMatch* keyword_match = match_.associated_keyword.get();
-  if (keyword_match) {
-    const int icon_width = keyword_view_->icon()->width() +
-                           GetIconAlignmentOffset() + (HorizontalPadding() * 2);
-    const int max_kw_x = width() - icon_width;
-    suggestion_width = animation_->CurrentValueBetween(max_kw_x, 0);
-  }
-  if (suggestion_tab_switch_button_) {
-    suggestion_tab_switch_button_->ProvideWidthHint(suggestion_width);
-    const gfx::Size ts_button_size =
-        suggestion_tab_switch_button_->GetPreferredSize();
-    if (ts_button_size.width() > 0) {
-      suggestion_tab_switch_button_->SetSize(ts_button_size);
-
-      // It looks nice to have the same margin on top, bottom and right side.
-      const int margin =
-          (suggestion_view_->height() - ts_button_size.height()) / 2;
-      suggestion_width -= ts_button_size.width() + margin;
-      suggestion_tab_switch_button_->SetPosition(
-          gfx::Point(suggestion_width, margin));
-      suggestion_tab_switch_button_->SetVisible(true);
-    } else {
-      suggestion_tab_switch_button_->SetVisible(false);
-    }
-  }
-  keyword_view_->SetBounds(suggestion_width, 0, width(), height());
-  suggestion_view_->SetBounds(0, 0, suggestion_width, height());
-}
-
 const char* OmniboxResultView::GetClassName() const {
   return "OmniboxResultView";
 }
diff --git a/chrome/browser/ui/views/omnibox/omnibox_result_view.h b/chrome/browser/ui/views/omnibox/omnibox_result_view.h
index 432cfa2b..7ba0fc9 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_result_view.h
+++ b/chrome/browser/ui/views/omnibox/omnibox_result_view.h
@@ -71,6 +71,7 @@
   void ButtonPressed(views::Button* sender, const ui::Event& event) override;
 
   // views::View:
+  void Layout() override;
   bool OnMousePressed(const ui::MouseEvent& event) override;
   bool OnMouseDragged(const ui::MouseEvent& event) override;
   void OnMouseReleased(const ui::MouseEvent& event) override;
@@ -96,7 +97,6 @@
   void OpenMatch(WindowOpenDisposition disposition);
 
   // views::View:
-  void Layout() override;
   const char* GetClassName() const override;
   void OnBoundsChanged(const gfx::Rect& previous_bounds) override;
 
diff --git a/chrome/browser/ui/views/omnibox/omnibox_tab_switch_button.cc b/chrome/browser/ui/views/omnibox/omnibox_tab_switch_button.cc
index 0fa0c318..629b6a8 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_tab_switch_button.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_tab_switch_button.cc
@@ -13,6 +13,7 @@
 #include "components/omnibox/browser/vector_icons.h"
 #include "components/strings/grit/components_strings.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/gfx/animation/slide_animation.h"
 #include "ui/gfx/color_utils.h"
 #include "ui/gfx/paint_vector_icon.h"
 
@@ -27,7 +28,9 @@
     : MdTextButton(result_view, views::style::CONTEXT_BUTTON_MD),
       text_height_(text_height),
       model_(model),
-      result_view_(result_view) {
+      result_view_(result_view),
+      initialized_(false),
+      animation_(new gfx::SlideAnimation(this)) {
   // TODO(krb): SetTooltipText(text);
   SetBgColorOverride(GetBackgroundColor());
   SetImage(STATE_NORMAL,
@@ -44,29 +47,56 @@
   } else {
     SetText(l10n_util::GetStringUTF16(IDS_OMNIBOX_TAB_SUGGEST_HINT));
   }
-  visible_ = true;
   set_corner_radius(CalculatePreferredSize().height() / 2.f);
+  animation_->SetSlideDuration(500);
+  SetElideBehavior(gfx::FADE_TAIL);
+}
+
+OmniboxTabSwitchButton::~OmniboxTabSwitchButton() = default;
+
+size_t OmniboxTabSwitchButton::CalculateGoalWidth(size_t parent_width,
+                                                  base::string16* goal_text) {
+  if (full_text_width_ * 5 <= parent_width) {
+    *goal_text = l10n_util::GetStringUTF16(IDS_OMNIBOX_TAB_SUGGEST_HINT);
+    return full_text_width_;
+  }
+  if (short_text_width_ * 5 <= parent_width) {
+    *goal_text = l10n_util::GetStringUTF16(IDS_OMNIBOX_TAB_SUGGEST_SHORT_HINT);
+    return short_text_width_;
+  }
+  *goal_text = base::string16();
+  if (icon_only_width_ * 5 <= parent_width)
+    return icon_only_width_;
+  return 0;
 }
 
 void OmniboxTabSwitchButton::ProvideWidthHint(size_t parent_width) {
-  visible_ = true;
-  if (full_text_width_ < parent_width / 5) {
-    SetText(l10n_util::GetStringUTF16(IDS_OMNIBOX_TAB_SUGGEST_HINT));
-  } else if (short_text_width_ < parent_width / 5) {
-    SetText(l10n_util::GetStringUTF16(IDS_OMNIBOX_TAB_SUGGEST_SHORT_HINT));
-  } else if (icon_only_width_ < parent_width / 5) {
-    SetText(base::ASCIIToUTF16(""));
-  } else {
-    visible_ = false;
+  size_t preferred_width = CalculateGoalWidth(parent_width, &goal_text_);
+  if (!initialized_) {
+    initialized_ = true;
+    goal_width_ = preferred_width;
+    animation_->Reset(1);
+    SetText(goal_text_);
+    return;
+  }
+  if (preferred_width != goal_width_) {
+    goal_width_ = preferred_width;
+    start_width_ = width();
+    // If growing/showing, set text-to-be and grow into it.
+    if (goal_width_ > start_width_)
+      SetText(goal_text_);
+    animation_->Reset(0);
+    animation_->Show();
   }
 }
 
 gfx::Size OmniboxTabSwitchButton::CalculatePreferredSize() const {
-  if (!visible_)
-    return gfx::Size();
   gfx::Size size = MdTextButton::CalculatePreferredSize();
   // Bump height if odd.
   size.set_height(text_height_ + (text_height_ & 1) + 2 * kVerticalPadding);
+  int current_width = animation_->CurrentValueBetween(
+      static_cast<int>(start_width_), static_cast<int>(goal_width_));
+  size.set_width(current_width);
   return size;
 }
 
@@ -93,6 +123,20 @@
   MdTextButton::StateChanged(old_state);
 }
 
+void OmniboxTabSwitchButton::AnimationProgressed(
+    const gfx::Animation* animation) {
+  if (animation != animation_.get()) {
+    MdTextButton::AnimationProgressed(animation);
+    return;
+  }
+
+  // If done shrinking, correct text.
+  if (animation_->GetCurrentValue() == 1 && goal_width_ < start_width_)
+    SetText(goal_text_);
+  result_view_->Layout();
+  result_view_->SchedulePaint();
+}
+
 void OmniboxTabSwitchButton::UpdateBackground() {
   if (model_->IsButtonSelected())
     SetPressed();
diff --git a/chrome/browser/ui/views/omnibox/omnibox_tab_switch_button.h b/chrome/browser/ui/views/omnibox/omnibox_tab_switch_button.h
index 3914d70..6d30c3b 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_tab_switch_button.h
+++ b/chrome/browser/ui/views/omnibox/omnibox_tab_switch_button.h
@@ -10,16 +10,23 @@
 class OmniboxPopupContentsView;
 class OmniboxResultView;
 
+namespace gfx {
+class SlideAnimation;
+}
+
 class OmniboxTabSwitchButton : public views::MdTextButton {
  public:
   OmniboxTabSwitchButton(OmniboxPopupContentsView* model,
                          OmniboxResultView* result_view,
                          int text_height);
 
+  ~OmniboxTabSwitchButton() override;
+
   // views::View
   gfx::Size CalculatePreferredSize() const override;
 
   // views::Button
+  void AnimationProgressed(const gfx::Animation* animation) override;
   void StateChanged(ButtonState old_state) override;
 
   // Called by parent views to change background on external (not mouse related)
@@ -39,6 +46,10 @@
   // pressed.
   void SetPressed();
 
+  // Helper function to translate parent width into goal width, and
+  // pass back the text at that width.
+  size_t CalculateGoalWidth(size_t parent_width, base::string16* goal_text);
+
   static constexpr int kVerticalPadding = 3;
   const int text_height_;
   OmniboxPopupContentsView* model_;
@@ -50,8 +61,13 @@
   static size_t short_text_width_;
   static size_t full_text_width_;
 
-  // To remember case of not being visible at all.
-  bool visible_;
+  // To distinguish start-up case, where we don't want animation.
+  bool initialized_;
+  // Animation starting width, and final value.
+  size_t start_width_, goal_width_;
+  // The text to be displayed when we reach |goal_width_|.
+  base::string16 goal_text_;
+  std::unique_ptr<gfx::SlideAnimation> animation_;
 
   DISALLOW_COPY_AND_ASSIGN(OmniboxTabSwitchButton);
 };
diff --git a/chrome/browser/ui/views/permission_bubble/permission_bubble_views_interactive_uitest_mac.mm b/chrome/browser/ui/views/permission_bubble/permission_bubble_views_interactive_uitest_mac.mm
new file mode 100644
index 0000000..86c9dc9
--- /dev/null
+++ b/chrome/browser/ui/views/permission_bubble/permission_bubble_views_interactive_uitest_mac.mm
@@ -0,0 +1,102 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import <Cocoa/Cocoa.h>
+
+#include <memory>
+
+#import "base/mac/scoped_nsobject.h"
+#include "base/run_loop.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/permissions/permission_request_manager_test_api.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_commands.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/interactive_test_utils.h"
+#include "chrome/test/views/scoped_macviews_browser_mode.h"
+#include "ui/base/test/ui_controls.h"
+#import "ui/base/test/windowed_nsnotification_observer.h"
+#include "ui/base/ui_base_features.h"
+#import "ui/events/test/cocoa_test_event_utils.h"
+
+class PermissionBubbleViewsInteractiveUITest : public InProcessBrowserTest {
+ public:
+  PermissionBubbleViewsInteractiveUITest() {}
+
+  void EnsureWindowActive(NSWindow* window, const char* message) {
+    SCOPED_TRACE(message);
+    EXPECT_TRUE(window);
+
+    // Activation is asynchronous on Mac. If the window didn't become active,
+    // wait for it.
+    if (![window isKeyWindow]) {
+      base::scoped_nsobject<WindowedNSNotificationObserver> waiter(
+          [[WindowedNSNotificationObserver alloc]
+              initForNotification:NSWindowDidBecomeKeyNotification
+                           object:window]);
+      [waiter wait];
+    }
+    EXPECT_TRUE([window isKeyWindow]);
+
+    // Whether or not we had to wait, flush the run loop. This ensures any
+    // asynchronous close operations have completed.
+    base::RunLoop().RunUntilIdle();
+  }
+
+  // Send Cmd+keycode in the key window to NSApp.
+  void SendAccelerator(ui::KeyboardCode keycode) {
+    bool shift = false;
+    bool alt = false;
+    bool control = false;
+    bool command = true;
+    // Note that although this takes an NSWindow, it's just used to create the
+    // NSEvent* which will be dispatched to NSApp (i.e. not NSWindow).
+    ui_controls::SendKeyPress([NSApp keyWindow], keycode, control, shift, alt,
+                              command);
+  }
+
+  void SetUpOnMainThread() override {
+    // Make the browser active (ensures the app can receive key events).
+    EXPECT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
+
+    test_api_ =
+        std::make_unique<test::PermissionRequestManagerTestApi>(browser());
+    EXPECT_TRUE(test_api_->manager());
+
+    test_api_->AddSimpleRequest(CONTENT_SETTINGS_TYPE_GEOLOCATION);
+
+    EXPECT_TRUE([browser()->window()->GetNativeWindow() isKeyWindow]);
+
+    // The PermissionRequestManager displays prompts asynchronously.
+    base::RunLoop().RunUntilIdle();
+
+    // The bubble should steal key focus when shown.
+    EnsureWindowActive(test_api_->GetPromptWindow(), "show permission bubble");
+  }
+
+ private:
+  std::unique_ptr<test::PermissionRequestManagerTestApi> test_api_;
+  test::ScopedMacViewsBrowserMode views_mode_{true};
+
+  DISALLOW_COPY_AND_ASSIGN(PermissionBubbleViewsInteractiveUITest);
+};
+
+// There is only one tab. Cmd+w will close it along with the browser window.
+IN_PROC_BROWSER_TEST_F(PermissionBubbleViewsInteractiveUITest,
+                       CmdWClosesWindow) {
+  base::scoped_nsobject<NSWindow> browser_window(
+      browser()->window()->GetNativeWindow(), base::scoped_policy::RETAIN);
+  EXPECT_TRUE([browser_window isVisible]);
+
+  content::WindowedNotificationObserver observer(
+      chrome::NOTIFICATION_BROWSER_CLOSED, content::Source<Browser>(browser()));
+
+  SendAccelerator(ui::VKEY_W);
+
+  // The actual window close happens via a posted task.
+  EXPECT_TRUE([browser_window isVisible]);
+  observer.Wait();
+  EXPECT_FALSE([browser_window isVisible]);
+}
diff --git a/chrome/browser/ui/views/toolbar/toolbar_view.cc b/chrome/browser/ui/views/toolbar/toolbar_view.cc
index cb998caf..01c086d 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_view.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_view.cc
@@ -341,17 +341,6 @@
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-// ToolbarView, AccessiblePaneView overrides:
-
-bool ToolbarView::SetPaneFocus(views::View* initial_focus) {
-  if (!AccessiblePaneView::SetPaneFocus(initial_focus))
-    return false;
-
-  location_bar_->SetShowFocusRect(true);
-  return true;
-}
-
-////////////////////////////////////////////////////////////////////////////////
 // ToolbarView, views::MenuButtonListener implementation:
 
 void ToolbarView::OnMenuButtonClicked(views::MenuButton* source,
@@ -636,11 +625,6 @@
   return true;
 }
 
-void ToolbarView::RemovePaneFocus() {
-  AccessiblePaneView::RemovePaneFocus();
-  location_bar_->SetShowFocusRect(false);
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 // ToolbarView, private:
 
diff --git a/chrome/browser/ui/views/toolbar/toolbar_view.h b/chrome/browser/ui/views/toolbar/toolbar_view.h
index 8691032..43c3148 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_view.h
+++ b/chrome/browser/ui/views/toolbar/toolbar_view.h
@@ -117,9 +117,6 @@
     return &app_menu_icon_controller_;
   }
 
-  // AccessiblePaneView:
-  bool SetPaneFocus(View* initial_focus) override;
-
   // views::MenuButtonListener:
   void OnMenuButtonClicked(views::MenuButton* source,
                            const gfx::Point& point,
@@ -167,7 +164,6 @@
  protected:
   // AccessiblePaneView:
   bool SetPaneFocusAndFocusDefault() override;
-  void RemovePaneFocus() override;
 
   bool is_display_mode_normal() const {
     return display_mode_ == DISPLAYMODE_NORMAL;
diff --git a/chrome/browser/ui/views_mode_controller.cc b/chrome/browser/ui/views_mode_controller.cc
index 9a673df..689cd6e 100644
--- a/chrome/browser/ui/views_mode_controller.cc
+++ b/chrome/browser/ui/views_mode_controller.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/views_mode_controller.h"
 
+#include "base/feature_list.h"
 #include "chrome/common/chrome_features.h"
 #include "ui/base/ui_base_features.h"
 
@@ -12,7 +13,8 @@
 namespace views_mode_controller {
 
 bool IsViewsBrowserCocoa() {
-  return features::IsViewsBrowserCocoa();
+  return features::IsViewsBrowserCocoa() &&
+         !base::FeatureList::IsEnabled(features::kUiFood);
 }
 
 }  // namespace views_mode_controller
diff --git a/chrome/browser/ui/webui/settings/chrome_cleanup_handler.cc b/chrome/browser/ui/webui/settings/chrome_cleanup_handler.cc
index f07c59b..7119710 100644
--- a/chrome/browser/ui/webui/settings/chrome_cleanup_handler.cc
+++ b/chrome/browser/ui/webui/settings/chrome_cleanup_handler.cc
@@ -194,14 +194,9 @@
 }
 
 void ChromeCleanupHandler::OnLogsEnabledChanged(bool logs_enabled) {
-  // Logs are considered managed if the logs themselves are managed or if the
-  // entire cleanup feature is disabled by policy.
-  PrefService* local_state = g_browser_process->local_state();
-  bool is_managed = !controller_->IsAllowedByPolicy() ||
-                    (local_state && local_state->IsManagedPreference(
-                                        prefs::kSwReporterReportingEnabled));
   FireWebUIListener("chrome-cleanup-upload-permission-change",
-                    base::Value(is_managed), base::Value(logs_enabled));
+                    base::Value(controller_->IsReportingManagedByPolicy()),
+                    base::Value(logs_enabled));
 }
 
 void ChromeCleanupHandler::OnLogsEnabledPrefChanged() {
diff --git a/chrome/browser/vr/testapp/gl_renderer.cc b/chrome/browser/vr/testapp/gl_renderer.cc
index 87df75c6..277c5fc 100644
--- a/chrome/browser/vr/testapp/gl_renderer.cc
+++ b/chrome/browser/vr/testapp/gl_renderer.cc
@@ -20,6 +20,13 @@
   // Do nothing for now.
 }
 
+bool ClearGlErrors() {
+  bool errors = false;
+  while (glGetError() != GL_NO_ERROR)
+    errors = true;
+  return errors;
+}
+
 }  // namespace
 
 GlRenderer::GlRenderer(const scoped_refptr<gl::GLSurface>& surface,
@@ -50,10 +57,18 @@
 void GlRenderer::RenderFrame() {
   context_->MakeCurrent(surface_.get());
 
+  // Checking and clearing GL errors can be expensive, but we can afford to do
+  // this in the testapp as a sanity check.  Clear errors before drawing UI,
+  // then assert no new errors after drawing.  See https://crbug.com/768905.
+  ClearGlErrors();
+
   glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
   glClear(GL_COLOR_BUFFER_BIT);
 
   vr_->DrawFrame();
+
+  DCHECK(!ClearGlErrors());
+
   PostRenderFrameTask(
       surface_->SwapBuffers(base::BindRepeating(&OnPresentedFrame)));
 }
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index a977b9a..426cef1 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -4526,6 +4526,8 @@
       sources += [
         "ppapi/ppapi_test.cc",
         "ppapi/ppapi_test.h",
+        "ppapi/ppapi_test_select_file_dialog_factory.cc",
+        "ppapi/ppapi_test_select_file_dialog_factory.h",
       ]
     }
 
@@ -4672,6 +4674,7 @@
       "../browser/ui/tabs/window_activity_watcher_interactive_uitest.cc",
       "../browser/ui/translate/translate_bubble_test_utils.h",
       "../browser/ui/views/accessibility/navigation_accessibility_uitest_win.cc",
+      "../browser/ui/views/permission_bubble/permission_bubble_views_interactive_uitest_mac.mm",
       "../browser/webauth_interactive_uitest.cc",
       "//ui/base/clipboard/clipboard_unittest.cc",
       "base/always_on_top_window_killer_win.cc",
diff --git a/chrome/test/chromedriver/test/test_expectations b/chrome/test/chromedriver/test/test_expectations
index 1dd62f9..b7fbe4f 100644
--- a/chrome/test/chromedriver/test/test_expectations
+++ b/chrome/test/chromedriver/test/test_expectations
@@ -85,8 +85,6 @@
     'WindowTest.testCanMaximizeTheWindow',
     # Flaky: https://bugs.chromium.org/p/chromedriver/issues/detail?id=528
     'PageLoadingTest.testShouldDoNothingIfThereIsNothingToGoBackTo',
-    # https://bugs.chromium.org/p/chromedriver/issues/detail?id=1117
-    'PageLoadingTest.testShouldBeAbleToAccessPagesWithAnInsecureSslCertificate',
     # Flaky: https://bugs.chromium.org/p/chromedriver/issues/detail?id=1113
     'FrameSwitchingTest.testShouldNotSwitchMagicallyToTheTopWindow',
     # Flaky: https://bugs.chromium.org/p/chromedriver/issues/detail?id=1151
@@ -104,8 +102,6 @@
     'AlertsTest.testPromptShouldUseDefaultValueIfNoKeysSent',
     'UnexpectedAlertBehaviorTest.canAcceptUnhandledAlert',
     'UnexpectedAlertBehaviorTest.canSpecifyUnhandledAlertBehaviourUsingCapabilities',
-    # https://bugs.chromium.org/p/chromium/issues/detail?id=764519
-    'FrameSwitchingTest.testShouldBeAbleToClickInAFrameThatRewritesTopWindowLocation',
 ]
 
 _OS_NEGATIVE_FILTER = {}
diff --git a/chrome/test/ppapi/ppapi_browsertest.cc b/chrome/test/ppapi/ppapi_browsertest.cc
index cfd94c0a..12dd3328 100644
--- a/chrome/test/ppapi/ppapi_browsertest.cc
+++ b/chrome/test/ppapi/ppapi_browsertest.cc
@@ -21,6 +21,7 @@
 #include "chrome/test/base/ui_test_utils.h"
 #include "chrome/test/nacl/nacl_browsertest_util.h"
 #include "chrome/test/ppapi/ppapi_test.h"
+#include "chrome/test/ppapi/ppapi_test_select_file_dialog_factory.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/nacl/common/buildflags.h"
 #include "components/nacl/common/nacl_switches.h"
@@ -674,8 +675,25 @@
   RUN_FILEIO_PRIVATE_SUBTESTS;
 }
 
+#define SETUP_FOR_FILEREF_TESTS                                              \
+  const char kContents[] = "Hello from browser";                             \
+  base::ScopedAllowBlockingForTesting allow_blocking;                        \
+  base::ScopedTempDir temp_dir;                                              \
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());                               \
+  base::FilePath existing_filename = temp_dir.GetPath().AppendASCII("foo");  \
+  ASSERT_EQ(                                                                 \
+      static_cast<int>(sizeof(kContents) - 1),                               \
+      base::WriteFile(existing_filename, kContents, sizeof(kContents) - 1)); \
+  PPAPITestSelectFileDialogFactory::SelectedFileInfoList file_info_list;     \
+  file_info_list.push_back(                                                  \
+      ui::SelectedFileInfo(existing_filename, existing_filename));           \
+  PPAPITestSelectFileDialogFactory test_dialog_factory(                      \
+      PPAPITestSelectFileDialogFactory::RESPOND_WITH_FILE_LIST,              \
+      file_info_list);
+
 // FileRef tests.
 #define RUN_FILEREF_SUBTESTS_1 \
+  SETUP_FOR_FILEREF_TESTS \
   RunTestViaHTTP( \
       LIST_TEST(FileRef_Create) \
       LIST_TEST(FileRef_GetFileSystemType) \
@@ -686,6 +704,7 @@
   )
 
 #define RUN_FILEREF_SUBTESTS_2 \
+  SETUP_FOR_FILEREF_TESTS \
   RunTestViaHTTP( \
       LIST_TEST(FileRef_QueryAndTouchFile) \
       LIST_TEST(FileRef_DeleteFileAndDirectory) \
diff --git a/chrome/test/ppapi/ppapi_filechooser_browsertest.cc b/chrome/test/ppapi/ppapi_filechooser_browsertest.cc
index 921d79f3..ca95349 100644
--- a/chrome/test/ppapi/ppapi_filechooser_browsertest.cc
+++ b/chrome/test/ppapi/ppapi_filechooser_browsertest.cc
@@ -12,16 +12,12 @@
 #include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "base/threading/thread_restrictions.h"
-#include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/safe_browsing/download_protection/download_protection_util.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/test/ppapi/ppapi_test.h"
+#include "chrome/test/ppapi/ppapi_test_select_file_dialog_factory.h"
 #include "components/download/quarantine/quarantine.h"
 #include "ppapi/shared_impl/test_utils.h"
-#include "ui/shell_dialogs/select_file_dialog.h"
-#include "ui/shell_dialogs/select_file_dialog_factory.h"
-#include "ui/shell_dialogs/select_file_policy.h"
-#include "ui/shell_dialogs/selected_file_info.h"
 
 #if defined(FULL_SAFE_BROWSING)
 #include "chrome/browser/safe_browsing/download_protection/download_protection_service.h"
@@ -34,108 +30,6 @@
 
 namespace {
 
-class TestSelectFileDialogFactory final : public ui::SelectFileDialogFactory {
- public:
-  using SelectedFileInfoList = std::vector<ui::SelectedFileInfo>;
-
-  enum Mode {
-    RESPOND_WITH_FILE_LIST,
-    CANCEL,
-    REPLACE_BASENAME,
-    NOT_REACHED,
-  };
-
-  TestSelectFileDialogFactory(Mode mode,
-                              const SelectedFileInfoList& selected_file_info)
-      : selected_file_info_(selected_file_info), mode_(mode) {
-    // Only safe because this class is 'final'
-    ui::SelectFileDialog::SetFactory(this);
-  }
-
-  // SelectFileDialogFactory
-  ui::SelectFileDialog* Create(
-      ui::SelectFileDialog::Listener* listener,
-      std::unique_ptr<ui::SelectFilePolicy> policy) override {
-    return new SelectFileDialog(listener, std::move(policy),
-                                selected_file_info_, mode_);
-  }
-
- private:
-  class SelectFileDialog : public ui::SelectFileDialog {
-   public:
-    SelectFileDialog(Listener* listener,
-                     std::unique_ptr<ui::SelectFilePolicy> policy,
-                     const SelectedFileInfoList& selected_file_info,
-                     Mode mode)
-        : ui::SelectFileDialog(listener, std::move(policy)),
-          selected_file_info_(selected_file_info),
-          mode_(mode) {}
-
-   protected:
-    // ui::SelectFileDialog
-    void SelectFileImpl(Type type,
-                        const base::string16& title,
-                        const base::FilePath& default_path,
-                        const FileTypeInfo* file_types,
-                        int file_type_index,
-                        const base::FilePath::StringType& default_extension,
-                        gfx::NativeWindow owning_window,
-                        void* params) override {
-      switch (mode_) {
-        case RESPOND_WITH_FILE_LIST:
-          break;
-
-        case CANCEL:
-          EXPECT_EQ(0u, selected_file_info_.size());
-          break;
-
-        case REPLACE_BASENAME:
-          EXPECT_EQ(1u, selected_file_info_.size());
-          for (auto& selected_file : selected_file_info_) {
-            selected_file =
-                ui::SelectedFileInfo(selected_file.file_path.DirName().Append(
-                                         default_path.BaseName()),
-                                     selected_file.local_path.DirName().Append(
-                                         default_path.BaseName()));
-          }
-          break;
-
-        case NOT_REACHED:
-          ADD_FAILURE() << "Unexpected SelectFileImpl invocation.";
-      }
-
-      base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE,
-          base::Bind(&SelectFileDialog::RespondToFileSelectionRequest, this,
-                     params));
-    }
-    bool HasMultipleFileTypeChoicesImpl() override { return false; }
-
-    // BaseShellDialog
-    bool IsRunning(gfx::NativeWindow owning_window) const override {
-      return false;
-    }
-    void ListenerDestroyed() override {}
-
-   private:
-    void RespondToFileSelectionRequest(void* params) {
-      if (selected_file_info_.size() == 0)
-        listener_->FileSelectionCanceled(params);
-      else if (selected_file_info_.size() == 1)
-        listener_->FileSelectedWithExtraInfo(selected_file_info_.front(), 0,
-                                             params);
-      else
-        listener_->MultiFilesSelectedWithExtraInfo(selected_file_info_, params);
-    }
-
-    SelectedFileInfoList selected_file_info_;
-    Mode mode_;
-  };
-
-  std::vector<ui::SelectedFileInfo> selected_file_info_;
-  Mode mode_;
-};
-
 class PPAPIFileChooserTest : public OutOfProcessPPAPITest {};
 
 #if defined(FULL_SAFE_BROWSING)
@@ -282,18 +176,18 @@
       static_cast<int>(sizeof(kContents) - 1),
       base::WriteFile(existing_filename, kContents, sizeof(kContents) - 1));
 
-  TestSelectFileDialogFactory::SelectedFileInfoList file_info_list;
+  PPAPITestSelectFileDialogFactory::SelectedFileInfoList file_info_list;
   file_info_list.push_back(
       ui::SelectedFileInfo(existing_filename, existing_filename));
-  TestSelectFileDialogFactory test_dialog_factory(
-      TestSelectFileDialogFactory::RESPOND_WITH_FILE_LIST, file_info_list);
+  PPAPITestSelectFileDialogFactory test_dialog_factory(
+      PPAPITestSelectFileDialogFactory::RESPOND_WITH_FILE_LIST, file_info_list);
   RunTestViaHTTP("FileChooser_OpenSimple");
 }
 
 IN_PROC_BROWSER_TEST_F(PPAPIFileChooserTest, FileChooser_Open_Cancel) {
-  TestSelectFileDialogFactory test_dialog_factory(
-      TestSelectFileDialogFactory::CANCEL,
-      TestSelectFileDialogFactory::SelectedFileInfoList());
+  PPAPITestSelectFileDialogFactory test_dialog_factory(
+      PPAPITestSelectFileDialogFactory::CANCEL,
+      PPAPITestSelectFileDialogFactory::SelectedFileInfoList());
   RunTestViaHTTP("FileChooser_OpenCancel");
 }
 
@@ -303,11 +197,11 @@
   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
   base::FilePath suggested_filename = temp_dir.GetPath().AppendASCII("foo");
 
-  TestSelectFileDialogFactory::SelectedFileInfoList file_info_list;
+  PPAPITestSelectFileDialogFactory::SelectedFileInfoList file_info_list;
   file_info_list.push_back(
       ui::SelectedFileInfo(suggested_filename, suggested_filename));
-  TestSelectFileDialogFactory test_dialog_factory(
-      TestSelectFileDialogFactory::RESPOND_WITH_FILE_LIST, file_info_list);
+  PPAPITestSelectFileDialogFactory test_dialog_factory(
+      PPAPITestSelectFileDialogFactory::RESPOND_WITH_FILE_LIST, file_info_list);
 
   RunTestViaHTTP("FileChooser_SaveAsSafeDefaultName");
   ASSERT_TRUE(base::PathExists(suggested_filename));
@@ -320,11 +214,11 @@
   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
   base::FilePath suggested_filename = temp_dir.GetPath().AppendASCII("foo");
 
-  TestSelectFileDialogFactory::SelectedFileInfoList file_info_list;
+  PPAPITestSelectFileDialogFactory::SelectedFileInfoList file_info_list;
   file_info_list.push_back(
       ui::SelectedFileInfo(suggested_filename, suggested_filename));
-  TestSelectFileDialogFactory test_dialog_factory(
-      TestSelectFileDialogFactory::REPLACE_BASENAME, file_info_list);
+  PPAPITestSelectFileDialogFactory test_dialog_factory(
+      PPAPITestSelectFileDialogFactory::REPLACE_BASENAME, file_info_list);
 
   RunTestViaHTTP("FileChooser_SaveAsSafeDefaultName");
   base::FilePath actual_filename =
@@ -343,11 +237,11 @@
   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
   base::FilePath suggested_filename = temp_dir.GetPath().AppendASCII("foo");
 
-  TestSelectFileDialogFactory::SelectedFileInfoList file_info_list;
+  PPAPITestSelectFileDialogFactory::SelectedFileInfoList file_info_list;
   file_info_list.push_back(
       ui::SelectedFileInfo(suggested_filename, suggested_filename));
-  TestSelectFileDialogFactory test_dialog_factory(
-      TestSelectFileDialogFactory::REPLACE_BASENAME, file_info_list);
+  PPAPITestSelectFileDialogFactory test_dialog_factory(
+      PPAPITestSelectFileDialogFactory::REPLACE_BASENAME, file_info_list);
 
   RunTestViaHTTP("FileChooser_SaveAsUnsafeDefaultName");
   base::FilePath actual_filename =
@@ -360,9 +254,9 @@
 }
 
 IN_PROC_BROWSER_TEST_F(PPAPIFileChooserTest, FileChooser_SaveAs_Cancel) {
-  TestSelectFileDialogFactory test_dialog_factory(
-      TestSelectFileDialogFactory::CANCEL,
-      TestSelectFileDialogFactory::SelectedFileInfoList());
+  PPAPITestSelectFileDialogFactory test_dialog_factory(
+      PPAPITestSelectFileDialogFactory::CANCEL,
+      PPAPITestSelectFileDialogFactory::SelectedFileInfoList());
   RunTestViaHTTP("FileChooser_SaveAsCancel");
 }
 
@@ -378,11 +272,11 @@
   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
   base::FilePath suggested_filename = temp_dir.GetPath().AppendASCII("foo");
 
-  TestSelectFileDialogFactory::SelectedFileInfoList file_info_list;
+  PPAPITestSelectFileDialogFactory::SelectedFileInfoList file_info_list;
   file_info_list.push_back(
       ui::SelectedFileInfo(suggested_filename, suggested_filename));
-  TestSelectFileDialogFactory test_dialog_factory(
-      TestSelectFileDialogFactory::REPLACE_BASENAME, file_info_list);
+  PPAPITestSelectFileDialogFactory test_dialog_factory(
+      PPAPITestSelectFileDialogFactory::REPLACE_BASENAME, file_info_list);
 
   RunTestViaHTTP("FileChooser_SaveAsDangerousExecutableAllowed");
   base::FilePath actual_filename =
@@ -411,11 +305,11 @@
   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
   base::FilePath suggested_filename = temp_dir.GetPath().AppendASCII("foo");
 
-  TestSelectFileDialogFactory::SelectedFileInfoList file_info_list;
+  PPAPITestSelectFileDialogFactory::SelectedFileInfoList file_info_list;
   file_info_list.push_back(
       ui::SelectedFileInfo(suggested_filename, suggested_filename));
-  TestSelectFileDialogFactory test_dialog_factory(
-      TestSelectFileDialogFactory::REPLACE_BASENAME, file_info_list);
+  PPAPITestSelectFileDialogFactory test_dialog_factory(
+      PPAPITestSelectFileDialogFactory::REPLACE_BASENAME, file_info_list);
 
   RunTestViaHTTP("FileChooser_SaveAsDangerousExecutableAllowed");
   base::FilePath actual_filename =
@@ -435,9 +329,9 @@
       std::make_pair(base::FilePath::StringType(FILE_PATH_LITERAL(".exe")),
                      safe_browsing::DownloadCheckResult::DANGEROUS));
 
-  TestSelectFileDialogFactory test_dialog_factory(
-      TestSelectFileDialogFactory::NOT_REACHED,
-      TestSelectFileDialogFactory::SelectedFileInfoList());
+  PPAPITestSelectFileDialogFactory test_dialog_factory(
+      PPAPITestSelectFileDialogFactory::NOT_REACHED,
+      PPAPITestSelectFileDialogFactory::SelectedFileInfoList());
   RunTestViaHTTP("FileChooser_SaveAsDangerousExecutableDisallowed");
 }
 
@@ -449,9 +343,9 @@
       std::make_pair(base::FilePath::StringType(FILE_PATH_LITERAL(".exe")),
                      safe_browsing::DownloadCheckResult::DANGEROUS));
 
-  TestSelectFileDialogFactory test_dialog_factory(
-      TestSelectFileDialogFactory::NOT_REACHED,
-      TestSelectFileDialogFactory::SelectedFileInfoList());
+  PPAPITestSelectFileDialogFactory test_dialog_factory(
+      PPAPITestSelectFileDialogFactory::NOT_REACHED,
+      PPAPITestSelectFileDialogFactory::SelectedFileInfoList());
   RunTestViaHTTP("FileChooser_SaveAsDangerousExtensionListDisallowed");
 }
 
@@ -470,11 +364,11 @@
   safe_browsing_test_configuration_.default_result =
       safe_browsing::DownloadCheckResult::DANGEROUS;
 
-  TestSelectFileDialogFactory::SelectedFileInfoList file_info_list;
+  PPAPITestSelectFileDialogFactory::SelectedFileInfoList file_info_list;
   file_info_list.push_back(
       ui::SelectedFileInfo(existing_filename, existing_filename));
-  TestSelectFileDialogFactory test_dialog_factory(
-      TestSelectFileDialogFactory::RESPOND_WITH_FILE_LIST, file_info_list);
+  PPAPITestSelectFileDialogFactory test_dialog_factory(
+      PPAPITestSelectFileDialogFactory::RESPOND_WITH_FILE_LIST, file_info_list);
   RunTestViaHTTP("FileChooser_OpenSimple");
 }
 
diff --git a/chrome/test/ppapi/ppapi_test.cc b/chrome/test/ppapi/ppapi_test.cc
index 4cca389..4d3001d 100644
--- a/chrome/test/ppapi/ppapi_test.cc
+++ b/chrome/test/ppapi/ppapi_test.cc
@@ -135,13 +135,6 @@
 }
 
 void PPAPITestBase::SetUp() {
-  // TODO(mek): Migrate the FileRef tests to no longer depend on StreamToFile.
-  if (base::StartsWith(
-          ::testing::UnitTest::GetInstance()->current_test_info()->name(),
-          "FileRef", base::CompareCase::SENSITIVE)) {
-    scoped_feature_list_.InitAndEnableFeature(ppapi::features::kStreamToFile);
-  }
-
   EnablePixelOutput();
   InProcessBrowserTest::SetUp();
 }
diff --git a/chrome/test/ppapi/ppapi_test_select_file_dialog_factory.cc b/chrome/test/ppapi/ppapi_test_select_file_dialog_factory.cc
new file mode 100644
index 0000000..e9118bb
--- /dev/null
+++ b/chrome/test/ppapi/ppapi_test_select_file_dialog_factory.cc
@@ -0,0 +1,103 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/test/ppapi/ppapi_test_select_file_dialog_factory.h"
+
+#include "base/bind.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/shell_dialogs/select_file_dialog.h"
+
+namespace {
+
+class PPAPITestSelectFileDialog : public ui::SelectFileDialog {
+ public:
+  PPAPITestSelectFileDialog(
+      Listener* listener,
+      std::unique_ptr<ui::SelectFilePolicy> policy,
+      const PPAPITestSelectFileDialogFactory::SelectedFileInfoList&
+          selected_file_info,
+      PPAPITestSelectFileDialogFactory::Mode mode)
+      : ui::SelectFileDialog(listener, std::move(policy)),
+        selected_file_info_(selected_file_info),
+        mode_(mode) {}
+
+ protected:
+  // ui::SelectFileDialog
+  void SelectFileImpl(Type type,
+                      const base::string16& title,
+                      const base::FilePath& default_path,
+                      const FileTypeInfo* file_types,
+                      int file_type_index,
+                      const base::FilePath::StringType& default_extension,
+                      gfx::NativeWindow owning_window,
+                      void* params) override {
+    switch (mode_) {
+      case PPAPITestSelectFileDialogFactory::RESPOND_WITH_FILE_LIST:
+        break;
+
+      case PPAPITestSelectFileDialogFactory::CANCEL:
+        EXPECT_EQ(0u, selected_file_info_.size());
+        break;
+
+      case PPAPITestSelectFileDialogFactory::REPLACE_BASENAME:
+        EXPECT_EQ(1u, selected_file_info_.size());
+        for (auto& selected_file : selected_file_info_) {
+          selected_file = ui::SelectedFileInfo(
+              selected_file.file_path.DirName().Append(default_path.BaseName()),
+              selected_file.local_path.DirName().Append(
+                  default_path.BaseName()));
+        }
+        break;
+
+      case PPAPITestSelectFileDialogFactory::NOT_REACHED:
+        ADD_FAILURE() << "Unexpected SelectFileImpl invocation.";
+    }
+
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE,
+        base::BindOnce(
+            &PPAPITestSelectFileDialog::RespondToFileSelectionRequest, this,
+            params));
+  }
+  bool HasMultipleFileTypeChoicesImpl() override { return false; }
+
+  // BaseShellDialog
+  bool IsRunning(gfx::NativeWindow owning_window) const override {
+    return false;
+  }
+  void ListenerDestroyed() override {}
+
+ private:
+  void RespondToFileSelectionRequest(void* params) {
+    if (selected_file_info_.size() == 0)
+      listener_->FileSelectionCanceled(params);
+    else if (selected_file_info_.size() == 1)
+      listener_->FileSelectedWithExtraInfo(selected_file_info_.front(), 0,
+                                           params);
+    else
+      listener_->MultiFilesSelectedWithExtraInfo(selected_file_info_, params);
+  }
+
+  PPAPITestSelectFileDialogFactory::SelectedFileInfoList selected_file_info_;
+  PPAPITestSelectFileDialogFactory::Mode mode_;
+};
+
+}  // namespace
+
+PPAPITestSelectFileDialogFactory::PPAPITestSelectFileDialogFactory(
+    Mode mode,
+    const SelectedFileInfoList& selected_file_info)
+    : selected_file_info_(selected_file_info), mode_(mode) {
+  // Only safe because this class is 'final'
+  ui::SelectFileDialog::SetFactory(this);
+}
+
+// SelectFileDialogFactory
+ui::SelectFileDialog* PPAPITestSelectFileDialogFactory::Create(
+    ui::SelectFileDialog::Listener* listener,
+    std::unique_ptr<ui::SelectFilePolicy> policy) {
+  return new PPAPITestSelectFileDialog(listener, std::move(policy),
+                                       selected_file_info_, mode_);
+}
diff --git a/chrome/test/ppapi/ppapi_test_select_file_dialog_factory.h b/chrome/test/ppapi/ppapi_test_select_file_dialog_factory.h
new file mode 100644
index 0000000..91a8858
--- /dev/null
+++ b/chrome/test/ppapi/ppapi_test_select_file_dialog_factory.h
@@ -0,0 +1,37 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_TEST_PPAPI_PPAPI_TEST_SELECT_FILE_DIALOG_FACTORY_H_
+#define CHROME_TEST_PPAPI_PPAPI_TEST_SELECT_FILE_DIALOG_FACTORY_H_
+
+#include "ui/shell_dialogs/select_file_dialog_factory.h"
+#include "ui/shell_dialogs/select_file_policy.h"
+#include "ui/shell_dialogs/selected_file_info.h"
+
+class PPAPITestSelectFileDialogFactory final
+    : public ui::SelectFileDialogFactory {
+ public:
+  using SelectedFileInfoList = std::vector<ui::SelectedFileInfo>;
+
+  enum Mode {
+    RESPOND_WITH_FILE_LIST,
+    CANCEL,
+    REPLACE_BASENAME,
+    NOT_REACHED,
+  };
+
+  PPAPITestSelectFileDialogFactory(
+      Mode mode,
+      const SelectedFileInfoList& selected_file_info);
+  // SelectFileDialogFactory
+  ui::SelectFileDialog* Create(
+      ui::SelectFileDialog::Listener* listener,
+      std::unique_ptr<ui::SelectFilePolicy> policy) override;
+
+ private:
+  std::vector<ui::SelectedFileInfo> selected_file_info_;
+  Mode mode_;
+};
+
+#endif  // CHROME_TEST_PPAPI_PPAPI_TEST_SELECT_FILE_DIALOG_FACTORY_H_
diff --git a/chromeos/services/device_sync/BUILD.gn b/chromeos/services/device_sync/BUILD.gn
index 38901258..c61a797 100644
--- a/chromeos/services/device_sync/BUILD.gn
+++ b/chromeos/services/device_sync/BUILD.gn
@@ -16,6 +16,8 @@
     "cryptauth_enroller_factory_impl.h",
     "cryptauth_token_fetcher_impl.cc",
     "cryptauth_token_fetcher_impl.h",
+    "device_sync_base.cc",
+    "device_sync_base.h",
     "device_sync_impl.cc",
     "device_sync_impl.h",
     "device_sync_service.cc",
@@ -50,6 +52,8 @@
   testonly = true
 
   sources = [
+    "fake_device_sync.cc",
+    "fake_device_sync.h",
     "fake_device_sync_observer.cc",
     "fake_device_sync_observer.h",
   ]
@@ -61,6 +65,7 @@
   deps = [
     ":device_sync",
     "//base",
+    "//chromeos/services/device_sync/public/cpp",
     "//chromeos/services/device_sync/public/mojom",
     "//testing/gtest",
   ]
@@ -80,6 +85,7 @@
     "//base",
     "//base/test:test_support",
     "//chromeos",
+    "//chromeos/services/device_sync/public/cpp:unit_tests",
     "//chromeos/services/device_sync/public/mojom",
     "//chromeos/services/device_sync/public/mojom:unit_tests",
     "//components/cryptauth",
diff --git a/chromeos/services/device_sync/device_sync_base.cc b/chromeos/services/device_sync/device_sync_base.cc
new file mode 100644
index 0000000..4866204
--- /dev/null
+++ b/chromeos/services/device_sync/device_sync_base.cc
@@ -0,0 +1,36 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/services/device_sync/device_sync_base.h"
+
+namespace chromeos {
+
+namespace device_sync {
+
+DeviceSyncBase::DeviceSyncBase() = default;
+
+DeviceSyncBase::~DeviceSyncBase() = default;
+
+void DeviceSyncBase::AddObserver(mojom::DeviceSyncObserverPtr observer,
+                                 AddObserverCallback callback) {
+  observers_.AddPtr(std::move(observer));
+  std::move(callback).Run();
+}
+
+void DeviceSyncBase::BindRequest(mojom::DeviceSyncRequest request) {
+  bindings_.AddBinding(this, std::move(request));
+}
+
+void DeviceSyncBase::NotifyOnEnrollmentFinished() {
+  observers_.ForAllPtrs(
+      [](auto* observer) { observer->OnEnrollmentFinished(); });
+}
+
+void DeviceSyncBase::NotifyOnNewDevicesSynced() {
+  observers_.ForAllPtrs([](auto* observer) { observer->OnNewDevicesSynced(); });
+}
+
+}  // namespace device_sync
+
+}  // namespace chromeos
\ No newline at end of file
diff --git a/chromeos/services/device_sync/device_sync_base.h b/chromeos/services/device_sync/device_sync_base.h
new file mode 100644
index 0000000..617b477
--- /dev/null
+++ b/chromeos/services/device_sync/device_sync_base.h
@@ -0,0 +1,48 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_SERVICES_DEVICE_SYNC_DEVICE_SYNC_BASE_H_
+#define CHROMEOS_SERVICES_DEVICE_SYNC_DEVICE_SYNC_BASE_H_
+
+#include "base/macros.h"
+#include "chromeos/services/device_sync/public/mojom/device_sync.mojom.h"
+#include "components/signin/core/browser/account_info.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/interface_ptr_set.h"
+
+namespace chromeos {
+
+namespace device_sync {
+
+// Base DeviceSync implementation.
+class DeviceSyncBase : public mojom::DeviceSync {
+ public:
+  ~DeviceSyncBase() override;
+
+  // mojom::DeviceSync:
+  void AddObserver(mojom::DeviceSyncObserverPtr observer,
+                   AddObserverCallback callback) override;
+
+  // Binds a request to this implementation. Should be called each time that the
+  // service receives a request.
+  void BindRequest(mojom::DeviceSyncRequest request);
+
+ protected:
+  DeviceSyncBase();
+
+  void NotifyOnEnrollmentFinished();
+  void NotifyOnNewDevicesSynced();
+
+ private:
+  mojo::InterfacePtrSet<mojom::DeviceSyncObserver> observers_;
+  mojo::BindingSet<mojom::DeviceSync> bindings_;
+
+  DISALLOW_COPY_AND_ASSIGN(DeviceSyncBase);
+};
+
+}  // namespace device_sync
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_SERVICES_DEVICE_SYNC_DEVICE_SYNC_BASE_H_
\ No newline at end of file
diff --git a/chromeos/services/device_sync/device_sync_impl.cc b/chromeos/services/device_sync/device_sync_impl.cc
index 949f95fd..b9839b3 100644
--- a/chromeos/services/device_sync/device_sync_impl.cc
+++ b/chromeos/services/device_sync/device_sync_impl.cc
@@ -45,7 +45,7 @@
     nullptr;
 
 // static
-std::unique_ptr<DeviceSyncImpl> DeviceSyncImpl::Factory::NewInstance(
+std::unique_ptr<DeviceSyncBase> DeviceSyncImpl::Factory::NewInstance(
     identity::IdentityManager* identity_manager,
     gcm::GCMDriver* gcm_driver,
     service_manager::Connector* connector,
@@ -70,7 +70,7 @@
 
 DeviceSyncImpl::Factory::~Factory() = default;
 
-std::unique_ptr<DeviceSyncImpl> DeviceSyncImpl::Factory::BuildInstance(
+std::unique_ptr<DeviceSyncBase> DeviceSyncImpl::Factory::BuildInstance(
     identity::IdentityManager* identity_manager,
     gcm::GCMDriver* gcm_driver,
     service_manager::Connector* connector,
@@ -128,16 +128,6 @@
     remote_device_provider_->RemoveObserver(this);
 }
 
-void DeviceSyncImpl::BindRequest(mojom::DeviceSyncRequest request) {
-  bindings_.AddBinding(this, std::move(request));
-}
-
-void DeviceSyncImpl::AddObserver(mojom::DeviceSyncObserverPtr observer,
-                                 AddObserverCallback callback) {
-  observers_.AddPtr(std::move(observer));
-  std::move(callback).Run();
-}
-
 void DeviceSyncImpl::ForceEnrollmentNow(ForceEnrollmentNowCallback callback) {
   if (status_ != Status::READY) {
     PA_LOG(WARNING) << "DeviceSyncImpl::ForceEnrollmentNow() invoked before "
@@ -260,14 +250,13 @@
   if (status_ == Status::WAITING_FOR_ENROLLMENT)
     CompleteInitializationAfterSuccessfulEnrollment();
 
-  observers_.ForAllPtrs(
-      [](auto* observer) { observer->OnEnrollmentFinished(); });
+  NotifyOnEnrollmentFinished();
 }
 
 void DeviceSyncImpl::OnSyncDeviceListChanged() {
   PA_LOG(INFO) << "DeviceSyncImpl: Synced devices changed; notifying "
                << "observers.";
-  observers_.ForAllPtrs([](auto* observer) { observer->OnNewDevicesSynced(); });
+  NotifyOnNewDevicesSynced();
 }
 
 void DeviceSyncImpl::ProcessPrimaryAccountInfo(
diff --git a/chromeos/services/device_sync/device_sync_impl.h b/chromeos/services/device_sync/device_sync_impl.h
index 145c5c9..b9117f4e 100644
--- a/chromeos/services/device_sync/device_sync_impl.h
+++ b/chromeos/services/device_sync/device_sync_impl.h
@@ -8,13 +8,12 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
+#include "chromeos/services/device_sync/device_sync_base.h"
 #include "chromeos/services/device_sync/public/mojom/device_sync.mojom.h"
 #include "components/cryptauth/cryptauth_enrollment_manager.h"
 #include "components/cryptauth/cryptauth_gcm_manager.h"
 #include "components/cryptauth/remote_device_provider.h"
 #include "components/signin/core/browser/account_info.h"
-#include "mojo/public/cpp/bindings/binding_set.h"
-#include "mojo/public/cpp/bindings/interface_ptr_set.h"
 #include "services/preferences/public/cpp/pref_service_factory.h"
 #include "services/preferences/public/mojom/preferences.mojom.h"
 
@@ -58,13 +57,13 @@
 // (3) Instantiate classes which communicate with the CryptAuth back-end.
 // (4) Check enrollment state; if not yet enrolled, enroll the device.
 // (5) When enrollment is valid, listen for device sync updates.
-class DeviceSyncImpl : public mojom::DeviceSync,
+class DeviceSyncImpl : public DeviceSyncBase,
                        public cryptauth::CryptAuthEnrollmentManager::Observer,
                        public cryptauth::RemoteDeviceProvider::Observer {
  public:
   class Factory {
    public:
-    static std::unique_ptr<DeviceSyncImpl> NewInstance(
+    static std::unique_ptr<DeviceSyncBase> NewInstance(
         identity::IdentityManager* identity_manager,
         gcm::GCMDriver* gcm_driver,
         service_manager::Connector* connector,
@@ -73,7 +72,7 @@
     static void SetInstanceForTesting(Factory* test_factory);
 
     virtual ~Factory();
-    virtual std::unique_ptr<DeviceSyncImpl> BuildInstance(
+    virtual std::unique_ptr<DeviceSyncBase> BuildInstance(
         identity::IdentityManager* identity_manager,
         gcm::GCMDriver* gcm_driver,
         service_manager::Connector* connector,
@@ -86,14 +85,8 @@
 
   ~DeviceSyncImpl() override;
 
-  // Binds a request to this implementation. Should be called each time that the
-  // service receives a request.
-  void BindRequest(mojom::DeviceSyncRequest request);
-
  protected:
   // mojom::DeviceSync:
-  void AddObserver(mojom::DeviceSyncObserverPtr observer,
-                   AddObserverCallback callback) override;
   void ForceEnrollmentNow(ForceEnrollmentNowCallback callback) override;
   void ForceSyncNow(ForceSyncNowCallback callback) override;
   void GetLocalDeviceMetadata(GetLocalDeviceMetadataCallback callback) override;
@@ -198,9 +191,6 @@
   std::unique_ptr<cryptauth::RemoteDeviceProvider> remote_device_provider_;
   std::unique_ptr<cryptauth::SoftwareFeatureManager> software_feature_manager_;
 
-  mojo::InterfacePtrSet<mojom::DeviceSyncObserver> observers_;
-  mojo::BindingSet<mojom::DeviceSync> bindings_;
-
   base::WeakPtrFactory<DeviceSyncImpl> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(DeviceSyncImpl);
diff --git a/chromeos/services/device_sync/device_sync_service.cc b/chromeos/services/device_sync/device_sync_service.cc
index b49d5a8a..8241171d 100644
--- a/chromeos/services/device_sync/device_sync_service.cc
+++ b/chromeos/services/device_sync/device_sync_service.cc
@@ -5,6 +5,7 @@
 #include "chromeos/services/device_sync/device_sync_service.h"
 
 #include "chromeos/components/proximity_auth/logging/logging.h"
+#include "chromeos/services/device_sync/device_sync_base.h"
 #include "chromeos/services/device_sync/device_sync_impl.h"
 #include "net/url_request/url_request_context_getter.h"
 #include "services/service_manager/public/cpp/service_context.h"
@@ -30,12 +31,12 @@
 
   // context() cannot be invoked until after the constructor is run, so
   // |device_sync_impl_| cannot be initialized until OnStart().
-  device_sync_impl_ = DeviceSyncImpl::Factory::NewInstance(
+  device_sync_ = DeviceSyncImpl::Factory::NewInstance(
       identity_manager_, gcm_driver_, context()->connector(),
       gcm_device_info_provider_, url_request_context_);
 
-  registry_.AddInterface(base::Bind(&DeviceSyncImpl::BindRequest,
-                                    base::Unretained(device_sync_impl_.get())));
+  registry_.AddInterface(base::Bind(&DeviceSyncBase::BindRequest,
+                                    base::Unretained(device_sync_.get())));
 }
 
 void DeviceSyncService::OnBindInterface(
diff --git a/chromeos/services/device_sync/device_sync_service.h b/chromeos/services/device_sync/device_sync_service.h
index eb97850a..78546e1 100644
--- a/chromeos/services/device_sync/device_sync_service.h
+++ b/chromeos/services/device_sync/device_sync_service.h
@@ -32,7 +32,7 @@
 
 namespace device_sync {
 
-class DeviceSyncImpl;
+class DeviceSyncBase;
 
 // Service which provides an implementation for
 // device_sync::mojom::DeviceSync. This service creates one
@@ -59,7 +59,7 @@
   cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider_;
   scoped_refptr<net::URLRequestContextGetter> url_request_context_;
 
-  std::unique_ptr<DeviceSyncImpl> device_sync_impl_;
+  std::unique_ptr<DeviceSyncBase> device_sync_;
   service_manager::BinderRegistry registry_;
 
   DISALLOW_COPY_AND_ASSIGN(DeviceSyncService);
diff --git a/chromeos/services/device_sync/device_sync_service_unittest.cc b/chromeos/services/device_sync/device_sync_service_unittest.cc
index 80a4990..21219c64 100644
--- a/chromeos/services/device_sync/device_sync_service_unittest.cc
+++ b/chromeos/services/device_sync/device_sync_service_unittest.cc
@@ -416,7 +416,7 @@
     ~FakeDeviceSyncImplFactory() override = default;
 
     // DeviceSyncImpl::Factory:
-    std::unique_ptr<DeviceSyncImpl> BuildInstance(
+    std::unique_ptr<DeviceSyncBase> BuildInstance(
         identity::IdentityManager* identity_manager,
         gcm::GCMDriver* gcm_driver,
         service_manager::Connector* connector,
diff --git a/chromeos/services/device_sync/fake_device_sync.cc b/chromeos/services/device_sync/fake_device_sync.cc
new file mode 100644
index 0000000..cb66e22
--- /dev/null
+++ b/chromeos/services/device_sync/fake_device_sync.cc
@@ -0,0 +1,92 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/services/device_sync/fake_device_sync.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/optional.h"
+#include "components/cryptauth/remote_device_ref.h"
+
+namespace chromeos {
+
+namespace device_sync {
+
+FakeDeviceSync::FakeDeviceSync() = default;
+
+FakeDeviceSync::~FakeDeviceSync() = default;
+
+void FakeDeviceSync::InvokePendingGetLocalDeviceMetadataCallback(
+    const base::Optional<cryptauth::RemoteDevice>& local_device_metadata) {
+  std::move(get_local_device_metadata_callback_queue_.front())
+      .Run(local_device_metadata);
+  get_local_device_metadata_callback_queue_.pop();
+}
+
+void FakeDeviceSync::InvokePendingGetSyncedDevicesCallback(
+    const base::Optional<std::vector<cryptauth::RemoteDevice>>&
+        remote_devices) {
+  std::move(get_synced_devices_callback_queue_.front()).Run(remote_devices);
+  get_synced_devices_callback_queue_.pop();
+}
+
+void FakeDeviceSync::InvokePendingSetSoftwareFeatureStateCallback(
+    const base::Optional<std::string>& error_code) {
+  std::move(set_software_feature_state_callback_queue_.front()).Run(error_code);
+  set_software_feature_state_callback_queue_.pop();
+}
+
+void FakeDeviceSync::InvokePendingFindEligibleDevicesCallback(
+    const base::Optional<std::string>& error_code,
+    mojom::FindEligibleDevicesResponsePtr find_eligible_devices_response_ptr) {
+  std::move(find_eligible_devices_callback_queue_.front())
+      .Run(error_code, std::move(find_eligible_devices_response_ptr));
+  find_eligible_devices_callback_queue_.pop();
+}
+
+void FakeDeviceSync::InvokePendingGetDebugInfoCallback(
+    mojom::DebugInfoPtr debug_info_ptr) {
+  std::move(get_debug_info_callback_queue_.front())
+      .Run(std::move(debug_info_ptr));
+  get_debug_info_callback_queue_.pop();
+}
+
+void FakeDeviceSync::ForceEnrollmentNow(ForceEnrollmentNowCallback callback) {
+  std::move(callback).Run(force_enrollment_now_completed_success_);
+}
+
+void FakeDeviceSync::ForceSyncNow(ForceSyncNowCallback callback) {
+  std::move(callback).Run(force_sync_now_completed_success_);
+}
+
+void FakeDeviceSync::GetLocalDeviceMetadata(
+    GetLocalDeviceMetadataCallback callback) {
+  get_local_device_metadata_callback_queue_.push(std::move(callback));
+}
+
+void FakeDeviceSync::GetSyncedDevices(GetSyncedDevicesCallback callback) {
+  get_synced_devices_callback_queue_.push(std::move(callback));
+}
+
+void FakeDeviceSync::SetSoftwareFeatureState(
+    const std::string& device_public_key,
+    cryptauth::SoftwareFeature software_feature,
+    bool enabled,
+    bool is_exclusive,
+    SetSoftwareFeatureStateCallback callback) {
+  set_software_feature_state_callback_queue_.push(std::move(callback));
+}
+
+void FakeDeviceSync::FindEligibleDevices(
+    cryptauth::SoftwareFeature software_feature,
+    FindEligibleDevicesCallback callback) {
+  find_eligible_devices_callback_queue_.push(std::move(callback));
+}
+
+void FakeDeviceSync::GetDebugInfo(GetDebugInfoCallback callback) {
+  get_debug_info_callback_queue_.push(std::move(callback));
+}
+
+}  // namespace device_sync
+
+}  // namespace chromeos
diff --git a/chromeos/services/device_sync/fake_device_sync.h b/chromeos/services/device_sync/fake_device_sync.h
new file mode 100644
index 0000000..323a420
--- /dev/null
+++ b/chromeos/services/device_sync/fake_device_sync.h
@@ -0,0 +1,84 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_SERVICES_DEVICE_SYNC_FAKE_DEVICE_SYNC_H_
+#define CHROMEOS_SERVICES_DEVICE_SYNC_FAKE_DEVICE_SYNC_H_
+
+#include <queue>
+
+#include "base/macros.h"
+#include "chromeos/services/device_sync/device_sync_base.h"
+
+namespace chromeos {
+
+namespace device_sync {
+
+// Test double DeviceSync implementation.
+class FakeDeviceSync : public DeviceSyncBase {
+ public:
+  FakeDeviceSync();
+  ~FakeDeviceSync() override;
+
+  using DeviceSyncBase::NotifyOnEnrollmentFinished;
+  using DeviceSyncBase::NotifyOnNewDevicesSynced;
+
+  void set_force_enrollment_now_completed_success(
+      bool force_enrollment_now_completed_success) {
+    force_enrollment_now_completed_success_ =
+        force_enrollment_now_completed_success;
+  }
+
+  void set_force_sync_now_completed_success(
+      bool force_sync_now_completed_success) {
+    force_sync_now_completed_success_ = force_sync_now_completed_success;
+  }
+
+  void InvokePendingGetLocalDeviceMetadataCallback(
+      const base::Optional<cryptauth::RemoteDevice>& local_device_metadata);
+  void InvokePendingGetSyncedDevicesCallback(
+      const base::Optional<std::vector<cryptauth::RemoteDevice>>&
+          remote_devices);
+  void InvokePendingSetSoftwareFeatureStateCallback(
+      const base::Optional<std::string>& error_code);
+  void InvokePendingFindEligibleDevicesCallback(
+      const base::Optional<std::string>& error_code,
+      mojom::FindEligibleDevicesResponsePtr find_eligible_devices_response_ptr);
+  void InvokePendingGetDebugInfoCallback(mojom::DebugInfoPtr debug_info_ptr);
+
+ protected:
+  // mojom::DeviceSync:
+  void ForceEnrollmentNow(ForceEnrollmentNowCallback callback) override;
+  void ForceSyncNow(ForceSyncNowCallback callback) override;
+  void GetLocalDeviceMetadata(GetLocalDeviceMetadataCallback callback) override;
+  void GetSyncedDevices(GetSyncedDevicesCallback callback) override;
+  void SetSoftwareFeatureState(
+      const std::string& device_public_key,
+      cryptauth::SoftwareFeature software_feature,
+      bool enabled,
+      bool is_exclusive,
+      SetSoftwareFeatureStateCallback callback) override;
+  void FindEligibleDevices(cryptauth::SoftwareFeature software_feature,
+                           FindEligibleDevicesCallback callback) override;
+  void GetDebugInfo(GetDebugInfoCallback callback) override;
+
+ private:
+  bool force_enrollment_now_completed_success_ = true;
+  bool force_sync_now_completed_success_ = true;
+
+  std::queue<GetLocalDeviceMetadataCallback>
+      get_local_device_metadata_callback_queue_;
+  std::queue<GetSyncedDevicesCallback> get_synced_devices_callback_queue_;
+  std::queue<SetSoftwareFeatureStateCallback>
+      set_software_feature_state_callback_queue_;
+  std::queue<FindEligibleDevicesCallback> find_eligible_devices_callback_queue_;
+  std::queue<GetDebugInfoCallback> get_debug_info_callback_queue_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeDeviceSync);
+};
+
+}  // namespace device_sync
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_SERVICES_DEVICE_SYNC_FAKE_DEVICE_SYNC_H_
diff --git a/chromeos/services/device_sync/public/cpp/BUILD.gn b/chromeos/services/device_sync/public/cpp/BUILD.gn
new file mode 100644
index 0000000..e669708a0
--- /dev/null
+++ b/chromeos/services/device_sync/public/cpp/BUILD.gn
@@ -0,0 +1,65 @@
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("cpp") {
+  sources = [
+    "device_sync_client.cc",
+    "device_sync_client.h",
+    "device_sync_client_impl.cc",
+    "device_sync_client_impl.h",
+  ]
+
+  public_deps = [
+    "//base",
+    "//chromeos/components/proximity_auth/logging",
+    "//chromeos/services/device_sync/public/mojom",
+    "//services/service_manager/public/cpp",
+  ]
+
+  deps = [
+    "//mojo/public/cpp/bindings",
+  ]
+}
+
+static_library("test_support") {
+  testonly = true
+
+  sources = [
+    "fake_device_sync_client.cc",
+    "fake_device_sync_client.h",
+  ]
+
+  public_deps = [
+    ":cpp",
+  ]
+
+  deps = [
+    "//base",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+
+  sources = [
+    "device_sync_client_impl_unittest.cc",
+  ]
+
+  deps = [
+    ":cpp",
+    ":test_support",
+    "//base",
+    "//base/test:test_support",
+    "//chromeos/services/device_sync",
+    "//chromeos/services/device_sync:test_support",
+    "//components/cryptauth",
+    "//components/cryptauth:test_support",
+    "//components/gcm_driver:test_support",
+    "//net",
+    "//services/identity/public/cpp:test_support",
+    "//services/service_manager/public/cpp:service_test_support",
+    "//services/service_manager/public/cpp/test:test_support",
+    "//testing/gtest",
+  ]
+}
diff --git a/chromeos/services/device_sync/public/cpp/device_sync_client.cc b/chromeos/services/device_sync/public/cpp/device_sync_client.cc
new file mode 100644
index 0000000..b4984f9
--- /dev/null
+++ b/chromeos/services/device_sync/public/cpp/device_sync_client.cc
@@ -0,0 +1,35 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/services/device_sync/public/cpp/device_sync_client.h"
+
+namespace chromeos {
+
+namespace device_sync {
+
+DeviceSyncClient::DeviceSyncClient() = default;
+
+DeviceSyncClient::~DeviceSyncClient() = default;
+
+void DeviceSyncClient::AddObserver(Observer* observer) {
+  observer_list_.AddObserver(observer);
+}
+
+void DeviceSyncClient::RemoveObserver(Observer* observer) {
+  observer_list_.RemoveObserver(observer);
+}
+
+void DeviceSyncClient::NotifyEnrollmentFinished() {
+  for (auto& observer : observer_list_)
+    observer.OnEnrollmentFinished();
+}
+
+void DeviceSyncClient::NotifyNewDevicesSynced() {
+  for (auto& observer : observer_list_)
+    observer.OnNewDevicesSynced();
+}
+
+}  // namespace device_sync
+
+}  // namespace chromeos
\ No newline at end of file
diff --git a/chromeos/services/device_sync/public/cpp/device_sync_client.h b/chromeos/services/device_sync/public/cpp/device_sync_client.h
new file mode 100644
index 0000000..64797a1
--- /dev/null
+++ b/chromeos/services/device_sync/public/cpp/device_sync_client.h
@@ -0,0 +1,76 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_SERVICES_DEVICE_SYNC_PUBLIC_CPP_DEVICE_SYNC_CLIENT_H_
+#define CHROMEOS_SERVICES_DEVICE_SYNC_PUBLIC_CPP_DEVICE_SYNC_CLIENT_H_
+
+#include <memory>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "base/optional.h"
+#include "chromeos/services/device_sync/public/mojom/device_sync.mojom.h"
+#include "components/cryptauth/remote_device_ref.h"
+
+namespace chromeos {
+
+namespace device_sync {
+
+// Provides clients access to the DeviceSync API.
+class DeviceSyncClient {
+ public:
+  class Observer {
+   public:
+    virtual void OnEnrollmentFinished() {}
+    virtual void OnNewDevicesSynced() {}
+
+   protected:
+    virtual ~Observer() = default;
+  };
+
+  using FindEligibleDevicesCallback =
+      base::OnceCallback<void(const base::Optional<std::string>&,
+                              cryptauth::RemoteDeviceRefList,
+                              cryptauth::RemoteDeviceRefList)>;
+
+  DeviceSyncClient();
+  virtual ~DeviceSyncClient();
+
+  void AddObserver(Observer* observer);
+  void RemoveObserver(Observer* observer);
+
+  virtual void ForceEnrollmentNow(
+      mojom::DeviceSync::ForceEnrollmentNowCallback callback) = 0;
+  virtual void ForceSyncNow(
+      mojom::DeviceSync::ForceSyncNowCallback callback) = 0;
+  virtual cryptauth::RemoteDeviceRefList GetSyncedDevices() = 0;
+  virtual base::Optional<cryptauth::RemoteDeviceRef>
+  GetLocalDeviceMetadata() = 0;
+  virtual void SetSoftwareFeatureState(
+      const std::string public_key,
+      cryptauth::SoftwareFeature software_feature,
+      bool enabled,
+      bool is_exclusive,
+      mojom::DeviceSync::SetSoftwareFeatureStateCallback callback) = 0;
+  virtual void FindEligibleDevices(cryptauth::SoftwareFeature software_feature,
+                                   FindEligibleDevicesCallback callback) = 0;
+  virtual void GetDebugInfo(
+      mojom::DeviceSync::GetDebugInfoCallback callback) = 0;
+
+ protected:
+  void NotifyEnrollmentFinished();
+  void NotifyNewDevicesSynced();
+
+ private:
+  base::ObserverList<Observer> observer_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(DeviceSyncClient);
+};
+
+}  // namespace device_sync
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_SERVICES_DEVICE_SYNC_PUBLIC_CPP_DEVICE_SYNC_CLIENT_H_
diff --git a/chromeos/services/device_sync/public/cpp/device_sync_client_impl.cc b/chromeos/services/device_sync/public/cpp/device_sync_client_impl.cc
new file mode 100644
index 0000000..ddd0e30
--- /dev/null
+++ b/chromeos/services/device_sync/public/cpp/device_sync_client_impl.cc
@@ -0,0 +1,182 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/services/device_sync/public/cpp/device_sync_client_impl.h"
+
+#include "chromeos/components/proximity_auth/logging/logging.h"
+#include "chromeos/services/device_sync/public/mojom/constants.mojom.h"
+#include "chromeos/services/device_sync/public/mojom/device_sync.mojom.h"
+#include "components/cryptauth/expiring_remote_device_cache.h"
+#include "components/cryptauth/remote_device.h"
+#include "services/service_manager/public/cpp/connector.h"
+
+namespace chromeos {
+
+namespace device_sync {
+
+DeviceSyncClientImpl::DeviceSyncClientImpl(
+    service_manager::Connector* connector)
+    : DeviceSyncClientImpl(connector, base::ThreadTaskRunnerHandle::Get()) {}
+
+DeviceSyncClientImpl::DeviceSyncClientImpl(
+    service_manager::Connector* connector,
+    scoped_refptr<base::TaskRunner> task_runner)
+    : binding_(this),
+      expiring_device_cache_(
+          std::make_unique<cryptauth::ExpiringRemoteDeviceCache>()),
+      weak_ptr_factory_(this) {
+  connector->BindInterface(mojom::kServiceName, &device_sync_ptr_);
+  device_sync_ptr_->AddObserver(GenerateInterfacePtr(), base::OnceClosure());
+
+  // Delay calling these until after the constructor finishes.
+  task_runner->PostTask(
+      FROM_HERE, base::BindOnce(&DeviceSyncClientImpl::LoadLocalDeviceMetadata,
+                                weak_ptr_factory_.GetWeakPtr()));
+  task_runner->PostTask(FROM_HERE,
+                        base::BindOnce(&DeviceSyncClientImpl::LoadSyncedDevices,
+                                       weak_ptr_factory_.GetWeakPtr()));
+}
+
+DeviceSyncClientImpl::~DeviceSyncClientImpl() = default;
+
+void DeviceSyncClientImpl::OnEnrollmentFinished() {
+  // Before notifying observers that enrollment has finished, sync down the
+  // local device metadata. This ensures that observers will have access to the
+  // metadata of the newly-synced local device as soon as
+  // NotifyOnEnrollmentFinished() is invoked.
+  LoadLocalDeviceMetadata();
+}
+
+void DeviceSyncClientImpl::OnNewDevicesSynced() {
+  // Before notifying observers that new devices have synced, sync down the new
+  // devices. This ensures that observers will have access to the synced devices
+  // as soon as NotifyOnNewDevicesSynced() is invoked.
+  LoadSyncedDevices();
+}
+
+void DeviceSyncClientImpl::ForceEnrollmentNow(
+    mojom::DeviceSync::ForceEnrollmentNowCallback callback) {
+  device_sync_ptr_->ForceEnrollmentNow(std::move(callback));
+}
+
+void DeviceSyncClientImpl::ForceSyncNow(
+    mojom::DeviceSync::ForceSyncNowCallback callback) {
+  device_sync_ptr_->ForceSyncNow(std::move(callback));
+}
+
+cryptauth::RemoteDeviceRefList DeviceSyncClientImpl::GetSyncedDevices() {
+  return expiring_device_cache_->GetNonExpiredRemoteDevices();
+}
+
+base::Optional<cryptauth::RemoteDeviceRef>
+DeviceSyncClientImpl::GetLocalDeviceMetadata() {
+  return local_device_id_
+             ? expiring_device_cache_->GetRemoteDevice(*local_device_id_)
+             : base::nullopt;
+}
+
+void DeviceSyncClientImpl::SetSoftwareFeatureState(
+    const std::string public_key,
+    cryptauth::SoftwareFeature software_feature,
+    bool enabled,
+    bool is_exclusive,
+    mojom::DeviceSync::SetSoftwareFeatureStateCallback callback) {
+  device_sync_ptr_->SetSoftwareFeatureState(
+      public_key, software_feature, enabled, is_exclusive, std::move(callback));
+}
+
+void DeviceSyncClientImpl::FindEligibleDevices(
+    cryptauth::SoftwareFeature software_feature,
+    FindEligibleDevicesCallback callback) {
+  device_sync_ptr_->FindEligibleDevices(
+      software_feature,
+      base::BindOnce(&DeviceSyncClientImpl::OnFindEligibleDevicesCompleted,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void DeviceSyncClientImpl::GetDebugInfo(
+    mojom::DeviceSync::GetDebugInfoCallback callback) {
+  device_sync_ptr_->GetDebugInfo(std::move(callback));
+}
+
+void DeviceSyncClientImpl::LoadSyncedDevices() {
+  device_sync_ptr_->GetSyncedDevices(
+      base::BindOnce(&DeviceSyncClientImpl::OnGetSyncedDevicesCompleted,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void DeviceSyncClientImpl::LoadLocalDeviceMetadata() {
+  device_sync_ptr_->GetLocalDeviceMetadata(
+      base::BindOnce(&DeviceSyncClientImpl::OnGetLocalDeviceMetadataCompleted,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void DeviceSyncClientImpl::OnGetSyncedDevicesCompleted(
+    const base::Optional<std::vector<cryptauth::RemoteDevice>>&
+        remote_devices) {
+  if (!remote_devices) {
+    PA_LOG(INFO) << "Tried to fetch synced devices before service was fully "
+                    "initialized; waiting for sync to complete before "
+                    "continuing.";
+    return;
+  }
+
+  expiring_device_cache_->SetRemoteDevicesAndInvalidateOldEntries(
+      *remote_devices);
+
+  NotifyNewDevicesSynced();
+}
+
+void DeviceSyncClientImpl::OnGetLocalDeviceMetadataCompleted(
+    const base::Optional<cryptauth::RemoteDevice>& local_device_metadata) {
+  if (!local_device_metadata) {
+    PA_LOG(INFO) << "Tried to get local device metadata before service was "
+                    "fully initialized; waiting for enrollment to complete "
+                    "before continuing.";
+    return;
+  }
+
+  local_device_id_ = local_device_metadata->GetDeviceId();
+  expiring_device_cache_->UpdateRemoteDevice(*local_device_metadata);
+
+  NotifyEnrollmentFinished();
+}
+
+void DeviceSyncClientImpl::OnFindEligibleDevicesCompleted(
+    FindEligibleDevicesCallback callback,
+    const base::Optional<std::string>& error_code,
+    mojom::FindEligibleDevicesResponsePtr response) {
+  cryptauth::RemoteDeviceRefList eligible_devices;
+  cryptauth::RemoteDeviceRefList ineligible_devices;
+
+  if (!error_code) {
+    std::transform(
+        response->eligible_devices.begin(), response->eligible_devices.end(),
+        std::back_inserter(eligible_devices), [this](const auto& device) {
+          return *expiring_device_cache_->GetRemoteDevice(device.GetDeviceId());
+        });
+    std::transform(
+        response->ineligible_devices.begin(),
+        response->ineligible_devices.end(),
+        std::back_inserter(ineligible_devices), [this](const auto& device) {
+          return *expiring_device_cache_->GetRemoteDevice(device.GetDeviceId());
+        });
+  }
+
+  std::move(callback).Run(error_code, eligible_devices, ineligible_devices);
+}
+
+mojom::DeviceSyncObserverPtr DeviceSyncClientImpl::GenerateInterfacePtr() {
+  mojom::DeviceSyncObserverPtr interface_ptr;
+  binding_.Bind(mojo::MakeRequest(&interface_ptr));
+  return interface_ptr;
+}
+
+void DeviceSyncClientImpl::FlushForTesting() {
+  device_sync_ptr_.FlushForTesting();
+}
+
+}  // namespace device_sync
+
+}  // namespace chromeos
diff --git a/chromeos/services/device_sync/public/cpp/device_sync_client_impl.h b/chromeos/services/device_sync/public/cpp/device_sync_client_impl.h
new file mode 100644
index 0000000..83fcc010
--- /dev/null
+++ b/chromeos/services/device_sync/public/cpp/device_sync_client_impl.h
@@ -0,0 +1,100 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_SERVICES_DEVICE_SYNC_PUBLIC_CPP_DEVICE_SYNC_CLIENT_IMPL_H_
+#define CHROMEOS_SERVICES_DEVICE_SYNC_PUBLIC_CPP_DEVICE_SYNC_CLIENT_IMPL_H_
+
+#include <memory>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "chromeos/services/device_sync/public/cpp/device_sync_client.h"
+#include "chromeos/services/device_sync/public/mojom/device_sync.mojom.h"
+#include "components/cryptauth/remote_device_ref.h"
+#include "mojo/public/cpp/bindings/binding.h"
+
+namespace base {
+class TaskRunner;
+}  // namespace base
+
+namespace cryptauth {
+class ExpiringRemoteDeviceCache;
+}  // namespace cryptauth
+
+namespace service_manager {
+class Connector;
+}  // namespace service_manager
+
+namespace chromeos {
+
+namespace device_sync {
+
+// Concrete implementation of DeviceSyncClient.
+class DeviceSyncClientImpl : public DeviceSyncClient,
+                             public device_sync::mojom::DeviceSyncObserver {
+ public:
+  explicit DeviceSyncClientImpl(service_manager::Connector* connector);
+  ~DeviceSyncClientImpl() override;
+
+  // DeviceSyncClient:
+  void ForceEnrollmentNow(
+      mojom::DeviceSync::ForceEnrollmentNowCallback callback) override;
+  void ForceSyncNow(mojom::DeviceSync::ForceSyncNowCallback callback) override;
+  cryptauth::RemoteDeviceRefList GetSyncedDevices() override;
+  base::Optional<cryptauth::RemoteDeviceRef> GetLocalDeviceMetadata() override;
+  void SetSoftwareFeatureState(
+      const std::string public_key,
+      cryptauth::SoftwareFeature software_feature,
+      bool enabled,
+      bool is_exclusive,
+      mojom::DeviceSync::SetSoftwareFeatureStateCallback callback) override;
+  void FindEligibleDevices(cryptauth::SoftwareFeature software_feature,
+                           FindEligibleDevicesCallback callback) override;
+  void GetDebugInfo(mojom::DeviceSync::GetDebugInfoCallback callback) override;
+
+  // device_sync::mojom::DeviceSyncObserver:
+  void OnEnrollmentFinished() override;
+  void OnNewDevicesSynced() override;
+
+ private:
+  friend class DeviceSyncClientImplTest;
+
+  DeviceSyncClientImpl(service_manager::Connector* connector,
+                       scoped_refptr<base::TaskRunner> task_runner);
+
+  void LoadSyncedDevices();
+  void LoadLocalDeviceMetadata();
+
+  void OnGetSyncedDevicesCompleted(
+      const base::Optional<std::vector<cryptauth::RemoteDevice>>&
+          remote_devices);
+  void OnGetLocalDeviceMetadataCompleted(
+      const base::Optional<cryptauth::RemoteDevice>& local_device_metadata);
+  void OnFindEligibleDevicesCompleted(
+      FindEligibleDevicesCallback callback,
+      const base::Optional<std::string>& error_code,
+      mojom::FindEligibleDevicesResponsePtr response);
+
+  mojom::DeviceSyncObserverPtr GenerateInterfacePtr();
+
+  void FlushForTesting();
+
+  mojom::DeviceSyncPtr device_sync_ptr_;
+  mojo::Binding<mojom::DeviceSyncObserver> binding_;
+  std::unique_ptr<cryptauth::ExpiringRemoteDeviceCache> expiring_device_cache_;
+  base::Optional<std::string> local_device_id_;
+
+  base::WeakPtrFactory<DeviceSyncClientImpl> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(DeviceSyncClientImpl);
+};
+
+}  // namespace device_sync
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_SERVICES_DEVICE_SYNC_PUBLIC_CPP_DEVICE_SYNC_CLIENT_IMPL_H_
diff --git a/chromeos/services/device_sync/public/cpp/device_sync_client_impl_unittest.cc b/chromeos/services/device_sync/public/cpp/device_sync_client_impl_unittest.cc
new file mode 100644
index 0000000..6a65ebf8
--- /dev/null
+++ b/chromeos/services/device_sync/public/cpp/device_sync_client_impl_unittest.cc
@@ -0,0 +1,604 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/services/device_sync/public/cpp/device_sync_client_impl.h"
+
+#include <algorithm>
+
+#include "base/memory/scoped_refptr.h"
+#include "base/no_destructor.h"
+#include "base/optional.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/test/null_task_runner.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/test/test_simple_task_runner.h"
+#include "chromeos/services/device_sync/device_sync_impl.h"
+#include "chromeos/services/device_sync/device_sync_service.h"
+#include "chromeos/services/device_sync/fake_device_sync.h"
+#include "chromeos/services/device_sync/public/mojom/constants.mojom.h"
+#include "chromeos/services/device_sync/public/mojom/device_sync.mojom.h"
+#include "components/cryptauth/fake_gcm_device_info_provider.h"
+#include "components/cryptauth/remote_device_test_util.h"
+#include "components/gcm_driver/fake_gcm_driver.h"
+#include "net/url_request/url_request_context_getter.h"
+#include "services/identity/public/cpp/identity_test_environment.h"
+#include "services/service_manager/public/cpp/test/test_connector_factory.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromeos {
+
+namespace device_sync {
+
+namespace {
+
+const char kTestEmail[] = "example@gmail.com";
+const char kTestGcmDeviceInfoLongDeviceId[] = "longDeviceId";
+const size_t kNumTestDevices = 5u;
+
+const cryptauth::GcmDeviceInfo& GetTestGcmDeviceInfo() {
+  static const base::NoDestructor<cryptauth::GcmDeviceInfo> gcm_device_info([] {
+    cryptauth::GcmDeviceInfo gcm_device_info;
+    gcm_device_info.set_long_device_id(kTestGcmDeviceInfoLongDeviceId);
+    return gcm_device_info;
+  }());
+
+  return *gcm_device_info;
+}
+
+class FakeDeviceSyncImplFactory : public DeviceSyncImpl::Factory {
+ public:
+  FakeDeviceSyncImplFactory(std::unique_ptr<FakeDeviceSync> fake_device_sync)
+      : fake_device_sync_(std::move(fake_device_sync)) {}
+
+  ~FakeDeviceSyncImplFactory() override = default;
+
+  // DeviceSyncImpl::Factory:
+  std::unique_ptr<DeviceSyncBase> BuildInstance(
+      identity::IdentityManager* identity_manager,
+      gcm::GCMDriver* gcm_driver,
+      service_manager::Connector* connector,
+      cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider,
+      scoped_refptr<net::URLRequestContextGetter> url_request_context)
+      override {
+    EXPECT_TRUE(fake_device_sync_);
+    return std::move(fake_device_sync_);
+  }
+
+ private:
+  std::unique_ptr<FakeDeviceSync> fake_device_sync_;
+};
+
+class FakeURLRequestContextGetter : public net::URLRequestContextGetter {
+ public:
+  FakeURLRequestContextGetter() : null_task_runner_(new base::NullTaskRunner) {}
+
+  net::URLRequestContext* GetURLRequestContext() override { return nullptr; }
+
+  scoped_refptr<base::SingleThreadTaskRunner> GetNetworkTaskRunner()
+      const override {
+    return null_task_runner_;
+  }
+
+ private:
+  ~FakeURLRequestContextGetter() override {}
+
+  scoped_refptr<base::SingleThreadTaskRunner> null_task_runner_;
+};
+
+class TestDeviceSyncClientObserver : public DeviceSyncClient::Observer {
+ public:
+  ~TestDeviceSyncClientObserver() override = default;
+
+  void set_closure_for_enrollment_finished(base::OnceClosure closure) {
+    EXPECT_FALSE(closure_for_enrollment_finished_);
+    closure_for_enrollment_finished_ = std::move(closure);
+  }
+
+  void set_closure_for_new_devices_synced(base::OnceClosure closure) {
+    EXPECT_FALSE(closure_for_new_devices_synced_);
+    closure_for_new_devices_synced_ = std::move(closure);
+  }
+
+  void OnEnrollmentFinished() override {
+    ++enrollment_finished_count_;
+
+    EXPECT_TRUE(closure_for_enrollment_finished_);
+    std::move(closure_for_enrollment_finished_).Run();
+  }
+
+  void OnNewDevicesSynced() override {
+    ++new_devices_synced_count_;
+
+    EXPECT_TRUE(closure_for_new_devices_synced_);
+    std::move(closure_for_new_devices_synced_).Run();
+  }
+
+  size_t enrollment_finished_count() { return enrollment_finished_count_; }
+  size_t new_devices_synced_count() { return new_devices_synced_count_; }
+
+ private:
+  size_t enrollment_finished_count_ = 0u;
+  size_t new_devices_synced_count_ = 0u;
+
+  base::OnceClosure closure_for_enrollment_finished_;
+  base::OnceClosure closure_for_new_devices_synced_;
+};
+
+}  // namespace
+
+class DeviceSyncClientImplTest : public testing::Test {
+ protected:
+  DeviceSyncClientImplTest()
+      : test_remote_device_list_(
+            cryptauth::CreateRemoteDeviceListForTest(kNumTestDevices)),
+        test_remote_device_ref_list_(
+            cryptauth::CreateRemoteDeviceRefListForTest(kNumTestDevices)) {}
+
+  // testing::Test:
+  void SetUp() override {
+    fake_gcm_driver_ = std::make_unique<gcm::FakeGCMDriver>();
+    fake_gcm_device_info_provider_ =
+        std::make_unique<cryptauth::FakeGcmDeviceInfoProvider>(
+            GetTestGcmDeviceInfo());
+    fake_url_request_context_getter_ =
+        base::MakeRefCounted<FakeURLRequestContextGetter>();
+
+    identity_test_environment_ =
+        std::make_unique<identity::IdentityTestEnvironment>();
+    identity_test_environment_->MakePrimaryAccountAvailable(kTestEmail);
+
+    auto fake_device_sync = std::make_unique<FakeDeviceSync>();
+    fake_device_sync_ = fake_device_sync.get();
+    fake_device_sync_impl_factory_ =
+        std::make_unique<FakeDeviceSyncImplFactory>(
+            std::move(fake_device_sync));
+    DeviceSyncImpl::Factory::SetInstanceForTesting(
+        fake_device_sync_impl_factory_.get());
+
+    auto device_sync_service = std::make_unique<DeviceSyncService>(
+        identity_test_environment_->identity_manager(), fake_gcm_driver_.get(),
+        fake_gcm_device_info_provider_.get(),
+        fake_url_request_context_getter_.get());
+
+    connector_factory_ =
+        service_manager::TestConnectorFactory::CreateForUniqueService(
+            std::move(device_sync_service));
+
+    test_observer_ = std::make_unique<TestDeviceSyncClientObserver>();
+
+    CreateClient();
+  }
+
+  void CreateClient() {
+    std::unique_ptr<service_manager::Connector> connector =
+        connector_factory_->CreateConnector();
+
+    // DeviceSyncClient's constructor posts two tasks to the TaskRunner. Idle
+    // the TaskRunner so that the tasks can be run via a RunLoop later on.
+    auto test_task_runner = base::MakeRefCounted<base::TestSimpleTaskRunner>();
+    client_ = base::WrapUnique(
+        new DeviceSyncClientImpl(connector.get(), test_task_runner));
+    test_task_runner->RunUntilIdle();
+  }
+
+  void InitializeClient(bool complete_enrollment_before_sync = true) {
+    client_->AddObserver(test_observer_.get());
+
+    SendPendingMojoMessages();
+
+    if (complete_enrollment_before_sync)
+      InvokeInitialGetLocalMetadataAndThenSync();
+    else
+      InvokeInitialSyncAndThenGetLocalMetadata();
+  }
+
+  void InvokeInitialGetLocalMetadataAndThenSync() {
+    EXPECT_FALSE(client_->GetLocalDeviceMetadata());
+    EXPECT_EQ(0u, test_observer_->enrollment_finished_count());
+
+    base::RunLoop enrollment_run_loop;
+    test_observer_->set_closure_for_enrollment_finished(
+        enrollment_run_loop.QuitClosure());
+    fake_device_sync_->InvokePendingGetLocalDeviceMetadataCallback(
+        test_remote_device_list_[0]);
+    enrollment_run_loop.Run();
+
+    // In the case where enrollment finishes before sync, the local device
+    // metadata must still be accessible.
+    EXPECT_EQ(test_remote_device_list_[0].public_key,
+              client_->GetLocalDeviceMetadata()->public_key());
+    EXPECT_EQ(1u, test_observer_->enrollment_finished_count());
+
+    EXPECT_EQ(1u, client_->GetSyncedDevices().size());
+    EXPECT_EQ(0u, test_observer_->new_devices_synced_count());
+
+    base::RunLoop sync_run_loop;
+    test_observer_->set_closure_for_new_devices_synced(
+        sync_run_loop.QuitClosure());
+    fake_device_sync_->InvokePendingGetSyncedDevicesCallback(
+        test_remote_device_list_);
+    sync_run_loop.Run();
+
+    VerifyRemoteDeviceRefListAndRemoteDeviceListAreEqual(
+        client_->GetSyncedDevices(), test_remote_device_list_);
+    EXPECT_EQ(1u, test_observer_->new_devices_synced_count());
+  }
+
+  void InvokeInitialSyncAndThenGetLocalMetadata() {
+    EXPECT_EQ(0u, client_->GetSyncedDevices().size());
+    EXPECT_EQ(0u, test_observer_->new_devices_synced_count());
+
+    base::RunLoop sync_run_loop;
+    test_observer_->set_closure_for_new_devices_synced(
+        sync_run_loop.QuitClosure());
+    fake_device_sync_->InvokePendingGetSyncedDevicesCallback(
+        test_remote_device_list_);
+    sync_run_loop.Run();
+
+    VerifyRemoteDeviceRefListAndRemoteDeviceListAreEqual(
+        client_->GetSyncedDevices(), test_remote_device_list_);
+    EXPECT_EQ(1u, test_observer_->new_devices_synced_count());
+
+    EXPECT_FALSE(client_->GetLocalDeviceMetadata());
+    EXPECT_EQ(0u, test_observer_->enrollment_finished_count());
+
+    base::RunLoop enrollment_run_loop;
+    test_observer_->set_closure_for_enrollment_finished(
+        enrollment_run_loop.QuitClosure());
+    fake_device_sync_->InvokePendingGetLocalDeviceMetadataCallback(
+        test_remote_device_list_[0]);
+    enrollment_run_loop.Run();
+    EXPECT_EQ(test_remote_device_list_[0].public_key,
+              client_->GetLocalDeviceMetadata()->public_key());
+
+    // Ensure that the rest of the synced devices are not removed from the cache
+    // when updating the local device metadata.
+    VerifyRemoteDeviceRefListAndRemoteDeviceListAreEqual(
+        client_->GetSyncedDevices(), test_remote_device_list_);
+    EXPECT_EQ(1u, test_observer_->enrollment_finished_count());
+  }
+
+  void TearDown() override {
+    DeviceSyncImpl::Factory::SetInstanceForTesting(nullptr);
+    client_->RemoveObserver(test_observer_.get());
+  }
+
+  void CallForceEnrollmentNow(bool expected_success) {
+    fake_device_sync_->set_force_enrollment_now_completed_success(
+        expected_success);
+
+    base::RunLoop run_loop;
+    client_->ForceEnrollmentNow(
+        base::BindOnce(&DeviceSyncClientImplTest::OnForceEnrollmentNowCompleted,
+                       base::Unretained(this), run_loop.QuitClosure()));
+    run_loop.Run();
+
+    EXPECT_EQ(expected_success, *force_enrollment_now_completed_success_);
+  }
+
+  void CallSyncNow(bool expected_success) {
+    fake_device_sync_->set_force_sync_now_completed_success(expected_success);
+
+    base::RunLoop run_loop;
+    client_->ForceSyncNow(
+        base::BindOnce(&DeviceSyncClientImplTest::OnForceSyncNowCompleted,
+                       base::Unretained(this), run_loop.QuitClosure()));
+    run_loop.Run();
+
+    EXPECT_EQ(expected_success, *force_sync_now_completed_success_);
+  }
+
+  void CallSetSoftwareFeatureState(
+      const base::Optional<std::string>& expected_error_code) {
+    base::RunLoop run_loop;
+
+    client_->SetSoftwareFeatureState(
+        test_remote_device_ref_list_[0].public_key(),
+        cryptauth::SoftwareFeature::BETTER_TOGETHER_HOST, true /* enabled */,
+        true /* enabled */,
+        base::BindOnce(
+            &DeviceSyncClientImplTest::OnSetSoftwareFeatureStateCompleted,
+            base::Unretained(this), run_loop.QuitClosure()));
+
+    SendPendingMojoMessages();
+
+    fake_device_sync_->InvokePendingSetSoftwareFeatureStateCallback(
+        expected_error_code);
+    run_loop.Run();
+
+    EXPECT_EQ(expected_error_code, set_software_feature_state_error_code_);
+  }
+
+  void CallFindEligibleDevices(
+      const base::Optional<std::string>& expected_error_code,
+      cryptauth::RemoteDeviceList expected_eligible_devices,
+      cryptauth::RemoteDeviceList expected_ineligible_devices) {
+    base::RunLoop run_loop;
+
+    client_->FindEligibleDevices(
+        cryptauth::SoftwareFeature::BETTER_TOGETHER_HOST,
+        base::BindOnce(
+            &DeviceSyncClientImplTest::OnFindEligibleDevicesCompleted,
+            base::Unretained(this), run_loop.QuitClosure()));
+
+    SendPendingMojoMessages();
+
+    fake_device_sync_->InvokePendingFindEligibleDevicesCallback(
+        expected_error_code,
+        mojom::FindEligibleDevicesResponse::New(expected_eligible_devices,
+                                                expected_ineligible_devices));
+    run_loop.Run();
+
+    EXPECT_EQ(expected_error_code,
+              std::get<0>(find_eligible_devices_error_code_and_response_));
+    VerifyRemoteDeviceRefListAndRemoteDeviceListAreEqual(
+        std::get<1>(find_eligible_devices_error_code_and_response_),
+        expected_eligible_devices);
+    VerifyRemoteDeviceRefListAndRemoteDeviceListAreEqual(
+        std::get<2>(find_eligible_devices_error_code_and_response_),
+        expected_ineligible_devices);
+  }
+
+  void CallGetDebugInfo() {
+    EXPECT_FALSE(debug_info_received_);
+
+    base::RunLoop run_loop;
+
+    client_->GetDebugInfo(
+        base::BindOnce(&DeviceSyncClientImplTest::OnGetDebugInfoCompleted,
+                       base::Unretained(this), run_loop.QuitClosure()));
+
+    SendPendingMojoMessages();
+
+    fake_device_sync_->InvokePendingGetDebugInfoCallback(
+        mojom::DebugInfo::New());
+    run_loop.Run();
+
+    EXPECT_TRUE(debug_info_received_);
+  }
+
+  // DeviceSyncClientImpl cached its devices in a RemoteDeviceCache, which
+  // stores devices in an unordered_map -- retrieved devices thus need to be
+  // sorted before comparison.
+  void VerifyRemoteDeviceRefListAndRemoteDeviceListAreEqual(
+      cryptauth::RemoteDeviceRefList remote_device_ref_list,
+      cryptauth::RemoteDeviceList remote_device_list) {
+    std::vector<std::string> ref_public_keys;
+    for (auto device : remote_device_ref_list)
+      ref_public_keys.push_back(device.public_key());
+    std::sort(ref_public_keys.begin(), ref_public_keys.end(),
+              [](auto public_key_1, auto public_key_2) {
+                return public_key_1 < public_key_2;
+              });
+
+    std::vector<std::string> public_keys;
+    for (auto device : remote_device_list)
+      public_keys.push_back(device.public_key);
+    std::sort(public_keys.begin(), public_keys.end(),
+              [](auto public_key_1, auto public_key_2) {
+                return public_key_1 < public_key_2;
+              });
+
+    EXPECT_EQ(ref_public_keys, public_keys);
+  }
+
+  void SendPendingMojoMessages() { client_->FlushForTesting(); }
+
+  const base::test::ScopedTaskEnvironment scoped_task_environment_;
+
+  std::unique_ptr<identity::IdentityTestEnvironment> identity_test_environment_;
+  std::unique_ptr<gcm::FakeGCMDriver> fake_gcm_driver_;
+  std::unique_ptr<cryptauth::FakeGcmDeviceInfoProvider>
+      fake_gcm_device_info_provider_;
+  scoped_refptr<FakeURLRequestContextGetter> fake_url_request_context_getter_;
+  FakeDeviceSync* fake_device_sync_;
+  std::unique_ptr<FakeDeviceSyncImplFactory> fake_device_sync_impl_factory_;
+  std::unique_ptr<service_manager::TestConnectorFactory> connector_factory_;
+  std::unique_ptr<TestDeviceSyncClientObserver> test_observer_;
+
+  std::unique_ptr<DeviceSyncClientImpl> client_;
+
+  cryptauth::RemoteDeviceList test_remote_device_list_;
+  const cryptauth::RemoteDeviceRefList test_remote_device_ref_list_;
+
+  base::Optional<bool> force_enrollment_now_completed_success_;
+  base::Optional<bool> force_sync_now_completed_success_;
+  base::Optional<std::string> set_software_feature_state_error_code_;
+  std::tuple<base::Optional<std::string>,
+             cryptauth::RemoteDeviceRefList,
+             cryptauth::RemoteDeviceRefList>
+      find_eligible_devices_error_code_and_response_;
+  bool debug_info_received_ = false;
+
+ private:
+  void OnForceEnrollmentNowCompleted(base::OnceClosure quit_closure,
+                                     bool success) {
+    force_enrollment_now_completed_success_ = success;
+    std::move(quit_closure).Run();
+  }
+
+  void OnForceSyncNowCompleted(base::OnceClosure quit_closure, bool success) {
+    force_sync_now_completed_success_ = success;
+    std::move(quit_closure).Run();
+  }
+
+  void OnSetSoftwareFeatureStateCompleted(
+      base::OnceClosure callback,
+      const base::Optional<std::string>& error_code) {
+    set_software_feature_state_error_code_ = error_code;
+    std::move(callback).Run();
+  }
+
+  void OnFindEligibleDevicesCompleted(
+      base::OnceClosure callback,
+      const base::Optional<std::string>& error_code,
+      cryptauth::RemoteDeviceRefList eligible_devices,
+      cryptauth::RemoteDeviceRefList ineligible_devices) {
+    find_eligible_devices_error_code_and_response_ =
+        std::make_tuple(error_code, eligible_devices, ineligible_devices);
+    std::move(callback).Run();
+  }
+
+  void OnGetDebugInfoCompleted(base::OnceClosure callback,
+                               mojom::DebugInfoPtr debug_info_ptr) {
+    debug_info_received_ = true;
+    std::move(callback).Run();
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(DeviceSyncClientImplTest);
+};
+
+TEST_F(DeviceSyncClientImplTest,
+       TestCompleteInitialSyncBeforeInitialEnrollment) {
+  InitializeClient(false /* complete_enrollment_before_sync */);
+}
+
+TEST_F(DeviceSyncClientImplTest, TestOnEnrollmentFinished) {
+  EXPECT_EQ(0u, test_observer_->enrollment_finished_count());
+
+  InitializeClient();
+
+  EXPECT_EQ(test_remote_device_list_[0].public_key,
+            client_->GetLocalDeviceMetadata()->public_key());
+  EXPECT_EQ(test_remote_device_list_[0].name,
+            client_->GetLocalDeviceMetadata()->name());
+
+  fake_device_sync_->NotifyOnEnrollmentFinished();
+
+  // The client calls and waits for DeviceSync::GetLocalDeviceMetadata() to
+  // finish before notifying observers that enrollment has finished.
+  EXPECT_EQ(1u, test_observer_->enrollment_finished_count());
+
+  SendPendingMojoMessages();
+
+  // Update the local device metadata.
+  test_remote_device_list_[0].name = "new name";
+
+  base::RunLoop run_loop;
+  test_observer_->set_closure_for_enrollment_finished(run_loop.QuitClosure());
+  fake_device_sync_->InvokePendingGetLocalDeviceMetadataCallback(
+      test_remote_device_list_[0]);
+  run_loop.Run();
+
+  EXPECT_EQ(2u, test_observer_->enrollment_finished_count());
+
+  EXPECT_EQ(test_remote_device_list_[0].public_key,
+            client_->GetLocalDeviceMetadata()->public_key());
+  EXPECT_EQ("new name", client_->GetLocalDeviceMetadata()->name());
+}
+
+TEST_F(DeviceSyncClientImplTest, TestOnNewDevicesSynced) {
+  EXPECT_EQ(0u, test_observer_->new_devices_synced_count());
+
+  InitializeClient();
+
+  VerifyRemoteDeviceRefListAndRemoteDeviceListAreEqual(
+      client_->GetSyncedDevices(), test_remote_device_list_);
+
+  fake_device_sync_->NotifyOnNewDevicesSynced();
+
+  // The client calls and waits for DeviceSync::GetLocalDeviceMetadata() to
+  // finish before notifying observers that enrollment has finished.
+  EXPECT_EQ(1u, test_observer_->new_devices_synced_count());
+
+  SendPendingMojoMessages();
+
+  // Change the synced device list.
+  cryptauth::RemoteDeviceList new_device_list(
+      {test_remote_device_list_[0], test_remote_device_list_[1]});
+
+  base::RunLoop run_loop;
+  test_observer_->set_closure_for_new_devices_synced(run_loop.QuitClosure());
+  fake_device_sync_->InvokePendingGetSyncedDevicesCallback(new_device_list);
+  run_loop.Run();
+
+  EXPECT_EQ(2u, test_observer_->new_devices_synced_count());
+
+  VerifyRemoteDeviceRefListAndRemoteDeviceListAreEqual(
+      client_->GetSyncedDevices(), new_device_list);
+}
+
+TEST_F(DeviceSyncClientImplTest, TestForceEnrollmentNow_ExpectSuccess) {
+  InitializeClient();
+
+  CallForceEnrollmentNow(true /* expected_success */);
+}
+
+TEST_F(DeviceSyncClientImplTest, TestForceEnrollmentNow_ExpectFailure) {
+  InitializeClient();
+
+  CallForceEnrollmentNow(false /* expected_success */);
+}
+
+TEST_F(DeviceSyncClientImplTest, TestSyncNow_ExpectSuccess) {
+  InitializeClient();
+
+  CallSyncNow(true /* expected_success */);
+}
+
+TEST_F(DeviceSyncClientImplTest, TestSyncNow_ExpectFailure) {
+  InitializeClient();
+
+  CallSyncNow(false /* expected_success */);
+}
+
+TEST_F(DeviceSyncClientImplTest, TestGetSyncedDevices_DeviceRemovedFromCache) {
+  InitializeClient();
+
+  VerifyRemoteDeviceRefListAndRemoteDeviceListAreEqual(
+      client_->GetSyncedDevices(), test_remote_device_list_);
+
+  // Remove a device from the test list, and inform |client_|.
+  cryptauth::RemoteDeviceList new_list(
+      {test_remote_device_list_[0], test_remote_device_list_[1],
+       test_remote_device_list_[2], test_remote_device_list_[3]});
+  client_->OnNewDevicesSynced();
+
+  SendPendingMojoMessages();
+
+  base::RunLoop run_loop;
+  test_observer_->set_closure_for_new_devices_synced(run_loop.QuitClosure());
+  fake_device_sync_->InvokePendingGetSyncedDevicesCallback(new_list);
+  run_loop.Run();
+
+  VerifyRemoteDeviceRefListAndRemoteDeviceListAreEqual(
+      client_->GetSyncedDevices(), new_list);
+}
+
+TEST_F(DeviceSyncClientImplTest, TestSetSoftwareFeatureState) {
+  InitializeClient();
+
+  CallSetSoftwareFeatureState("error_code");
+}
+
+TEST_F(DeviceSyncClientImplTest, TestFindEligibleDevices_NoErrorCode) {
+  InitializeClient();
+
+  cryptauth::RemoteDeviceList expected_eligible_devices(
+      {test_remote_device_list_[0], test_remote_device_list_[1]});
+  cryptauth::RemoteDeviceList expected_ineligible_devices(
+      {test_remote_device_list_[2], test_remote_device_list_[3],
+       test_remote_device_list_[4]});
+
+  CallFindEligibleDevices(base::nullopt, expected_eligible_devices,
+                          expected_ineligible_devices);
+}
+
+TEST_F(DeviceSyncClientImplTest, TestFindEligibleDevices_ErrorCode) {
+  InitializeClient();
+
+  CallFindEligibleDevices("error_code", cryptauth::RemoteDeviceList(),
+                          cryptauth::RemoteDeviceList());
+}
+
+TEST_F(DeviceSyncClientImplTest, TestGetDebugInfo) {
+  InitializeClient();
+
+  CallGetDebugInfo();
+}
+
+}  // namespace device_sync
+
+}  // namespace chromeos
diff --git a/chromeos/services/device_sync/public/cpp/fake_device_sync_client.cc b/chromeos/services/device_sync/public/cpp/fake_device_sync_client.cc
new file mode 100644
index 0000000..4722adfa
--- /dev/null
+++ b/chromeos/services/device_sync/public/cpp/fake_device_sync_client.cc
@@ -0,0 +1,81 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/services/device_sync/public/cpp/fake_device_sync_client.h"
+
+#include "components/cryptauth/remote_device.h"
+#include "components/cryptauth/remote_device_cache.h"
+
+namespace chromeos {
+
+namespace device_sync {
+
+FakeDeviceSyncClient::FakeDeviceSyncClient() = default;
+
+FakeDeviceSyncClient::~FakeDeviceSyncClient() = default;
+
+void FakeDeviceSyncClient::ForceEnrollmentNow(
+    mojom::DeviceSync::ForceEnrollmentNowCallback callback) {
+  std::move(callback).Run(force_enrollment_now_success_);
+}
+
+void FakeDeviceSyncClient::ForceSyncNow(
+    mojom::DeviceSync::ForceSyncNowCallback callback) {
+  std::move(callback).Run(force_sync_now_success_);
+}
+
+cryptauth::RemoteDeviceRefList FakeDeviceSyncClient::GetSyncedDevices() {
+  return synced_devices_;
+}
+
+base::Optional<cryptauth::RemoteDeviceRef>
+FakeDeviceSyncClient::GetLocalDeviceMetadata() {
+  return local_device_metadata_;
+}
+
+void FakeDeviceSyncClient::SetSoftwareFeatureState(
+    const std::string public_key,
+    cryptauth::SoftwareFeature software_feature,
+    bool enabled,
+    bool is_exclusive,
+    mojom::DeviceSync::SetSoftwareFeatureStateCallback callback) {
+  set_software_feature_state_callback_queue_.push(std::move(callback));
+}
+
+void FakeDeviceSyncClient::FindEligibleDevices(
+    cryptauth::SoftwareFeature software_feature,
+    FindEligibleDevicesCallback callback) {
+  find_eligible_devices_callback_queue_.push(std::move(callback));
+}
+
+void FakeDeviceSyncClient::GetDebugInfo(
+    mojom::DeviceSync::GetDebugInfoCallback callback) {
+  get_debug_info_callback_queue_.push(std::move(callback));
+}
+
+void FakeDeviceSyncClient::InvokePendingSetSoftwareFeatureStateCallback(
+    const base::Optional<std::string>& error_code) {
+  std::move(set_software_feature_state_callback_queue_.front()).Run(error_code);
+  set_software_feature_state_callback_queue_.pop();
+}
+
+void FakeDeviceSyncClient::InvokePendingFindEligibleDevicesCallback(
+    const base::Optional<std::string>& error_code,
+    cryptauth::RemoteDeviceRefList eligible_devices,
+    cryptauth::RemoteDeviceRefList ineligible_devices) {
+  std::move(find_eligible_devices_callback_queue_.front())
+      .Run(error_code, eligible_devices, ineligible_devices);
+  find_eligible_devices_callback_queue_.pop();
+}
+
+void FakeDeviceSyncClient::InvokePendingGetDebugInfoCallback(
+    mojom::DebugInfoPtr debug_info_ptr) {
+  std::move(get_debug_info_callback_queue_.front())
+      .Run(std::move(debug_info_ptr));
+  get_debug_info_callback_queue_.pop();
+}
+
+}  // namespace device_sync
+
+}  // namespace chromeos
\ No newline at end of file
diff --git a/chromeos/services/device_sync/public/cpp/fake_device_sync_client.h b/chromeos/services/device_sync/public/cpp/fake_device_sync_client.h
new file mode 100644
index 0000000..08ee6d5
--- /dev/null
+++ b/chromeos/services/device_sync/public/cpp/fake_device_sync_client.h
@@ -0,0 +1,89 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_SERVICES_DEVICE_SYNC_PUBLIC_CPP_FAKE_DEVICE_SYNC_CLIENT_H_
+#define CHROMEOS_SERVICES_DEVICE_SYNC_PUBLIC_CPP_FAKE_DEVICE_SYNC_CLIENT_H_
+
+#include <memory>
+#include <queue>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/optional.h"
+#include "chromeos/services/device_sync/public/cpp/device_sync_client.h"
+#include "chromeos/services/device_sync/public/mojom/device_sync.mojom.h"
+#include "components/cryptauth/remote_device_ref.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+
+namespace chromeos {
+
+namespace device_sync {
+
+// Test double implementation of DeviceSyncClient.
+class FakeDeviceSyncClient : public DeviceSyncClient {
+ public:
+  FakeDeviceSyncClient();
+  ~FakeDeviceSyncClient() override;
+
+  void InvokePendingSetSoftwareFeatureStateCallback(
+      const base::Optional<std::string>& error_code);
+  void InvokePendingFindEligibleDevicesCallback(
+      const base::Optional<std::string>& error_code,
+      cryptauth::RemoteDeviceRefList eligible_devices,
+      cryptauth::RemoteDeviceRefList ineligible_devices);
+  void InvokePendingGetDebugInfoCallback(mojom::DebugInfoPtr debug_info_ptr);
+
+  void set_force_enrollment_now_success(bool force_enrollment_now_success) {
+    force_enrollment_now_success_ = force_enrollment_now_success;
+  }
+
+  void set_force_sync_now_success(bool force_sync_now_success) {
+    force_sync_now_success_ = force_sync_now_success;
+  }
+
+  void set_synced_devices(cryptauth::RemoteDeviceRefList synced_devices) {
+    synced_devices_ = synced_devices;
+  }
+
+  void setlocal_device_metadata(
+      base::Optional<cryptauth::RemoteDeviceRef> local_device_metadata) {
+    local_device_metadata_ = local_device_metadata;
+  }
+
+ private:
+  // DeviceSyncClient:
+  void ForceEnrollmentNow(
+      mojom::DeviceSync::ForceEnrollmentNowCallback callback) override;
+  void ForceSyncNow(mojom::DeviceSync::ForceSyncNowCallback callback) override;
+  cryptauth::RemoteDeviceRefList GetSyncedDevices() override;
+  base::Optional<cryptauth::RemoteDeviceRef> GetLocalDeviceMetadata() override;
+  void SetSoftwareFeatureState(
+      const std::string public_key,
+      cryptauth::SoftwareFeature software_feature,
+      bool enabled,
+      bool is_exclusive,
+      mojom::DeviceSync::SetSoftwareFeatureStateCallback callback) override;
+  void FindEligibleDevices(cryptauth::SoftwareFeature software_feature,
+                           FindEligibleDevicesCallback callback) override;
+  void GetDebugInfo(mojom::DeviceSync::GetDebugInfoCallback callback) override;
+
+  bool force_enrollment_now_success_;
+  bool force_sync_now_success_;
+  cryptauth::RemoteDeviceRefList synced_devices_;
+  base::Optional<cryptauth::RemoteDeviceRef> local_device_metadata_;
+
+  std::queue<mojom::DeviceSync::SetSoftwareFeatureStateCallback>
+      set_software_feature_state_callback_queue_;
+  std::queue<FindEligibleDevicesCallback> find_eligible_devices_callback_queue_;
+  std::queue<mojom::DeviceSync::GetDebugInfoCallback>
+      get_debug_info_callback_queue_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeDeviceSyncClient);
+};
+
+}  // namespace device_sync
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_SERVICES_DEVICE_SYNC_PUBLIC_CPP_FAKE_DEVICE_SYNC_CLIENT_H_
diff --git a/chromeos/services/secure_channel/BUILD.gn b/chromeos/services/secure_channel/BUILD.gn
index 192a9026..b1afcc1 100644
--- a/chromeos/services/secure_channel/BUILD.gn
+++ b/chromeos/services/secure_channel/BUILD.gn
@@ -30,6 +30,8 @@
     "connection_details.h",
     "connection_medium.cc",
     "connection_medium.h",
+    "connection_role.cc",
+    "connection_role.h",
     "multiplexed_channel.cc",
     "multiplexed_channel.h",
     "multiplexed_channel_impl.cc",
@@ -38,6 +40,10 @@
     "pending_ble_initiator_connection_request.h",
     "pending_ble_listener_connection_request.cc",
     "pending_ble_listener_connection_request.h",
+    "pending_connection_manager.cc",
+    "pending_connection_manager.h",
+    "pending_connection_manager_impl.cc",
+    "pending_connection_manager_impl.h",
     "pending_connection_request.h",
     "pending_connection_request_base.h",
     "pending_connection_request_delegate.cc",
@@ -78,6 +84,8 @@
     "fake_message_receiver.h",
     "fake_multiplexed_channel.cc",
     "fake_multiplexed_channel.h",
+    "fake_pending_connection_manager.cc",
+    "fake_pending_connection_manager.h",
     "fake_pending_connection_request.cc",
     "fake_pending_connection_request.h",
     "fake_pending_connection_request_delegate.cc",
@@ -108,6 +116,7 @@
     "multiplexed_channel_impl_unittest.cc",
     "pending_ble_initiator_connection_request_unittest.cc",
     "pending_ble_listener_connection_request_unittest.cc",
+    "pending_connection_manager_impl_unittest.cc",
     "pending_connection_request_base_unittest.cc",
     "single_client_message_proxy_impl_unittest.cc",
   ]
diff --git a/chromeos/services/secure_channel/connection_role.cc b/chromeos/services/secure_channel/connection_role.cc
new file mode 100644
index 0000000..b40ea0c
--- /dev/null
+++ b/chromeos/services/secure_channel/connection_role.cc
@@ -0,0 +1,25 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/services/secure_channel/connection_role.h"
+
+namespace chromeos {
+
+namespace secure_channel {
+
+std::ostream& operator<<(std::ostream& stream, const ConnectionRole& role) {
+  switch (role) {
+    case ConnectionRole::kInitiatorRole:
+      stream << "[initiator role]";
+      break;
+    case ConnectionRole::kListenerRole:
+      stream << "[listener role]";
+      break;
+  }
+  return stream;
+}
+
+}  // namespace secure_channel
+
+}  // namespace chromeos
diff --git a/chromeos/services/secure_channel/connection_role.h b/chromeos/services/secure_channel/connection_role.h
new file mode 100644
index 0000000..0f98abf
--- /dev/null
+++ b/chromeos/services/secure_channel/connection_role.h
@@ -0,0 +1,30 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_SERVICES_SECURE_CHANNEL_CONNECTION_ROLE_H_
+#define CHROMEOS_SERVICES_SECURE_CHANNEL_CONNECTION_ROLE_H_
+
+#include <ostream>
+
+namespace chromeos {
+
+namespace secure_channel {
+
+// Enumeration of roles which can be used for a connection.
+enum class ConnectionRole {
+  // Initiates a connection to a remote device, which must be in the listener
+  // role.
+  kInitiatorRole,
+
+  // Listens for incoming connections from remote devices in the initiator role.
+  kListenerRole
+};
+
+std::ostream& operator<<(std::ostream& stream, const ConnectionRole& role);
+
+}  // namespace secure_channel
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_SERVICES_SECURE_CHANNEL_CONNECTION_ROLE_H_
diff --git a/chromeos/services/secure_channel/fake_pending_connection_manager.cc b/chromeos/services/secure_channel/fake_pending_connection_manager.cc
new file mode 100644
index 0000000..82e13be7
--- /dev/null
+++ b/chromeos/services/secure_channel/fake_pending_connection_manager.cc
@@ -0,0 +1,44 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/services/secure_channel/fake_pending_connection_manager.h"
+
+#include "chromeos/services/secure_channel/authenticated_channel.h"
+
+namespace chromeos {
+
+namespace secure_channel {
+
+FakePendingConnectionManager::FakePendingConnectionManager(Delegate* delegate)
+    : PendingConnectionManager(delegate) {}
+
+FakePendingConnectionManager::~FakePendingConnectionManager() = default;
+
+void FakePendingConnectionManager::HandleConnectionRequest(
+    const ConnectionDetails& connection_details,
+    ClientConnectionParameters client_connection_parameters,
+    ConnectionRole connection_role) {
+  handled_requests_.push_back(std::make_tuple(
+      connection_details, std::move(client_connection_parameters),
+      connection_role));
+}
+
+FakePendingConnectionManagerDelegate::FakePendingConnectionManagerDelegate() =
+    default;
+
+FakePendingConnectionManagerDelegate::~FakePendingConnectionManagerDelegate() =
+    default;
+
+void FakePendingConnectionManagerDelegate::OnConnection(
+    std::unique_ptr<AuthenticatedChannel> authenticated_channel,
+    std::vector<ClientConnectionParameters> clients,
+    const ConnectionDetails& connection_details) {
+  received_connections_list_.push_back(
+      std::make_tuple(std::move(authenticated_channel), std::move(clients),
+                      connection_details));
+}
+
+}  // namespace secure_channel
+
+}  // namespace chromeos
diff --git a/chromeos/services/secure_channel/fake_pending_connection_manager.h b/chromeos/services/secure_channel/fake_pending_connection_manager.h
new file mode 100644
index 0000000..0c7cfcc
--- /dev/null
+++ b/chromeos/services/secure_channel/fake_pending_connection_manager.h
@@ -0,0 +1,75 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_SERVICES_SECURE_CHANNEL_FAKE_PENDING_CONNECTION_MANAGER_H_
+#define CHROMEOS_SERVICES_SECURE_CHANNEL_FAKE_PENDING_CONNECTION_MANAGER_H_
+
+#include <tuple>
+#include <vector>
+
+#include "base/macros.h"
+#include "chromeos/services/secure_channel/client_connection_parameters.h"
+#include "chromeos/services/secure_channel/connection_details.h"
+#include "chromeos/services/secure_channel/connection_role.h"
+#include "chromeos/services/secure_channel/pending_connection_manager.h"
+
+namespace chromeos {
+
+namespace secure_channel {
+
+// Test PendingConnectionManager implementation.
+class FakePendingConnectionManager : public PendingConnectionManager {
+ public:
+  FakePendingConnectionManager(Delegate* delegate);
+  ~FakePendingConnectionManager() override;
+
+  using HandledRequestsList = std::vector<std::tuple<ConnectionDetails,
+                                                     ClientConnectionParameters,
+                                                     ConnectionRole>>;
+  HandledRequestsList& handled_requests() { return handled_requests_; }
+
+  // Make NotifyOnConnection() public for testing.
+  using PendingConnectionManager::NotifyOnConnection;
+
+ private:
+  void HandleConnectionRequest(
+      const ConnectionDetails& connection_details,
+      ClientConnectionParameters client_connection_parameters,
+      ConnectionRole connection_role) override;
+
+  HandledRequestsList handled_requests_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakePendingConnectionManager);
+};
+
+// Test PendingConnectionManager::Delegate implementation.
+class FakePendingConnectionManagerDelegate
+    : public PendingConnectionManager::Delegate {
+ public:
+  FakePendingConnectionManagerDelegate();
+  ~FakePendingConnectionManagerDelegate() override;
+
+  using ReceivedConnectionsList =
+      std::vector<std::tuple<std::unique_ptr<AuthenticatedChannel>,
+                             std::vector<ClientConnectionParameters>,
+                             ConnectionDetails>>;
+  ReceivedConnectionsList& received_connections_list() {
+    return received_connections_list_;
+  }
+
+ private:
+  void OnConnection(std::unique_ptr<AuthenticatedChannel> authenticated_channel,
+                    std::vector<ClientConnectionParameters> clients,
+                    const ConnectionDetails& connection_details) override;
+
+  ReceivedConnectionsList received_connections_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakePendingConnectionManagerDelegate);
+};
+
+}  // namespace secure_channel
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_SERVICES_SECURE_CHANNEL_FAKE_PENDING_CONNECTION_MANAGER_H_
diff --git a/chromeos/services/secure_channel/pending_connection_manager.cc b/chromeos/services/secure_channel/pending_connection_manager.cc
new file mode 100644
index 0000000..0ffb456
--- /dev/null
+++ b/chromeos/services/secure_channel/pending_connection_manager.cc
@@ -0,0 +1,30 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/services/secure_channel/pending_connection_manager.h"
+
+#include "chromeos/services/secure_channel/authenticated_channel.h"
+
+namespace chromeos {
+
+namespace secure_channel {
+
+PendingConnectionManager::PendingConnectionManager(Delegate* delegate)
+    : delegate_(delegate) {
+  DCHECK(delegate);
+}
+
+PendingConnectionManager::~PendingConnectionManager() = default;
+
+void PendingConnectionManager::NotifyOnConnection(
+    std::unique_ptr<AuthenticatedChannel> authenticated_channel,
+    std::vector<ClientConnectionParameters> clients,
+    const ConnectionDetails& connection_details) {
+  delegate_->OnConnection(std::move(authenticated_channel), std::move(clients),
+                          connection_details);
+}
+
+}  // namespace secure_channel
+
+}  // namespace chromeos
diff --git a/chromeos/services/secure_channel/pending_connection_manager.h b/chromeos/services/secure_channel/pending_connection_manager.h
new file mode 100644
index 0000000..e5f2dd1
--- /dev/null
+++ b/chromeos/services/secure_channel/pending_connection_manager.h
@@ -0,0 +1,65 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_SERVICES_SECURE_CHANNEL_PENDING_CONNECTION_MANAGER_H_
+#define CHROMEOS_SERVICES_SECURE_CHANNEL_PENDING_CONNECTION_MANAGER_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/macros.h"
+#include "chromeos/services/secure_channel/client_connection_parameters.h"
+#include "chromeos/services/secure_channel/connection_details.h"
+#include "chromeos/services/secure_channel/connection_role.h"
+
+namespace chromeos {
+
+namespace secure_channel {
+
+class AuthenticatedChannel;
+
+// Attempts to create connections to remote devices. If a connection request
+// fails or is canceled, the client will be notified. If a connection is
+// created successfully, PendingConnectionManager notifies its delegate.
+class PendingConnectionManager {
+ public:
+  class Delegate {
+   public:
+    virtual ~Delegate() = default;
+    virtual void OnConnection(
+        std::unique_ptr<AuthenticatedChannel> authenticated_channel,
+        std::vector<ClientConnectionParameters> clients,
+        const ConnectionDetails& connection_details) = 0;
+  };
+
+  virtual ~PendingConnectionManager();
+
+  // Attempts a connection according to the provided |connection_details|. If
+  // other clients have requested a connection with the same details, a single
+  // connection attempt is created which combines all clients which would like
+  // to connect to the same device.
+  virtual void HandleConnectionRequest(
+      const ConnectionDetails& connection_details,
+      ClientConnectionParameters client_connection_parameters,
+      ConnectionRole connection_role) = 0;
+
+ protected:
+  PendingConnectionManager(Delegate* delegate);
+
+  void NotifyOnConnection(
+      std::unique_ptr<AuthenticatedChannel> authenticated_channel,
+      std::vector<ClientConnectionParameters> clients,
+      const ConnectionDetails& connection_details);
+
+ private:
+  Delegate* delegate_;
+
+  DISALLOW_COPY_AND_ASSIGN(PendingConnectionManager);
+};
+
+}  // namespace secure_channel
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_SERVICES_SECURE_CHANNEL_PENDING_CONNECTION_MANAGER_H_
diff --git a/chromeos/services/secure_channel/pending_connection_manager_impl.cc b/chromeos/services/secure_channel/pending_connection_manager_impl.cc
new file mode 100644
index 0000000..12e1129
--- /dev/null
+++ b/chromeos/services/secure_channel/pending_connection_manager_impl.cc
@@ -0,0 +1,55 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/services/secure_channel/pending_connection_manager_impl.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/no_destructor.h"
+
+namespace chromeos {
+
+namespace secure_channel {
+
+// static
+PendingConnectionManagerImpl::Factory*
+    PendingConnectionManagerImpl::Factory::test_factory_ = nullptr;
+
+// static
+PendingConnectionManagerImpl::Factory*
+PendingConnectionManagerImpl::Factory::Get() {
+  if (test_factory_)
+    return test_factory_;
+
+  static base::NoDestructor<Factory> factory;
+  return factory.get();
+}
+
+// static
+void PendingConnectionManagerImpl::Factory::SetFactoryForTesting(
+    Factory* test_factory) {
+  test_factory_ = test_factory;
+}
+
+PendingConnectionManagerImpl::Factory::~Factory() = default;
+
+std::unique_ptr<PendingConnectionManager>
+PendingConnectionManagerImpl::Factory::BuildInstance(Delegate* delegate) {
+  return base::WrapUnique(new PendingConnectionManagerImpl(delegate));
+}
+
+PendingConnectionManagerImpl::PendingConnectionManagerImpl(Delegate* delegate)
+    : PendingConnectionManager(delegate) {}
+
+PendingConnectionManagerImpl::~PendingConnectionManagerImpl() = default;
+
+void PendingConnectionManagerImpl::HandleConnectionRequest(
+    const ConnectionDetails& connection_details,
+    ClientConnectionParameters client_connection_parameters,
+    ConnectionRole connection_role) {
+  NOTIMPLEMENTED();
+}
+
+}  // namespace secure_channel
+
+}  // namespace chromeos
diff --git a/chromeos/services/secure_channel/pending_connection_manager_impl.h b/chromeos/services/secure_channel/pending_connection_manager_impl.h
new file mode 100644
index 0000000..da95053c
--- /dev/null
+++ b/chromeos/services/secure_channel/pending_connection_manager_impl.h
@@ -0,0 +1,54 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_SERVICES_SECURE_CHANNEL_PENDING_CONNECTION_MANAGER_IMPL_H_
+#define CHROMEOS_SERVICES_SECURE_CHANNEL_PENDING_CONNECTION_MANAGER_IMPL_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/macros.h"
+#include "chromeos/services/secure_channel/client_connection_parameters.h"
+#include "chromeos/services/secure_channel/connection_details.h"
+#include "chromeos/services/secure_channel/connection_role.h"
+#include "chromeos/services/secure_channel/pending_connection_manager.h"
+
+namespace chromeos {
+
+namespace secure_channel {
+
+// Concrete PendingConnectionManager implementation.
+// TODO(khorimoto): Implement.
+class PendingConnectionManagerImpl : public PendingConnectionManager {
+ public:
+  class Factory {
+   public:
+    static Factory* Get();
+    static void SetFactoryForTesting(Factory* test_factory);
+    virtual ~Factory();
+    virtual std::unique_ptr<PendingConnectionManager> BuildInstance(
+        Delegate* delegate);
+
+   private:
+    static Factory* test_factory_;
+  };
+
+  ~PendingConnectionManagerImpl() override;
+
+ private:
+  PendingConnectionManagerImpl(Delegate* delegate);
+
+  void HandleConnectionRequest(
+      const ConnectionDetails& connection_details,
+      ClientConnectionParameters client_connection_parameters,
+      ConnectionRole connection_role) override;
+
+  DISALLOW_COPY_AND_ASSIGN(PendingConnectionManagerImpl);
+};
+
+}  // namespace secure_channel
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_SERVICES_SECURE_CHANNEL_PENDING_CONNECTION_MANAGER_IMPL_H_
diff --git a/chromeos/services/secure_channel/pending_connection_manager_impl_unittest.cc b/chromeos/services/secure_channel/pending_connection_manager_impl_unittest.cc
new file mode 100644
index 0000000..4743943
--- /dev/null
+++ b/chromeos/services/secure_channel/pending_connection_manager_impl_unittest.cc
@@ -0,0 +1,47 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/services/secure_channel/pending_connection_manager_impl.h"
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/test/scoped_task_environment.h"
+#include "chromeos/services/secure_channel/fake_pending_connection_manager.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromeos {
+
+namespace secure_channel {
+
+class SecureChannelPendingConnectionManagerImplTest : public testing::Test {
+ protected:
+  SecureChannelPendingConnectionManagerImplTest() = default;
+  ~SecureChannelPendingConnectionManagerImplTest() override = default;
+
+  // testing::Test:
+  void SetUp() override {
+    fake_delegate_ = std::make_unique<FakePendingConnectionManagerDelegate>();
+
+    manager_ = PendingConnectionManagerImpl::Factory::Get()->BuildInstance(
+        fake_delegate_.get());
+  }
+
+ private:
+  const base::test::ScopedTaskEnvironment scoped_task_environment_;
+
+  std::unique_ptr<FakePendingConnectionManagerDelegate> fake_delegate_;
+
+  std::unique_ptr<PendingConnectionManager> manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(SecureChannelPendingConnectionManagerImplTest);
+};
+
+TEST_F(SecureChannelPendingConnectionManagerImplTest, Test) {
+  // TODO(khorimoto): Add tests once implementation is complete.
+}
+
+}  // namespace secure_channel
+
+}  // namespace chromeos
diff --git a/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerUma.java b/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerUma.java
index e7f69d6e..c060c95 100644
--- a/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerUma.java
+++ b/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerUma.java
@@ -268,6 +268,6 @@
     }
 
     void assertNativeIsLoaded() {
-        assert LibraryLoader.isInitialized();
+        assert LibraryLoader.getInstance().isInitialized();
     }
 }
diff --git a/components/crash/content/browser/crash_dump_manager_android.cc b/components/crash/content/browser/crash_dump_manager_android.cc
index 9d5d271..e506deac 100644
--- a/components/crash/content/browser/crash_dump_manager_android.cc
+++ b/components/crash/content/browser/crash_dump_manager_android.cc
@@ -43,7 +43,9 @@
   kRendererForegroundVisibleSubframeCrash = 6,
   kGpuCrashAll = 7,
   kRendererCrashAll = 8,
-  kMaxValue = kRendererCrashAll
+  kRendererForegroundVisibleMainFrameIntentionalKill = 9,
+  kRendererForegroundVisibleNormalTermNoMinidump = 10,
+  kMaxValue = kRendererForegroundVisibleNormalTermNoMinidump
 };
 
 void LogCount(ProcessedCrashCounts type) {
@@ -73,25 +75,31 @@
 
   if (info.process_type == content::PROCESS_TYPE_RENDERER) {
     if (app_foreground && renderer_visible) {
-      if (android_oom_kill) {
-        LogCount(
-            renderer_sub_frame
-                ? ProcessedCrashCounts::kRendererForegroundVisibleSubframeOom
-                : ProcessedCrashCounts::kRendererForegroundVisibleOom);
-      }
       if (has_crash_dump) {
         LogCount(
             renderer_sub_frame
                 ? ProcessedCrashCounts::kRendererForegroundVisibleSubframeCrash
                 : ProcessedCrashCounts::kRendererForegroundVisibleCrash);
+      } else if (intentional_kill) {
+        LogCount(renderer_sub_frame
+                     ? ProcessedCrashCounts::
+                           kRendererForegroundVisibleSubframeIntentionalKill
+                     : ProcessedCrashCounts::
+                           kRendererForegroundVisibleMainFrameIntentionalKill);
+      } else if (info.normal_termination) {
+        LogCount(ProcessedCrashCounts::
+                     kRendererForegroundVisibleNormalTermNoMinidump);
+      } else {
+        DCHECK(android_oom_kill);
+        LogCount(
+            renderer_sub_frame
+                ? ProcessedCrashCounts::kRendererForegroundVisibleSubframeOom
+                : ProcessedCrashCounts::kRendererForegroundVisibleOom);
       }
     }
+
     if (app_foreground && intentional_kill) {
       LogCount(ProcessedCrashCounts::kRendererForegroundIntentionalKill);
-      if (renderer_visible && renderer_sub_frame) {
-        LogCount(ProcessedCrashCounts::
-                     kRendererForegroundVisibleSubframeIntentionalKill);
-      }
     }
     if (has_crash_dump) {
       LogCount(ProcessedCrashCounts::kRendererCrashAll);
diff --git a/components/cronet/stale_host_resolver.cc b/components/cronet/stale_host_resolver.cc
index d8e7dc9..c305b2c 100644
--- a/components/cronet/stale_host_resolver.cc
+++ b/components/cronet/stale_host_resolver.cc
@@ -85,7 +85,7 @@
 // network data, or stale cached data.
 class StaleHostResolver::RequestImpl {
  public:
-  RequestImpl();
+  explicit RequestImpl(const base::TickClock* tick_clock);
   ~RequestImpl();
 
   // A callback for the caller to decide whether a stale entry is usable or not.
@@ -170,6 +170,8 @@
   // A timer that fires when the |Request| should return stale results, if the
   // underlying network request has not finished yet.
   base::OneShotTimer stale_timer_;
+  // Shared instance of tick clock, overridden for testing.
+  const base::TickClock* tick_clock_;
 
   // The address list the underlying network request will fill in. (Can't be the
   // one passed to |Start()|, or else the network request would overwrite stale
@@ -190,10 +192,12 @@
   Handle* handle_;
 };
 
-StaleHostResolver::RequestImpl::RequestImpl()
+StaleHostResolver::RequestImpl::RequestImpl(const base::TickClock* tick_clock)
     : result_addresses_(nullptr),
       returning_result_(false),
       stale_error_(net::ERR_DNS_CACHE_MISS),
+      stale_timer_(tick_clock),
+      tick_clock_(tick_clock),
       restore_size_(0),
       current_size_(0),
       handle_(nullptr) {}
@@ -341,7 +345,7 @@
     int error,
     bool returned_stale_data) {
   if (have_stale_data())
-    RecordTimeDelta(base::TimeTicks::Now(), stale_timer_.desired_run_time());
+    RecordTimeDelta(tick_clock_->NowTicks(), stale_timer_.desired_run_time());
 
   if (returned_stale_data && stale_error_ == net::OK && error == net::OK) {
     RecordAddressListDelta(
@@ -384,10 +388,10 @@
                                const net::CompletionCallback& callback,
                                std::unique_ptr<Request>* out_req,
                                const net::NetLogWithSource& net_log) {
+  DCHECK(tick_clock_);
   StaleHostResolver::RequestImpl::StaleEntryUsableCallback usable_callback =
       base::Bind(&StaleEntryIsUsable, options_);
-
-  RequestImpl* request = new RequestImpl();
+  RequestImpl* request = new RequestImpl(tick_clock_);
   int rv =
       request->Start(inner_resolver_.get(), info, priority, addresses, callback,
                      out_req, net_log, usable_callback, options_.delay);
@@ -431,4 +435,10 @@
   return inner_resolver_->GetDnsConfigAsValue();
 }
 
+void StaleHostResolver::SetTickClockForTesting(
+    const base::TickClock* tick_clock) {
+  tick_clock_ = tick_clock;
+  inner_resolver_->SetTickClockForTesting(tick_clock);
+}
+
 }  // namespace net
diff --git a/components/cronet/stale_host_resolver.h b/components/cronet/stale_host_resolver.h
index 2a9115b1..e200d95 100644
--- a/components/cronet/stale_host_resolver.h
+++ b/components/cronet/stale_host_resolver.h
@@ -7,10 +7,18 @@
 
 #include <unordered_set>
 
+#include "base/time/default_tick_clock.h"
 #include "net/dns/host_resolver.h"
 #include "net/dns/host_resolver_impl.h"
 
+namespace base {
+class TickClock;
+}  // namespace base
+
 namespace cronet {
+namespace {
+class StaleHostResolverTest;
+}  // namespace
 
 // A HostResolver that wraps a HostResolverImpl and uses it to make requests,
 // but "impatiently" returns stale data (if available and usable) after a delay,
@@ -84,14 +92,21 @@
 
  private:
   class RequestImpl;
+  friend class StaleHostResolverTest;
 
   // Called from |Request| when a request is complete and can be destroyed.
   void OnRequestComplete(Request* request);
 
+  // Set |tick_clock_| for testing. Must be set before issuing any requests.
+  void SetTickClockForTesting(const base::TickClock* tick_clock);
+
   // The underlying HostResolverImpl that will be used to make cache and network
   // requests.
   std::unique_ptr<net::HostResolverImpl> inner_resolver_;
 
+  // Shared instance of tick clock, overridden for testing.
+  const base::TickClock* tick_clock_ = base::DefaultTickClock::GetInstance();
+
   // Options that govern when a stale response can or can't be returned.
   StaleOptions options_;
 
diff --git a/components/cronet/stale_host_resolver_unittest.cc b/components/cronet/stale_host_resolver_unittest.cc
index 6cc3bb3c..126e58a 100644
--- a/components/cronet/stale_host_resolver_unittest.cc
+++ b/components/cronet/stale_host_resolver_unittest.cc
@@ -14,11 +14,14 @@
 #include "base/memory/ref_counted.h"
 #include "base/run_loop.h"
 #include "base/test/scoped_task_environment.h"
+#include "base/test/simple_test_tick_clock.h"
 #include "base/test/test_timeouts.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
 #include "base/values.h"
 #include "build/build_config.h"
 #include "components/cronet/url_request_context_config.h"
+#include "net/base/mock_network_change_notifier.h"
 #include "net/base/net_errors.h"
 #include "net/base/network_change_notifier.h"
 #include "net/cert/cert_verifier.h"
@@ -34,10 +37,6 @@
 #include "net/url_request/url_request_context_builder.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if defined(OS_ANDROID)
-#include "net/android/network_change_notifier_factory_android.h"
-#endif
-
 namespace cronet {
 
 namespace {
@@ -104,7 +103,10 @@
         mock_proc_(new MockHostResolverProc()),
         resolver_(nullptr),
         resolve_pending_(false),
-        resolve_complete_(false) {}
+        resolve_complete_(false) {
+    // Make value clock not empty.
+    tick_clock_.Advance(base::TimeDelta::FromMicroseconds(1));
+  }
 
   ~StaleHostResolverTest() override {}
 
@@ -137,6 +139,7 @@
 
     stale_resolver_ = std::make_unique<StaleHostResolver>(
         std::move(inner_resolver), options_);
+    stale_resolver_->SetTickClockForTesting(&tick_clock_);
     resolver_ = stale_resolver_.get();
   }
 
@@ -162,14 +165,6 @@
     resolver_ = nullptr;
   }
 
-  void CreateNetworkChangeNotifier() {
-#if defined(OS_ANDROID)
-    net::NetworkChangeNotifier::SetFactory(
-        new net::NetworkChangeNotifierFactoryAndroid());
-#endif
-    net::NetworkChangeNotifier::Create();
-  }
-
   // Creates a cache entry for |kHostname| that is |age_sec| seconds old.
   void CreateCacheEntry(int age_sec, int error) {
     DCHECK(resolver_);
@@ -182,7 +177,7 @@
         error == net::OK ? MakeAddressList(kCacheAddress) : net::AddressList(),
         net::HostCache::Entry::SOURCE_UNKNOWN, ttl);
     base::TimeDelta age = base::TimeDelta::FromSeconds(age_sec);
-    base::TimeTicks then = base::TimeTicks::Now() - age;
+    base::TimeTicks then = tick_clock_.NowTicks() - age;
     resolver_->GetHostCache()->Set(key, entry, then, ttl);
   }
 
@@ -198,7 +193,7 @@
     DCHECK(resolver_->GetHostCache());
 
     net::HostCache::Key key(kHostname, net::ADDRESS_FAMILY_IPV4, 0);
-    base::TimeTicks now = base::TimeTicks::Now();
+    base::TimeTicks now = tick_clock_.NowTicks();
     const net::HostCache::Entry* entry;
     net::HostCache::EntryStaleness stale;
     entry = resolver_->GetHostCache()->LookupStale(key, now, &stale);
@@ -274,6 +269,8 @@
       base::ResetAndReturn(&resolve_closure_).Run();
   }
 
+  void AdvanceTickClock(base::TimeDelta delta) { tick_clock_.Advance(delta); }
+
   bool resolve_complete() const { return resolve_complete_; }
   int resolve_error() const { return resolve_error_; }
   const net::AddressList& resolve_addresses() const {
@@ -283,6 +280,8 @@
  private:
   // Needed for HostResolver to run HostResolverProc callbacks.
   base::test::ScopedTaskEnvironment scoped_task_environment_;
+  base::SimpleTestTickClock tick_clock_;
+  net::test::MockNetworkChangeNotifier mock_network_change_notifier_;
 
   scoped_refptr<MockHostResolverProc> mock_proc_;
 
@@ -412,12 +411,10 @@
 // CancelWithFreshCache makes no sense; the request would've returned
 // synchronously.
 
-// Limited expired time cases are flaky under iOS and MACOS (crbug.com/792173).
 // Disallow other networks cases fail under Fuchsia (crbug.com/816143).
 // TODO(https://crbug.com/829097): Fix memory leaks and re-enable under ASAN.
 // Flaky on Win buildbots. See crbug.com/836106
-#if defined(OS_IOS) || defined(OS_FUCHSIA) || defined(OS_MACOSX) || \
-    defined(ADDRESS_SANITIZER) || defined(OS_WIN) || defined(OS_LINUX)
+#if defined(ADDRESS_SANITIZER) || defined(OS_WIN) || defined(OS_LINUX)
 #define MAYBE_StaleUsability DISABLED_StaleUsability
 #else
 #define MAYBE_StaleUsability StaleUsability
@@ -478,7 +475,6 @@
   };
 
   SetStaleDelay(kNoStaleDelaySec);
-  CreateNetworkChangeNotifier();
 
   for (size_t i = 0; i < arraysize(kUsabilityTestCases); ++i) {
     const auto& test_case = kUsabilityTestCases[i];
@@ -487,11 +483,16 @@
                       test_case.allow_other_network);
     CreateResolver();
     CreateCacheEntry(kCacheEntryTTLSec + test_case.age_sec, test_case.error);
+
+    AdvanceTickClock(base::TimeDelta::FromMilliseconds(1));
     for (int j = 0; j < test_case.network_changes; ++j)
       OnNetworkChange();
+
+    AdvanceTickClock(base::TimeDelta::FromMilliseconds(1));
     for (int j = 0; j < test_case.stale_use - 1; ++j)
       LookupStale();
 
+    AdvanceTickClock(base::TimeDelta::FromMilliseconds(1));
     Resolve();
     WaitForResolve();
     EXPECT_TRUE(resolve_complete()) << i;
@@ -514,6 +515,10 @@
             << i;
       }
     }
+    // Make sure that all tasks complete so jobs are freed properly.
+    AdvanceTickClock(base::TimeDelta::FromSeconds(kLongStaleDelaySec));
+    base::RunLoop run_loop;
+    run_loop.RunUntilIdle();
 
     DestroyResolver();
   }
diff --git a/components/cryptauth/BUILD.gn b/components/cryptauth/BUILD.gn
index 791f0b9..49fa0aa 100644
--- a/components/cryptauth/BUILD.gn
+++ b/components/cryptauth/BUILD.gn
@@ -53,6 +53,8 @@
     "device_to_device_initiator_helper.h",
     "device_to_device_secure_context.cc",
     "device_to_device_secure_context.h",
+    "expiring_remote_device_cache.cc",
+    "expiring_remote_device_cache.h",
     "foreground_eid_generator.cc",
     "foreground_eid_generator.h",
     "gcm_device_info_provider.h",
@@ -197,6 +199,7 @@
     "device_to_device_authenticator_unittest.cc",
     "device_to_device_operations_unittest.cc",
     "device_to_device_secure_context_unittest.cc",
+    "expiring_remote_device_cache_unittest.cc",
     "fake_secure_message_delegate_unittest.cc",
     "foreground_eid_generator_unittest.cc",
     "local_device_data_provider_unittest.cc",
diff --git a/components/cryptauth/expiring_remote_device_cache.cc b/components/cryptauth/expiring_remote_device_cache.cc
new file mode 100644
index 0000000..7cc4532
--- /dev/null
+++ b/components/cryptauth/expiring_remote_device_cache.cc
@@ -0,0 +1,49 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/cryptauth/expiring_remote_device_cache.h"
+
+#include "base/stl_util.h"
+#include "components/cryptauth/remote_device_cache.h"
+
+namespace cryptauth {
+
+ExpiringRemoteDeviceCache::ExpiringRemoteDeviceCache()
+    : remote_device_cache_(std::make_unique<RemoteDeviceCache>()) {}
+
+ExpiringRemoteDeviceCache::~ExpiringRemoteDeviceCache() = default;
+
+void ExpiringRemoteDeviceCache::SetRemoteDevicesAndInvalidateOldEntries(
+    const RemoteDeviceList& remote_devices) {
+  remote_device_cache_->SetRemoteDevices(remote_devices);
+
+  device_ids_from_last_set_call_.clear();
+  for (const auto& device : remote_devices)
+    device_ids_from_last_set_call_.insert(device.GetDeviceId());
+}
+
+RemoteDeviceRefList ExpiringRemoteDeviceCache::GetNonExpiredRemoteDevices()
+    const {
+  // Only add to the output list if the entry is not stale.
+  RemoteDeviceRefList remote_devices;
+  for (auto device : remote_device_cache_->GetRemoteDevices()) {
+    if (device_ids_from_last_set_call_.count(device.GetDeviceId()) != 0)
+      remote_devices.push_back(device);
+  }
+
+  return remote_devices;
+}
+
+void ExpiringRemoteDeviceCache::UpdateRemoteDevice(
+    const RemoteDevice& remote_device) {
+  remote_device_cache_->SetRemoteDevices({remote_device});
+  device_ids_from_last_set_call_.insert(remote_device.GetDeviceId());
+}
+
+base::Optional<RemoteDeviceRef> ExpiringRemoteDeviceCache::GetRemoteDevice(
+    const std::string& device_id) const {
+  return remote_device_cache_->GetRemoteDevice(device_id);
+}
+
+}  // namespace cryptauth
\ No newline at end of file
diff --git a/components/cryptauth/expiring_remote_device_cache.h b/components/cryptauth/expiring_remote_device_cache.h
new file mode 100644
index 0000000..a96beae
--- /dev/null
+++ b/components/cryptauth/expiring_remote_device_cache.h
@@ -0,0 +1,54 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_CRYPTAUTH_EXPIRING_REMOTE_DEVICE_CACHE_H_
+#define COMPONENTS_CRYPTAUTH_EXPIRING_REMOTE_DEVICE_CACHE_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/optional.h"
+#include "components/cryptauth/remote_device.h"
+#include "components/cryptauth/remote_device_ref.h"
+
+namespace cryptauth {
+
+class RemoteDeviceCache;
+
+// A helper class around RemoteDeviceCache which keeps track of which devices
+// have been removed from, or "expired" in, the cache.
+//
+// If the set of devices provided to SetRemoteDevicesAndInvalidateOldEntries()
+// is different from the set provided to a previous call to
+// SetRemoteDevicesAndInvalidateOldEntries(), then the devices in the previous
+// call which are not in the new call will be marked as stale. Stale devices are
+// still valid RemoteDeviceRefs (preventing clients from segfaulting), but will
+// not be returned by GetNonExpiredRemoteDevices().
+class ExpiringRemoteDeviceCache {
+ public:
+  ExpiringRemoteDeviceCache();
+  virtual ~ExpiringRemoteDeviceCache();
+
+  void SetRemoteDevicesAndInvalidateOldEntries(
+      const RemoteDeviceList& remote_devices);
+
+  RemoteDeviceRefList GetNonExpiredRemoteDevices() const;
+
+  // Add or update a RemoteDevice without marking any other devices in the cache
+  // as stale.
+  void UpdateRemoteDevice(const RemoteDevice& remote_device);
+
+  base::Optional<RemoteDeviceRef> GetRemoteDevice(
+      const std::string& device_id) const;
+
+ private:
+  std::unique_ptr<RemoteDeviceCache> remote_device_cache_;
+  std::set<std::string> device_ids_from_last_set_call_;
+
+  DISALLOW_COPY_AND_ASSIGN(ExpiringRemoteDeviceCache);
+};
+
+}  // namespace cryptauth
+
+#endif  // COMPONENTS_CRYPTAUTH_EXPIRING_REMOTE_DEVICE_CACHE_H_
diff --git a/components/cryptauth/expiring_remote_device_cache_unittest.cc b/components/cryptauth/expiring_remote_device_cache_unittest.cc
new file mode 100644
index 0000000..3a510c8
--- /dev/null
+++ b/components/cryptauth/expiring_remote_device_cache_unittest.cc
@@ -0,0 +1,74 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/cryptauth/expiring_remote_device_cache.h"
+
+#include <algorithm>
+
+#include "components/cryptauth/remote_device_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cryptauth {
+
+class ExpiringRemoteDeviceCacheTest : public testing::Test {
+ protected:
+  ExpiringRemoteDeviceCacheTest()
+      : test_remote_device_list_(CreateRemoteDeviceListForTest(5)),
+        test_remote_device_ref_list_(CreateRemoteDeviceRefListForTest(5)){};
+
+  // testing::Test:
+  void SetUp() override {
+    cache_ = std::make_unique<ExpiringRemoteDeviceCache>();
+  }
+
+  void VerifyCacheRemoteDevices(
+      RemoteDeviceRefList expected_remote_device_ref_list) {
+    RemoteDeviceRefList remote_device_ref_list =
+        cache_->GetNonExpiredRemoteDevices();
+    std::sort(remote_device_ref_list.begin(), remote_device_ref_list.end(),
+              [](const auto& device_1, const auto& device_2) {
+                return device_1 < device_2;
+              });
+
+    EXPECT_EQ(expected_remote_device_ref_list, remote_device_ref_list);
+  }
+
+  const RemoteDeviceList test_remote_device_list_;
+  const RemoteDeviceRefList test_remote_device_ref_list_;
+  std::unique_ptr<ExpiringRemoteDeviceCache> cache_;
+
+  DISALLOW_COPY_AND_ASSIGN(ExpiringRemoteDeviceCacheTest);
+};
+
+TEST_F(ExpiringRemoteDeviceCacheTest,
+       TestSetRemoteDevices_RemoteDeviceRefsRemoved) {
+  cache_->SetRemoteDevicesAndInvalidateOldEntries(test_remote_device_list_);
+
+  VerifyCacheRemoteDevices(test_remote_device_ref_list_);
+
+  cache_->SetRemoteDevicesAndInvalidateOldEntries(RemoteDeviceList());
+
+  VerifyCacheRemoteDevices(RemoteDeviceRefList());
+}
+
+TEST_F(ExpiringRemoteDeviceCacheTest,
+       TestSetRemoteDevices_DeviceRemovedAndAddedBack) {
+  cache_->SetRemoteDevicesAndInvalidateOldEntries(test_remote_device_list_);
+  cache_->SetRemoteDevicesAndInvalidateOldEntries(RemoteDeviceList());
+  cache_->SetRemoteDevicesAndInvalidateOldEntries(test_remote_device_list_);
+
+  VerifyCacheRemoteDevices(test_remote_device_ref_list_);
+}
+
+TEST_F(ExpiringRemoteDeviceCacheTest, TestUpdateRemoteDevice) {
+  cache_->SetRemoteDevicesAndInvalidateOldEntries(test_remote_device_list_);
+
+  VerifyCacheRemoteDevices(test_remote_device_ref_list_);
+
+  cache_->UpdateRemoteDevice(test_remote_device_list_[0]);
+
+  VerifyCacheRemoteDevices(test_remote_device_ref_list_);
+}
+
+}  // namespace cryptauth
\ No newline at end of file
diff --git a/components/cryptauth/remote_device_cache_unittest.cc b/components/cryptauth/remote_device_cache_unittest.cc
index 28e3b3d3..55b06c1 100644
--- a/components/cryptauth/remote_device_cache_unittest.cc
+++ b/components/cryptauth/remote_device_cache_unittest.cc
@@ -80,6 +80,4 @@
   EXPECT_EQ(remote_device.name, remote_device_ref.name());
 }
 
-// GetRemoteDevice
-
 }  // namespace cryptauth
\ No newline at end of file
diff --git a/components/drive/BUILD.gn b/components/drive/BUILD.gn
index 62924ef..7d04d20 100644
--- a/components/drive/BUILD.gn
+++ b/components/drive/BUILD.gn
@@ -166,6 +166,8 @@
       "chromeos/sync/remove_performer.h",
       "chromeos/sync_client.cc",
       "chromeos/sync_client.h",
+      "chromeos/team_drive_change_list_loader.cc",
+      "chromeos/team_drive_change_list_loader.h",
       "chromeos/team_drive_list_loader.cc",
       "chromeos/team_drive_list_loader.h",
     ]
diff --git a/components/drive/DEPS b/components/drive/DEPS
index 75fb956e3c..0341d6a 100644
--- a/components/drive/DEPS
+++ b/components/drive/DEPS
@@ -54,6 +54,7 @@
   r"|search_metadata_unittest\.cc"
   r"|start_page_token_loader_unittest\.cc"
   r"|sync_client_unittest\.cc"
+  r"|team_drive_change_list_loader_unittest\.cc"
   r"|team_drive_list_loader_unittest\.cc"
   r")": [
     "+content/public/test/test_browser_thread_bundle.h",
diff --git a/components/drive/chromeos/change_list_loader.cc b/components/drive/chromeos/change_list_loader.cc
index 3ce9a1a..3ccda8d 100644
--- a/components/drive/chromeos/change_list_loader.cc
+++ b/components/drive/chromeos/change_list_loader.cc
@@ -19,6 +19,7 @@
 #include "base/time/time.h"
 #include "components/drive/chromeos/change_list_loader_observer.h"
 #include "components/drive/chromeos/change_list_processor.h"
+#include "components/drive/chromeos/drive_file_util.h"
 #include "components/drive/chromeos/loader_controller.h"
 #include "components/drive/chromeos/resource_metadata.h"
 #include "components/drive/chromeos/root_folder_id_loader.h"
@@ -277,8 +278,8 @@
   std::string* start_page_token = new std::string();
   base::PostTaskAndReplyWithResult(
       blocking_task_runner_.get(), FROM_HERE,
-      base::Bind(&ResourceMetadata::GetStartPageToken,
-                 base::Unretained(resource_metadata_), start_page_token),
+      base::Bind(&GetStartPageToken, base::Unretained(resource_metadata_),
+                 team_drive_id_, start_page_token),
       base::Bind(&ChangeListLoader::LoadAfterGetLocalStartPageToken,
                  weak_ptr_factory_.GetWeakPtr(), is_initial_load,
                  base::Owned(start_page_token)));
diff --git a/components/drive/chromeos/team_drive_change_list_loader.cc b/components/drive/chromeos/team_drive_change_list_loader.cc
new file mode 100644
index 0000000..c82d09a8
--- /dev/null
+++ b/components/drive/chromeos/team_drive_change_list_loader.cc
@@ -0,0 +1,137 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/drive/chromeos/team_drive_change_list_loader.h"
+
+#include <memory>
+
+#include "base/sequenced_task_runner.h"
+#include "components/drive/chromeos/change_list_loader.h"
+#include "components/drive/chromeos/directory_loader.h"
+#include "components/drive/chromeos/root_folder_id_loader.h"
+#include "components/drive/chromeos/start_page_token_loader.h"
+
+namespace drive {
+namespace internal {
+
+namespace {
+
+class ConstantRootFolderIdLoader : public RootFolderIdLoader {
+ public:
+  explicit ConstantRootFolderIdLoader(const std::string& team_drive_id)
+      : team_drive_id_(team_drive_id) {}
+  ~ConstantRootFolderIdLoader() override = default;
+
+  void GetRootFolderId(const RootFolderIdCallback& callback) override {
+    callback.Run(FILE_ERROR_OK, team_drive_id_);
+  }
+
+ private:
+  const std::string team_drive_id_;
+};
+
+}  // namespace
+
+TeamDriveChangeListLoader::TeamDriveChangeListLoader(
+    const std::string& team_drive_id,
+    const base::FilePath& root_entry_path,
+    EventLogger* logger,
+    base::SequencedTaskRunner* blocking_task_runner,
+    ResourceMetadata* resource_metadata,
+    JobScheduler* scheduler,
+    LoaderController* apply_task_controller)
+    : team_drive_id_(team_drive_id), root_entry_path_(root_entry_path) {
+  root_folder_id_loader_ =
+      std::make_unique<ConstantRootFolderIdLoader>(team_drive_id_);
+
+  start_page_token_loader_ =
+      std::make_unique<StartPageTokenLoader>(team_drive_id_, scheduler);
+
+  change_list_loader_ = std::make_unique<ChangeListLoader>(
+      logger, blocking_task_runner, resource_metadata, scheduler,
+      root_folder_id_loader_.get(), start_page_token_loader_.get(),
+      apply_task_controller, team_drive_id_, root_entry_path_);
+  change_list_loader_->AddObserver(this);
+
+  directory_loader_ = std::make_unique<DirectoryLoader>(
+      logger, blocking_task_runner, resource_metadata, scheduler,
+      root_folder_id_loader_.get(), start_page_token_loader_.get(),
+      apply_task_controller, root_entry_path_);
+  directory_loader_->AddObserver(this);
+}
+
+TeamDriveChangeListLoader::~TeamDriveChangeListLoader() = default;
+
+// DriveChangeListLoader overrides
+void TeamDriveChangeListLoader::AddObserver(
+    ChangeListLoaderObserver* observer) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  change_list_loader_observers_.AddObserver(observer);
+}
+
+void TeamDriveChangeListLoader::RemoveObserver(
+    ChangeListLoaderObserver* observer) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  change_list_loader_observers_.RemoveObserver(observer);
+}
+
+bool TeamDriveChangeListLoader::IsRefreshing() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  return change_list_loader_->IsRefreshing();
+}
+
+void TeamDriveChangeListLoader::LoadIfNeeded(
+    const FileOperationCallback& callback) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  change_list_loader_->LoadIfNeeded(callback);
+}
+
+void TeamDriveChangeListLoader::ReadDirectory(
+    const base::FilePath& directory_path,
+    const ReadDirectoryEntriesCallback& entries_callback,
+    const FileOperationCallback& completion_callback) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK(root_entry_path_.IsParent(directory_path));
+
+  directory_loader_->ReadDirectory(directory_path, entries_callback,
+                                   completion_callback);
+}
+
+void TeamDriveChangeListLoader::CheckForUpdates(
+    const FileOperationCallback& callback) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  change_list_loader_->CheckForUpdates(callback);
+}
+
+void TeamDriveChangeListLoader::OnDirectoryReloaded(
+    const base::FilePath& directory_path) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  for (auto& observer : change_list_loader_observers_) {
+    observer.OnDirectoryReloaded(directory_path);
+  }
+}
+
+void TeamDriveChangeListLoader::OnFileChanged(const FileChange& changed_files) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  for (auto& observer : change_list_loader_observers_) {
+    observer.OnFileChanged(changed_files);
+  }
+}
+
+void TeamDriveChangeListLoader::OnLoadFromServerComplete() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  for (auto& observer : change_list_loader_observers_) {
+    observer.OnLoadFromServerComplete();
+  }
+}
+
+void TeamDriveChangeListLoader::OnInitialLoadComplete() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  for (auto& observer : change_list_loader_observers_) {
+    observer.OnInitialLoadComplete();
+  }
+}
+
+}  // namespace internal
+}  // namespace drive
diff --git a/components/drive/chromeos/team_drive_change_list_loader.h b/components/drive/chromeos/team_drive_change_list_loader.h
new file mode 100644
index 0000000..91f519c
--- /dev/null
+++ b/components/drive/chromeos/team_drive_change_list_loader.h
@@ -0,0 +1,80 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_DRIVE_CHROMEOS_TEAM_DRIVE_CHANGE_LIST_LOADER_H_
+#define COMPONENTS_DRIVE_CHROMEOS_TEAM_DRIVE_CHANGE_LIST_LOADER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/observer_list.h"
+#include "components/drive/chromeos/change_list_loader_observer.h"
+#include "components/drive/chromeos/drive_change_list_loader.h"
+
+namespace drive {
+
+class EventLogger;
+class JobScheduler;
+
+namespace internal {
+
+class ChangeListLoader;
+class DirectoryLoader;
+class LoaderController;
+class RootFolderIdLoader;
+class StartPageTokenLoader;
+
+// TeamDriveChangeListLoader loads change lists for a specific team drive id.
+// It uses directory loader and change list loader to retrieve change lists
+// into resouce metadata. There should be one TeamDriveChangeListLoader created
+// for every team drive that the user has access to.
+class TeamDriveChangeListLoader : public DriveChangeListLoader,
+                                  public ChangeListLoaderObserver {
+ public:
+  TeamDriveChangeListLoader(const std::string& team_drive_id,
+                            const base::FilePath& root_entry_path,
+                            EventLogger* logger,
+                            base::SequencedTaskRunner* blocking_task_runner,
+                            ResourceMetadata* resource_metadata,
+                            JobScheduler* scheduler,
+                            LoaderController* apply_task_controller);
+
+  ~TeamDriveChangeListLoader() override;
+
+  // DriveChangeListLoader overrides
+  void AddObserver(ChangeListLoaderObserver* observer) override;
+  void RemoveObserver(ChangeListLoaderObserver* observer) override;
+  bool IsRefreshing() override;
+  void LoadIfNeeded(const FileOperationCallback& callback) override;
+  void ReadDirectory(const base::FilePath& directory_path,
+                     const ReadDirectoryEntriesCallback& entries_callback,
+                     const FileOperationCallback& completion_callback) override;
+  void CheckForUpdates(const FileOperationCallback& callback) override;
+
+  // ChangeListLoaderObserver overrides
+  void OnDirectoryReloaded(const base::FilePath& directory_path) override;
+  void OnFileChanged(const FileChange& changed_files) override;
+  void OnLoadFromServerComplete() override;
+  void OnInitialLoadComplete() override;
+
+ private:
+  std::unique_ptr<RootFolderIdLoader> root_folder_id_loader_;
+  std::unique_ptr<StartPageTokenLoader> start_page_token_loader_;
+  std::unique_ptr<ChangeListLoader> change_list_loader_;
+  std::unique_ptr<DirectoryLoader> directory_loader_;
+
+  const std::string team_drive_id_;
+  const base::FilePath root_entry_path_;
+  base::ObserverList<ChangeListLoaderObserver> change_list_loader_observers_;
+
+  THREAD_CHECKER(thread_checker_);
+
+  DISALLOW_COPY_AND_ASSIGN(TeamDriveChangeListLoader);
+};
+
+}  // namespace internal
+}  // namespace drive
+
+#endif  // COMPONENTS_DRIVE_CHROMEOS_TEAM_DRIVE_CHANGE_LIST_LOADER_H_
diff --git a/components/drive/job_scheduler_unittest.cc b/components/drive/job_scheduler_unittest.cc
index b7654acb..1fb1475 100644
--- a/components/drive/job_scheduler_unittest.cc
+++ b/components/drive/job_scheduler_unittest.cc
@@ -21,6 +21,7 @@
 #include "components/drive/chromeos/drive_test_util.h"
 #include "components/drive/drive_pref_names.h"
 #include "components/drive/event_logger.h"
+#include "components/drive/file_system_core_util.h"
 #include "components/drive/service/fake_drive_service.h"
 #include "components/drive/service/test_util.h"
 #include "components/prefs/testing_pref_service.h"
@@ -216,8 +217,9 @@
   google_apis::DriveApiErrorCode error = google_apis::DRIVE_OTHER_ERROR;
   std::unique_ptr<google_apis::StartPageToken> start_page_token;
   scheduler_->GetStartPageToken(
-      "team_drive_id", google_apis::test_util::CreateCopyResultCallback(
-                           &error, &start_page_token));
+      util::kTeamDriveIdDefaultCorpus,
+      google_apis::test_util::CreateCopyResultCallback(&error,
+                                                       &start_page_token));
   base::RunLoop().RunUntilIdle();
   ASSERT_EQ(google_apis::HTTP_SUCCESS, error);
   ASSERT_TRUE(start_page_token);
diff --git a/components/drive/service/fake_drive_service.cc b/components/drive/service/fake_drive_service.cc
index 0cb89b2..58989a7 100644
--- a/components/drive/service/fake_drive_service.cc
+++ b/components/drive/service/fake_drive_service.cc
@@ -307,11 +307,20 @@
 
 void FakeDriveService::AddTeamDrive(const std::string& id,
                                     const std::string& name) {
+  AddTeamDrive(id, name, "");
+}
+
+void FakeDriveService::AddTeamDrive(const std::string& id,
+                                    const std::string& name,
+                                    const std::string& start_page_token) {
   std::unique_ptr<TeamDriveResource> team_drive;
   team_drive.reset(new TeamDriveResource);
   team_drive->set_id(id);
   team_drive->set_name(name);
   team_drive_value_.push_back(std::move(team_drive));
+
+  team_drive_start_page_tokens_[id] = std::make_unique<StartPageToken>();
+  team_drive_start_page_tokens_[id]->set_start_page_token(start_page_token);
 }
 
 void FakeDriveService::RemoveAppByProductId(const std::string& product_id) {
@@ -722,10 +731,15 @@
     return CancelCallback();
   }
 
+  std::unique_ptr<StartPageToken> start_page_token;
+  if (team_drive_id.empty()) {
+    start_page_token = std::make_unique<StartPageToken>(*start_page_token_);
+  } else {
+    auto it = team_drive_start_page_tokens_.find(team_drive_id);
+    DCHECK(it != team_drive_start_page_tokens_.end());
+    start_page_token = std::make_unique<StartPageToken>(*(it->second));
+  }
   ++start_page_token_load_count_;
-  // TODO(slangley): Needs to support team_drive_id.
-  std::unique_ptr<StartPageToken> start_page_token =
-      std::make_unique<StartPageToken>(*start_page_token_);
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE,
       base::BindOnce(callback, HTTP_SUCCESS, std::move(start_page_token)));
diff --git a/components/drive/service/fake_drive_service.h b/components/drive/service/fake_drive_service.h
index c838c04..e70c5901 100644
--- a/components/drive/service/fake_drive_service.h
+++ b/components/drive/service/fake_drive_service.h
@@ -62,6 +62,11 @@
   // Adds a Team Drive to the Team Drive resource list.
   void AddTeamDrive(const std::string& id, const std::string& name);
 
+  // Adds a Team Drive to the Team Drive resource list with a start page token.
+  void AddTeamDrive(const std::string& id,
+                    const std::string& name,
+                    const std::string& start_page_token);
+
   // Removes an app by product id.
   void RemoveAppByProductId(const std::string& product_id);
 
@@ -430,6 +435,8 @@
   std::unique_ptr<base::DictionaryValue> app_info_value_;
   std::vector<std::unique_ptr<google_apis::TeamDriveResource>>
       team_drive_value_;
+  std::map<std::string, std::unique_ptr<google_apis::StartPageToken>>
+      team_drive_start_page_tokens_;
 
   std::map<GURL, UploadSession> upload_sessions_;
   int64_t date_seq_;
diff --git a/components/drive/service/fake_drive_service_unittest.cc b/components/drive/service/fake_drive_service_unittest.cc
index 235c75f..d614d6d3 100644
--- a/components/drive/service/fake_drive_service_unittest.cc
+++ b/components/drive/service/fake_drive_service_unittest.cc
@@ -772,7 +772,7 @@
   DriveApiErrorCode error = DRIVE_OTHER_ERROR;
   std::unique_ptr<StartPageToken> start_page_token;
   fake_service_.GetStartPageToken(
-      "team_drive_id",
+      util::kTeamDriveIdDefaultCorpus,
       test_util::CreateCopyResultCallback(&error, &start_page_token));
   base::RunLoop().RunUntilIdle();
 
diff --git a/components/drive/team_drive_change_list_loader_unittest.cc b/components/drive/team_drive_change_list_loader_unittest.cc
new file mode 100644
index 0000000..20253ab
--- /dev/null
+++ b/components/drive/team_drive_change_list_loader_unittest.cc
@@ -0,0 +1,146 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/drive/chromeos/team_drive_change_list_loader.h"
+#include "base/command_line.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/drive/chromeos/drive_test_util.h"
+#include "components/drive/chromeos/file_cache.h"
+#include "components/drive/chromeos/loader_controller.h"
+#include "components/drive/chromeos/resource_metadata.h"
+#include "components/drive/event_logger.h"
+#include "components/drive/file_system_core_util.h"
+#include "components/drive/job_scheduler.h"
+#include "components/drive/resource_metadata_storage.h"
+#include "components/drive/service/fake_drive_service.h"
+#include "components/drive/service/test_util.h"
+#include "components/prefs/testing_pref_service.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace drive {
+namespace internal {
+
+namespace {
+
+constexpr char kTeamDriveId[] = "team_drive_id";
+constexpr char kTeamDriveName[] = "team_drive_name";
+constexpr char kTestStartPageToken[] = "1";
+
+}  // namespace
+
+class TeamDriveChangeListLoaderTest : public testing::Test {
+ protected:
+  void SetUp() override {
+    base::CommandLine::ForCurrentProcess()->AppendSwitch(
+        google_apis::kEnableTeamDrives);
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+
+    pref_service_.reset(new TestingPrefServiceSimple);
+    test_util::RegisterDrivePrefs(pref_service_->registry());
+
+    logger_.reset(new EventLogger);
+
+    drive_service_.reset(new FakeDriveService);
+    ASSERT_TRUE(test_util::SetUpTestEntries(drive_service_.get()));
+
+    scheduler_.reset(new JobScheduler(
+        pref_service_.get(), logger_.get(), drive_service_.get(),
+        base::ThreadTaskRunnerHandle::Get().get(), nullptr));
+    metadata_storage_.reset(new ResourceMetadataStorage(
+        temp_dir_.GetPath(), base::ThreadTaskRunnerHandle::Get().get()));
+    ASSERT_TRUE(metadata_storage_->Initialize());
+
+    cache_.reset(new FileCache(metadata_storage_.get(), temp_dir_.GetPath(),
+                               base::ThreadTaskRunnerHandle::Get().get(),
+                               NULL /* free_disk_space_getter */));
+    ASSERT_TRUE(cache_->Initialize());
+
+    metadata_.reset(
+        new ResourceMetadata(metadata_storage_.get(), cache_.get(),
+                             base::ThreadTaskRunnerHandle::Get().get()));
+    ASSERT_EQ(FILE_ERROR_OK, metadata_->Initialize());
+
+    loader_controller_.reset(new LoaderController);
+    base::FilePath root_entry_path =
+        util::GetDriveTeamDrivesRootPath().Append(kTeamDriveName);
+
+    team_drive_change_list_loader_ =
+        std::make_unique<TeamDriveChangeListLoader>(
+            kTeamDriveId, root_entry_path, logger_.get(),
+            base::ThreadTaskRunnerHandle::Get().get(), metadata_.get(),
+            scheduler_.get(), loader_controller_.get());
+  }
+
+  void AddTeamDriveEntry(const std::string& id,
+                         const std::string& name,
+                         const std::string& start_page_token) {
+    // Setup the team drive in the fake drive service and metadata.
+    drive_service_->AddTeamDrive(id, name, start_page_token);
+
+    std::string root_local_id;
+    ASSERT_EQ(FILE_ERROR_OK,
+              metadata_->GetIdByPath(util::GetDriveTeamDrivesRootPath(),
+                                     &root_local_id));
+
+    std::string local_id;
+    ASSERT_EQ(FILE_ERROR_OK, metadata_->AddEntry(
+                                 CreateDirectoryEntryWithResourceId(
+                                     name, id, root_local_id, start_page_token),
+                                 &local_id));
+  }
+
+  // Creates a ResourceEntry for a directory with explicitly set resource_id.
+  ResourceEntry CreateDirectoryEntryWithResourceId(
+      const std::string& title,
+      const std::string& resource_id,
+      const std::string& parent_local_id,
+      const std::string& start_page_token) {
+    ResourceEntry entry;
+    entry.set_title(title);
+    entry.set_resource_id(resource_id);
+    entry.set_parent_local_id(parent_local_id);
+    entry.mutable_file_info()->set_is_directory(true);
+    entry.mutable_directory_specific_info()->set_start_page_token(
+        start_page_token);
+    return entry;
+  }
+
+  content::TestBrowserThreadBundle thread_bundle_;
+  base::ScopedTempDir temp_dir_;
+  std::unique_ptr<TestingPrefServiceSimple> pref_service_;
+  std::unique_ptr<EventLogger> logger_;
+  std::unique_ptr<FakeDriveService> drive_service_;
+  std::unique_ptr<JobScheduler> scheduler_;
+  std::unique_ptr<ResourceMetadataStorage, test_util::DestroyHelperForTests>
+      metadata_storage_;
+  std::unique_ptr<FileCache, test_util::DestroyHelperForTests> cache_;
+  std::unique_ptr<ResourceMetadata, test_util::DestroyHelperForTests> metadata_;
+  std::unique_ptr<LoaderController> loader_controller_;
+  std::unique_ptr<TeamDriveChangeListLoader> team_drive_change_list_loader_;
+};
+
+// When loading, if the local resource metadata contains a start page token then
+// we do not load from the server, just use local metadata.
+TEST_F(TeamDriveChangeListLoaderTest, MetadataRefresh) {
+  AddTeamDriveEntry(kTeamDriveId, kTeamDriveName, kTestStartPageToken);
+  EXPECT_FALSE(team_drive_change_list_loader_->IsRefreshing());
+
+  FileError error = FILE_ERROR_FAILED;
+  team_drive_change_list_loader_->LoadIfNeeded(
+      google_apis::test_util::CreateCopyResultCallback(&error));
+  EXPECT_TRUE(team_drive_change_list_loader_->IsRefreshing());
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(FILE_ERROR_OK, error);
+
+  EXPECT_FALSE(team_drive_change_list_loader_->IsRefreshing());
+  EXPECT_EQ(1, drive_service_->start_page_token_load_count());
+  EXPECT_EQ(0, drive_service_->change_list_load_count());
+}
+
+// TODO(slangley): Add tests for processing team drive change lists.
+
+}  // namespace internal
+}  // namespace drive
diff --git a/components/password_manager/core/browser/form_parsing/BUILD.gn b/components/password_manager/core/browser/form_parsing/BUILD.gn
index 7c5e844..7c55906 100644
--- a/components/password_manager/core/browser/form_parsing/BUILD.gn
+++ b/components/password_manager/core/browser/form_parsing/BUILD.gn
@@ -4,13 +4,28 @@
 
 import("//build/config/sanitizers/sanitizers.gni")
 
+# Determine whetner fuzzer_test targets are built.
+does_fuzzer_test_compile =
+    !disable_libfuzzer && (use_fuzzing_engine || use_drfuzz || is_linux)
+
 static_library("form_parsing") {
   sources = [
-    # TODO(https://crbug.com/831123): Make compilation of ios_form_parser only for iOS, when it is not needed for experimentation anymore.
-    "ios_form_parser.cc",
-    "ios_form_parser.h",
+    "form_parser.cc",
+    "form_parser.h",
   ]
 
+  # TODO(crbug.com/845426): Provide a single parser for all platforms, then
+  # remove the ios_* variant. This will be possible once the form_parser.* is
+  # used by default and causing no known issues. Note that (the
+  # non-iOS-specific) 'form_parser.*' is used by NewPasswordFormManager on
+  # every platform, including iOS.
+  if (does_fuzzer_test_compile || is_ios) {
+    sources += [
+      "ios_form_parser.cc",
+      "ios_form_parser.h",
+    ]
+  }
+
   deps = [
     "//base",
     "//components/autofill/core/common",
@@ -20,9 +35,13 @@
 source_set("unit_tests") {
   testonly = true
   sources = [
-    "ios_form_parser_unittest.cc",
+    "form_parser_unittest.cc",
   ]
 
+  if (does_fuzzer_test_compile || is_ios) {
+    sources += [ "ios_form_parser_unittest.cc" ]
+  }
+
   deps = [
     ":form_parsing",
     "//base",
diff --git a/components/password_manager/core/browser/form_parsing/form_parser.cc b/components/password_manager/core/browser/form_parsing/form_parser.cc
new file mode 100644
index 0000000..acddf93
--- /dev/null
+++ b/components/password_manager/core/browser/form_parsing/form_parser.cc
@@ -0,0 +1,441 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/password_manager/core/browser/form_parsing/form_parser.h"
+
+#include <algorithm>
+#include <iterator>
+#include <utility>
+#include <vector>
+
+#include "base/stl_util.h"
+#include "base/strings/string_split.h"
+#include "components/autofill/core/common/form_data.h"
+#include "components/autofill/core/common/password_form.h"
+
+using autofill::FormFieldData;
+using autofill::PasswordForm;
+
+namespace password_manager {
+
+namespace {
+
+constexpr char kAutocompleteUsername[] = "username";
+constexpr char kAutocompleteCurrentPassword[] = "current-password";
+constexpr char kAutocompleteNewPassword[] = "new-password";
+constexpr char kAutocompleteCreditCardPrefix[] = "cc-";
+
+// The susbset of autocomplete flags related to passwords.
+enum class AutocompleteFlag {
+  NONE,
+  USERNAME,
+  CURRENT_PASSWORD,
+  NEW_PASSWORD,
+  // Represents the whole family of cc-* flags.
+  CREDIT_CARD
+};
+
+// The autocomplete attribute has one of the following structures:
+//   [section-*] [shipping|billing] [type_hint] field_type
+//   on | off | false
+// (see
+// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofilling-form-controls%3A-the-autocomplete-attribute).
+// For password forms, only the field_type is relevant. So parsing the attribute
+// amounts to just taking the last token.  If that token is one of "username",
+// "current-password" or "new-password", this returns an appropriate enum value.
+// If the token starts with a "cc-" prefix, this returns CREDIT_CARD.
+// Otherwise, returns NONE.
+AutocompleteFlag ExtractAutocompleteFlag(const std::string& attribute) {
+  std::vector<base::StringPiece> tokens =
+      base::SplitStringPiece(attribute, base::kWhitespaceASCII,
+                             base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+  if (tokens.empty())
+    return AutocompleteFlag::NONE;
+
+  const base::StringPiece& field_type = tokens.back();
+  if (base::LowerCaseEqualsASCII(field_type, kAutocompleteUsername))
+    return AutocompleteFlag::USERNAME;
+  if (base::LowerCaseEqualsASCII(field_type, kAutocompleteCurrentPassword))
+    return AutocompleteFlag::CURRENT_PASSWORD;
+  if (base::LowerCaseEqualsASCII(field_type, kAutocompleteNewPassword))
+    return AutocompleteFlag::NEW_PASSWORD;
+
+  if (base::StartsWith(field_type, kAutocompleteCreditCardPrefix,
+                       base::CompareCase::SENSITIVE))
+    return AutocompleteFlag::CREDIT_CARD;
+
+  return AutocompleteFlag::NONE;
+}
+
+// Helper to spare map::find boilerplate when caching field's autocomplete
+// attributes.
+class AutocompleteCache {
+ public:
+  AutocompleteCache();
+
+  ~AutocompleteCache();
+
+  // Computes and stores the AutocompleteFlag for |field| based on its
+  // autocomplete attribute. Note that this cannot be done on-demand during
+  // RetrieveFor, because the cache spares space and look-up time by not storing
+  // AutocompleteFlag::NONE values, hence for all elements without an
+  // autocomplete attribute, every retrieval would result in a new computation.
+  void Store(const FormFieldData* field);
+
+  // Retrieves the value previously stored for |field|.
+  AutocompleteFlag RetrieveFor(const FormFieldData* field) const;
+
+ private:
+  std::map<const FormFieldData*, AutocompleteFlag> cache_;
+
+  DISALLOW_COPY_AND_ASSIGN(AutocompleteCache);
+};
+
+AutocompleteCache::AutocompleteCache() = default;
+
+AutocompleteCache::~AutocompleteCache() = default;
+
+void AutocompleteCache::Store(const FormFieldData* field) {
+  const AutocompleteFlag flag =
+      ExtractAutocompleteFlag(field->autocomplete_attribute);
+  // Only store non-trivial flags. Most of the elements will have the NONE
+  // value, so spare storage and lookup time by assuming anything not stored in
+  // |cache_| has the NONE flag.
+  if (flag != AutocompleteFlag::NONE)
+    cache_[field] = flag;
+}
+
+AutocompleteFlag AutocompleteCache::RetrieveFor(
+    const FormFieldData* field) const {
+  auto it = cache_.find(field);
+  return it == cache_.end() ? AutocompleteFlag::NONE : it->second;
+}
+
+// Helper struct that is used to return results from the parsing function.
+struct ParseResult {
+  const FormFieldData* username_field = nullptr;
+  const FormFieldData* password_field = nullptr;
+  const FormFieldData* new_password_field = nullptr;
+  const FormFieldData* confirmation_password_field = nullptr;
+
+  bool IsEmpty() {
+    return password_field == nullptr && new_password_field == nullptr;
+  }
+};
+
+// Returns text fields from |fields|.
+std::vector<const FormFieldData*> GetTextFields(
+    const std::vector<FormFieldData>& fields) {
+  std::vector<const FormFieldData*> result;
+  result.reserve(fields.size());
+  for (const auto& field : fields) {
+    if (field.IsTextInputElement())
+      result.push_back(&field);
+  }
+  return result;
+}
+
+// Filters fields that have credit card related autocomplete attributes out of
+// |fields|. Uses |autocomplete_cache| to get the autocomplete attributes.
+void FilterCreditCardFields(std::vector<const FormFieldData*>* fields,
+                            const AutocompleteCache& autocomplete_cache) {
+  base::EraseIf(*fields, [&autocomplete_cache](const FormFieldData* field) {
+    return autocomplete_cache.RetrieveFor(field) ==
+           AutocompleteFlag::CREDIT_CARD;
+  });
+}
+
+// Returns true iff there is a password field.
+bool HasPasswordField(const std::vector<const FormFieldData*>& fields) {
+  for (const FormFieldData* field : fields)
+    if (field->form_control_type == "password")
+      return true;
+  return false;
+}
+
+// Tries to parse |fields| based on autocomplete attributes from
+// |autocomplete_cache|. Assumption on the usage autocomplete attributes:
+// 1. Not more than 1 field with autocomplete=username.
+// 2. Not more than 1 field with autocomplete=current-password.
+// 3. Not more than 2 fields with autocomplete=new-password.
+// 4. Only password fields have "*-password" attribute and only non-password
+//    fields have the "username" attribute.
+// Are these assumptions violated, or is there no password with an autocomplete
+// attribute, parsing is unsuccessful. Returns nullptr if parsing is
+// unsuccessful.
+std::unique_ptr<ParseResult> ParseUsingAutocomplete(
+    const std::vector<const FormFieldData*>& fields,
+    const AutocompleteCache& autocomplete_cache) {
+  std::unique_ptr<ParseResult> result = std::make_unique<ParseResult>();
+  for (const FormFieldData* field : fields) {
+    AutocompleteFlag flag = autocomplete_cache.RetrieveFor(field);
+    const bool is_password = field->form_control_type == "password";
+    switch (flag) {
+      case AutocompleteFlag::USERNAME:
+        if (is_password || result->username_field)
+          return nullptr;
+        result->username_field = field;
+        break;
+      case AutocompleteFlag::CURRENT_PASSWORD:
+        if (!is_password || result->password_field)
+          return nullptr;
+        result->password_field = field;
+        break;
+      case AutocompleteFlag::NEW_PASSWORD:
+        if (!is_password)
+          return nullptr;
+        // The first field with autocomplete=new-password is considered to be
+        // new_password_field and the second is confirmation_password_field.
+        if (!result->new_password_field)
+          result->new_password_field = field;
+        else if (!result->confirmation_password_field)
+          result->confirmation_password_field = field;
+        else
+          return nullptr;
+        break;
+      case AutocompleteFlag::CREDIT_CARD:
+      case AutocompleteFlag::NONE:
+        break;
+    }
+  }
+
+  if (result->IsEmpty())
+    return nullptr;
+  return result;
+}
+
+// Returns only relevant password fields from |fields|. Namely
+// 1. If there is a focusable password field, return only focusable.
+// 2. If mode == SAVING return only non-empty fields (for saving empty fields
+// are useless).
+// Note that focusability is the proxy for visibility.
+std::vector<const FormFieldData*> GetRelevantPasswords(
+    const std::vector<const FormFieldData*>& fields,
+    FormParsingMode mode) {
+  std::vector<const FormFieldData*> result;
+  result.reserve(fields.size());
+
+  const bool consider_only_non_empty = mode == FormParsingMode::SAVING;
+  bool found_focusable = false;
+
+  for (const FormFieldData* field : fields) {
+    if (field->form_control_type != "password")
+      continue;
+    if (consider_only_non_empty && field->value.empty())
+      continue;
+    result.push_back(field);
+    found_focusable |= field->is_focusable;
+  }
+
+  if (found_focusable) {
+    base::EraseIf(result, [](const FormFieldData* field) {
+      return !field->is_focusable;
+    });
+  }
+
+  return result;
+}
+
+// Detects different password fields from |passwords|.
+void LocateSpecificPasswords(const std::vector<const FormFieldData*>& passwords,
+                             const FormFieldData** current_password,
+                             const FormFieldData** new_password,
+                             const FormFieldData** confirmation_password) {
+  DCHECK(current_password);
+  DCHECK(!*current_password);
+  DCHECK(new_password);
+  DCHECK(!*new_password);
+  DCHECK(confirmation_password);
+  DCHECK(!*confirmation_password);
+
+  switch (passwords.size()) {
+    case 1:
+      *current_password = passwords[0];
+      break;
+    case 2:
+      if (!passwords[0]->value.empty() &&
+          passwords[0]->value == passwords[1]->value) {
+        // Two identical non-empty passwords: assume we are seeing a new
+        // password with a confirmation. This can be either a sign-up form or a
+        // password change form that does not ask for the old password.
+        *new_password = passwords[0];
+        *confirmation_password = passwords[1];
+      } else {
+        // Assume first is old password, second is new (no choice but to guess).
+        // If the passwords are both empty, it is impossible to tell if they
+        // are the old and the new one, or the new one and its confirmation. In
+        // that case Chrome errs on the side of filling and classifies them as
+        // old & new to allow filling of change password forms.
+        *current_password = passwords[0];
+        *new_password = passwords[1];
+      }
+      break;
+    default:
+      // If there are more than 3 passwords it is not very clear what this form
+      // it is. Consider only the first 3 passwords in such case as a
+      // best-effort solution.
+      if (!passwords[0]->value.empty() &&
+          passwords[0]->value == passwords[1]->value &&
+          passwords[0]->value == passwords[2]->value) {
+        // All passwords are the same. Assume that the first field is the
+        // current password.
+        *current_password = passwords[0];
+      } else if (passwords[1]->value == passwords[2]->value) {
+        // New password is the duplicated one, and comes second; or empty form
+        // with at least 3 password fields.
+        *current_password = passwords[0];
+        *new_password = passwords[1];
+        *confirmation_password = passwords[2];
+      } else if (passwords[0]->value == passwords[1]->value) {
+        // It is strange that the new password comes first, but trust more which
+        // fields are duplicated than the ordering of fields. Assume that
+        // any password fields after the new password contain sensitive
+        // information that isn't actually a password (security hint, SSN, etc.)
+        *new_password = passwords[0];
+        *confirmation_password = passwords[1];
+      } else {
+        // Three different passwords, or first and last match with middle
+        // different. No idea which is which. Let's save the first password.
+        // Password selection in a prompt will allow to correct the choice.
+        *current_password = passwords[0];
+      }
+  }
+}
+
+// Tries to find username field among text fields from |fields| occurring before
+// |first_relevant_password|. Returns nullptr if the username is not found.
+const FormFieldData* FindUsernameFieldBaseHeuristics(
+    const std::vector<const FormFieldData*>& fields,
+    const FormFieldData* first_relevant_password,
+    FormParsingMode mode) {
+  DCHECK(first_relevant_password);
+
+  // Let username_candidates be all non-password fields before
+  // |first_relevant_password|.
+  auto first_relevant_password_it =
+      std::find(fields.begin(), fields.end(), first_relevant_password);
+  DCHECK(first_relevant_password_it != fields.end());
+
+  // For saving filter out empty fields.
+  const bool consider_only_non_empty = mode == FormParsingMode::SAVING;
+
+  // Search through the text input fields preceding |first_relevant_password|
+  // and find the closest one focusable and the closest one in general.
+
+  const FormFieldData* focusable_username = nullptr;
+  const FormFieldData* username = nullptr;
+  // Do reverse search to find the closest candidates preceding the password.
+  for (auto it = std::reverse_iterator<
+           std::vector<const FormFieldData*>::const_iterator>(
+           first_relevant_password_it);
+       it != fields.rend(); ++it) {
+    if ((*it)->form_control_type == "password")
+      continue;
+    if (consider_only_non_empty && (*it)->value.empty())
+      continue;
+    if (!username)
+      username = *it;
+    if ((*it)->is_focusable) {
+      focusable_username = *it;
+      break;
+    }
+  }
+
+  return focusable_username ? focusable_username : username;
+}
+
+std::unique_ptr<ParseResult> ParseUsingBaseHeuristics(
+    const std::vector<const FormFieldData*>& fields,
+    FormParsingMode mode) {
+  // Try to find password elements (current, new, confirmation).
+  std::vector<const FormFieldData*> passwords =
+      GetRelevantPasswords(fields, mode);
+  if (passwords.empty())
+    return nullptr;
+
+  auto result = std::make_unique<ParseResult>();
+  LocateSpecificPasswords(passwords, &result->password_field,
+                          &result->new_password_field,
+                          &result->confirmation_password_field);
+  if (result->IsEmpty())
+    return nullptr;
+
+  // If password elements are found then try to find a username.
+  result->username_field =
+      FindUsernameFieldBaseHeuristics(fields, passwords[0], mode);
+  return result;
+}
+
+// Set username and password fields from |parse_result| in |password_form|.
+void SetFields(const ParseResult& parse_result, PasswordForm* password_form) {
+  if (parse_result.username_field) {
+    password_form->username_element = parse_result.username_field->name;
+    password_form->username_value = parse_result.username_field->value;
+  }
+
+  if (parse_result.password_field) {
+    password_form->password_element = parse_result.password_field->name;
+    password_form->password_value = parse_result.password_field->value;
+  }
+
+  if (parse_result.new_password_field) {
+    password_form->new_password_element = parse_result.new_password_field->name;
+    password_form->new_password_value = parse_result.new_password_field->value;
+  }
+
+  if (parse_result.confirmation_password_field) {
+    password_form->confirmation_password_element =
+        parse_result.confirmation_password_field->name;
+  }
+}
+
+}  // namespace
+
+std::unique_ptr<PasswordForm> ParseFormData(
+    const autofill::FormData& form_data,
+    const autofill::FormsPredictionsMap* form_predictions,
+    FormParsingMode mode) {
+  // TODO(crbug.com/845426): Use predictions.
+
+  std::vector<const FormFieldData*> fields = GetTextFields(form_data.fields);
+
+  // Fill the cache with autocomplete flags.
+  AutocompleteCache autocomplete_cache;
+  for (const FormFieldData* input : fields)
+    autocomplete_cache.Store(input);
+
+  FilterCreditCardFields(&fields, autocomplete_cache);
+
+  // Skip forms without password fields.
+  if (!HasPasswordField(fields))
+    return nullptr;
+
+  // Create parse result and set non-field related information.
+  auto result = std::make_unique<PasswordForm>();
+  result->origin = form_data.origin;
+  result->signon_realm = form_data.origin.GetOrigin().spec();
+  result->action = form_data.action;
+  result->form_data = form_data;
+
+  // TODO(crbug.com/845426): Add parsing with trusted server-side hints.
+
+  // Try to parse with autocomplete attributes.
+  auto autocomplete_parse_result =
+      ParseUsingAutocomplete(fields, autocomplete_cache);
+  if (autocomplete_parse_result) {
+    SetFields(*autocomplete_parse_result, result.get());
+    return result;
+  }
+
+  // Try to parse with base heuristic.
+  auto base_heuristics_parse_result = ParseUsingBaseHeuristics(fields, mode);
+  if (base_heuristics_parse_result) {
+    SetFields(*base_heuristics_parse_result, result.get());
+    return result;
+  }
+
+  return nullptr;
+}
+
+}  // namespace password_manager
diff --git a/components/password_manager/core/browser/form_parsing/form_parser.h b/components/password_manager/core/browser/form_parsing/form_parser.h
new file mode 100644
index 0000000..35ca96a
--- /dev/null
+++ b/components/password_manager/core/browser/form_parsing/form_parser.h
@@ -0,0 +1,35 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_FORM_PARSING_FORM_PARSER_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_FORM_PARSING_FORM_PARSER_H_
+
+#include <memory>
+#include <vector>
+
+#include "components/autofill/core/common/password_form_field_prediction_map.h"
+
+namespace autofill {
+struct FormData;
+struct PasswordForm;
+}  // namespace autofill
+
+namespace password_manager {
+
+enum class FormParsingMode { FILLING, SAVING };
+
+// TODO(crbug.com/845426): Add the UsernameDetectionMethod enum and log data
+// into the "PasswordManager.UsernameDetectionMethod" histogram.
+
+// Parse DOM information |form_data| into Password Manager's form representation
+// PasswordForm. |form_predictions| are an optional source of server-side
+// predictions about field types. Return nullptr when parsing is unsuccessful.
+std::unique_ptr<autofill::PasswordForm> ParseFormData(
+    const autofill::FormData& form_data,
+    const autofill::FormsPredictionsMap* form_predictions,
+    FormParsingMode mode);
+
+}  // namespace password_manager
+
+#endif  // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_FORM_PARSING_IOS_FORM_PARSER_H_
diff --git a/components/password_manager/core/browser/form_parsing/form_parser_unittest.cc b/components/password_manager/core/browser/form_parsing/form_parser_unittest.cc
new file mode 100644
index 0000000..ac40acb
--- /dev/null
+++ b/components/password_manager/core/browser/form_parsing/form_parser_unittest.cc
@@ -0,0 +1,747 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/password_manager/core/browser/form_parsing/form_parser.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+
+#include "base/strings/string16.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/common/form_data.h"
+#include "components/autofill/core/common/form_field_data.h"
+#include "components/autofill/core/common/password_form.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+using autofill::FormData;
+using autofill::FormFieldData;
+using autofill::PasswordForm;
+using base::ASCIIToUTF16;
+
+namespace password_manager {
+
+namespace {
+
+// Use this value in FieldDataDescription.value to get an arbitrary unique value
+// generated in GetFormDataAndExpectation().
+constexpr char kNonimportantValue[] = "non-important unique";
+
+// Use this in FieldDataDescription below to mark the expected username and
+// password fields. The *_FILLING variants apply to FormParsingMode::FILLING
+// only, the *_SAVING variants to FormParsingMode::SAVING only, the suffix-less
+// variants to both.
+enum class ElementRole {
+  NONE,
+  USERNAME_FILLING,
+  USERNAME_SAVING,
+  USERNAME,
+  CURRENT_PASSWORD_FILLING,
+  CURRENT_PASSWORD_SAVING,
+  CURRENT_PASSWORD,
+  NEW_PASSWORD_FILLING,
+  NEW_PASSWORD_SAVING,
+  NEW_PASSWORD,
+  CONFIRMATION_PASSWORD_FILLING,
+  CONFIRMATION_PASSWORD_SAVING,
+  CONFIRMATION_PASSWORD
+};
+
+// Expected FormFieldData are constructed based on these descriptions.
+struct FieldDataDescription {
+  ElementRole role = ElementRole::NONE;
+  bool is_focusable = true;
+  bool is_enabled = true;
+  const char* autocomplete_attribute = nullptr;
+  const char* value = kNonimportantValue;
+  const char* form_control_type = "text";
+};
+
+// Describes a test case for the parser.
+struct FormParsingTestCase {
+  const char* description_for_logging;
+  std::vector<FieldDataDescription> fields;
+};
+
+// Returns numbers which are distinct from each other within the scope of one
+// test.
+uint32_t GetUniqueId() {
+  static uint32_t counter = 0;
+  return counter++;
+}
+
+// Use to add a number suffix which is unique in the scope of the test.
+base::string16 StampUniqueSuffix(const char* base_str) {
+  return ASCIIToUTF16(base_str) + ASCIIToUTF16("_") +
+         base::UintToString16(GetUniqueId());
+}
+
+// Describes which renderer IDs are expected for username/password fields
+// identified in a PasswordForm.
+struct ParseResultIds {
+  uint32_t username_id = FormFieldData::kNotSetFormControlRendererId;
+  uint32_t password_id = FormFieldData::kNotSetFormControlRendererId;
+  uint32_t new_password_id = FormFieldData::kNotSetFormControlRendererId;
+  uint32_t confirmation_password_id =
+      FormFieldData::kNotSetFormControlRendererId;
+
+  bool IsEmpty() const {
+    return username_id == FormFieldData::kNotSetFormControlRendererId &&
+           password_id == FormFieldData::kNotSetFormControlRendererId &&
+           new_password_id == FormFieldData::kNotSetFormControlRendererId &&
+           confirmation_password_id ==
+               FormFieldData::kNotSetFormControlRendererId;
+  }
+};
+
+// Creates a FormData to be fed to the parser. Includes FormFieldData as
+// described in |fields_description|. Generates |fill_result| and |save_result|
+// expectations about the result in FILLING and SAVING mode, respectively.
+FormData GetFormDataAndExpectation(
+    const std::vector<FieldDataDescription>& fields_description,
+    ParseResultIds* fill_result,
+    ParseResultIds* save_result) {
+  FormData form_data;
+  form_data.action = GURL("http://example1.com");
+  form_data.origin = GURL("http://example2.com");
+  for (const FieldDataDescription& field_description : fields_description) {
+    FormFieldData field;
+    const uint32_t unique_id = GetUniqueId();
+    field.unique_renderer_id = unique_id;
+    field.id = StampUniqueSuffix("html_id");
+    field.name = StampUniqueSuffix("html_name");
+    field.form_control_type = field_description.form_control_type;
+    field.is_focusable = field_description.is_focusable;
+    field.is_enabled = field_description.is_enabled;
+    if (field_description.value == kNonimportantValue) {
+      field.value = StampUniqueSuffix("value");
+    } else {
+      field.value = ASCIIToUTF16(field_description.value);
+    }
+    if (field_description.autocomplete_attribute)
+      field.autocomplete_attribute = field_description.autocomplete_attribute;
+    form_data.fields.push_back(field);
+    switch (field_description.role) {
+      case ElementRole::NONE:
+        break;
+      case ElementRole::USERNAME_FILLING:
+        fill_result->username_id = unique_id;
+        break;
+      case ElementRole::USERNAME_SAVING:
+        save_result->username_id = unique_id;
+        break;
+      case ElementRole::USERNAME:
+        fill_result->username_id = unique_id;
+        save_result->username_id = unique_id;
+        break;
+      case ElementRole::CURRENT_PASSWORD_FILLING:
+        fill_result->password_id = unique_id;
+        break;
+      case ElementRole::CURRENT_PASSWORD_SAVING:
+        save_result->password_id = unique_id;
+        break;
+      case ElementRole::CURRENT_PASSWORD:
+        fill_result->password_id = unique_id;
+        save_result->password_id = unique_id;
+        break;
+      case ElementRole::NEW_PASSWORD_FILLING:
+        fill_result->new_password_id = unique_id;
+        break;
+      case ElementRole::NEW_PASSWORD_SAVING:
+        save_result->new_password_id = unique_id;
+        break;
+      case ElementRole::NEW_PASSWORD:
+        fill_result->new_password_id = unique_id;
+        save_result->new_password_id = unique_id;
+        break;
+      case ElementRole::CONFIRMATION_PASSWORD_FILLING:
+        fill_result->confirmation_password_id = unique_id;
+        break;
+      case ElementRole::CONFIRMATION_PASSWORD_SAVING:
+        save_result->confirmation_password_id = unique_id;
+        break;
+      case ElementRole::CONFIRMATION_PASSWORD:
+        fill_result->confirmation_password_id = unique_id;
+        save_result->confirmation_password_id = unique_id;
+        break;
+    }
+  }
+  return form_data;
+}
+
+// Check that |fields| has a field with unique renderer ID |renderer_id| which
+// has the name |element_name| and value |*element_value|. If |renderer_id| is
+// FormFieldData::kNotSetFormControlRendererId, then instead check that
+// |element_name| and |*element_value| are empty. Set |element_kind| to identify
+// the type of the field in logging: 'username', 'password', etc. The argument
+// |element_value| can be null, in which case all checks involving it are
+// skipped (useful for the confirmation password value, which is not represented
+// in PasswordForm).
+void CheckField(const std::vector<FormFieldData>& fields,
+                uint32_t renderer_id,
+                const base::string16& element_name,
+                const base::string16* element_value,
+                const char* element_kind) {
+  SCOPED_TRACE(testing::Message("Looking for element of kind ")
+               << element_kind);
+
+  if (renderer_id == FormFieldData::kNotSetFormControlRendererId) {
+    EXPECT_EQ(base::string16(), element_name);
+    if (element_value)
+      EXPECT_EQ(base::string16(), *element_value);
+    return;
+  }
+
+  auto field_it = std::find_if(fields.begin(), fields.end(),
+                               [renderer_id](const FormFieldData& field) {
+                                 return field.unique_renderer_id == renderer_id;
+                               });
+  ASSERT_TRUE(field_it != fields.end())
+      << "Could not find a field with renderer ID " << renderer_id;
+  EXPECT_EQ(element_name, field_it->name);
+  if (element_value)
+    EXPECT_EQ(*element_value, field_it->value);
+}
+
+// Check that the information distilled from |form_data| into |password_form| is
+// matching |expectations|.
+void CheckPasswordFormFields(const PasswordForm& password_form,
+                             const FormData& form_data,
+                             const ParseResultIds& expectations) {
+  CheckField(form_data.fields, expectations.username_id,
+             password_form.username_element, &password_form.username_value,
+             "username");
+
+  CheckField(form_data.fields, expectations.password_id,
+             password_form.password_element, &password_form.password_value,
+             "password");
+
+  CheckField(form_data.fields, expectations.new_password_id,
+             password_form.new_password_element,
+             &password_form.new_password_value, "new_password");
+
+  CheckField(form_data.fields, expectations.confirmation_password_id,
+             password_form.confirmation_password_element, nullptr,
+             "confirmation_password");
+}
+
+// Iterates over |test_cases|, creates a FormData for each, runs the parser and
+// checks the results.
+void CheckTestData(const std::vector<FormParsingTestCase>& test_cases) {
+  for (const FormParsingTestCase& test_case : test_cases) {
+    ParseResultIds fill_result;
+    ParseResultIds save_result;
+    const FormData form_data =
+        GetFormDataAndExpectation(test_case.fields, &fill_result, &save_result);
+    for (auto mode : {FormParsingMode::FILLING, FormParsingMode::SAVING}) {
+      SCOPED_TRACE(
+          testing::Message("Test description: ")
+          << test_case.description_for_logging << ", parsing mode = "
+          << (mode == FormParsingMode::FILLING ? "Filling" : "Saving"));
+
+      std::unique_ptr<PasswordForm> parsed_form =
+          ParseFormData(form_data, nullptr, mode);
+
+      const ParseResultIds& expected_ids =
+          mode == FormParsingMode::FILLING ? fill_result : save_result;
+
+      if (expected_ids.IsEmpty()) {
+        EXPECT_FALSE(parsed_form) << "Expected no parsed results";
+      } else {
+        ASSERT_TRUE(parsed_form) << "Expected successful parsing";
+        CheckPasswordFormFields(*parsed_form, form_data, expected_ids);
+      }
+    }
+  }
+}
+
+TEST(FormParserTest, NotPasswordForm) {
+  CheckTestData({
+      {
+          "No fields", {},
+      },
+      {
+          "No password fields",
+          {
+              {.form_control_type = "text"}, {.form_control_type = "text"},
+          },
+      },
+  });
+}
+
+TEST(FormParserTest, SkipNotTextFields) {
+  CheckTestData({
+      {
+          "Select between username and password fields",
+          {{.role = ElementRole::USERNAME},
+           {.form_control_type = "select"},
+           {.role = ElementRole::CURRENT_PASSWORD,
+            .form_control_type = "password"}},
+      },
+  });
+}
+
+TEST(FormParserTest, OnlyPasswordFields) {
+  CheckTestData({
+      {
+          "1 password field",
+          {
+              {.role = ElementRole::CURRENT_PASSWORD,
+               .form_control_type = "password"},
+          },
+      },
+      {
+          "2 password fields, new and confirmation password",
+          {
+              {.role = ElementRole::NEW_PASSWORD,
+               .form_control_type = "password",
+               .value = "pw"},
+              {.role = ElementRole::CONFIRMATION_PASSWORD,
+               .form_control_type = "password",
+               .value = "pw"},
+          },
+      },
+      {
+          "2 password fields, current and new password",
+          {
+              {.role = ElementRole::CURRENT_PASSWORD,
+               .form_control_type = "password",
+               .value = "pw1"},
+              {.role = ElementRole::NEW_PASSWORD,
+               .form_control_type = "password",
+               .value = "pw2"},
+          },
+      },
+      {
+          "3 password fields, current, new, confirm password",
+          {
+              {.role = ElementRole::CURRENT_PASSWORD,
+               .form_control_type = "password",
+               .value = "pw1"},
+              {.role = ElementRole::NEW_PASSWORD,
+               .form_control_type = "password",
+               .value = "pw2"},
+              {.role = ElementRole::CONFIRMATION_PASSWORD,
+               .form_control_type = "password",
+               .value = "pw2"},
+          },
+      },
+      {
+          "3 password fields with different values",
+          {
+              {.role = ElementRole::CURRENT_PASSWORD,
+               .form_control_type = "password",
+               .value = "pw1"},
+              {.form_control_type = "password", .value = "pw2"},
+              {.form_control_type = "password", .value = "pw3"},
+          },
+      },
+      {
+          "4 password fields, only the first 3 are considered",
+          {
+              {.role = ElementRole::CURRENT_PASSWORD,
+               .form_control_type = "password",
+               .value = "pw1"},
+              {.role = ElementRole::NEW_PASSWORD,
+               .form_control_type = "password",
+               .value = "pw2"},
+              {.role = ElementRole::CONFIRMATION_PASSWORD,
+               .form_control_type = "password",
+               .value = "pw2"},
+              {.form_control_type = "password", .value = "pw3"},
+          },
+      },
+      {
+          "4 password fields, 4th same value as 3rd and 2nd, only the first 3 "
+          "are considered",
+          {
+              {.role = ElementRole::CURRENT_PASSWORD,
+               .form_control_type = "password",
+               .value = "pw1"},
+              {.role = ElementRole::NEW_PASSWORD,
+               .form_control_type = "password",
+               .value = "pw2"},
+              {.role = ElementRole::CONFIRMATION_PASSWORD,
+               .form_control_type = "password",
+               .value = "pw2"},
+              {.form_control_type = "password", .value = "pw2"},
+          },
+      },
+      {
+          "4 password fields, all same value",
+          {
+              {.role = ElementRole::CURRENT_PASSWORD,
+               .form_control_type = "password",
+               .value = "pw"},
+              {.form_control_type = "password", .value = "pw"},
+              {.form_control_type = "password", .value = "pw"},
+              {.form_control_type = "password", .value = "pw"},
+          },
+      },
+  });
+}
+
+TEST(FormParserTest, TestFocusability) {
+  CheckTestData({
+      {
+          "non-focusable fields are considered when there are no focusable "
+          "fields",
+          {
+              {.role = ElementRole::CURRENT_PASSWORD,
+               .form_control_type = "password",
+               .is_focusable = false},
+              {.role = ElementRole::NEW_PASSWORD,
+               .form_control_type = "password",
+               .is_focusable = false},
+          },
+      },
+      {
+          "non-focusable should be skipped when there are focusable fields",
+          {
+              {.form_control_type = "password", .is_focusable = false},
+              {.role = ElementRole::CURRENT_PASSWORD,
+               .form_control_type = "password",
+               .is_focusable = true},
+          },
+      },
+      {
+          "non-focusable text fields before password",
+          {
+              {.form_control_type = "text", .is_focusable = false},
+              {.role = ElementRole::USERNAME,
+               .form_control_type = "text",
+               .is_focusable = false},
+              {.role = ElementRole::CURRENT_PASSWORD,
+               .form_control_type = "password",
+               .is_focusable = true},
+          },
+      },
+      {
+          "focusable and non-focusable text fields before password",
+          {
+              {.role = ElementRole::USERNAME,
+               .form_control_type = "text",
+               .is_focusable = true},
+              {.form_control_type = "text", .is_focusable = false},
+              {.role = ElementRole::CURRENT_PASSWORD,
+               .form_control_type = "password",
+               .is_focusable = true},
+          },
+      },
+      {
+          "many passwords, some of them focusable",
+          {
+              {.form_control_type = "password", .is_focusable = false},
+              {.role = ElementRole::CURRENT_PASSWORD,
+               .form_control_type = "password",
+               .is_focusable = true},
+              {.role = ElementRole::NEW_PASSWORD,
+               .form_control_type = "password",
+               .is_focusable = true,
+               .value = "pw"},
+              {.form_control_type = "password", .is_focusable = false},
+              {.form_control_type = "password", .is_focusable = false},
+              {.form_control_type = "password", .is_focusable = false},
+              {.form_control_type = "password", .is_focusable = false},
+              {.role = ElementRole::CONFIRMATION_PASSWORD,
+               .form_control_type = "password",
+               .is_focusable = true,
+               .value = "pw"},
+              {.form_control_type = "password", .is_focusable = false},
+              {.form_control_type = "password", .is_focusable = false},
+          },
+      },
+  });
+}
+
+TEST(FormParserTest, TextAndPasswordFields) {
+  CheckTestData({
+      {
+          "Simple empty sign-in form",
+          // Forms with empty fields cannot be saved, so the parsing result for
+          // saving is empty.
+          {{.role = ElementRole::USERNAME_FILLING,
+            .form_control_type = "text",
+            .value = ""},
+           {.role = ElementRole::CURRENT_PASSWORD_FILLING,
+            .form_control_type = "password",
+            .value = ""}},
+      },
+      {
+          "Simple sign-in form with filled data",
+          {{.role = ElementRole::USERNAME, .form_control_type = "text"},
+           {.role = ElementRole::CURRENT_PASSWORD,
+            .form_control_type = "password"}},
+      },
+      {
+          "Empty sign-in form with an extra text field",
+          {{.form_control_type = "text", .value = ""},
+           {.role = ElementRole::USERNAME_FILLING,
+            .form_control_type = "text",
+            .value = ""},
+           {.role = ElementRole::CURRENT_PASSWORD_FILLING,
+            .form_control_type = "password",
+            .value = ""}},
+      },
+      {
+          "Non-empty sign-in form with an extra text field",
+          {{.role = ElementRole::USERNAME_SAVING, .form_control_type = "text"},
+           {.role = ElementRole::USERNAME_FILLING,
+            .form_control_type = "text",
+            .value = ""},
+           {.role = ElementRole::CURRENT_PASSWORD,
+            .form_control_type = "password"}},
+      },
+      {
+          "Empty sign-in form with an extra invisible text field",
+          {{.role = ElementRole::USERNAME_FILLING,
+            .form_control_type = "text",
+            .value = ""},
+           {.form_control_type = "text", .is_focusable = false, .value = ""},
+           {.role = ElementRole::CURRENT_PASSWORD_FILLING,
+            .form_control_type = "password",
+            .value = ""}},
+      },
+      {
+          "Non-empty sign-in form with an extra invisible text field",
+          {{.role = ElementRole::USERNAME, .form_control_type = "text"},
+           {.form_control_type = "text", .is_focusable = false},
+           {.role = ElementRole::CURRENT_PASSWORD,
+            .form_control_type = "password"}},
+      },
+      {
+          "Simple empty sign-in form with empty username",
+          // Filled forms with a username field which is left empty are
+          // suspicious. The parser will just omit the username altogether.
+          {{.role = ElementRole::USERNAME_FILLING,
+            .form_control_type = "text",
+            .value = ""},
+           {.role = ElementRole::CURRENT_PASSWORD,
+            .form_control_type = "password"}},
+      },
+      {
+          "Simple empty sign-in form with empty password",
+          // Empty password, nothing to save.
+          {{.role = ElementRole::USERNAME_FILLING, .form_control_type = "text"},
+           {.role = ElementRole::CURRENT_PASSWORD_FILLING,
+            .form_control_type = "password",
+            .value = ""}},
+      },
+  });
+}
+
+TEST(FormParserTest, TestAutocomplete) {
+  CheckTestData({
+      {
+          "All possible password autocomplete attributes and some fields "
+          "without autocomplete",
+          {
+              {.role = ElementRole::USERNAME,
+               .form_control_type = "text",
+               .autocomplete_attribute = "username"},
+              {.form_control_type = "text"},
+              {.form_control_type = "password"},
+              {.role = ElementRole::CURRENT_PASSWORD,
+               .form_control_type = "password",
+               .autocomplete_attribute = "current-password"},
+              {.role = ElementRole::NEW_PASSWORD,
+               .form_control_type = "password",
+               .autocomplete_attribute = "new-password",
+               .value = "np"},
+              {.form_control_type = "password"},
+              {.role = ElementRole::CONFIRMATION_PASSWORD,
+               .form_control_type = "password",
+               .autocomplete_attribute = "new-password",
+               .value = "np"},
+          },
+      },
+      {
+          "Non-password autocomplete attributes are skipped",
+          {
+              {.form_control_type = "text", .autocomplete_attribute = "email"},
+              {.role = ElementRole::USERNAME, .form_control_type = "text"},
+              {.role = ElementRole::CURRENT_PASSWORD,
+               .form_control_type = "password"},
+              {.role = ElementRole::NEW_PASSWORD,
+               .form_control_type = "password",
+               .value = "pw"},
+              {.role = ElementRole::CONFIRMATION_PASSWORD,
+               .form_control_type = "password",
+               .value = "pw"},
+              // NB: 'password' is not a valid autocomplete type hint.
+              {.form_control_type = "password",
+               .autocomplete_attribute = "password"},
+          },
+      },
+      {
+          "Basic heuristics kick in if autocomplete analysis fails",
+          {
+              {.role = ElementRole::USERNAME,
+               .form_control_type = "text",
+               .autocomplete_attribute = "email"},
+              // NB: 'password' is not a valid autocomplete type hint.
+              {.role = ElementRole::CURRENT_PASSWORD,
+               .form_control_type = "password",
+               .autocomplete_attribute = "password"},
+              {.role = ElementRole::NEW_PASSWORD,
+               .form_control_type = "password"},
+          },
+      },
+      {
+          "Partial autocomplete analysis is not complemented by basic "
+          "heuristics",
+          // Username not found because there was a valid autocomplete mark-up
+          // but it did not include the plain text field.
+          {
+              {.form_control_type = "text"},
+              {.role = ElementRole::CURRENT_PASSWORD,
+               .form_control_type = "password",
+               .autocomplete_attribute = "current-password"},
+          },
+      },
+      {
+          "Partial autocomplete analysis fails if no passwords are found",
+          // The attribute 'username' is ignored, because there was no password
+          // marked up.
+          {
+              {.form_control_type = "text",
+               .autocomplete_attribute = "username"},
+              {.role = ElementRole::USERNAME, .form_control_type = "text"},
+              {.role = ElementRole::CURRENT_PASSWORD,
+               .form_control_type = "password"},
+          },
+      },
+      {
+          "Multiple username autocomplete attributes, fallback to base "
+          "heuristics",
+          {
+              {.form_control_type = "text",
+               .autocomplete_attribute = "username"},
+              {.role = ElementRole::USERNAME,
+               .form_control_type = "text",
+               .autocomplete_attribute = "username"},
+              {.role = ElementRole::CURRENT_PASSWORD,
+               .form_control_type = "password"},
+              {.role = ElementRole::NEW_PASSWORD,
+               .form_control_type = "password",
+               .autocomplete_attribute = "current-password"},
+          },
+      },
+      {
+          "Parsing complex autocomplete attributes",
+          {
+              // Valid information about form sections, in addition to the
+              // username hint.
+              {.role = ElementRole::USERNAME,
+               .form_control_type = "text",
+               .autocomplete_attribute = "section-test billing username"},
+              {.form_control_type = "text"},
+              // Invalid composition, but the parser is simplistic and just
+              // grabs the last token.
+              {.role = ElementRole::CURRENT_PASSWORD,
+               .form_control_type = "password",
+               .autocomplete_attribute = "new-password current-password"},
+              {.form_control_type = "password"},
+          },
+      },
+      {
+          "Ignored autocomplete attributes",
+          {
+              // 'off' is ignored.
+              {.role = ElementRole::USERNAME,
+               .form_control_type = "text",
+               .autocomplete_attribute = "off"},
+              // Invalid composition, the parser ignores all but the last token.
+              {.role = ElementRole::CURRENT_PASSWORD,
+               .form_control_type = "password",
+               .autocomplete_attribute = "new-password abc"},
+              {.role = ElementRole::NEW_PASSWORD,
+               .form_control_type = "password"},
+          },
+      },
+      {
+          "Swapped username/password autocomplete attributes",
+          // Swap means ignoring autocomplete analysis and falling back to basic
+          // heuristics.
+          {
+              {.role = ElementRole::USERNAME,
+               .form_control_type = "text",
+               .autocomplete_attribute = "current-password"},
+              {.role = ElementRole::CURRENT_PASSWORD,
+               .form_control_type = "password"},
+              {.role = ElementRole::NEW_PASSWORD,
+               .form_control_type = "password",
+               .autocomplete_attribute = "username"},
+          },
+      },
+      {
+          "Autocomplete mark-up overrides visibility",
+          {
+              {.role = ElementRole::USERNAME,
+               .is_focusable = false,
+               .form_control_type = "text",
+               .autocomplete_attribute = "username"},
+              {.is_focusable = true, .form_control_type = "text"},
+              {.is_focusable = true, .form_control_type = "password"},
+              {.role = ElementRole::CURRENT_PASSWORD,
+               .form_control_type = "password",
+               .is_focusable = false,
+               .autocomplete_attribute = "current-password"},
+          },
+      },
+  });
+}
+
+TEST(FormParserTest, DisabledFields) {
+  CheckTestData({
+      {
+          "The disabled attribute is ignored",
+          {
+              {.is_enabled = true, .form_control_type = "text"},
+              {.role = ElementRole::USERNAME,
+               .is_enabled = false,
+               .form_control_type = "text"},
+              {.role = ElementRole::CURRENT_PASSWORD,
+               .form_control_type = "password",
+               .is_enabled = false},
+              {.role = ElementRole::NEW_PASSWORD,
+               .form_control_type = "password",
+               .is_enabled = true},
+          },
+      },
+  });
+}
+
+TEST(FormParserTest, SkippingFieldsWithCreditCardFields) {
+  CheckTestData({
+      {
+          "Simple form, all fields are credit-card-related",
+          {
+              {.form_control_type = "text",
+               .autocomplete_attribute = "cc-name"},
+              {.form_control_type = "password",
+               .autocomplete_attribute = "cc-any-string"},
+          },
+      },
+      {
+          "Non-CC fields are considered",
+          {
+              {.role = ElementRole::USERNAME, .form_control_type = "text"},
+              {.form_control_type = "text",
+               .autocomplete_attribute = "cc-name"},
+              {.form_control_type = "password",
+               .autocomplete_attribute = "cc-any-string"},
+              {.role = ElementRole::CURRENT_PASSWORD,
+               .form_control_type = "password"},
+          },
+      },
+  });
+}
+
+}  // namespace
+
+}  // namespace password_manager
diff --git a/components/password_manager/core/browser/form_parsing/fuzzer/BUILD.gn b/components/password_manager/core/browser/form_parsing/fuzzer/BUILD.gn
index cba3911..99fd109a 100644
--- a/components/password_manager/core/browser/form_parsing/fuzzer/BUILD.gn
+++ b/components/password_manager/core/browser/form_parsing/fuzzer/BUILD.gn
@@ -9,10 +9,35 @@
   sources = [
     "data_accessor.cc",
     "data_accessor.h",
+    "form_data_producer.cc",
+    "form_data_producer.h",
   ]
 
   deps = [
     "//base",
+    "//components/autofill/core/common",
+    "//components/password_manager/core/browser/form_parsing",
+    "//url",
+  ]
+}
+
+# The part of fuzzer support which depends on protobufs is separate from the
+# rest, which is also linked in unittests. This is because protobuf support for
+# the fuzzer gets compiled with 'lite' runtime for unittests but without it for
+# fuzzers. As a result, if the proto_library target is shared both by the fuzzer
+# and the unittest target, linker errors are the result.
+static_library("fuzzer_support_proto") {
+  sources = [
+    "form_data_proto_producer.cc",
+    "form_data_proto_producer.h",
+  ]
+
+  deps = [
+    ":form_data_essentials_proto",
+    "//base",
+    "//components/autofill/core/common",
+    "//components/password_manager/core/browser/form_parsing",
+    "//url",
   ]
 }
 
@@ -29,10 +54,20 @@
   ]
 }
 
+# TODO(crbug.com/845426): There are currently four fuzzers, all combinations of
+# two binary parameters. The first parameter is whether the input data for the
+# fuzzed code is prepared directly from the raw fuzzing string, or from a
+# protobuf description. The second parameter is whether the fuzzed code is the
+# iOS-specific FormData parser, or the generic one. Ultimately, there will be
+# only the generic one, but currently it is under development, so both parsers
+# are used (and need to be fuzzed). The iOS parser is older and its fuzzer's
+# name did not hint at the "iOS" restriction. It is better to keep that name
+# the same, because the security team watches statistics of the fuzzers
+# (https://crbug.com/828705#c11) and renaming causes confusion. So instead, the
+# new fuzzer has the "_generic" suffix on its target name.
+
 fuzzer_test("password_manager_form_parser_fuzzer") {
   sources = [
-    "form_data_producer.cc",
-    "form_data_producer.h",
     "form_parser_fuzzer.cc",
   ]
 
@@ -42,7 +77,6 @@
     "//base:i18n",
     "//components/autofill/core/common",
     "//components/password_manager/core/browser/form_parsing",
-    "//url",
   ]
 
   dict = "form_parser_fuzzer.dict"
@@ -50,19 +84,53 @@
 
 fuzzer_test("password_manager_form_parser_proto_fuzzer") {
   sources = [
-    "form_data_proto_producer.cc",
-    "form_data_proto_producer.h",
     "form_parser_proto_fuzzer.cc",
   ]
 
   deps = [
     ":form_data_essentials_proto",
+    ":fuzzer_support",
+    ":fuzzer_support_proto",
     "//base",
     "//base:i18n",
     "//components/autofill/core/common",
     "//components/password_manager/core/browser/form_parsing",
     "//third_party/libprotobuf-mutator",
-    "//url",
+  ]
+
+  dict = "form_parser_fuzzer.dict"
+}
+
+fuzzer_test("password_manager_form_parser_fuzzer_generic") {
+  sources = [
+    "form_parser_fuzzer_generic.cc",
+  ]
+
+  deps = [
+    ":fuzzer_support",
+    "//base",
+    "//base:i18n",
+    "//components/autofill/core/common",
+    "//components/password_manager/core/browser/form_parsing",
+  ]
+
+  dict = "form_parser_fuzzer.dict"
+}
+
+fuzzer_test("password_manager_form_parser_proto_fuzzer_generic") {
+  sources = [
+    "form_parser_proto_fuzzer_generic.cc",
+  ]
+
+  deps = [
+    ":form_data_essentials_proto",
+    ":fuzzer_support",
+    ":fuzzer_support_proto",
+    "//base",
+    "//base:i18n",
+    "//components/autofill/core/common",
+    "//components/password_manager/core/browser/form_parsing",
+    "//third_party/libprotobuf-mutator",
   ]
 
   dict = "form_parser_fuzzer.dict"
diff --git a/components/password_manager/core/browser/form_parsing/fuzzer/form_parser_fuzzer_generic.cc b/components/password_manager/core/browser/form_parsing/fuzzer/form_parser_fuzzer_generic.cc
new file mode 100644
index 0000000..eb97633
--- /dev/null
+++ b/components/password_manager/core/browser/form_parsing/fuzzer/form_parser_fuzzer_generic.cc
@@ -0,0 +1,44 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/at_exit.h"
+#include "base/i18n/icu_util.h"
+#include "components/autofill/core/common/password_form.h"
+#include "components/password_manager/core/browser/form_parsing/form_parser.h"
+#include "components/password_manager/core/browser/form_parsing/fuzzer/data_accessor.h"
+#include "components/password_manager/core/browser/form_parsing/fuzzer/form_data_producer.h"
+
+namespace password_manager {
+
+// ICU is used inside GURL parser, which is used by GenerateWithDataAccessor.
+struct IcuEnvironment {
+  IcuEnvironment() { CHECK(base::i18n::InitializeICU()); }
+  // used by ICU integration.
+  base::AtExitManager at_exit_manager;
+};
+
+IcuEnvironment* env = new IcuEnvironment();
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  DataAccessor accessor(data, size);
+  FormParsingMode mode = accessor.ConsumeBit() ? FormParsingMode::FILLING
+                                               : FormParsingMode::SAVING;
+  autofill::FormData form_data = GenerateWithDataAccessor(&accessor);
+
+  std::unique_ptr<autofill::PasswordForm> result =
+      ParseFormData(form_data, nullptr, mode);
+  if (result) {
+    // Create a copy of the result -- running the copy-constructor might
+    // discover some invalid data in |result|.
+    autofill::PasswordForm copy(*result);
+  }
+  return 0;
+}
+
+}  // namespace password_manager
diff --git a/components/password_manager/core/browser/form_parsing/fuzzer/form_parser_proto_fuzzer_generic.cc b/components/password_manager/core/browser/form_parsing/fuzzer/form_parser_proto_fuzzer_generic.cc
new file mode 100644
index 0000000..b02e04b
--- /dev/null
+++ b/components/password_manager/core/browser/form_parsing/fuzzer/form_parser_proto_fuzzer_generic.cc
@@ -0,0 +1,43 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/at_exit.h"
+#include "base/i18n/icu_util.h"
+#include "components/autofill/core/common/password_form.h"
+#include "components/password_manager/core/browser/form_parsing/form_parser.h"
+#include "components/password_manager/core/browser/form_parsing/fuzzer/form_data_essentials.pb.h"
+#include "components/password_manager/core/browser/form_parsing/fuzzer/form_data_proto_producer.h"
+#include "testing/libfuzzer/proto/lpm_interface.h"
+
+namespace password_manager {
+
+// ICU is used inside GURL parser, which is used by GenerateWithDataAccessor.
+struct IcuEnvironment {
+  IcuEnvironment() { CHECK(base::i18n::InitializeICU()); }
+  // used by ICU integration.
+  base::AtExitManager at_exit_manager;
+};
+
+IcuEnvironment* env = new IcuEnvironment();
+
+DEFINE_BINARY_PROTO_FUZZER(const ::form_data_fuzzer::Form& form_proto) {
+  FormParsingMode mode = form_proto.is_mode_filling() ? FormParsingMode::FILLING
+                                                      : FormParsingMode::SAVING;
+  autofill::FormData form_data = GenerateWithProto(form_proto);
+
+  std::unique_ptr<autofill::PasswordForm> result =
+      ParseFormData(form_data, nullptr, mode);
+  if (result) {
+    // Create a copy of the result -- running the copy-constructor might
+    // discover some invalid data in |result|.
+    autofill::PasswordForm copy(*result);
+  }
+}
+
+}  // namespace password_manager
diff --git a/components/password_manager/core/browser/new_password_form_manager.cc b/components/password_manager/core/browser/new_password_form_manager.cc
index 150b7ab..f605548 100644
--- a/components/password_manager/core/browser/new_password_form_manager.cc
+++ b/components/password_manager/core/browser/new_password_form_manager.cc
@@ -6,7 +6,7 @@
 
 #include "components/password_manager/core/browser/browser_save_password_progress_logger.h"
 #include "components/password_manager/core/browser/form_fetcher_impl.h"
-#include "components/password_manager/core/browser/form_parsing/ios_form_parser.h"
+#include "components/password_manager/core/browser/form_parsing/form_parser.h"
 #include "components/password_manager/core/browser/password_form_filling.h"
 #include "components/password_manager/core/browser/password_form_metrics_recorder.h"
 #include "components/password_manager/core/browser/password_manager_client.h"
@@ -21,28 +21,14 @@
 
 namespace {
 
-// On iOS, id are used for field identification, whereas on other platforms
-// name field is used. Set id equal to name.
-// TODO(https://crbug.com/831123): Remove this method when the browser side form
-// parsing is ready.
-FormData PreprocessFormData(const FormData& form) {
-  FormData result_form = form;
-  for (auto& field : result_form.fields)
-    field.id = field.name;
-  return result_form;
-}
-
 // Helper function for calling form parsing and logging results if logging is
 // active.
 std::unique_ptr<autofill::PasswordForm> ParseFormAndMakeLogging(
     PasswordManagerClient* client,
     const FormData& form) {
-  // iOS form parsing is a prototype for parsing on all platforms. Call it for
-  // developing and experimentation purposes.
-  // TODO(https://crbug.com/831123): Call general form parsing instead of iOS
-  // one when it is ready.
+  // TODO(crbug.com/831123): Add predictions.
   std::unique_ptr<autofill::PasswordForm> password_form =
-      ParseFormData(PreprocessFormData(form), FormParsingMode::FILLING);
+      ParseFormData(form, nullptr, FormParsingMode::FILLING);
 
   if (password_manager_util::IsLoggingActive(client)) {
     BrowserSavePasswordProgressLogger logger(client->GetLogManager());
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 9b28c2cc..853c7dc 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -163,7 +163,7 @@
 #   persistent IDs for all fields (but not for groups!) are needed. These are
 #   specified by the 'id' keys of each policy. NEVER CHANGE EXISTING IDs,
 #   because doing so would break the deployed wire format!
-#   For your editing convenience: highest ID currently used: 450
+#   For your editing convenience: highest ID currently used: 451
 #   And don't forget to also update the EnterprisePolicies enum of
 #   histograms.xml (run 'python tools/metrics/histograms/update_policies.py').
 #   A policy can be delete if and only if a policy is never launched but has
@@ -12188,7 +12188,7 @@
       'name': 'EnableSyncConsent',
       'type': 'main',
       'schema': { 'type': 'boolean'},
-      'supported_on': ['chrome_os: 66-'],
+      'supported_on': ['chrome_os:66-'],
       'features': {
         'dynamic_refresh': True,
         'per_profile': True,
diff --git a/components/signin/core/browser/android/java/src/org/chromium/components/signin/SystemAccountManagerDelegate.java b/components/signin/core/browser/android/java/src/org/chromium/components/signin/SystemAccountManagerDelegate.java
index 23d08410..e5e086a 100644
--- a/components/signin/core/browser/android/java/src/org/chromium/components/signin/SystemAccountManagerDelegate.java
+++ b/components/signin/core/browser/android/java/src/org/chromium/components/signin/SystemAccountManagerDelegate.java
@@ -189,7 +189,7 @@
      * @param elapsedMs the elapsed time in milliseconds.
      */
     protected static void recordElapsedTimeHistogram(String histogramName, long elapsedMs) {
-        if (!LibraryLoader.isInitialized()) return;
+        if (!LibraryLoader.getInstance().isInitialized()) return;
         RecordHistogram.recordTimesHistogram(histogramName, elapsedMs, TimeUnit.MILLISECONDS);
     }
 
diff --git a/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.cc b/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.cc
index bd59e15..1f148b4 100644
--- a/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.cc
+++ b/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.cc
@@ -98,6 +98,21 @@
   }
 }
 
+SubresourceFilterSafeBrowsingActivationThrottle::ConfigResult::ConfigResult(
+    base::Optional<Configuration> config,
+    bool warning,
+    ActivationList matched_list)
+    : config(config), warning(warning), matched_list(matched_list) {}
+
+SubresourceFilterSafeBrowsingActivationThrottle::ConfigResult::ConfigResult() =
+    default;
+
+SubresourceFilterSafeBrowsingActivationThrottle::ConfigResult::ConfigResult(
+    const ConfigResult&) = default;
+
+SubresourceFilterSafeBrowsingActivationThrottle::ConfigResult::~ConfigResult() =
+    default;
+
 void SubresourceFilterSafeBrowsingActivationThrottle::CheckCurrentUrl() {
   DCHECK(database_client_);
   check_start_times_.push_back(base::TimeTicks::Now());
@@ -112,36 +127,48 @@
 void SubresourceFilterSafeBrowsingActivationThrottle::NotifyResult() {
   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("loading"),
                "SubresourceFilterSafeBrowsingActivationThrottle::NotifyResult");
-  // Compute the matched list and notify observers of the check result.
   DCHECK(!check_results_.empty());
-  ActivationList matched_list = ActivationList::NONE;
-  bool warning = false;
+
+  // For forced activation, the configuration is always the same, but we still
+  // need to compute the warning value base on the final check result.
+  bool forced_activation = client_->ForceActivationInCurrentWebContents();
+  std::vector<ConfigResult> matched_configurations;
+  if (forced_activation) {
+    const auto& check_result = check_results_.back();
+    bool warning = false;
+    GetListForThreatTypeAndMetadata(check_result.threat_type,
+                                    check_result.threat_metadata, &warning);
+    matched_configurations.push_back(
+        ConfigResult(Configuration::MakeForForcedActivation(), warning,
+                     ActivationList::NONE));
+    TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("loading"), "ActivationForced");
+  } else {
+    // Otherwise loop over all the results, computing their configurations and
+    // associated warning booleans.
+    for (const auto& current_result : check_results_) {
+      matched_configurations.push_back(
+          GetHighestPriorityConfiguration(current_result));
+    }
+  }
+
+  // Get the activation decision and the matched configuration and warning.
+  ConfigResult selection;
+  ActivationDecision activation_decision =
+      GetActivationDecision(matched_configurations, &selection);
+  Configuration matched_configuration =
+      selection.config.has_value() ? selection.config.value() : Configuration();
+  bool warning = selection.warning;
+
+  // Compute the matched list and notify observers of the check result.
+  // TODO(ericrobinson): Send the vector of check results to observers.
   const auto& check_result = check_results_.back();
   DCHECK(check_result.finished);
-  matched_list = GetListForThreatTypeAndMetadata(
-      check_result.threat_type, check_result.threat_metadata, &warning);
   SubresourceFilterObserverManager::FromWebContents(
       navigation_handle()->GetWebContents())
       ->NotifySafeBrowsingCheckComplete(navigation_handle(),
                                         check_result.threat_type,
                                         check_result.threat_metadata);
 
-  Configuration matched_configuration;
-  ActivationDecision activation_decision = ActivationDecision::UNKNOWN;
-  if (client_->ForceActivationInCurrentWebContents()) {
-    TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("loading"), "ActivationForced");
-    activation_decision = ActivationDecision::ACTIVATED;
-    matched_configuration = Configuration::MakeForForcedActivation();
-  } else {
-    base::Optional<Configuration> config =
-        GetHighestPriorityConfiguration(matched_list);
-    if (config.has_value()) {
-      matched_configuration = config.value();
-    }
-    activation_decision = GetActivationDecision(config);
-  }
-  DCHECK_NE(activation_decision, ActivationDecision::UNKNOWN);
-
   // Check for whitelisted status last, so that the client gets an accurate
   // indication of whether there would be activation otherwise.
   // Note that the client is responsible for noticing if we're forcing
@@ -159,7 +186,7 @@
   }
 
   LogMetricsOnChecksComplete(
-      matched_list, activation_decision,
+      selection.matched_list, activation_decision,
       matched_configuration.activation_options.activation_level);
 
   auto* driver_factory = ContentSubresourceFilterDriverFactory::FromWebContents(
@@ -217,11 +244,16 @@
   return true;
 }
 
-base::Optional<Configuration> SubresourceFilterSafeBrowsingActivationThrottle::
-    GetHighestPriorityConfiguration(ActivationList matched_list) {
-  base::Optional<Configuration> selected_config;
-
+SubresourceFilterSafeBrowsingActivationThrottle::ConfigResult
+SubresourceFilterSafeBrowsingActivationThrottle::
+    GetHighestPriorityConfiguration(
+        const SubresourceFilterSafeBrowsingClient::CheckResult& result) {
+  DCHECK(result.finished);
+  bool warning = false;
+  ActivationList matched_list = GetListForThreatTypeAndMetadata(
+      result.threat_type, result.threat_metadata, &warning);
   // If it's http or https, find the best config.
+  base::Optional<Configuration> selected_config;
   if (navigation_handle()->GetURL().SchemeIsHTTPOrHTTPS()) {
     const auto& decreasing_configs =
         GetEnabledConfigurations()->configs_by_decreasing_priority();
@@ -242,17 +274,36 @@
                selected_config.has_value()
                    ? selected_config->ToTracedValue()
                    : std::make_unique<base::trace_event::TracedValue>());
-  return selected_config;
+  return ConfigResult(selected_config, warning, matched_list);
 }
 
 ActivationDecision
 SubresourceFilterSafeBrowsingActivationThrottle::GetActivationDecision(
-    const base::Optional<Configuration>& config) {
-  if (!config.has_value()) {
+    const std::vector<ConfigResult>& configurations,
+    ConfigResult* selected_config) {
+  auto selected_itr = configurations.end();
+  bool consider_redirects = base::FeatureList::IsEnabled(
+      kSafeBrowsingSubresourceFilterConsiderRedirects);
+  for (auto itr = configurations.begin(); itr != configurations.end(); itr++) {
+    // Prefer later configs when there's a tie.
+    // Rank no matching config slightly below priority zero.
+    if (!consider_redirects || selected_itr == configurations.end() ||
+        !selected_itr->config.has_value() ||
+        (itr->config.has_value() &&
+         itr->config->activation_conditions.priority >=
+             selected_itr->config->activation_conditions.priority)) {
+      selected_itr = itr;
+    }
+  }
+  DCHECK(selected_itr != configurations.end());
+  *selected_config = *selected_itr;
+
+  if (!selected_config->config.has_value()) {
     return ActivationDecision::ACTIVATION_CONDITIONS_NOT_MET;
   }
 
-  auto activation_level = config->activation_options.activation_level;
+  auto activation_level =
+      selected_config->config->activation_options.activation_level;
   return activation_level == ActivationLevel::DISABLED
              ? ActivationDecision::ACTIVATION_DISABLED
              : ActivationDecision::ACTIVATED;
diff --git a/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.h b/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.h
index 13e7688..588e4da5 100644
--- a/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.h
+++ b/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.h
@@ -8,8 +8,10 @@
 #include <stddef.h>
 
 #include <memory>
+#include <utility>
 #include <vector>
 
+#include "base/feature_list.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
@@ -53,6 +55,19 @@
       const SubresourceFilterSafeBrowsingClient::CheckResult& result);
 
  private:
+  // Highest priority config for a check result.
+  struct ConfigResult {
+    base::Optional<Configuration> config;
+    bool warning;
+    ActivationList matched_list;
+
+    ConfigResult(base::Optional<Configuration> config,
+                 bool warning,
+                 ActivationList matched_list);
+    ~ConfigResult();
+    ConfigResult();
+    ConfigResult(const ConfigResult& result);
+  };
   void CheckCurrentUrl();
   void NotifyResult();
 
@@ -62,12 +77,13 @@
   bool HasFinishedAllSafeBrowsingChecks() const;
   // Gets the configuration with the highest priority among those activated.
   // Returns it, or none if no valid activated configurations.
-  base::Optional<Configuration> GetHighestPriorityConfiguration(
-      ActivationList matched_list);
+  ConfigResult GetHighestPriorityConfiguration(
+      const SubresourceFilterSafeBrowsingClient::CheckResult& result);
   // Gets the ActivationDecision for the given Configuration.
   // Returns it, or ACTIVATION_CONDITIONS_NOT_MET if no Configuration.
   ActivationDecision GetActivationDecision(
-      const base::Optional<Configuration>& config);
+      const std::vector<ConfigResult>& configurations,
+      ConfigResult* selected_config);
 
   // Returns whether a main-frame navigation satisfies the activation
   // |conditions| of a given configuration, except for |priority|.
diff --git a/components/subresource_filter/core/browser/subresource_filter_features.cc b/components/subresource_filter/core/browser/subresource_filter_features.cc
index 92d0bbc..8f17a00 100644
--- a/components/subresource_filter/core/browser/subresource_filter_features.cc
+++ b/components/subresource_filter/core/browser/subresource_filter_features.cc
@@ -234,6 +234,9 @@
 const base::Feature kSafeBrowsingSubresourceFilterExperimentalUI{
     "SubresourceFilterExperimentalUI", base::FEATURE_ENABLED_BY_DEFAULT};
 
+const base::Feature kSafeBrowsingSubresourceFilterConsiderRedirects{
+    "SubresourceFilterConsiderRedirects", base::FEATURE_ENABLED_BY_DEFAULT};
+
 // Legacy name `activation_state` is used in variation parameters.
 const char kActivationLevelParameterName[] = "activation_state";
 const char kActivationLevelDryRun[] = "dryrun";
diff --git a/components/subresource_filter/core/browser/subresource_filter_features.h b/components/subresource_filter/core/browser/subresource_filter_features.h
index 82d22c3..8fa16d7 100644
--- a/components/subresource_filter/core/browser/subresource_filter_features.h
+++ b/components/subresource_filter/core/browser/subresource_filter_features.h
@@ -194,6 +194,9 @@
 // Enables the new experimental UI for the Subresource Filter.
 extern const base::Feature kSafeBrowsingSubresourceFilterExperimentalUI;
 
+// Safe Browsing Activation Throttle considers all checks in a redirect chain.
+extern const base::Feature kSafeBrowsingSubresourceFilterConsiderRedirects;
+
 // Name/values of the variation parameter controlling maximum activation level.
 extern const char kActivationLevelParameterName[];
 extern const char kActivationLevelDryRun[];
diff --git a/components/viz/service/display_embedder/gpu_display_provider.cc b/components/viz/service/display_embedder/gpu_display_provider.cc
index 3da085b..6f8d1ae 100644
--- a/components/viz/service/display_embedder/gpu_display_provider.cc
+++ b/components/viz/service/display_embedder/gpu_display_provider.cc
@@ -208,8 +208,18 @@
     return std::make_unique<SoftwareOutputDevice>();
 
 #if defined(OS_WIN)
-  return CreateSoftwareOutputDeviceWinGpu(
-      surface_handle, &output_device_backing_, display_client);
+  HWND child_hwnd;
+  auto device = CreateSoftwareOutputDeviceWinGpu(
+      surface_handle, &output_device_backing_, display_client, &child_hwnd);
+
+  // If |child_hwnd| isn't null then a new child HWND was created. Send an IPC
+  // to browser process for SetParent() syscall.
+  if (child_hwnd) {
+    gpu_channel_manager_delegate_->SendCreatedChildWindow(surface_handle,
+                                                          child_hwnd);
+  }
+
+  return device;
 #elif defined(OS_MACOSX)
   return std::make_unique<SoftwareOutputDeviceMac>(task_runner_);
 #elif defined(OS_ANDROID)
diff --git a/components/viz/service/display_embedder/software_output_device_win.cc b/components/viz/service/display_embedder/software_output_device_win.cc
index 7d0e5f2..1a484c9 100644
--- a/components/viz/service/display_embedder/software_output_device_win.cc
+++ b/components/viz/service/display_embedder/software_output_device_win.cc
@@ -6,6 +6,8 @@
 
 #include "base/memory/shared_memory.h"
 #include "base/threading/thread_checker.h"
+#include "base/win/windows_version.h"
+#include "base/win/wrapped_window_proc.h"
 #include "components/viz/common/display/use_layered_window.h"
 #include "components/viz/common/resources/resource_sizes.h"
 #include "components/viz/service/display_embedder/output_device_backing.h"
@@ -14,8 +16,10 @@
 #include "skia/ext/platform_canvas.h"
 #include "skia/ext/skia_utils_win.h"
 #include "third_party/skia/include/core/SkCanvas.h"
+#include "ui/base/win/hidden_window.h"
 #include "ui/gfx/gdi_util.h"
 #include "ui/gfx/skia_util.h"
+#include "ui/gfx/win/hwnd_util.h"
 
 namespace viz {
 namespace {
@@ -92,6 +96,7 @@
 }
 
 // SoftwareOutputDevice implementation that draws directly to the provided HWND.
+// The backing buffer for paint is shared for all instances of this class.
 class SoftwareOutputDeviceWinDirect : public SoftwareOutputDeviceWinBase,
                                       public OutputDeviceBacking::Client {
  public:
@@ -332,6 +337,96 @@
   std::move(swap_ack_callback_).Run();
 }
 
+// WindowProc callback function for SoftwareOutputDeviceWinDirectChild.
+LRESULT CALLBACK IntermediateWindowProc(HWND window,
+                                        UINT message,
+                                        WPARAM w_param,
+                                        LPARAM l_param) {
+  switch (message) {
+    case WM_ERASEBKGND:
+      // Prevent Windows from erasing all window content on resize.
+      return 1;
+    default:
+      return DefWindowProc(window, message, w_param, l_param);
+  }
+}
+
+// SoftwareOutputDevice implementation that creates a child HWND and draws
+// directly to it. This is intended to be used in the GPU process. The child
+// HWND is initially parented to a hidden window and needs be reparented to the
+// appropriate browser HWND. The backing buffer for paint is shared for all
+// instances of this class.
+class SoftwareOutputDeviceWinDirectChild
+    : public SoftwareOutputDeviceWinDirect {
+ public:
+  static std::unique_ptr<SoftwareOutputDeviceWinDirectChild> Create(
+      OutputDeviceBacking* backing);
+  ~SoftwareOutputDeviceWinDirectChild() override;
+
+  // SoftwareOutputDeviceWinBase implementation.
+  void ResizeDelegated() override;
+
+ private:
+  static wchar_t* GetWindowClass();
+
+  SoftwareOutputDeviceWinDirectChild(HWND child_hwnd,
+                                     OutputDeviceBacking* backing);
+
+  DISALLOW_COPY_AND_ASSIGN(SoftwareOutputDeviceWinDirectChild);
+};
+
+// static
+std::unique_ptr<SoftwareOutputDeviceWinDirectChild>
+SoftwareOutputDeviceWinDirectChild::Create(OutputDeviceBacking* backing) {
+  // Create a child window that is initially parented to a hidden window.
+  // |child_hwnd| needs to be reparented to a browser created HWND to be
+  // visible.
+  HWND child_hwnd =
+      CreateWindowEx(WS_EX_NOPARENTNOTIFY, GetWindowClass(), L"",
+                     WS_CHILDWINDOW | WS_DISABLED | WS_VISIBLE, 0, 0, 0, 0,
+                     ui::GetHiddenWindow(), nullptr, nullptr, nullptr);
+  DCHECK(child_hwnd);
+  return base::WrapUnique(
+      new SoftwareOutputDeviceWinDirectChild(child_hwnd, backing));
+}
+
+SoftwareOutputDeviceWinDirectChild::~SoftwareOutputDeviceWinDirectChild() {
+  DestroyWindow(hwnd());
+}
+
+void SoftwareOutputDeviceWinDirectChild::ResizeDelegated() {
+  SoftwareOutputDeviceWinDirect::ResizeDelegated();
+  // Resize the child HWND to match the content size.
+  SetWindowPos(hwnd(), nullptr, 0, 0, viewport_pixel_size_.width(),
+               viewport_pixel_size_.height(),
+               SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOCOPYBITS |
+                   SWP_NOOWNERZORDER | SWP_NOZORDER);
+}
+
+// static
+wchar_t* SoftwareOutputDeviceWinDirectChild::GetWindowClass() {
+  static ATOM window_class = 0;
+
+  // Register window class on first call.
+  if (!window_class) {
+    WNDCLASSEX intermediate_class;
+    base::win::InitializeWindowClass(
+        L"Intermediate Software Window",
+        &base::win::WrappedWindowProc<IntermediateWindowProc>, CS_OWNDC, 0, 0,
+        nullptr, reinterpret_cast<HBRUSH>(GetStockObject(BLACK_BRUSH)), nullptr,
+        nullptr, nullptr, &intermediate_class);
+    window_class = RegisterClassEx(&intermediate_class);
+    DCHECK(window_class);
+  }
+
+  return reinterpret_cast<wchar_t*>(window_class);
+}
+
+SoftwareOutputDeviceWinDirectChild::SoftwareOutputDeviceWinDirectChild(
+    HWND child_hwnd,
+    OutputDeviceBacking* backing)
+    : SoftwareOutputDeviceWinDirect(child_hwnd, backing) {}
+
 }  // namespace
 
 std::unique_ptr<SoftwareOutputDevice> CreateSoftwareOutputDeviceWinBrowser(
@@ -346,7 +441,8 @@
 std::unique_ptr<SoftwareOutputDevice> CreateSoftwareOutputDeviceWinGpu(
     HWND hwnd,
     OutputDeviceBacking* backing,
-    mojom::DisplayClient* display_client) {
+    mojom::DisplayClient* display_client,
+    HWND* out_child_hwnd) {
   if (NeedsToUseLayerWindow(hwnd)) {
     DCHECK(display_client);
 
@@ -356,11 +452,19 @@
     display_client->CreateLayeredWindowUpdater(
         mojo::MakeRequest(&layered_window_updater));
 
+    *out_child_hwnd = nullptr;
+
     return std::make_unique<SoftwareOutputDeviceWinProxy>(
         hwnd, std::move(layered_window_updater));
-  }
+  } else {
+    auto software_output_device =
+        SoftwareOutputDeviceWinDirectChild::Create(backing);
 
-  return std::make_unique<SoftwareOutputDeviceWinDirect>(hwnd, backing);
+    // The child HWND needs to be parented to the browser HWND to be visible.
+    *out_child_hwnd = software_output_device->hwnd();
+
+    return software_output_device;
+  }
 }
 
 }  // namespace viz
diff --git a/components/viz/service/display_embedder/software_output_device_win.h b/components/viz/service/display_embedder/software_output_device_win.h
index aa2dd64..60ce8c66 100644
--- a/components/viz/service/display_embedder/software_output_device_win.h
+++ b/components/viz/service/display_embedder/software_output_device_win.h
@@ -27,7 +27,8 @@
 VIZ_SERVICE_EXPORT std::unique_ptr<SoftwareOutputDevice>
 CreateSoftwareOutputDeviceWinGpu(HWND hwnd,
                                  OutputDeviceBacking* backing,
-                                 mojom::DisplayClient* display_client);
+                                 mojom::DisplayClient* display_client,
+                                 HWND* out_child_hwnd);
 
 }  // namespace viz
 
diff --git a/components/viz/service/gl/gpu_service_impl.cc b/components/viz/service/gl/gpu_service_impl.cc
index a97ffcf..755cdf8 100644
--- a/components/viz/service/gl/gpu_service_impl.cc
+++ b/components/viz/service/gl/gpu_service_impl.cc
@@ -617,10 +617,9 @@
 }
 
 #if defined(OS_WIN)
-void GpuServiceImpl::SendAcceleratedSurfaceCreatedChildWindow(
-    gpu::SurfaceHandle parent_window,
-    gpu::SurfaceHandle child_window) {
-  DCHECK(main_runner_->BelongsToCurrentThread());
+void GpuServiceImpl::SendCreatedChildWindow(gpu::SurfaceHandle parent_window,
+                                            gpu::SurfaceHandle child_window) {
+  // This can be called from main or display compositor thread.
   (*gpu_host_)->SetChildSurface(parent_window, child_window);
 }
 #endif
diff --git a/components/viz/service/gl/gpu_service_impl.h b/components/viz/service/gl/gpu_service_impl.h
index 6aa65a0..f260b9c 100644
--- a/components/viz/service/gl/gpu_service_impl.h
+++ b/components/viz/service/gl/gpu_service_impl.h
@@ -149,9 +149,8 @@
                          const std::string& shader) override;
   void ExitProcess() override;
 #if defined(OS_WIN)
-  void SendAcceleratedSurfaceCreatedChildWindow(
-      gpu::SurfaceHandle parent_window,
-      gpu::SurfaceHandle child_window) override;
+  void SendCreatedChildWindow(gpu::SurfaceHandle parent_window,
+                              gpu::SurfaceHandle child_window) override;
 #endif
   void SetActiveURL(const GURL& url) override;
 
diff --git a/components/viz/service/main/viz_main_impl.cc b/components/viz/service/main/viz_main_impl.cc
index 1bfec3a..ad3e2014 100644
--- a/components/viz/service/main/viz_main_impl.cc
+++ b/components/viz/service/main/viz_main_impl.cc
@@ -46,7 +46,12 @@
 std::unique_ptr<base::Thread> CreateAndStartCompositorThread() {
   auto thread = std::make_unique<base::Thread>("VizCompositorThread");
   base::Thread::Options thread_options;
-  thread_options.message_loop_type = base::MessageLoop::TYPE_DEFAULT;
+#if defined(OS_WIN)
+  // Windows needs a UI message loop for child HWND. Other platforms can use the
+  // default message loop type.
+  thread_options.message_loop_type = base::MessageLoop::TYPE_UI;
+#endif
+
 #if defined(OS_ANDROID) || defined(OS_CHROMEOS)
   thread_options.priority = base::ThreadPriority::DISPLAY;
 #endif
diff --git a/content/browser/accessibility/accessibility_tree_formatter_utils_win.cc b/content/browser/accessibility/accessibility_tree_formatter_utils_win.cc
index f5281e2..a54957c6 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_utils_win.cc
+++ b/content/browser/accessibility/accessibility_tree_formatter_utils_win.cc
@@ -131,7 +131,10 @@
       QUOTE(IA2_ROLE_VIEW_PORT),
       QUOTE(IA2_ROLE_COMPLEMENTARY_CONTENT),
       QUOTE(IA2_ROLE_LANDMARK),
-      QUOTE(IA2_ROLE_LEVEL_BAR)};
+      QUOTE(IA2_ROLE_LEVEL_BAR),
+      QUOTE(IA2_ROLE_CONTENT_DELETION),
+      QUOTE(IA2_ROLE_CONTENT_INSERTION),
+  };
 
   return GetNameForPlatformConstant(ia2_table, arraysize(ia2_table), ia2_role);
 }
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index fdcba6cb..c17b467f 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -261,13 +261,13 @@
   if ((always_fatal_flags | fatal_flags) & log_level) {
     LOG(DFATAL) << log_domain << ": " << message;
   } else if (log_level & (G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL)) {
-    LOG(ERROR) << log_domain << ": " << message;
 #if defined(THREAD_SANITIZER)
     // TODO(thomasanderson): This is temporary debugging for
     // https://crbug.com/821704.  Revert this CL once we have the stack trace:
     // https://chromium-review.googlesource.com/#/c/chromium/src/+/1069247
     base::debug::StackTrace().Print();
 #endif
+    LOG(ERROR) << log_domain << ": " << message;
   } else if (log_level & (G_LOG_LEVEL_WARNING)) {
     LOG(WARNING) << log_domain << ": " << message;
   } else if (log_level &
diff --git a/content/browser/child_process_launcher_helper_linux.cc b/content/browser/child_process_launcher_helper_linux.cc
index 5db82c3..ca3da85 100644
--- a/content/browser/child_process_launcher_helper_linux.cc
+++ b/content/browser/child_process_launcher_helper_linux.cc
@@ -15,7 +15,6 @@
 #include "content/public/common/content_switches.h"
 #include "content/public/common/result_codes.h"
 #include "content/public/common/sandboxed_process_launcher_delegate.h"
-#include "gpu/config/gpu_switches.h"
 #include "services/service_manager/sandbox/linux/sandbox_linux.h"
 #include "services/service_manager/zygote/common/common_sandbox_support_linux.h"
 #include "services/service_manager/zygote/common/zygote_handle.h"
@@ -50,10 +49,7 @@
   options->fds_to_remap = files_to_register.GetMappingWithIDAdjustment(
       base::GlobalDescriptors::kBaseDescriptor);
 
-  if (GetProcessType() == switches::kRendererProcess ||
-      (GetProcessType() == switches::kGpuProcess &&
-       base::CommandLine::ForCurrentProcess()->HasSwitch(
-           switches::kEnableOOPRasterization))) {
+  if (GetProcessType() == switches::kRendererProcess) {
     const int sandbox_fd = SandboxHostLinux::GetInstance()->GetChildSocket();
     options->fds_to_remap.push_back(
         std::make_pair(sandbox_fd, service_manager::GetSandboxFD()));
diff --git a/content/browser/frame_host/render_frame_host_manager.cc b/content/browser/frame_host/render_frame_host_manager.cc
index 7f6e271..8b3ad7b1 100644
--- a/content/browser/frame_host/render_frame_host_manager.cc
+++ b/content/browser/frame_host/render_frame_host_manager.cc
@@ -1134,22 +1134,6 @@
                                   SiteInstanceRelation::RELATED);
   }
 
-  // (UGLY) HEURISTIC, process-per-site only:
-  //
-  // If this navigation is generated, then it probably corresponds to a search
-  // query.  Given that search results typically lead to users navigating to
-  // other sites, we don't really want to use the search engine hostname to
-  // determine the site instance for this navigation.
-  //
-  // NOTE: This can be removed once we have a way to transition between
-  //       RenderViews in response to a link click.
-  //
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kProcessPerSite) &&
-      ui::PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_GENERATED)) {
-    return SiteInstanceDescriptor(current_instance_impl);
-  }
-
   if (!frame_tree_node_->IsMainFrame()) {
     SiteInstance* parent_site_instance =
         frame_tree_node_->parent()->current_frame_host()->GetSiteInstance();
diff --git a/content/browser/renderer_host/delegated_frame_host.cc b/content/browser/renderer_host/delegated_frame_host.cc
index fbf9180..d70473e5a 100644
--- a/content/browser/renderer_host/delegated_frame_host.cc
+++ b/content/browser/renderer_host/delegated_frame_host.cc
@@ -276,10 +276,6 @@
   return client_->DelegatedFrameHostGetGutterColor();
 }
 
-gfx::Size DelegatedFrameHost::GetRequestedRendererSize() const {
-  return pending_surface_dip_size_;
-}
-
 void DelegatedFrameHost::DidCreateNewRendererCompositorFrameSink(
     viz::mojom::CompositorFrameSinkClient* renderer_compositor_frame_sink) {
   ResetCompositorFrameSinkSupport();
@@ -527,10 +523,9 @@
 }
 
 void DelegatedFrameHost::TakeFallbackContentFrom(DelegatedFrameHost* other) {
-  if (!other->HasFallbackSurface())
+  if (!other->HasFallbackSurface() || HasFallbackSurface())
     return;
-  if (HasFallbackSurface())
-    return;
+
   if (!HasPrimarySurface()) {
     client_->DelegatedFrameHostGetLayer()->SetShowPrimarySurface(
         *other->client_->DelegatedFrameHostGetLayer()->GetFallbackSurfaceId(),
@@ -539,6 +534,7 @@
         cc::DeadlinePolicy::UseDefaultDeadline(),
         false /* stretch_content_to_fill_bounds */);
   }
+
   client_->DelegatedFrameHostGetLayer()->SetFallbackSurfaceId(
       *other->client_->DelegatedFrameHostGetLayer()->GetFallbackSurfaceId());
 }
diff --git a/content/browser/renderer_host/delegated_frame_host.h b/content/browser/renderer_host/delegated_frame_host.h
index 95b363f..4f3730f 100644
--- a/content/browser/renderer_host/delegated_frame_host.h
+++ b/content/browser/renderer_host/delegated_frame_host.h
@@ -127,7 +127,6 @@
                     const gfx::Size& dip_size,
                     cc::DeadlinePolicy deadline_policy);
   bool HasSavedFrame() const;
-  gfx::Size GetRequestedRendererSize() const;
   void SetCompositor(ui::Compositor* compositor);
   void ResetCompositor();
   // Note: |src_subrect| is specified in DIP dimensions while |output_size|
diff --git a/content/browser/renderer_host/input/mouse_latency_browsertest.cc b/content/browser/renderer_host/input/mouse_latency_browsertest.cc
index e899b03..29caf73b 100644
--- a/content/browser/renderer_host/input/mouse_latency_browsertest.cc
+++ b/content/browser/renderer_host/input/mouse_latency_browsertest.cc
@@ -39,8 +39,6 @@
     "<html>"
     "<head>"
     "<title>Mouse event trace events reported.</title>"
-    "<script src=\"../../resources/testharness.js\"></script>"
-    "<script src=\"../../resources/testharnessreport.js\"></script>"
     "<script>"
     "  let i=0;"
     "  document.addEventListener('mousemove', () => {"
@@ -48,6 +46,12 @@
     "    while(performance.now() < end);"
     "    document.body.style.backgroundColor = 'rgb(' + (i++) + ',0,0)'"
     "  });"
+    "  document.addEventListener('wheel', (e) => {"
+    "    if (!e.cancelable)"
+    "      return;"
+    "    var end = performance.now() + 50;"
+    "    while(performance.now() < end);"
+    "  });"
     "</script>"
     "<style>"
     "body {"
@@ -214,6 +218,24 @@
     runner_->Run();
   }
 
+  // Generate mouse wheel scroll.
+  void DoSyncCoalescedMouseWheel(const gfx::PointF position,
+                                 const gfx::Vector2dF& delta) {
+    SyntheticSmoothScrollGestureParams params;
+    params.gesture_source_type = SyntheticGestureParams::MOUSE_INPUT;
+    params.anchor = position;
+    params.distances.push_back(delta);
+
+    GetWidgetHost()->QueueSyntheticGesture(
+        SyntheticGesture::Create(params),
+        base::BindOnce(&MouseLatencyBrowserTest::OnSyntheticGestureCompleted,
+                       base::Unretained(this)));
+
+    // Runs until we get the OnSyntheticGestureCompleted callback
+    runner_ = std::make_unique<base::RunLoop>();
+    runner_->Run();
+  }
+
   void StartTracing() {
     base::trace_event::TraceConfig trace_config(
         "{"
@@ -245,6 +267,37 @@
     return trace_data_;
   }
 
+  void AssertTraceIdsBeginAndEnd(const base::Value& trace_data,
+                                 const std::string& trace_event_name) {
+    const base::DictionaryValue* trace_data_dict;
+    ASSERT_TRUE(trace_data.GetAsDictionary(&trace_data_dict));
+
+    const base::ListValue* traceEvents;
+    ASSERT_TRUE(trace_data_dict->GetList("traceEvents", &traceEvents));
+
+    std::map<std::string, int> trace_ids;
+
+    for (size_t i = 0; i < traceEvents->GetSize(); ++i) {
+      const base::DictionaryValue* traceEvent;
+      ASSERT_TRUE(traceEvents->GetDictionary(i, &traceEvent));
+
+      std::string name;
+      ASSERT_TRUE(traceEvent->GetString("name", &name));
+
+      if (name != trace_event_name)
+        continue;
+
+      std::string id;
+      if (traceEvent->GetString("id", &id))
+        ++trace_ids[id];
+    }
+
+    for (auto i : trace_ids) {
+      // Each trace id should show up once for the begin, and once for the end.
+      EXPECT_EQ(2, i.second);
+    }
+  }
+
  private:
   base::MessageLoop loop_;
   std::unique_ptr<base::RunLoop> runner_;
@@ -331,34 +384,18 @@
       ->WaitFor("InputLatency::MouseUp");
   const base::Value& trace_data = StopTracing();
 
-  const base::DictionaryValue* trace_data_dict;
-  trace_data.GetAsDictionary(&trace_data_dict);
-  ASSERT_TRUE(trace_data.GetAsDictionary(&trace_data_dict));
+  AssertTraceIdsBeginAndEnd(trace_data, "InputLatency::MouseMove");
+}
 
-  const base::ListValue* traceEvents;
-  ASSERT_TRUE(trace_data_dict->GetList("traceEvents", &traceEvents));
+IN_PROC_BROWSER_TEST_F(MouseLatencyBrowserTest,
+                       CoalescedMouseWheelsCorrectlyTerminated) {
+  LoadURL();
 
-  std::map<std::string, int> trace_ids;
+  StartTracing();
+  DoSyncCoalescedMouseWheel(gfx::PointF(100, 100), gfx::Vector2dF(0, -100));
+  const base::Value& trace_data = StopTracing();
 
-  for (size_t i = 0; i < traceEvents->GetSize(); ++i) {
-    const base::DictionaryValue* traceEvent;
-    ASSERT_TRUE(traceEvents->GetDictionary(i, &traceEvent));
-
-    std::string name;
-    ASSERT_TRUE(traceEvent->GetString("name", &name));
-
-    if (name != "InputLatency::MouseMove")
-      continue;
-
-    std::string id;
-    if (traceEvent->GetString("id", &id))
-      ++trace_ids[id];
-  }
-
-  for (auto i : trace_ids) {
-    // Each trace id should show up once for the begin, and once for the end.
-    EXPECT_EQ(2, i.second);
-  }
+  AssertTraceIdsBeginAndEnd(trace_data, "InputLatency::MouseWheel");
 }
 
 }  // namespace content
diff --git a/content/browser/renderer_host/input/mouse_wheel_event_queue.cc b/content/browser/renderer_host/input/mouse_wheel_event_queue.cc
index c3f4641..b50ea82 100644
--- a/content/browser/renderer_host/input/mouse_wheel_event_queue.cc
+++ b/content/browser/renderer_host/input/mouse_wheel_event_queue.cc
@@ -59,6 +59,10 @@
   if (event_sent_for_gesture_ack_ && !wheel_queue_.empty()) {
     QueuedWebMouseWheelEvent* last_event = wheel_queue_.back().get();
     if (last_event->CanCoalesceWith(event)) {
+      // Terminate the LatencyInfo of the event before it gets coalesced away.
+      event.latency.AddLatencyNumber(
+          ui::INPUT_EVENT_LATENCY_TERMINATED_NO_SWAP_COMPONENT, 0);
+
       last_event->CoalesceWith(event);
       TRACE_EVENT_INSTANT2("input", "MouseWheelEventQueue::CoalescedWheelEvent",
                            TRACE_EVENT_SCOPE_THREAD, "total_dx",
diff --git a/content/browser/renderer_host/media/media_devices_dispatcher_host.cc b/content/browser/renderer_host/media/media_devices_dispatcher_host.cc
index 6818c22..673bba7 100644
--- a/content/browser/renderer_host/media/media_devices_dispatcher_host.cc
+++ b/content/browser/renderer_host/media/media_devices_dispatcher_host.cc
@@ -36,15 +36,18 @@
 ToVectorAudioInputDeviceCapabilitiesPtr(
     const std::vector<blink::mojom::AudioInputDeviceCapabilities>&
         capabilities_vector,
-    const url::Origin& security_origin,
-    const std::string& salt) {
+    const MediaDeviceSaltAndOrigin& salt_and_origin) {
   std::vector<blink::mojom::AudioInputDeviceCapabilitiesPtr> result;
   result.reserve(capabilities_vector.size());
   for (auto& capabilities : capabilities_vector) {
     blink::mojom::AudioInputDeviceCapabilitiesPtr capabilities_ptr =
         blink::mojom::AudioInputDeviceCapabilities::New();
     capabilities_ptr->device_id =
-        GetHMACForMediaDeviceID(salt, security_origin, capabilities.device_id);
+        GetHMACForMediaDeviceID(salt_and_origin.device_id_salt,
+                                salt_and_origin.origin, capabilities.device_id);
+    capabilities_ptr->group_id =
+        GetHMACForMediaDeviceID(salt_and_origin.group_id_salt,
+                                salt_and_origin.origin, capabilities.group_id);
     capabilities_ptr->parameters = capabilities.parameters;
     result.push_back(std::move(capabilities_ptr));
   }
@@ -292,8 +295,7 @@
 }
 
 struct MediaDevicesDispatcherHost::AudioInputCapabilitiesRequest {
-  std::string device_id_salt;
-  url::Origin security_origin;
+  MediaDeviceSaltAndOrigin salt_and_origin;
   GetAudioInputCapabilitiesCallback client_callback;
 };
 
@@ -302,8 +304,7 @@
     const MediaDeviceSaltAndOrigin& salt_and_origin) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   pending_audio_input_capabilities_requests_.push_back(
-      AudioInputCapabilitiesRequest{salt_and_origin.device_id_salt,
-                                    salt_and_origin.origin,
+      AudioInputCapabilitiesRequest{salt_and_origin,
                                     std::move(client_callback)});
   if (pending_audio_input_capabilities_requests_.size() > 1U)
     return;
@@ -337,7 +338,7 @@
   DCHECK_EQ(num_pending_audio_input_parameters_, 0U);
   for (const auto& device_info : enumeration[MEDIA_DEVICE_TYPE_AUDIO_INPUT]) {
     blink::mojom::AudioInputDeviceCapabilities capabilities(
-        device_info.device_id,
+        device_info.device_id, device_info.group_id,
         media::AudioParameters::UnavailableDeviceParams());
     if (device_info.device_id == default_device_id)
       current_audio_input_capabilities_.insert(
@@ -386,8 +387,7 @@
   for (auto& request : pending_audio_input_capabilities_requests_) {
     std::move(request.client_callback)
         .Run(ToVectorAudioInputDeviceCapabilitiesPtr(
-            current_audio_input_capabilities_, request.security_origin,
-            request.device_id_salt));
+            current_audio_input_capabilities_, request.salt_and_origin));
   }
 
   current_audio_input_capabilities_.clear();
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 fe8544b..72ea255 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.cc
+++ b/content/browser/renderer_host/render_widget_host_view_android.cc
@@ -954,26 +954,16 @@
 
   viz::CompositorFrameMetadata metadata = frame.metadata.Clone();
 
-  bool has_content = !current_surface_size_.IsEmpty();
-
   base::Closure ack_callback =
       base::Bind(&RenderWidgetHostViewAndroid::SendReclaimCompositorResources,
                  weak_ptr_factory_.GetWeakPtr(), true /* is_swap_ack */);
 
   ack_callbacks_.push(ack_callback);
 
-  viz::BeginFrameAck ack = frame.metadata.begin_frame_ack;
-  if (!has_content) {
-    EvictDelegatedFrame();
-
-    ack.has_damage = false;
-    OnDidNotProduceFrame(ack);
-  } else {
-    delegated_frame_host_->SubmitCompositorFrame(
-        local_surface_id, std::move(frame), std::move(hit_test_region_list));
-    frame_evictor_->SwappedFrame(!host()->is_hidden());
-    AcknowledgeBeginFrame(ack);
-  }
+  delegated_frame_host_->SubmitCompositorFrame(
+      local_surface_id, std::move(frame), std::move(hit_test_region_list));
+  frame_evictor_->SwappedFrame(!host()->is_hidden());
+  AcknowledgeBeginFrame();
 
   if (host()->is_hidden())
     RunAckCallbacks();
@@ -1008,11 +998,10 @@
   }
 
   delegated_frame_host_->DidNotProduceFrame(ack);
-  AcknowledgeBeginFrame(ack);
+  AcknowledgeBeginFrame();
 }
 
-void RenderWidgetHostViewAndroid::AcknowledgeBeginFrame(
-    const viz::BeginFrameAck& ack) {
+void RenderWidgetHostViewAndroid::AcknowledgeBeginFrame() {
   // AcknowledgeBeginFrame is not called for the synchronous compositor path.
   if (begin_frame_source_)
     begin_frame_source_->DidFinishFrame(this);
diff --git a/content/browser/renderer_host/render_widget_host_view_android.h b/content/browser/renderer_host/render_widget_host_view_android.h
index adc0b61e..2ff49e7 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.h
+++ b/content/browser/renderer_host/render_widget_host_view_android.h
@@ -391,7 +391,7 @@
   };
   void AddBeginFrameRequest(BeginFrameRequestType request);
   void ClearBeginFrameRequest(BeginFrameRequestType request);
-  void AcknowledgeBeginFrame(const viz::BeginFrameAck& ack);
+  void AcknowledgeBeginFrame();
   void StartObservingRootWindow();
   void StopObservingRootWindow();
   void SendBeginFramePaused();
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc
index 73b1078..253d8bb5 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -831,12 +831,6 @@
   }
 }
 
-gfx::Size RenderWidgetHostViewAura::GetRequestedRendererSize() const {
-  return delegated_frame_host_
-             ? delegated_frame_host_->GetRequestedRendererSize()
-             : RenderWidgetHostViewBase::GetRequestedRendererSize();
-}
-
 uint32_t RenderWidgetHostViewAura::GetCaptureSequenceNumber() const {
   return latest_capture_sequence_number_;
 }
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.h b/content/browser/renderer_host/render_widget_host_view_aura.h
index fc7f253..529dc23 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.h
+++ b/content/browser/renderer_host/render_widget_host_view_aura.h
@@ -136,7 +136,6 @@
   void Destroy() override;
   void SetTooltipText(const base::string16& tooltip_text) override;
   void DisplayTooltipText(const base::string16& tooltip_text) override;
-  gfx::Size GetRequestedRendererSize() const override;
   uint32_t GetCaptureSequenceNumber() const override;
   bool IsSurfaceAvailableForCopy() const override;
   void CopyFromSurface(
diff --git a/content/browser/renderer_host/render_widget_host_view_cocoa.mm b/content/browser/renderer_host/render_widget_host_view_cocoa.mm
index d86576c..8c656e7 100644
--- a/content/browser/renderer_host/render_widget_host_view_cocoa.mm
+++ b/content/browser/renderer_host/render_widget_host_view_cocoa.mm
@@ -238,15 +238,6 @@
   [[self window] makeFirstResponder:nil];
   [NSApp updateWindows];
 
-  // Debug key to check if the current input context still holds onto the view.
-  NSTextInputContext* currentContext = [NSTextInputContext currentInputContext];
-  auto* crashKey = base::debug::AllocateCrashKeyString(
-      "text-input-context-client", base::debug::CrashKeySize::Size32);
-  base::debug::ScopedCrashKeyString textInputContextCrashKey(
-      crashKey, currentContext && [currentContext client] == self
-                    ? "text input still held on"
-                    : "text input no longer held on");
-
   [super dealloc];
 }
 
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index d845ebe..ea99d60 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -1058,7 +1058,8 @@
               ->GetRenderWidgetHost()
               ->GetView());
 
-  WaitForChildFrameSurfaceReady(nested_iframe_node->current_frame_host());
+  RenderFrameSubmissionObserver frame_observer(nested_iframe_node);
+  frame_observer.WaitForMetadataChange();
 
   // Verify that applying a CSS scale transform does not impact the size of the
   // content of the nested iframe.
@@ -7966,8 +7967,9 @@
       child->current_frame_host()->GetView(),
       proxy_to_parent->cross_process_frame_connector()->get_view_for_testing());
 
-  // Make sure that the child frame has submitted a compositor frame.
-  WaitForChildFrameSurfaceReady(child->current_frame_host());
+  // Make sure that the child frame has submitted a compositor frame
+  RenderFrameSubmissionObserver frame_observer(child);
+  frame_observer.WaitForMetadataChange();
 
   // Send a postMessage from the child to its parent.  This verifies that the
   // parent's proxy in the child's SiteInstance was also restored.
@@ -12413,4 +12415,50 @@
   EXPECT_EQ(width, 700);
 }
 
+class SitePerProcessAndProcessPerSiteBrowserTest
+    : public SitePerProcessBrowserTest {
+ public:
+  SitePerProcessAndProcessPerSiteBrowserTest() {}
+
+ protected:
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    SitePerProcessBrowserTest::SetUpCommandLine(command_line);
+    command_line->AppendSwitch(switches::kProcessPerSite);
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SitePerProcessAndProcessPerSiteBrowserTest);
+};
+
+// Verify that when --site-per-process is combined with --process-per-site, a
+// cross-site, browser-initiated navigation with a generated page transition
+// does not stay in the old SiteInstance.  See https://crbug.com/825411.
+IN_PROC_BROWSER_TEST_F(SitePerProcessAndProcessPerSiteBrowserTest,
+                       GeneratedTransitionsSwapProcesses) {
+  EXPECT_TRUE(NavigateToURL(
+      shell(), embedded_test_server()->GetURL("foo.com", "/title1.html")));
+  scoped_refptr<SiteInstance> foo_site_instance(
+      web_contents()->GetSiteInstance());
+
+  // Navigate cross-site via a generated transition.  This would normally
+  // happen for search queries.
+  TestNavigationObserver observer(web_contents());
+  NavigationController::LoadURLParams params(
+      embedded_test_server()->GetURL("bar.com", "/title2.html"));
+  params.transition_type = ui::PAGE_TRANSITION_GENERATED;
+  web_contents()->GetController().LoadURLWithParams(params);
+  observer.Wait();
+
+  // Ensure the original SiteInstance wasn't reused.
+  EXPECT_NE(foo_site_instance, web_contents()->GetSiteInstance());
+
+  // Ensure the new page can access cookies without getting killed.
+  EXPECT_TRUE(ExecuteScript(web_contents(), "document.cookie = 'foo=bar';"));
+  std::string cookie;
+  EXPECT_TRUE(ExecuteScriptAndExtractString(
+      web_contents(), "window.domAutomationController.send(document.cookie);",
+      &cookie));
+  EXPECT_EQ("foo=bar", cookie);
+}
+
 }  // namespace content
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index bf7b9ebc..a11fe0b 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -405,6 +405,8 @@
 
   if (base::FeatureList::IsEnabled(features::kLazyFrameLoading))
     WebRuntimeFeatures::EnableLazyFrameLoading(true);
+  if (base::FeatureList::IsEnabled(features::kLazyFrameVisibleLoadTimeMetrics))
+    WebRuntimeFeatures::EnableLazyFrameVisibleLoadTimeMetrics(true);
 
   WebRuntimeFeatures::EnableV8ContextSnapshot(
       base::FeatureList::IsEnabled(features::kV8ContextSnapshot));
diff --git a/content/gpu/gpu_main.cc b/content/gpu/gpu_main.cc
index e6a589f..c4fc2512 100644
--- a/content/gpu/gpu_main.cc
+++ b/content/gpu/gpu_main.cc
@@ -43,7 +43,6 @@
 #include "gpu/ipc/service/gpu_watchdog_thread.h"
 #include "media/gpu/buildflags.h"
 #include "third_party/angle/src/gpu_info_util/SystemInfo.h"
-#include "third_party/skia/include/core/SkGraphics.h"
 #include "ui/events/platform/platform_event_source.h"
 #include "ui/gfx/switches.h"
 #include "ui/gl/gl_context.h"
@@ -78,12 +77,10 @@
 #endif
 
 #if defined(OS_LINUX)
-#include "content/common/font_config_ipc_linux.h"
 #include "content/gpu/gpu_sandbox_hook_linux.h"
 #include "content/public/common/sandbox_init.h"
 #include "services/service_manager/sandbox/linux/sandbox_linux.h"
 #include "services/service_manager/zygote/common/common_sandbox_support_linux.h"
-#include "third_party/skia/include/ports/SkFontConfigInterface.h"
 #endif
 
 #if defined(OS_MACOSX)
@@ -339,15 +336,6 @@
       nullptr);
 #endif
 
-  if (gpu_preferences.enable_oop_rasterization) {
-    SkGraphics::Init();
-#if defined(OS_LINUX)
-    // Set up the font IPC so that the GPU process can create typefaces.
-    SkFontConfigInterface::SetGlobal(
-        sk_make_sp<FontConfigIPC>(service_manager::GetSandboxFD()));
-#endif
-  }
-
   base::HighResolutionTimerManager hi_res_timer_manager;
 
   {
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index a9f3fa4..977f819c 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -180,6 +180,8 @@
 
 const base::Feature kLazyFrameLoading{"LazyFrameLoading",
                                       base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kLazyFrameVisibleLoadTimeMetrics{
+    "LazyFrameVisibleLoadTimeMetrics", base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Enable lazy initialization of the media controls.
 const base::Feature kLazyInitializeMediaControls{
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index 017ecc32..2134f63 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -51,6 +51,7 @@
 CONTENT_EXPORT extern const base::Feature kKeyboardLockAPI;
 CONTENT_EXPORT extern const base::Feature kLayeredAPI;
 CONTENT_EXPORT extern const base::Feature kLazyFrameLoading;
+CONTENT_EXPORT extern const base::Feature kLazyFrameVisibleLoadTimeMetrics;
 CONTENT_EXPORT extern const base::Feature kLazyInitializeMediaControls;
 CONTENT_EXPORT extern const base::Feature kLazyParseCSS;
 CONTENT_EXPORT extern const base::Feature kLowPriorityIframes;
diff --git a/content/public/test/browser_test_utils.cc b/content/public/test/browser_test_utils.cc
index 7d52b82..15adb76 100644
--- a/content/public/test/browser_test_utils.cc
+++ b/content/public/test/browser_test_utils.cc
@@ -1615,6 +1615,16 @@
   return false;
 }
 
+RenderFrameMetadataProvider* RenderFrameMetadataProviderFromFrameTreeNode(
+    FrameTreeNode* node) {
+  DCHECK(node);
+  DCHECK(node->current_frame_host());
+  DCHECK(node->current_frame_host()->GetRenderWidgetHost());
+  return node->current_frame_host()
+      ->GetRenderWidgetHost()
+      ->render_frame_metadata_provider();
+}
+
 RenderFrameMetadataProvider* RenderFrameMetadataProviderFromWebContents(
     WebContents* web_contents) {
   DCHECK(web_contents);
@@ -1895,6 +1905,11 @@
 }
 
 RenderFrameSubmissionObserver::RenderFrameSubmissionObserver(
+    FrameTreeNode* node)
+    : RenderFrameSubmissionObserver(
+          RenderFrameMetadataProviderFromFrameTreeNode(node)) {}
+
+RenderFrameSubmissionObserver::RenderFrameSubmissionObserver(
     WebContents* web_contents)
     : RenderFrameSubmissionObserver(
           RenderFrameMetadataProviderFromWebContents(web_contents)) {}
diff --git a/content/public/test/browser_test_utils.h b/content/public/test/browser_test_utils.h
index d8f799f..99feb9a 100644
--- a/content/public/test/browser_test_utils.h
+++ b/content/public/test/browser_test_utils.h
@@ -72,6 +72,7 @@
 
 class BrowserContext;
 struct FrameVisualProperties;
+class FrameTreeNode;
 class InterstitialPage;
 class MessageLoopRunner;
 class NavigationHandle;
@@ -712,6 +713,7 @@
  public:
   explicit RenderFrameSubmissionObserver(
       RenderFrameMetadataProvider* render_frame_metadata_provider);
+  explicit RenderFrameSubmissionObserver(FrameTreeNode* node);
   explicit RenderFrameSubmissionObserver(WebContents* web_contents);
   ~RenderFrameSubmissionObserver() override;
 
diff --git a/content/renderer/media/stream/media_stream_constraints_util_audio.cc b/content/renderer/media/stream/media_stream_constraints_util_audio.cc
index 9fad33a..69a8d3a 100644
--- a/content/renderer/media/stream/media_stream_constraints_util_audio.cc
+++ b/content/renderer/media/stream/media_stream_constraints_util_audio.cc
@@ -217,6 +217,9 @@
     if (!capability.DeviceID().empty())
       device_id_set_ = DiscreteSet<std::string>({capability.DeviceID()});
 
+    if (!capability.GroupID().empty())
+      group_id_set_ = DiscreteSet<std::string>({capability.GroupID()});
+
     MediaStreamAudioSource* source = capability.source();
 
     if (!source) {
@@ -322,6 +325,13 @@
       return;
     }
 
+    group_id_set_ = group_id_set_.Intersection(
+        StringSetFromConstraint(constraint_set.group_id));
+    if (group_id_set_.IsEmpty()) {
+      failed_constraint_name_ = constraint_set.group_id.GetName();
+      return;
+    }
+
     goog_array_geometry_set_ = goog_array_geometry_set_.Intersection(
         StringSetFromConstraint(constraint_set.goog_array_geometry));
     if (goog_array_geometry_set_.IsEmpty()) {
@@ -382,6 +392,16 @@
       }
     }
 
+    if (constraint_set.group_id.HasIdeal()) {
+      for (const blink::WebString& ideal_value :
+           constraint_set.group_id.Ideal()) {
+        if (group_id_set_.Contains(ideal_value.Utf8())) {
+          fitness += 1.0;
+          break;
+        }
+      }
+    }
+
     for (size_t i = 0; i < NUM_BOOL_CONSTRAINTS; ++i) {
       if ((constraint_set.*kBlinkBoolConstraintFields[i]).HasIdeal() &&
           bool_sets_[i].Contains(
@@ -544,6 +564,7 @@
 
   const char* failed_constraint_name_ = nullptr;
   DiscreteSet<std::string> device_id_set_;
+  DiscreteSet<std::string> group_id_set_;
   std::array<DiscreteSet<bool>, NUM_BOOL_CONSTRAINTS> bool_sets_;
   DiscreteSet<std::string> goog_array_geometry_set_;
   DiscreteSet<std::string> echo_cancellation_type_set_;
@@ -657,15 +678,26 @@
 
 AudioDeviceCaptureCapability::AudioDeviceCaptureCapability(
     std::string device_id,
+    std::string group_id,
     const media::AudioParameters& parameters)
-    : device_id_(std::move(device_id)), parameters_(parameters) {
+    : device_id_(std::move(device_id)),
+      group_id_(std::move(group_id)),
+      parameters_(parameters) {
   DCHECK(!device_id_.empty());
 }
 
+AudioDeviceCaptureCapability::AudioDeviceCaptureCapability(
+    const AudioDeviceCaptureCapability& other) = default;
+
 const std::string& AudioDeviceCaptureCapability::DeviceID() const {
   return source_ ? source_->device().id : device_id_;
 }
 
+const std::string& AudioDeviceCaptureCapability::GroupID() const {
+  return source_ && source_->device().group_id ? *source_->device().group_id
+                                               : group_id_;
+}
+
 const media::AudioParameters& AudioDeviceCaptureCapability::Parameters() const {
   return source_ ? source_->device().input : parameters_;
 }
diff --git a/content/renderer/media/stream/media_stream_constraints_util_audio.h b/content/renderer/media/stream/media_stream_constraints_util_audio.h
index 6eb8438..767ac21 100644
--- a/content/renderer/media/stream/media_stream_constraints_util_audio.h
+++ b/content/renderer/media/stream/media_stream_constraints_util_audio.h
@@ -41,10 +41,12 @@
   AudioDeviceCaptureCapability();
 
   // This creates an AudioDeviceCaptureCapability where the device ID is limited
-  // to |device_id| and other settings are limited by the given |parameters|.
-  // |device_id| must not be empty. Intended to be used by getUserMedia() with
-  // device capture for devices that are not currently in use.
+  // to |device_id|, the group ID is limited to |group_id| and other settings
+  // are limited by the given |parameters|. |device_id| must not be empty.
+  // Intended to be used by getUserMedia() with device capture for devices that
+  // are not currently in use.
   AudioDeviceCaptureCapability(std::string device_id,
+                               std::string group_id,
                                const media::AudioParameters& parameters);
 
   // This creates an AudioDeviceCaptureCapability where the device ID and other
@@ -53,6 +55,8 @@
   // getUserMedia() with device capture for devices that are currently in use.
   explicit AudioDeviceCaptureCapability(MediaStreamAudioSource* source);
 
+  AudioDeviceCaptureCapability(const AudioDeviceCaptureCapability& other);
+
   // If this capability represents a device currently in use, this method
   // returns a pointer to the MediaStreamAudioSource object associated with the
   // device. Otherwise, it returns null.
@@ -64,6 +68,9 @@
   // processing constraints.
   const std::string& DeviceID() const;
 
+  // Returns the group ID of the device associated with this capability.
+  const std::string& GroupID() const;
+
   // Returns the audio parameters for the device associated with this
   // capability. If DeviceID() returns an empty string, these parameters contain
   // default values that work well for content capture.
@@ -72,6 +79,7 @@
  private:
   MediaStreamAudioSource* source_ = nullptr;
   std::string device_id_;
+  std::string group_id_;
   media::AudioParameters parameters_;
 };
 
diff --git a/content/renderer/media/stream/media_stream_constraints_util_audio_unittest.cc b/content/renderer/media/stream/media_stream_constraints_util_audio_unittest.cc
index 7b1a1897..0558174 100644
--- a/content/renderer/media/stream/media_stream_constraints_util_audio_unittest.cc
+++ b/content/renderer/media/stream/media_stream_constraints_util_audio_unittest.cc
@@ -66,7 +66,7 @@
     ResetFactory();
     if (IsDeviceCapture()) {
       capabilities_.emplace_back(
-          "default_device",
+          "default_device", "fake_group1",
           media::AudioParameters(media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
                                  media::CHANNEL_LAYOUT_STEREO,
                                  media::AudioParameters::kAudioCDSampleRate,
@@ -78,7 +78,7 @@
           media::AudioParameters::kAudioCDSampleRate, 1000);
       hw_echo_canceller_parameters.set_effects(
           media::AudioParameters::ECHO_CANCELLER);
-      capabilities_.emplace_back("hw_echo_canceller_device",
+      capabilities_.emplace_back("hw_echo_canceller_device", "fake_group2",
                                  hw_echo_canceller_parameters);
 
       media::AudioParameters experimental_hw_echo_canceller_parameters(
@@ -88,6 +88,7 @@
       experimental_hw_echo_canceller_parameters.set_effects(
           media::AudioParameters::EXPERIMENTAL_ECHO_CANCELLER);
       capabilities_.emplace_back("experimental_hw_echo_canceller_device",
+                                 "fake_group3",
                                  experimental_hw_echo_canceller_parameters);
 
       media::AudioParameters geometry_parameters(
@@ -95,7 +96,8 @@
           media::CHANNEL_LAYOUT_STEREO,
           media::AudioParameters::kAudioCDSampleRate, 1000);
       geometry_parameters.set_mic_positions(kMicPositions);
-      capabilities_.emplace_back("geometry device", geometry_parameters);
+      capabilities_.emplace_back("geometry device", "fake_group4",
+                                 geometry_parameters);
 
       default_device_ = &capabilities_[0];
       hw_echo_canceller_device_ = &capabilities_[1];
@@ -565,6 +567,29 @@
   }
 }
 
+TEST_P(MediaStreamConstraintsUtilAudioTest, ExactGroupID) {
+  for (const auto& device : capabilities_) {
+    constraint_factory_.basic().group_id.SetExact(
+        blink::WebString::FromASCII(device.GroupID()));
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    CheckDevice(device, result);
+    CheckBoolDefaults(AudioSettingsBoolMembers(),
+                      {&AudioProcessingProperties::enable_sw_echo_cancellation},
+                      result);
+    bool has_hw_echo_cancellation =
+        device.Parameters().effects() & media::AudioParameters::ECHO_CANCELLER;
+    EXPECT_EQ(IsDeviceCapture() && !has_hw_echo_cancellation,
+              result.audio_processing_properties().enable_sw_echo_cancellation);
+    if (&device == geometry_device_) {
+      EXPECT_EQ(kMicPositions,
+                result.audio_processing_properties().goog_array_geometry);
+    } else {
+      CheckGeometryDefaults(result);
+    }
+  }
+}
+
 // Tests the echoCancellation constraint with a device without hardware echo
 // cancellation.
 TEST_P(MediaStreamConstraintsUtilAudioTest, EchoCancellationWithSw) {
@@ -1559,9 +1584,10 @@
                                    false /* render_to_associated_sink */);
 
   const std::string kUnusedDeviceID = "unused_device";
+  const std::string kGroupID = "fake_group";
   AudioDeviceCaptureCapabilities capabilities;
   capabilities.emplace_back(processed_source.get());
-  capabilities.emplace_back(kUnusedDeviceID,
+  capabilities.emplace_back(kUnusedDeviceID, kGroupID,
                             media::AudioParameters::UnavailableDeviceParams());
 
   {
diff --git a/content/renderer/media/stream/user_media_processor.cc b/content/renderer/media/stream/user_media_processor.cc
index 36854bb6..41674c1 100644
--- a/content/renderer/media/stream/user_media_processor.cc
+++ b/content/renderer/media/stream/user_media_processor.cc
@@ -445,10 +445,12 @@
       if (source->device().type == MEDIA_DEVICE_AUDIO_CAPTURE)
         audio_source = static_cast<MediaStreamAudioSource*>(source);
     }
-    if (audio_source)
+    if (audio_source) {
       capabilities.emplace_back(audio_source);
-    else
-      capabilities.emplace_back(device->device_id, device->parameters);
+    } else {
+      capabilities.emplace_back(device->device_id, device->group_id,
+                                device->parameters);
+    }
   }
 
   SelectAudioSettings(web_request, capabilities);
diff --git a/content/test/data/accessibility/html/del-expected-win.txt b/content/test/data/accessibility/html/del-expected-win.txt
index ebd72812..ee914c9d 100644
--- a/content/test/data/accessibility/html/del-expected-win.txt
+++ b/content/test/data/accessibility/html/del-expected-win.txt
@@ -1,5 +1,5 @@
 ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE
 ++IA2_ROLE_PARAGRAPH
 ++++ROLE_SYSTEM_STATICTEXT name='I am '
-++++ROLE_SYSTEM_GROUPING
+++++IA2_ROLE_CONTENT_DELETION
 ++++++ROLE_SYSTEM_STATICTEXT name='vegetarian'
diff --git a/content/test/data/accessibility/html/frameset-expected-win.txt b/content/test/data/accessibility/html/frameset-expected-win.txt
index 4f061fe7..e9eb027 100644
--- a/content/test/data/accessibility/html/frameset-expected-win.txt
+++ b/content/test/data/accessibility/html/frameset-expected-win.txt
@@ -3,10 +3,10 @@
 ++++ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE
 ++++++IA2_ROLE_PARAGRAPH
 ++++++++ROLE_SYSTEM_STATICTEXT name='My favorite browser is '
-++++++++ROLE_SYSTEM_GROUPING
+++++++++IA2_ROLE_CONTENT_DELETION
 ++++++++++ROLE_SYSTEM_STATICTEXT name='ABC'
 ++++++++ROLE_SYSTEM_STATICTEXT name=' '
-++++++++ROLE_SYSTEM_GROUPING
+++++++++IA2_ROLE_CONTENT_INSERTION
 ++++++++++ROLE_SYSTEM_STATICTEXT name='Chrome'
 ++++++++ROLE_SYSTEM_STATICTEXT name='!'
 ++IA2_ROLE_SECTION FOCUSABLE
diff --git a/content/test/data/accessibility/html/ins-expected-win.txt b/content/test/data/accessibility/html/ins-expected-win.txt
index 06c8a64a..89fb89f 100644
--- a/content/test/data/accessibility/html/ins-expected-win.txt
+++ b/content/test/data/accessibility/html/ins-expected-win.txt
@@ -1,9 +1,9 @@
 ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE
 ++IA2_ROLE_PARAGRAPH
 ++++ROLE_SYSTEM_STATICTEXT name='My favorite browser is '
-++++ROLE_SYSTEM_GROUPING
+++++IA2_ROLE_CONTENT_DELETION
 ++++++ROLE_SYSTEM_STATICTEXT name='ABC'
 ++++ROLE_SYSTEM_STATICTEXT name=' '
-++++ROLE_SYSTEM_GROUPING
+++++IA2_ROLE_CONTENT_INSERTION
 ++++++ROLE_SYSTEM_STATICTEXT name='Chrome'
 ++++ROLE_SYSTEM_STATICTEXT name='!'
diff --git a/content/test/data/browsing_data/site_data.html b/content/test/data/browsing_data/site_data.html
index ecb8fdc..298899a1 100644
--- a/content/test/data/browsing_data/site_data.html
+++ b/content/test/data/browsing_data/site_data.html
@@ -10,7 +10,7 @@
   }
 
   function setCookie() {
-    document.cookie = 'foo=bar';
+    document.cookie = 'foo=bar; Max-Age=1000';
     success_();
   }
 
@@ -18,6 +18,15 @@
     domAutomationController.send(document.cookie == 'foo=bar');
   }
 
+  function setSessionCookie() {
+    document.cookie = 'bar=session';
+    success_();
+  }
+
+  function hasSessionCookie() {
+    domAutomationController.send(document.cookie == 'bar=session');
+  }
+
   function setLocalStorage() {
     localStorage.setItem('foo', 'bar');
     success_();
@@ -39,7 +48,7 @@
   function setServiceWorker() {
     navigator.serviceWorker.register('empty_worker.js').then(function() {
       navigator.serviceWorker.ready.then(success_);
-    });
+    }).catch(failure_);
   }
 
   function hasServiceWorker() {
@@ -51,7 +60,7 @@
   function setCacheStorage() {
     caches.open("cache").then(function (cache) {
       cache.put("/foo", new Response("bar")).then(success_);
-    });
+    }).catch(failure_);
   }
 
   function hasCacheStorage() {
diff --git a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
index 9748251c..b252854d 100644
--- a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
+++ b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
@@ -120,6 +120,9 @@
         'in-parameter-passed-as-inout-argument-and-global.html',
         ['nvidia'], bug=792210)
 
+    self.Skip('conformance2/rendering/blitframebuffer-size-overflow.html',
+        ['intel', 'amd', 'no_angle'], bug=844308)
+
     # Windows only.
     self.Fail('conformance2/buffers/uniform-buffers.html',
         ['win'], bug=757098)
@@ -207,6 +210,8 @@
         ['win', 'nvidia', 'opengl'], bug=782254)
     self.Fail('deqp/functional/gles3/shaderpackingfunction.html',
         ['win', 'nvidia', 'opengl'], bug=795030)
+    self.Skip('conformance2/rendering/blitframebuffer-size-overflow.html',
+        ['win', 'nvidia', 'opengl'], bug=830046)
     self.Flaky('conformance2/transform_feedback/switching-objects.html',
         ['win', 'nvidia', 'opengl', 'no_passthrough'], bug=832238)
 
@@ -437,6 +442,10 @@
     self.Fail('deqp/functional/gles3/negativeshaderapi.html',
         ['mac', 'amd', 'intel'], bug=811614)
 
+    # Mac NVIDIA
+    self.Skip('conformance2/rendering/blitframebuffer-size-overflow.html',
+        ['mac', 'nvidia', 'no_angle'], bug=844308)
+
     # Mac Retina NVIDIA
     self.Fail('deqp/functional/gles3/shaderindexing/mat_01.html',
         ['mac', ('nvidia', 0xfe9)], bug=728271)
diff --git a/docs/security/side-channel-threat-model.md b/docs/security/side-channel-threat-model.md
index 4aa3ba8..f74f104 100644
--- a/docs/security/side-channel-threat-model.md
+++ b/docs/security/side-channel-threat-model.md
@@ -338,6 +338,9 @@
 
 ### Gating Access To APIs That Enable Exploitation
 
+**Note:** This section explores ideas but we are not currently planning on
+implementing anything along these lines.
+
 Although we want to support applications that necessarily need access to
 features that enable exploitation, such as `SharedArrayBuffer`, we don’t
 necessarily need to make the features available unconditionally. For example, a
diff --git a/gin/v8_isolate_memory_dump_provider_unittest.cc b/gin/v8_isolate_memory_dump_provider_unittest.cc
index cbfda4e..db9d1ebe 100644
--- a/gin/v8_isolate_memory_dump_provider_unittest.cc
+++ b/gin/v8_isolate_memory_dump_provider_unittest.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "base/trace_event/process_memory_dump.h"
+#include "base/trace_event/trace_event.h"
 #include "gin/public/isolate_holder.h"
 #include "gin/test/v8_test.h"
 
@@ -35,14 +36,14 @@
   bool did_dump_isolate_stats = false;
   bool did_dump_space_stats = false;
   bool did_dump_objects_stats = false;
-  for (const auto& it : allocator_dumps) {
-    const std::string& dump_name = it.first;
-    if (dump_name.find("v8/isolate") != std::string::npos) {
+  for (const auto& name_dump : allocator_dumps) {
+    const std::string& name = name_dump.first;
+    if (name.find("v8/isolate") != std::string::npos) {
       did_dump_isolate_stats = true;
     }
-    if (dump_name.find("heap_spaces") != std::string::npos) {
+    if (name.find("heap_spaces") != std::string::npos) {
       did_dump_space_stats = true;
-    } else if (dump_name.find("heap_objects") != std::string::npos) {
+    } else if (name.find("heap_objects") != std::string::npos) {
       did_dump_objects_stats = true;
     }
   }
@@ -64,12 +65,12 @@
 
   bool did_dump_detached_contexts = false;
   bool did_dump_native_contexts = false;
-  for (const auto& it : allocator_dumps) {
-    const std::string& dump_name = it.first;
-    if (dump_name.find("contexts/detached_context") != std::string::npos) {
+  for (const auto& name_dump : allocator_dumps) {
+    const std::string& name = name_dump.first;
+    if (name.find("contexts/detached_context") != std::string::npos) {
       did_dump_detached_contexts = true;
     }
-    if (dump_name.find("contexts/native_context") != std::string::npos) {
+    if (name.find("contexts/native_context") != std::string::npos) {
       did_dump_native_contexts = true;
     }
   }
@@ -78,4 +79,46 @@
   ASSERT_TRUE(did_dump_native_contexts);
 }
 
+TEST_F(V8MemoryDumpProviderTest, DumpCodeStatistics) {
+  // Code stats are disabled unless this category is enabled.
+  base::trace_event::TraceLog::GetInstance()->SetEnabled(
+      base::trace_event::TraceConfig(
+          TRACE_DISABLED_BY_DEFAULT("memory-infra.v8.code_stats"), ""),
+      base::trace_event::TraceLog::RECORDING_MODE);
+
+  base::trace_event::MemoryDumpArgs dump_args = {
+      base::trace_event::MemoryDumpLevelOfDetail::LIGHT};
+  std::unique_ptr<base::trace_event::ProcessMemoryDump> process_memory_dump(
+      new base::trace_event::ProcessMemoryDump(dump_args));
+  instance_->isolate_memory_dump_provider_for_testing()->OnMemoryDump(
+      dump_args, process_memory_dump.get());
+  const base::trace_event::ProcessMemoryDump::AllocatorDumpsMap&
+      allocator_dumps = process_memory_dump->allocator_dumps();
+
+  bool did_dump_bytecode_size = false;
+  bool did_dump_code_size = false;
+  bool did_dump_external_scripts_size = false;
+
+  for (const auto& name_dump : allocator_dumps) {
+    const std::string& name = name_dump.first;
+    if (name.find("heap_spaces") != std::string::npos) {
+      for (const base::trace_event::MemoryAllocatorDump::Entry& entry :
+           name_dump.second->entries()) {
+        if (entry.name == "bytecode_and_metadata_size") {
+          did_dump_bytecode_size = true;
+        } else if (entry.name == "code_and_metadata_size") {
+          did_dump_code_size = true;
+        } else if (entry.name == "external_script_source_size") {
+          did_dump_external_scripts_size = true;
+        }
+      }
+    }
+  }
+  base::trace_event::TraceLog::GetInstance()->SetDisabled();
+
+  ASSERT_TRUE(did_dump_bytecode_size);
+  ASSERT_TRUE(did_dump_code_size);
+  ASSERT_TRUE(did_dump_external_scripts_size);
+}
+
 }  // namespace gin
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index 295fb33b..bc5007c9 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -8566,27 +8566,22 @@
     return;
   }
 
-  base::CheckedNumeric<GLint> src_width_temp = srcX1;
-  src_width_temp -= srcX0;
-  base::CheckedNumeric<GLint> src_height_temp = srcY1;
-  src_height_temp -= srcY0;
-  base::CheckedNumeric<GLint> dst_width_temp = dstX1;
-  dst_width_temp -= dstX0;
-  base::CheckedNumeric<GLint> dst_height_temp = dstY1;
-  dst_height_temp -= dstY0;
-  if (!src_width_temp.Abs().IsValid() || !src_height_temp.Abs().IsValid() ||
-      !dst_width_temp.Abs().IsValid() || !dst_height_temp.Abs().IsValid()) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, func_name,
-                       "the width or height of src or dst region overflowed");
-    return;
-  }
 
   if (workarounds().adjust_src_dst_region_for_blitframebuffer) {
     gfx::Size read_size = GetBoundReadFramebufferSize();
     gfx::Rect src_bounds(0, 0, read_size.width(), read_size.height());
     GLint src_x = srcX1 > srcX0 ? srcX0 : srcX1;
     GLint src_y = srcY1 > srcY0 ? srcY0 : srcY1;
+    base::CheckedNumeric<GLint> src_width_temp = srcX1;
+    src_width_temp -= srcX0;
+    base::CheckedNumeric<GLint> src_height_temp = srcY1;
+    src_height_temp -= srcY0;
     GLuint src_width = 0, src_height = 0;
+    if (!src_width_temp.IsValid() || !src_height_temp.IsValid()) {
+      LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, func_name,
+                         "the width or height of src region overflow");
+      return;
+    }
     if (!src_width_temp.Abs().AssignIfValid(&src_width))
       src_width = 0;
     if (!src_height_temp.Abs().AssignIfValid(&src_height))
diff --git a/gpu/ipc/in_process_command_buffer.cc b/gpu/ipc/in_process_command_buffer.cc
index 548dd69..79205abf 100644
--- a/gpu/ipc/in_process_command_buffer.cc
+++ b/gpu/ipc/in_process_command_buffer.cc
@@ -1243,8 +1243,8 @@
   }
 
   // In the GPU process forward the request back to the browser process.
-  gpu_channel_manager_delegate_->SendAcceleratedSurfaceCreatedChildWindow(
-      parent_window, child_window);
+  gpu_channel_manager_delegate_->SendCreatedChildWindow(parent_window,
+                                                        child_window);
 }
 #endif
 
diff --git a/gpu/ipc/service/gles2_command_buffer_stub.cc b/gpu/ipc/service/gles2_command_buffer_stub.cc
index 113dc1f..0c66a18 100644
--- a/gpu/ipc/service/gles2_command_buffer_stub.cc
+++ b/gpu/ipc/service/gles2_command_buffer_stub.cc
@@ -377,8 +377,8 @@
     SurfaceHandle parent_window,
     SurfaceHandle child_window) {
   GpuChannelManager* gpu_channel_manager = channel_->gpu_channel_manager();
-  gpu_channel_manager->delegate()->SendAcceleratedSurfaceCreatedChildWindow(
-      parent_window, child_window);
+  gpu_channel_manager->delegate()->SendCreatedChildWindow(parent_window,
+                                                          child_window);
 }
 #endif
 
diff --git a/gpu/ipc/service/gpu_channel_manager_delegate.h b/gpu/ipc/service/gpu_channel_manager_delegate.h
index 9039738..6f0688d3 100644
--- a/gpu/ipc/service/gpu_channel_manager_delegate.h
+++ b/gpu/ipc/service/gpu_channel_manager_delegate.h
@@ -44,9 +44,11 @@
   virtual void ExitProcess() = 0;
 
 #if defined(OS_WIN)
-  virtual void SendAcceleratedSurfaceCreatedChildWindow(
-      SurfaceHandle parent_window,
-      SurfaceHandle child_window) = 0;
+  // Tells the delegate that |child_window| was created in the GPU process and
+  // to send an IPC to make SetParent() syscall. This syscall is blocked by the
+  // GPU sandbox and must be made in the browser process.
+  virtual void SendCreatedChildWindow(SurfaceHandle parent_window,
+                                      SurfaceHandle child_window) = 0;
 #endif
 
   // Sets the currently active URL.  Use GURL() to clear the URL.
diff --git a/gpu/ipc/service/gpu_channel_test_common.cc b/gpu/ipc/service/gpu_channel_test_common.cc
index 2da975b..2233bab 100644
--- a/gpu/ipc/service/gpu_channel_test_common.cc
+++ b/gpu/ipc/service/gpu_channel_test_common.cc
@@ -39,9 +39,8 @@
                          const std::string& shader) override {}
   void ExitProcess() override {}
 #if defined(OS_WIN)
-  void SendAcceleratedSurfaceCreatedChildWindow(
-      SurfaceHandle parent_window,
-      SurfaceHandle child_window) override {}
+  void SendCreatedChildWindow(SurfaceHandle parent_window,
+                              SurfaceHandle child_window) override {}
 #endif
 
  private:
diff --git a/infra/config/global/cr-buildbucket.cfg b/infra/config/global/cr-buildbucket.cfg
index 552145a0..0d26b06 100644
--- a/infra/config/global/cr-buildbucket.cfg
+++ b/infra/config/global/cr-buildbucket.cfg
@@ -43,6 +43,11 @@
     role: WRITER
     group: "google/luci-task-force@google.com"
   }
+  acls {
+    # Allow luci-migration to bump next build number.
+    role: WRITER
+    identity: "luci-migration@appspot.gserviceaccount.com"
+  }
 }
 
 acl_sets {
@@ -67,6 +72,11 @@
     role: WRITER
     group: "service-account-chromium-tryserver"
   }
+  acls {
+    # Allow luci-migration to bump next build number.
+    role: WRITER
+    identity: "luci-migration@appspot.gserviceaccount.com"
+  }
 }
 
 # CI builders (of which are few) may use high number of concurrent Goma jobs.
@@ -1332,6 +1342,7 @@
       recipe {
         name: "chromium_clang_coverage_reports"
       }
+      mixins: "fyi-ci"
       execution_timeout_secs: 72000  # 20h
     }
     ############################################################################
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_view_controller.mm b/ios/chrome/browser/ui/omnibox/omnibox_view_controller.mm
index 09a5038..5b76662 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_view_controller.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_view_controller.mm
@@ -7,6 +7,7 @@
 #include "components/strings/grit/components_strings.h"
 #import "ios/chrome/browser/ui/omnibox/omnibox_container_view.h"
 #include "ios/chrome/browser/ui/ui_util.h"
+#include "ios/chrome/browser/ui/uikit_ui_util.h"
 #include "ui/base/l10n/l10n_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -59,10 +60,14 @@
 
 - (void)viewDidLoad {
   [super viewDidLoad];
-  [self.view setLeadingImageHidden:!IsIPadIdiom()];
   self.textField.placeholder = l10n_util::GetNSString(IDS_OMNIBOX_EMPTY_HINT);
 }
 
+- (void)traitCollectionDidChange:(UITraitCollection*)previousTraitCollection {
+  [super traitCollectionDidChange:previousTraitCollection];
+  [self updateLeadingImageVisibility];
+}
+
 #pragma mark - public methods
 
 - (OmniboxTextFieldIOS*)textField {
@@ -75,4 +80,10 @@
   [self.view setLeadingImage:icon];
 }
 
+#pragma mark - private
+
+- (void)updateLeadingImageVisibility {
+  [self.view setLeadingImageHidden:!IsRegularXRegularSizeClass(self)];
+}
+
 @end
diff --git a/ios/chrome/browser/ui/reading_list/OWNERS b/ios/chrome/browser/ui/reading_list/OWNERS
index 66536df..33da4bf 100644
--- a/ios/chrome/browser/ui/reading_list/OWNERS
+++ b/ios/chrome/browser/ui/reading_list/OWNERS
@@ -1,4 +1,5 @@
 gambard@chromium.org
+kkhorimoto@chromium.org
 noyau@chromium.org
 olivierrobin@chromium.org
 
diff --git a/ios/third_party/material_components_ios/README.chromium b/ios/third_party/material_components_ios/README.chromium
index 5426c28..6363f688 100644
--- a/ios/third_party/material_components_ios/README.chromium
+++ b/ios/third_party/material_components_ios/README.chromium
@@ -1,7 +1,7 @@
 Name: Material Components for iOS
 URL: https://github.com/material-components/material-components-ios
 Version: 0
-Revision: c94e6d2c5bdb2a1f63b835453460a35f15312505
+Revision: 7e7ca3549d8e21c8749c3daf1f9a6dce5be840d3
 License: Apache 2.0
 License File: LICENSE
 Security Critical: yes
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm
index a09e8d6..78c7655 100644
--- a/ios/web/web_state/ui/crw_web_controller.mm
+++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -5273,12 +5273,14 @@
 
 - (void)URLDidChangeWithoutDocumentChange:(const GURL&)newURL {
   DCHECK(newURL == net::GURLWithNSURL([_webView URL]));
+  DCHECK(!_documentURL.host().empty() || _documentURL.SchemeIsFile());
 
   if (base::FeatureList::IsEnabled(
           web::features::kCrashOnUnexpectedURLChange)) {
     if (_documentURL.GetOrigin() != newURL.GetOrigin()) {
-      if (newURL.username().find(_documentURL.host()) != std::string::npos ||
-          newURL.password().find(_documentURL.host()) != std::string::npos) {
+      if (!_documentURL.host().empty() &&
+          (newURL.username().find(_documentURL.host()) != std::string::npos ||
+           newURL.password().find(_documentURL.host()) != std::string::npos)) {
         CHECK(false);
       }
     }
diff --git a/net/dns/host_cache.cc b/net/dns/host_cache.cc
index b900828..bb8e40a9 100644
--- a/net/dns/host_cache.cc
+++ b/net/dns/host_cache.cc
@@ -10,6 +10,7 @@
 #include "base/metrics/field_trial.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/time/default_tick_clock.h"
 #include "base/trace_event/trace_event.h"
 #include "base/values.h"
 #include "net/base/net_errors.h"
@@ -154,11 +155,12 @@
     : max_entries_(max_entries),
       network_changes_(0),
       restore_size_(0),
-      delegate_(nullptr) {}
+      delegate_(nullptr),
+      tick_clock_(base::DefaultTickClock::GetInstance()) {}
 
 HostCache::~HostCache() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  RecordEraseAll(ERASE_DESTRUCT, base::TimeTicks::Now());
+  RecordEraseAll(ERASE_DESTRUCT, tick_clock_->NowTicks());
 }
 
 const HostCache::Entry* HostCache::Lookup(const Key& key,
@@ -266,7 +268,7 @@
 
 void HostCache::clear() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  RecordEraseAll(ERASE_CLEAR, base::TimeTicks::Now());
+  RecordEraseAll(ERASE_CLEAR, tick_clock_->NowTicks());
 
   // Don't bother scheduling a write if there's nothing to clear.
   if (size() == 0)
@@ -287,7 +289,7 @@
   }
 
   bool changed = false;
-  base::TimeTicks now = base::TimeTicks::Now();
+  base::TimeTicks now = tick_clock_->NowTicks();
   for (EntryMap::iterator it = entries_.begin(); it != entries_.end();) {
     EntryMap::iterator next_it = std::next(it);
 
@@ -322,6 +324,9 @@
     entry_dict->SetInteger(kFlagsKey, key.host_resolver_flags);
 
     if (include_staleness) {
+      // The kExpirationKey value is using TimeTicks instead of Time used if
+      // |include_staleness| is false, so it cannot be used to deserialize.
+      // This is ok as it is used only for netlog.
       entry_dict->SetString(kExpirationKey,
                             NetLog::TickCountToString(entry.expires()));
       entry_dict->SetInteger(kTtlKey, entry.ttl().InMilliseconds());
@@ -330,7 +335,7 @@
       // Convert expiration time in TimeTicks to Time for serialization, using a
       // string because base::Value doesn't handle 64-bit integers.
       base::Time expiration_time =
-          base::Time::Now() - (base::TimeTicks::Now() - entry.expires());
+          base::Time::Now() - (tick_clock_->NowTicks() - entry.expires());
       entry_dict->SetString(
           kExpirationKey,
           base::Int64ToString(expiration_time.ToInternalValue()));
@@ -384,7 +389,7 @@
       return false;
 
     base::TimeTicks expiration_time =
-        base::TimeTicks::Now() -
+        tick_clock_->NowTicks() -
         (base::Time::Now() - base::Time::FromInternalValue(time_internal));
 
     Key key(hostname, static_cast<AddressFamily>(address_family), flags);
@@ -549,7 +554,7 @@
   hostname.CopyToString(&cache_key.hostname);
 
   const HostCache::Entry* entry =
-      LookupStale(cache_key, base::TimeTicks::Now(), stale_out);
+      LookupStale(cache_key, tick_clock_->NowTicks(), stale_out);
   if (!entry) {
     // Might not have found the cache entry because the address_family or
     // host_resolver_flags in cache_key do not match those used for the
@@ -558,7 +563,7 @@
     cache_key.address_family = net::ADDRESS_FAMILY_IPV4;
     cache_key.host_resolver_flags =
         net::HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6;
-    entry = LookupStale(cache_key, base::TimeTicks::Now(), stale_out);
+    entry = LookupStale(cache_key, tick_clock_->NowTicks(), stale_out);
     if (!entry)
       return false;
   }
diff --git a/net/dns/host_cache.h b/net/dns/host_cache.h
index 816c038..ce2cb68 100644
--- a/net/dns/host_cache.h
+++ b/net/dns/host_cache.h
@@ -25,6 +25,7 @@
 
 namespace base {
 class ListValue;
+class TickClock;
 }
 
 namespace net {
@@ -194,6 +195,10 @@
 
   void set_persistence_delegate(PersistenceDelegate* delegate);
 
+  void set_tick_clock_for_testing(const base::TickClock* tick_clock) {
+    tick_clock_ = tick_clock;
+  }
+
   // Empties the cache.
   void clear();
 
@@ -264,6 +269,8 @@
   size_t restore_size_;
 
   PersistenceDelegate* delegate_;
+  // Shared tick clock, overridden for testing.
+  const base::TickClock* tick_clock_;
 
   THREAD_CHECKER(thread_checker_);
 
diff --git a/net/dns/host_resolver_impl.cc b/net/dns/host_resolver_impl.cc
index bdf16b8..7a29f958 100644
--- a/net/dns/host_resolver_impl.cc
+++ b/net/dns/host_resolver_impl.cc
@@ -42,6 +42,7 @@
 #include "base/task_scheduler/post_task.h"
 #include "base/threading/scoped_blocking_call.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "base/time/default_tick_clock.h"
 #include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
 #include "base/values.h"
@@ -598,14 +599,15 @@
               RequestPriority priority,
               const CompletionCallback& callback,
               AddressList* addresses,
-              Job* job)
+              Job* job,
+              base::TimeTicks request_time)
       : source_net_log_(source_net_log),
         info_(info),
         priority_(priority),
         job_(job),
         callback_(callback),
         addresses_(addresses),
-        request_time_(base::TimeTicks::Now()) {}
+        request_time_(request_time) {}
 
   ~RequestImpl() override;
 
@@ -691,7 +693,8 @@
            const ProcTaskParams& params,
            const Callback& callback,
            scoped_refptr<base::TaskRunner> proc_task_runner,
-           const NetLogWithSource& job_net_log)
+           const NetLogWithSource& job_net_log,
+           const base::TickClock* tick_clock)
       : key_(key),
         params_(params),
         callback_(callback),
@@ -700,7 +703,8 @@
         attempt_number_(0),
         completed_attempt_number_(0),
         completed_attempt_error_(ERR_UNEXPECTED),
-        net_log_(job_net_log) {
+        net_log_(job_net_log),
+        tick_clock_(tick_clock) {
     if (!params_.resolver_proc.get())
       params_.resolver_proc = HostResolverProc::GetDefault();
     // If default is unset, use the system proc.
@@ -743,7 +747,7 @@
 
   void StartLookupAttempt() {
     DCHECK(network_task_runner_->BelongsToCurrentThread());
-    base::TimeTicks start_time = base::TimeTicks::Now();
+    base::TimeTicks start_time = tick_clock_->NowTicks();
     ++attempt_number_;
     // Dispatch the lookup attempt to a worker thread.
     proc_task_runner_->PostTask(
@@ -846,7 +850,7 @@
     if (was_retry_attempt) {
       // If retry attempt finishes before 1st attempt, then get stats on how
       // much time is saved by having spawned an extra attempt.
-      retry_attempt_finished_time_ = base::TimeTicks::Now();
+      retry_attempt_finished_time_ = tick_clock_->NowTicks();
     }
 
     if (error != OK) {
@@ -865,7 +869,7 @@
                             const int error,
                             const int os_error) const {
     DCHECK(network_task_runner_->BelongsToCurrentThread());
-    base::TimeDelta duration = base::TimeTicks::Now() - start_time;
+    base::TimeDelta duration = tick_clock_->NowTicks() - start_time;
     if (error == OK)
       UMA_HISTOGRAM_LONG_TIMES_100("Net.DNS.ProcTask.SuccessTime", duration);
     else
@@ -907,7 +911,7 @@
     if (!first_attempt_to_complete && is_first_attempt && !was_canceled()) {
       UMA_HISTOGRAM_LONG_TIMES_100(
           "DNS.AttemptTimeSavedByRetry",
-          base::TimeTicks::Now() - retry_attempt_finished_time_);
+          tick_clock_->NowTicks() - retry_attempt_finished_time_);
     }
 
     if (was_canceled() || !first_attempt_to_complete) {
@@ -921,7 +925,7 @@
         UMA_HISTOGRAM_ENUMERATION("DNS.AttemptCancelled", attempt_number, 100);
     }
 
-    base::TimeDelta duration = base::TimeTicks::Now() - start_time;
+    base::TimeDelta duration = tick_clock_->NowTicks() - start_time;
     if (error == OK)
       UMA_HISTOGRAM_LONG_TIMES_100("DNS.AttemptSuccessDuration", duration);
     else
@@ -964,6 +968,8 @@
 
   NetLogWithSource net_log_;
 
+  const base::TickClock* tick_clock_;
+
   DISALLOW_COPY_AND_ASSIGN(ProcTask);
 };
 
@@ -1000,13 +1006,15 @@
   DnsTask(DnsClient* client,
           const Key& key,
           Delegate* delegate,
-          const NetLogWithSource& job_net_log)
+          const NetLogWithSource& job_net_log,
+          const base::TickClock* tick_clock)
       : client_(client),
         key_(key),
         delegate_(delegate),
         net_log_(job_net_log),
         num_completed_transactions_(0),
-        task_start_time_(base::TimeTicks::Now()) {
+        tick_clock_(tick_clock),
+        task_start_time_(tick_clock_->NowTicks()) {
     DCHECK(client);
     DCHECK(delegate_);
   }
@@ -1059,7 +1067,7 @@
             family == ADDRESS_FAMILY_IPV6 ? dns_protocol::kTypeAAAA
                                           : dns_protocol::kTypeA,
             base::BindOnce(&DnsTask::OnTransactionComplete,
-                           base::Unretained(this), base::TimeTicks::Now()),
+                           base::Unretained(this), tick_clock_->NowTicks()),
             net_log_);
     trans->SetRequestContext(delegate_->url_request_context());
     trans->SetRequestPriority(delegate_->priority());
@@ -1071,8 +1079,7 @@
                              int net_error,
                              const DnsResponse* response) {
     DCHECK(transaction);
-    base::TimeDelta duration = base::TimeTicks::Now() - start_time;
-
+    base::TimeDelta duration = tick_clock_->NowTicks() - start_time;
     if (net_error != OK && !(net_error == ERR_NAME_NOT_RESOLVED && response &&
                              response->IsValid())) {
       UMA_HISTOGRAM_LONG_TIMES_100("AsyncDNS.TransactionFailure", duration);
@@ -1140,10 +1147,8 @@
         addr_list_[0].GetFamily() == ADDRESS_FAMILY_IPV6) {
       // Sort addresses if needed.  Sort could complete synchronously.
       client_->GetAddressSorter()->Sort(
-          addr_list_,
-          base::Bind(&DnsTask::OnSortComplete,
-                     AsWeakPtr(),
-                     base::TimeTicks::Now()));
+          addr_list_, base::Bind(&DnsTask::OnSortComplete, AsWeakPtr(),
+                                 tick_clock_->NowTicks()));
     } else {
       OnSuccess(addr_list_);
     }
@@ -1154,13 +1159,13 @@
                       const AddressList& addr_list) {
     if (!success) {
       UMA_HISTOGRAM_LONG_TIMES_100("AsyncDNS.SortFailure",
-                                   base::TimeTicks::Now() - start_time);
+                                   tick_clock_->NowTicks() - start_time);
       OnFailure(ERR_DNS_SORT_ERROR, DnsResponse::DNS_PARSE_OK);
       return;
     }
 
     UMA_HISTOGRAM_LONG_TIMES_100("AsyncDNS.SortSuccess",
-                                 base::TimeTicks::Now() - start_time);
+                                 tick_clock_->NowTicks() - start_time);
 
     // AddressSorter prunes unusable destinations.
     if (addr_list.empty()) {
@@ -1209,6 +1214,7 @@
   // IPv6 addresses must appear first in the list.
   AddressList addr_list_;
 
+  const base::TickClock* tick_clock_;
   base::TimeTicks task_start_time_;
 
   DISALLOW_COPY_AND_ASSIGN(DnsTask);
@@ -1226,7 +1232,8 @@
       const Key& key,
       RequestPriority priority,
       scoped_refptr<base::TaskRunner> proc_task_runner,
-      const NetLogWithSource& source_net_log)
+      const NetLogWithSource& source_net_log,
+      const base::TickClock* tick_clock)
       : resolver_(resolver),
         key_(key),
         priority_tracker_(priority),
@@ -1235,7 +1242,8 @@
         had_dns_config_(false),
         num_occupied_job_slots_(0),
         dns_task_error_(OK),
-        creation_time_(base::TimeTicks::Now()),
+        tick_clock_(tick_clock),
+        creation_time_(tick_clock_->NowTicks()),
         priority_change_time_(creation_time_),
         net_log_(
             NetLogWithSource::Make(source_net_log.net_log(),
@@ -1463,7 +1471,7 @@
   void UpdatePriority() {
     if (is_queued()) {
       if (priority() != static_cast<RequestPriority>(handle_.priority()))
-        priority_change_time_ = base::TimeTicks::Now();
+        priority_change_time_ = tick_clock_->NowTicks();
       handle_ = resolver_->dispatcher_->ChangePriority(handle_, priority());
     }
   }
@@ -1486,7 +1494,7 @@
 
     had_dns_config_ = resolver_->HaveDnsConfig();
 
-    start_time_ = base::TimeTicks::Now();
+    start_time_ = tick_clock_->NowTicks();
     base::TimeDelta queue_time = start_time_ - creation_time_;
     base::TimeDelta queue_time_after_change =
         start_time_ - priority_change_time_;
@@ -1513,11 +1521,11 @@
   // PrioritizedDispatcher with tighter limits.
   void StartProcTask() {
     DCHECK(!is_dns_running());
-    proc_task_ =
-        new ProcTask(key_, resolver_->proc_params_,
-                     base::Bind(&Job::OnProcTaskComplete,
-                                base::Unretained(this), base::TimeTicks::Now()),
-                     proc_task_runner_, net_log_);
+    proc_task_ = new ProcTask(
+        key_, resolver_->proc_params_,
+        base::Bind(&Job::OnProcTaskComplete, base::Unretained(this),
+                   tick_clock_->NowTicks()),
+        proc_task_runner_, net_log_, tick_clock_);
 
     // Start() could be called from within Resolve(), hence it must NOT directly
     // call OnProcTaskComplete, for example, on synchronous failure.
@@ -1531,7 +1539,7 @@
     DCHECK(is_proc_running());
 
     if (dns_task_error_ != OK) {
-      base::TimeDelta duration = base::TimeTicks::Now() - start_time;
+      base::TimeDelta duration = tick_clock_->NowTicks() - start_time;
       if (net_error == OK) {
         UMA_HISTOGRAM_LONG_TIMES_100("AsyncDNS.FallbackSuccess", duration);
         if ((dns_task_error_ == ERR_NAME_NOT_RESOLVED) &&
@@ -1568,7 +1576,7 @@
   void StartDnsTask() {
     DCHECK(resolver_->HaveDnsConfig());
     dns_task_.reset(new DnsTask(resolver_->dns_client_.get(), key_, this,
-                                net_log_));
+                                net_log_, tick_clock_));
 
     dns_task_->StartFirstTransaction();
     // Schedule a second transaction, if needed.
@@ -1633,7 +1641,7 @@
                          base::TimeDelta ttl) override {
     DCHECK(is_dns_running());
 
-    base::TimeDelta duration = base::TimeTicks::Now() - start_time;
+    base::TimeDelta duration = tick_clock_->NowTicks() - start_time;
     if (net_error != OK) {
       OnDnsTaskFailure(dns_task_->AsWeakPtr(), duration, net_error);
       return;
@@ -1689,7 +1697,7 @@
     };
     Category category = RESOLVE_MAX;  // Illegal value for later DCHECK only.
 
-    base::TimeDelta duration = base::TimeTicks::Now() - start_time_;
+    base::TimeDelta duration = tick_clock_->NowTicks() - start_time_;
     if (error == OK) {
       if (had_non_speculative_request_) {
         category = RESOLVE_SUCCESS;
@@ -1814,7 +1822,7 @@
       if (did_complete) {
         // Record effective total time from creation to completion.
         RecordTotalTime(req->info().is_speculative(), false,
-                        base::TimeTicks::Now() - req->request_time());
+                        tick_clock_->NowTicks() - req->request_time());
       }
       req->OnJobCompleted(this, entry.error(), entry.addresses());
 
@@ -1866,6 +1874,7 @@
   // Result of DnsTask.
   int dns_task_error_;
 
+  const base::TickClock* tick_clock_;
   const base::TimeTicks creation_time_;
   base::TimeTicks priority_change_time_;
   base::TimeTicks start_time_;
@@ -1953,7 +1962,7 @@
   Job* job;
   if (jobit == jobs_.end()) {
     job = new Job(weak_ptr_factory_.GetWeakPtr(), key, priority,
-                  proc_task_runner_, source_net_log);
+                  proc_task_runner_, source_net_log, tick_clock_);
     job->Schedule(false);
 
     // Check for queue overflow.
@@ -1973,8 +1982,9 @@
   }
 
   // Can't complete synchronously. Create and attach request.
-  auto req = std::make_unique<RequestImpl>(source_net_log, info, priority,
-                                           callback, addresses, job);
+  auto req =
+      std::make_unique<RequestImpl>(source_net_log, info, priority, callback,
+                                    addresses, job, tick_clock_->NowTicks());
   job->AddRequest(req.get());
   *out_req = std::move(req);
 
@@ -1993,6 +2003,7 @@
       last_ipv6_probe_result_(true),
       additional_resolver_flags_(0),
       fallback_to_proctask_(true),
+      tick_clock_(base::DefaultTickClock::GetInstance()),
       weak_ptr_factory_(this),
       probe_weak_ptr_factory_(this) {
   if (options.enable_caching)
@@ -2211,6 +2222,12 @@
   }
 }
 
+void HostResolverImpl::SetTickClockForTesting(
+    const base::TickClock* tick_clock) {
+  tick_clock_ = tick_clock;
+  cache_->set_tick_clock_for_testing(tick_clock);
+}
+
 void HostResolverImpl::ClearDnsOverHttpsServers() {
   if (dns_over_https_servers_.size() == 0)
     return;
@@ -2270,9 +2287,9 @@
 
   const HostCache::Entry* cache_entry;
   if (allow_stale)
-    cache_entry = cache_->LookupStale(key, base::TimeTicks::Now(), stale_info);
+    cache_entry = cache_->LookupStale(key, tick_clock_->NowTicks(), stale_info);
   else
-    cache_entry = cache_->Lookup(key, base::TimeTicks::Now());
+    cache_entry = cache_->Lookup(key, tick_clock_->NowTicks());
   if (!cache_entry)
     return false;
 
@@ -2367,7 +2384,7 @@
                                    base::TimeDelta ttl) {
   // Don't cache an error unless it has a positive TTL.
   if (cache_.get() && (entry.error() == OK || ttl > base::TimeDelta()))
-    cache_->Set(key, entry, base::TimeTicks::Now(), ttl);
+    cache_->Set(key, entry, tick_clock_->NowTicks(), ttl);
 }
 
 void HostResolverImpl::RemoveJob(Job* job) {
@@ -2414,11 +2431,11 @@
   // Cache the result for kIPv6ProbePeriodMs (measured from after
   // IsGloballyReachable() completes).
   bool cached = true;
-  if ((base::TimeTicks::Now() - last_ipv6_probe_time_).InMilliseconds() >
+  if ((tick_clock_->NowTicks() - last_ipv6_probe_time_).InMilliseconds() >
       kIPv6ProbePeriodMs) {
     last_ipv6_probe_result_ =
         IsGloballyReachable(IPAddress(kIPv6ProbeAddress), net_log);
-    last_ipv6_probe_time_ = base::TimeTicks::Now();
+    last_ipv6_probe_time_ = tick_clock_->NowTicks();
     cached = false;
   }
   net_log.AddEvent(NetLogEventType::HOST_RESOLVER_IMPL_IPV6_REACHABILITY_CHECK,
diff --git a/net/dns/host_resolver_impl.h b/net/dns/host_resolver_impl.h
index c98b12a..ffd89ba 100644
--- a/net/dns/host_resolver_impl.h
+++ b/net/dns/host_resolver_impl.h
@@ -22,6 +22,10 @@
 #include "net/url_request/url_request_context_getter.h"
 #include "url/gurl.h"
 
+namespace base {
+class TickClock;
+}  // namespace base
+
 namespace net {
 
 class AddressList;
@@ -170,6 +174,8 @@
     proc_params_ = proc_params;
   }
 
+  void SetTickClockForTesting(const base::TickClock* tick_clock);
+
  protected:
   // Callback from HaveOnlyLoopbackAddresses probe.
   void SetHaveOnlyLoopbackAddresses(bool result);
@@ -370,6 +376,9 @@
   URLRequestContext* url_request_context_;
   std::vector<DnsConfig::DnsOverHttpsServerConfig> dns_over_https_servers_;
 
+  // Shared tick clock, overridden for testing.
+  const base::TickClock* tick_clock_;
+
   THREAD_CHECKER(thread_checker_);
 
   base::WeakPtrFactory<HostResolverImpl> weak_ptr_factory_;
diff --git a/net/proxy_resolution/multi_threaded_proxy_resolver.cc b/net/proxy_resolution/multi_threaded_proxy_resolver.cc
index 9431ff9..19c06fba 100644
--- a/net/proxy_resolution/multi_threaded_proxy_resolver.cc
+++ b/net/proxy_resolution/multi_threaded_proxy_resolver.cc
@@ -153,17 +153,7 @@
 
 class Job : public base::RefCountedThreadSafe<Job> {
  public:
-  // Identifies the subclass of Job (only being used for debugging purposes).
-  enum Type {
-    TYPE_GET_PROXY_FOR_URL,
-    TYPE_CREATE_RESOLVER,
-  };
-
-  Job(Type type, CompletionOnceCallback callback)
-      : type_(type),
-        callback_(std::move(callback)),
-        executor_(NULL),
-        was_cancelled_(false) {}
+  Job() : executor_(NULL), was_cancelled_(false) {}
 
   void set_executor(Executor* executor) {
     executor_ = executor;
@@ -184,16 +174,6 @@
   // Returns true if Cancel() has been called.
   bool was_cancelled() const { return was_cancelled_; }
 
-  Type type() const { return type_; }
-
-  // Returns true if this job still has a user callback. Some jobs
-  // do not have a user callback, because they were helper jobs
-  // scheduled internally (for example TYPE_CREATE_RESOLVER).
-  //
-  // Otherwise jobs that correspond with user-initiated work will
-  // have a non-null callback up until the callback is run.
-  bool has_user_callback() const { return !callback_.is_null(); }
-
   // This method is called when the job is inserted into a wait queue
   // because no executors were ready to accept it.
   virtual void WaitingForThread() {}
@@ -214,19 +194,11 @@
       executor_->OnJobCompleted(this);
   }
 
-  void RunUserCallback(int result) {
-    DCHECK(has_user_callback());
-    // Reset the callback so has_user_callback() will now return false.
-    std::move(callback_).Run(result);
-  }
-
   friend class base::RefCountedThreadSafe<Job>;
 
   virtual ~Job() = default;
 
  private:
-  const Type type_;
-  CompletionOnceCallback callback_;
   Executor* executor_;
   bool was_cancelled_;
 };
@@ -252,9 +224,7 @@
  public:
   CreateResolverJob(const scoped_refptr<PacFileData>& script_data,
                     ProxyResolverFactory* factory)
-      : Job(TYPE_CREATE_RESOLVER, CompletionOnceCallback()),
-        script_data_(script_data),
-        factory_(factory) {}
+      : script_data_(script_data), factory_(factory) {}
 
   // Runs on the worker thread.
   void Run(scoped_refptr<base::SingleThreadTaskRunner> origin_runner) override {
@@ -296,11 +266,13 @@
                     ProxyInfo* results,
                     CompletionOnceCallback callback,
                     const NetLogWithSource& net_log)
-      : Job(TYPE_GET_PROXY_FOR_URL, std::move(callback)),
+      : callback_(std::move(callback)),
         results_(results),
         net_log_(net_log),
         url_(url),
-        was_waiting_for_thread_(false) {}
+        was_waiting_for_thread_(false) {
+    DCHECK(callback_);
+  }
 
   NetLogWithSource* net_log() { return &net_log_; }
 
@@ -344,11 +316,13 @@
       if (result_code >= OK) {  // Note: unit-tests use values > 0.
         results_->Use(results_buf_);
       }
-      RunUserCallback(result_code);
+      std::move(callback_).Run(result_code);
     }
     OnJobCompleted();
   }
 
+  CompletionOnceCallback callback_;
+
   // Must only be used on the "origin" thread.
   ProxyInfo* results_;
 
diff --git a/net/quic/chromium/quic_chromium_client_session.cc b/net/quic/chromium/quic_chromium_client_session.cc
index a00778c..929f305 100644
--- a/net/quic/chromium/quic_chromium_client_session.cc
+++ b/net/quic/chromium/quic_chromium_client_session.cc
@@ -2183,7 +2183,7 @@
 
   CHECK_NE(NetworkChangeNotifier::kInvalidNetworkHandle, network);
 
-  if (GetNumActiveStreams() == 0) {
+  if (GetNumActiveStreams() == 0 && GetNumDrainingStreams() == 0) {
     HistogramAndLogMigrationFailure(migration_net_log,
                                     MIGRATION_STATUS_NO_MIGRATABLE_STREAMS,
                                     connection_id(), "No active streams");
@@ -2331,7 +2331,7 @@
     NetworkChangeNotifier::NetworkHandle network,
     const NetLogWithSource& migration_net_log) {
   // Close idle sessions.
-  if (GetNumActiveStreams() == 0) {
+  if (GetNumActiveStreams() == 0 && GetNumDrainingStreams() == 0) {
     HistogramAndLogMigrationFailure(migration_net_log,
                                     MIGRATION_STATUS_NO_MIGRATABLE_STREAMS,
                                     connection_id(), "No active streams");
diff --git a/net/quic/chromium/quic_stream_factory_test.cc b/net/quic/chromium/quic_stream_factory_test.cc
index d496e89..0b42c245 100644
--- a/net/quic/chromium/quic_stream_factory_test.cc
+++ b/net/quic/chromium/quic_stream_factory_test.cc
@@ -2923,6 +2923,161 @@
   EXPECT_TRUE(quic_data2.AllWriteDataConsumed());
 }
 
+// Regression test for http://crbug.com/847569.
+// This test verifies that the connection migrates to the alternate network
+// early when there is no active stream but a draining stream.
+TEST_P(QuicStreamFactoryTest, MigrateSessionWithDrainingStream) {
+  InitializeConnectionMigrationV2Test(
+      {kDefaultNetworkForTests, kNewNetworkForTests});
+  ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
+  crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
+  crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
+
+  // Using a testing task runner so that we can control time.
+  auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>();
+  QuicStreamFactoryPeer::SetTaskRunner(factory_.get(), task_runner.get());
+
+  scoped_mock_network_change_notifier_->mock_network_change_notifier()
+      ->QueueNetworkMadeDefault(kDefaultNetworkForTests);
+
+  MockQuicData quic_data1;
+  quic::QuicStreamOffset header_stream_offset = 0;
+  quic_data1.AddWrite(SYNCHRONOUS,
+                      ConstructInitialSettingsPacket(1, &header_stream_offset));
+  quic_data1.AddWrite(SYNCHRONOUS, ConstructGetRequestPacket(
+                                       2, GetNthClientInitiatedStreamId(0),
+                                       true, true, &header_stream_offset));
+  // Read an out of order packet with FIN to drain the stream.
+  quic_data1.AddRead(
+      ASYNC, ConstructOkResponsePacket(2, GetNthClientInitiatedStreamId(0),
+                                       false, true));  // keep sending version.
+  quic_data1.AddWrite(SYNCHRONOUS,
+                      client_maker_.MakeAckPacket(3, 2, 2, 2, 1, true));
+  quic_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  quic_data1.AddSocketDataToFactory(socket_factory_.get());
+
+  // Set up the second socket data provider that is used after migration.
+  MockQuicData quic_data2;
+  // Connectivity probe to be sent on the new path.
+  quic_data2.AddWrite(
+      SYNCHRONOUS, client_maker_.MakeConnectivityProbingPacket(4, false, 1338));
+  quic_data2.AddRead(ASYNC, ERR_IO_PENDING);  // Pause
+  // Connectivity probe to receive from the server.
+  quic_data2.AddRead(
+      ASYNC, server_maker_.MakeConnectivityProbingPacket(3, false, 1338));
+  quic_data2.AddWrite(ASYNC, client_maker_.MakeAckPacket(5, 2, 3, 3, 1, true));
+  // Ping packet to send after migration is completed.
+  quic_data2.AddWrite(ASYNC, client_maker_.MakePingPacket(6, false));
+  quic_data2.AddRead(
+      ASYNC, ConstructOkResponsePacket(1, GetNthClientInitiatedStreamId(0),
+                                       false, false));
+  quic_data2.AddWrite(SYNCHRONOUS,
+                      client_maker_.MakeAckPacket(7, 1, 3, 1, 1, true));
+  quic_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  quic_data2.AddSocketDataToFactory(socket_factory_.get());
+
+  // Create request and QuicHttpStream.
+  QuicStreamRequest request(factory_.get());
+  EXPECT_EQ(ERR_IO_PENDING,
+            request.Request(host_port_pair_, version_, privacy_mode_,
+                            DEFAULT_PRIORITY, SocketTag(),
+                            /*cert_verify_flags=*/0, url_, net_log_,
+                            &net_error_details_, callback_.callback()));
+  EXPECT_THAT(callback_.WaitForResult(), IsOk());
+  std::unique_ptr<HttpStream> stream = CreateStream(&request);
+  EXPECT_TRUE(stream.get());
+
+  // Cause QUIC stream to be created.
+  HttpRequestInfo request_info;
+  request_info.method = "GET";
+  request_info.url = url_;
+  request_info.traffic_annotation =
+      MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+  EXPECT_EQ(OK, stream->InitializeStream(&request_info, true, DEFAULT_PRIORITY,
+                                         net_log_, CompletionOnceCallback()));
+
+  // Ensure that session is alive and active.
+  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
+  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+
+  // Send GET request on stream.
+  HttpResponseInfo response;
+  HttpRequestHeaders request_headers;
+  EXPECT_EQ(OK, stream->SendRequest(request_headers, &response,
+                                    callback_.callback()));
+
+  // Run the message loop to receive the out of order packet which contains a
+  // FIN and drains the stream.
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(0u, session->GetNumActiveStreams());
+
+  // Cause the connection to report path degrading to the session.
+  // Session should still start to probe the alternate network.
+  session->connection()->OnPathDegradingTimeout();
+  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+
+  // Next connectivity probe is scheduled to be sent in 2 *
+  // kDefaultRTTMilliSecs.
+  EXPECT_EQ(1u, task_runner->GetPendingTaskCount());
+  base::TimeDelta next_task_delay = task_runner->NextPendingTaskDelay();
+  EXPECT_EQ(base::TimeDelta::FromMilliseconds(2 * kDefaultRTTMilliSecs),
+            next_task_delay);
+
+  // The connection should still be alive, and not marked as going away.
+  EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
+  EXPECT_EQ(OK, stream->ReadResponseHeaders(callback_.callback()));
+
+  // Resume quic data and a connectivity probe response will be read on the new
+  // socket.
+  quic_data2.Resume();
+
+  EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
+  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  // EXPECT_EQ(1u, session->GetNumActiveStreams());
+
+  // There should be three pending tasks, the nearest one will complete
+  // migration to the new network.
+  EXPECT_EQ(3u, task_runner->GetPendingTaskCount());
+  next_task_delay = task_runner->NextPendingTaskDelay();
+  EXPECT_EQ(base::TimeDelta(), next_task_delay);
+  task_runner->FastForwardBy(next_task_delay);
+
+  // Now there are two pending tasks, the nearest one was to send connectivity
+  // probe and has been cancelled due to successful migration.
+  EXPECT_EQ(2u, task_runner->GetPendingTaskCount());
+  next_task_delay = task_runner->NextPendingTaskDelay();
+  EXPECT_EQ(base::TimeDelta::FromMilliseconds(2 * kDefaultRTTMilliSecs),
+            next_task_delay);
+  task_runner->FastForwardBy(next_task_delay);
+
+  // There's one more task to mgirate back to the default network in 0.4s.
+  EXPECT_EQ(1u, task_runner->GetPendingTaskCount());
+  next_task_delay = task_runner->NextPendingTaskDelay();
+  base::TimeDelta expected_delay =
+      base::TimeDelta::FromSeconds(kMinRetryTimeForDefaultNetworkSecs) -
+      base::TimeDelta::FromMilliseconds(2 * kDefaultRTTMilliSecs);
+  EXPECT_EQ(expected_delay, next_task_delay);
+
+  // Deliver a signal that the alternate network now becomes default to session,
+  // this will cancel mgirate back to default network timer.
+  scoped_mock_network_change_notifier_->mock_network_change_notifier()
+      ->NotifyNetworkMadeDefault(kNewNetworkForTests);
+
+  task_runner->FastForwardBy(next_task_delay);
+  EXPECT_EQ(0u, task_runner->GetPendingTaskCount());
+
+  // Verify that the session is still alive.
+  EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
+  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+
+  stream.reset();
+  EXPECT_TRUE(quic_data1.AllReadDataConsumed());
+  EXPECT_TRUE(quic_data1.AllWriteDataConsumed());
+  EXPECT_TRUE(quic_data2.AllReadDataConsumed());
+  EXPECT_TRUE(quic_data2.AllWriteDataConsumed());
+}
+
 // Regression test for http://crbug.com/835444.
 // This test verifies that the connection migrates to the alternate network
 // when the alternate network is connected after path has been degrading.
diff --git a/net/quic/chromium/quic_test_packet_maker.cc b/net/quic/chromium/quic_test_packet_maker.cc
index ae122af..baad710d 100644
--- a/net/quic/chromium/quic_test_packet_maker.cc
+++ b/net/quic/chromium/quic_test_packet_maker.cc
@@ -358,13 +358,37 @@
     quic::QuicPacketNumber smallest_received,
     quic::QuicPacketNumber least_unacked,
     bool send_feedback) {
-  return MakeAckPacket(packet_number, largest_received, smallest_received,
+  return MakeAckPacket(packet_number, 1, largest_received, smallest_received,
                        least_unacked, send_feedback,
                        quic::QuicTime::Delta::Zero());
 }
 
 std::unique_ptr<quic::QuicReceivedPacket> QuicTestPacketMaker::MakeAckPacket(
     quic::QuicPacketNumber packet_number,
+    quic::QuicPacketNumber first_received,
+    quic::QuicPacketNumber largest_received,
+    quic::QuicPacketNumber smallest_received,
+    quic::QuicPacketNumber least_unacked,
+    bool send_feedback) {
+  return MakeAckPacket(packet_number, first_received, largest_received,
+                       smallest_received, least_unacked, send_feedback,
+                       quic::QuicTime::Delta::Zero());
+}
+
+std::unique_ptr<quic::QuicReceivedPacket> QuicTestPacketMaker::MakeAckPacket(
+    quic::QuicPacketNumber packet_number,
+    quic::QuicPacketNumber largest_received,
+    quic::QuicPacketNumber smallest_received,
+    quic::QuicPacketNumber least_unacked,
+    bool send_feedback,
+    quic::QuicTime::Delta ack_delay_time) {
+  return MakeAckPacket(packet_number, 1, largest_received, smallest_received,
+                       least_unacked, send_feedback, ack_delay_time);
+}
+
+std::unique_ptr<quic::QuicReceivedPacket> QuicTestPacketMaker::MakeAckPacket(
+    quic::QuicPacketNumber packet_number,
+    quic::QuicPacketNumber first_received,
     quic::QuicPacketNumber largest_received,
     quic::QuicPacketNumber smallest_received,
     quic::QuicPacketNumber least_unacked,
@@ -385,7 +409,8 @@
     ack.received_packet_times.push_back(std::make_pair(i, clock_->Now()));
   }
   if (largest_received > 0) {
-    ack.packets.AddRange(1, largest_received + 1);
+    DCHECK_GE(largest_received, first_received);
+    ack.packets.AddRange(first_received, largest_received + 1);
   }
   quic::QuicFramer framer(quic::test::SupportedVersions(quic::ParsedQuicVersion(
                               quic::PROTOCOL_QUIC_CRYPTO, version_)),
diff --git a/net/quic/chromium/quic_test_packet_maker.h b/net/quic/chromium/quic_test_packet_maker.h
index afefe2b..190803d 100644
--- a/net/quic/chromium/quic_test_packet_maker.h
+++ b/net/quic/chromium/quic_test_packet_maker.h
@@ -119,6 +119,21 @@
       bool send_feedback);
   std::unique_ptr<quic::QuicReceivedPacket> MakeAckPacket(
       quic::QuicPacketNumber packet_number,
+      quic::QuicPacketNumber first_received,
+      quic::QuicPacketNumber largest_received,
+      quic::QuicPacketNumber smallest_received,
+      quic::QuicPacketNumber least_unacked,
+      bool send_feedback);
+  std::unique_ptr<quic::QuicReceivedPacket> MakeAckPacket(
+      quic::QuicPacketNumber packet_number,
+      quic::QuicPacketNumber largest_received,
+      quic::QuicPacketNumber smallest_received,
+      quic::QuicPacketNumber least_unacked,
+      bool send_feedback,
+      quic::QuicTime::Delta ack_delay_time);
+  std::unique_ptr<quic::QuicReceivedPacket> MakeAckPacket(
+      quic::QuicPacketNumber packet_number,
+      quic::QuicPacketNumber first_received,
       quic::QuicPacketNumber largest_received,
       quic::QuicPacketNumber smallest_received,
       quic::QuicPacketNumber least_unacked,
diff --git a/net/spdy/spdy_write_queue.cc b/net/spdy/spdy_write_queue.cc
index d803d91..2eda1d4 100644
--- a/net/spdy/spdy_write_queue.cc
+++ b/net/spdy/spdy_write_queue.cc
@@ -115,26 +115,15 @@
   // Defer deletion until queue iteration is complete, as
   // SpdyBuffer::~SpdyBuffer() can result in callbacks into SpdyWriteQueue.
   std::vector<std::unique_ptr<SpdyBufferProducer>> erased_buffer_producers;
-
-  // Do the actual deletion and removal, preserving FIFO-ness.
   base::circular_deque<PendingWrite>& queue = queue_[priority];
-  auto out_it = queue.begin();
-  for (auto it = queue.begin(); it != queue.end(); ++it) {
-    // Loop invariant: elements between |begin| and |old_it| are the ones
-    // preserved, contigously, in their original order.  Elements between
-    // |old_it| and |it| are undefined.  The distance between |old_it| and |it|
-    // is the number elements moved to |erased_buffer_producers| so far.
-    // Elements between |it| and |end| have not been touched yet.
+  for (auto it = queue.begin(); it != queue.end();) {
     if (it->stream.get() == stream.get()) {
       erased_buffer_producers.push_back(std::move(it->frame_producer));
+      it = queue.erase(it);
     } else {
-      *out_it = std::move(*it);
-      ++out_it;
+      ++it;
     }
   }
-  // The number of elements preserved is the distance between |begin| and
-  // |old_it|.  The rest of the container shall be erased.
-  queue.erase(out_it, queue.end());
   removing_writes_ = false;
 
   // Iteration on |queue| is completed.  Now |erased_buffer_producers| goes out
@@ -145,29 +134,21 @@
     spdy::SpdyStreamId last_good_stream_id) {
   CHECK(!removing_writes_);
   removing_writes_ = true;
-  std::vector<std::unique_ptr<SpdyBufferProducer>> erased_buffer_producers;
 
+  // Defer deletion until queue iteration is complete, as
+  // SpdyBuffer::~SpdyBuffer() can result in callbacks into SpdyWriteQueue.
+  std::vector<std::unique_ptr<SpdyBufferProducer>> erased_buffer_producers;
   for (int i = MINIMUM_PRIORITY; i <= MAXIMUM_PRIORITY; ++i) {
-    // Do the actual deletion and removal, preserving FIFO-ness.
     base::circular_deque<PendingWrite>& queue = queue_[i];
-    auto out_it = queue.begin();
-    for (auto it = queue.begin(); it != queue.end(); ++it) {
-      // Loop invariant: elements between |begin| and |old_it| are the ones
-      // preserved, contigously, in their original order.  Elements between
-      // |old_it| and |it| are undefined.  The distance between |old_it| and
-      // |it| is the number elements moved to |erased_buffer_producers| so far.
-      // Elements between |it| and |end| have not been touched yet.
+    for (auto it = queue.begin(); it != queue.end();) {
       if (it->stream.get() && (it->stream->stream_id() > last_good_stream_id ||
                                it->stream->stream_id() == 0)) {
         erased_buffer_producers.push_back(std::move(it->frame_producer));
+        it = queue.erase(it);
       } else {
-        *out_it = std::move(*it);
-        ++out_it;
+        ++it;
       }
     }
-    // The number of elements preserved is the distance between |begin| and
-    // |old_it|.  The rest of the container shall be erased.
-    queue.erase(out_it, queue.end());
   }
   removing_writes_ = false;
 
@@ -195,23 +176,14 @@
 
   base::circular_deque<PendingWrite>& old_queue = queue_[old_priority];
   base::circular_deque<PendingWrite>& new_queue = queue_[new_priority];
-  auto out_it = old_queue.begin();
-  for (auto it = old_queue.begin(); it != old_queue.end(); ++it) {
-    // Loop invariant: elements between |begin| and |old_it| are the ones
-    // kept in |old_queue|, contigously, in their original order.  Elements
-    // between |old_it| and |it| are undefined.  The distance between |old_it|
-    // and |it| is the number elements moved to |new_queue| so far. Elements
-    // between |it| and |end| have not been touched yet.
+  for (auto it = old_queue.begin(); it != old_queue.end();) {
     if (it->stream.get() == stream.get()) {
       new_queue.push_back(std::move(*it));
+      it = old_queue.erase(it);
     } else {
-      *out_it = std::move(*it);
-      ++out_it;
+      ++it;
     }
   }
-  // The number of elements kept in |old_queue| is the distance between |begin|
-  // and |old_it|.  The rest of the container shall be erased.
-  old_queue.erase(out_it, old_queue.end());
 }
 
 void SpdyWriteQueue::Clear() {
diff --git a/ppapi/tests/test_file_ref.cc b/ppapi/tests/test_file_ref.cc
index 6291cc9..125e95e4 100644
--- a/ppapi/tests/test_file_ref.cc
+++ b/ppapi/tests/test_file_ref.cc
@@ -13,6 +13,7 @@
 #include "ppapi/c/pp_errors.h"
 #include "ppapi/c/ppb_file_io.h"
 #include "ppapi/c/private/ppb_testing_private.h"
+#include "ppapi/cpp/dev/file_chooser_dev.h"
 #include "ppapi/cpp/directory_entry.h"
 #include "ppapi/cpp/file_io.h"
 #include "ppapi/cpp/file_ref.h"
@@ -52,22 +53,18 @@
 }
 
 std::string TestFileRef::MakeExternalFileRef(pp::FileRef* file_ref_ext) {
-  pp::URLRequestInfo request(instance_);
-  request.SetURL("test_url_loader_data/hello.txt");
-  request.SetStreamToFile(true);
+  pp::FileChooser_Dev file_chooser(instance(), PP_FILECHOOSERMODE_OPEN, "*");
+  ASSERT_FALSE(file_chooser.is_null());
 
-  TestCompletionCallback callback(instance_->pp_instance(), callback_type());
+  TestCompletionCallbackWithOutput<std::vector<pp::FileRef> >
+      filechooser_callback(instance_->pp_instance(), callback_type());
+  filechooser_callback.WaitForResult(
+      file_chooser.Show(filechooser_callback.GetCallback()));
 
-  pp::URLLoader loader(instance_);
-  callback.WaitForResult(loader.Open(request, callback.GetCallback()));
-  CHECK_CALLBACK_BEHAVIOR(callback);
-  ASSERT_EQ(PP_OK, callback.result());
+  const std::vector<pp::FileRef>& output_ref = filechooser_callback.output();
+  ASSERT_EQ(1u, output_ref.size());
 
-  pp::URLResponseInfo response_info(loader.GetResponseInfo());
-  ASSERT_FALSE(response_info.is_null());
-  ASSERT_EQ(200, response_info.GetStatusCode());
-
-  *file_ref_ext = pp::FileRef(response_info.GetBodyAsFileRef());
+  *file_ref_ext = output_ref[0];
   ASSERT_EQ(PP_FILESYSTEMTYPE_EXTERNAL, file_ref_ext->GetFileSystemType());
   PASS();
 }
diff --git a/sandbox/linux/syscall_broker/broker_client.cc b/sandbox/linux/syscall_broker/broker_client.cc
index 50395c0..fa7855c 100644
--- a/sandbox/linux/syscall_broker/broker_client.cc
+++ b/sandbox/linux/syscall_broker/broker_client.cc
@@ -159,7 +159,9 @@
   return PathOnlySyscall(COMMAND_RMDIR, path);
 }
 
-int BrokerClient::Stat(const char* pathname, struct stat* sb) const {
+int BrokerClient::Stat(const char* pathname,
+                       bool follow_links,
+                       struct stat* sb) const {
   if (!pathname || !sb)
     return -EFAULT;
 
@@ -168,10 +170,13 @@
                          pathname, nullptr)) {
     return -broker_permission_list_.denied_errno();
   }
-  return StatFamilySyscall(COMMAND_STAT, pathname, sb, sizeof(*sb));
+  return StatFamilySyscall(COMMAND_STAT, pathname, follow_links, sb,
+                           sizeof(*sb));
 }
 
-int BrokerClient::Stat64(const char* pathname, struct stat64* sb) const {
+int BrokerClient::Stat64(const char* pathname,
+                         bool follow_links,
+                         struct stat64* sb) const {
   if (!pathname || !sb)
     return -EFAULT;
 
@@ -180,7 +185,8 @@
                          pathname, nullptr)) {
     return -broker_permission_list_.denied_errno();
   }
-  return StatFamilySyscall(COMMAND_STAT64, pathname, sb, sizeof(*sb));
+  return StatFamilySyscall(COMMAND_STAT64, pathname, follow_links, sb,
+                           sizeof(*sb));
 }
 
 int BrokerClient::Unlink(const char* path) const {
@@ -294,11 +300,13 @@
 // This function needs to be async signal safe.
 int BrokerClient::StatFamilySyscall(BrokerCommand syscall_type,
                                     const char* pathname,
+                                    bool follow_links,
                                     void* result_ptr,
                                     size_t expected_result_size) const {
   BrokerSimpleMessage message;
   RAW_CHECK(message.AddIntToMessage(syscall_type));
   RAW_CHECK(message.AddStringToMessage(pathname));
+  RAW_CHECK(message.AddIntToMessage(static_cast<int>(follow_links)));
 
   int returned_fd = -1;
   BrokerSimpleMessage reply;
diff --git a/sandbox/linux/syscall_broker/broker_client.h b/sandbox/linux/syscall_broker/broker_client.h
index ead90d4..a228afec9 100644
--- a/sandbox/linux/syscall_broker/broker_client.h
+++ b/sandbox/linux/syscall_broker/broker_client.h
@@ -67,9 +67,9 @@
   // Can be used in place of rmdir().
   int Rmdir(const char* path) const;
 
-  // Can be used in place of stat()/stat64().
-  int Stat(const char* pathname, struct stat* sb) const;
-  int Stat64(const char* pathname, struct stat64* sb) const;
+  // Can be used in place of stat()/stat64()/lstat()/lstat64()
+  int Stat(const char* pathname, bool follow_links, struct stat* sb) const;
+  int Stat64(const char* pathname, bool folllow_links, struct stat64* sb) const;
 
   // Can be used in place of rmdir().
   int Unlink(const char* unlink) const;
@@ -87,6 +87,7 @@
 
   int StatFamilySyscall(BrokerCommand syscall_type,
                         const char* pathname,
+                        bool follow_links,
                         void* result_ptr,
                         size_t expected_result_size) const;
 
diff --git a/sandbox/linux/syscall_broker/broker_host.cc b/sandbox/linux/syscall_broker/broker_host.cc
index 7855d98..631c90b 100644
--- a/sandbox/linux/syscall_broker/broker_host.cc
+++ b/sandbox/linux/syscall_broker/broker_host.cc
@@ -186,6 +186,7 @@
                     const BrokerPermissionList& permission_list,
                     BrokerCommand command_type,
                     const std::string& requested_filename,
+                    bool follow_links,
                     BrokerSimpleMessage* reply) {
   const char* file_to_access = nullptr;
   if (!CommandStatIsSafe(allowed_command_set, permission_list,
@@ -195,7 +196,9 @@
   }
   if (command_type == COMMAND_STAT) {
     struct stat sb;
-    if (stat(file_to_access, &sb) < 0) {
+    int sts =
+        follow_links ? stat(file_to_access, &sb) : lstat(file_to_access, &sb);
+    if (sts < 0) {
       RAW_CHECK(reply->AddIntToMessage(-errno));
       return;
     }
@@ -211,7 +214,9 @@
     return;
 #else
     struct stat64 sb;
-    if (stat64(file_to_access, &sb) < 0) {
+    int sts = follow_links ? stat64(file_to_access, &sb)
+                           : lstat64(file_to_access, &sb);
+    if (sts < 0) {
       RAW_CHECK(reply->AddIntToMessage(-errno));
       return;
     }
@@ -316,9 +321,12 @@
       const char* requested_filename;
       if (!message->ReadString(&requested_filename))
         return false;
+      int follow_links;
+      if (!message->ReadInt(&follow_links))
+        return false;
       StatFileForIPC(allowed_command_set, permission_list,
                      static_cast<BrokerCommand>(command_type),
-                     requested_filename, reply);
+                     requested_filename, !!follow_links, reply);
       break;
     }
     case COMMAND_UNLINK: {
diff --git a/sandbox/linux/syscall_broker/broker_process.cc b/sandbox/linux/syscall_broker/broker_process.cc
index 40fff3f..c510b8a 100644
--- a/sandbox/linux/syscall_broker/broker_process.cc
+++ b/sandbox/linux/syscall_broker/broker_process.cc
@@ -137,14 +137,18 @@
   return broker_client_->Rmdir(pathname);
 }
 
-int BrokerProcess::Stat(const char* pathname, struct stat* sb) const {
+int BrokerProcess::Stat(const char* pathname,
+                        bool follow_links,
+                        struct stat* sb) const {
   RAW_CHECK(initialized_);
-  return broker_client_->Stat(pathname, sb);
+  return broker_client_->Stat(pathname, follow_links, sb);
 }
 
-int BrokerProcess::Stat64(const char* pathname, struct stat64* sb) const {
+int BrokerProcess::Stat64(const char* pathname,
+                          bool follow_links,
+                          struct stat64* sb) const {
   RAW_CHECK(initialized_);
-  return broker_client_->Stat64(pathname, sb);
+  return broker_client_->Stat64(pathname, follow_links, sb);
 }
 
 int BrokerProcess::Unlink(const char* pathname) const {
@@ -255,12 +259,29 @@
 #if defined(__NR_stat)
     case __NR_stat:
       return broker_process->Stat(reinterpret_cast<const char*>(args.args[0]),
+                                  true,
                                   reinterpret_cast<struct stat*>(args.args[1]));
 #endif
 #if defined(__NR_stat64)
     case __NR_stat64:
       return broker_process->Stat64(
-          reinterpret_cast<const char*>(args.args[0]),
+          reinterpret_cast<const char*>(args.args[0]), true,
+          reinterpret_cast<struct stat64*>(args.args[1]));
+#endif
+#if defined(__NR_lstat)
+    case __NR_lstat:
+      // See https://crbug.com/847096
+      BROKER_UNPOISON_STRING(reinterpret_cast<const char*>(args.args[0]));
+      return broker_process->Stat(reinterpret_cast<const char*>(args.args[0]),
+                                  false,
+                                  reinterpret_cast<struct stat*>(args.args[1]));
+#endif
+#if defined(__NR_lstat64)
+    case __NR_lstat64:
+      // See https://crbug.com/847096
+      BROKER_UNPOISON_STRING(reinterpret_cast<const char*>(args.args[0]));
+      return broker_process->Stat64(
+          reinterpret_cast<const char*>(args.args[0]), false,
           reinterpret_cast<struct stat64*>(args.args[1]));
 #endif
 #if defined(__NR_fstatat)
@@ -270,6 +291,7 @@
       if (static_cast<int>(args.args[3]) != 0)
         return -EINVAL;
       return broker_process->Stat(reinterpret_cast<const char*>(args.args[1]),
+                                  true,
                                   reinterpret_cast<struct stat*>(args.args[2]));
 #endif
 #if defined(__NR_newfstatat)
@@ -279,6 +301,7 @@
       if (static_cast<int>(args.args[3]) != 0)
         return -EINVAL;
       return broker_process->Stat(reinterpret_cast<const char*>(args.args[1]),
+                                  true,
                                   reinterpret_cast<struct stat*>(args.args[2]));
 #endif
 #if defined(__NR_unlink)
diff --git a/sandbox/linux/syscall_broker/broker_process.h b/sandbox/linux/syscall_broker/broker_process.h
index f1dc0c6..1359930 100644
--- a/sandbox/linux/syscall_broker/broker_process.h
+++ b/sandbox/linux/syscall_broker/broker_process.h
@@ -101,9 +101,9 @@
   // Can be used in place of rmdir().
   int Rmdir(const char* path) const;
 
-  // Can be used in place of stat()/stat64().
-  int Stat(const char* pathname, struct stat* sb) const;
-  int Stat64(const char* pathname, struct stat64* sb) const;
+  // Can be used in place of stat()/stat64()/lstat()/lstat64().
+  int Stat(const char* pathname, bool follow_links, struct stat* sb) const;
+  int Stat64(const char* pathname, bool follow_links, struct stat64* sb) const;
 
   // Can be used in place of unlink().
   int Unlink(const char* path) const;
diff --git a/sandbox/linux/syscall_broker/broker_process_unittest.cc b/sandbox/linux/syscall_broker/broker_process_unittest.cc
index 376dbe7..8df69d0 100644
--- a/sandbox/linux/syscall_broker/broker_process_unittest.cc
+++ b/sandbox/linux/syscall_broker/broker_process_unittest.cc
@@ -731,7 +731,7 @@
   unlink(permfile_name);
 }
 
-void TestStatHelper(bool fast_check_in_client) {
+void TestStatHelper(bool fast_check_in_client, bool follow_links) {
   ScopedTemporaryFile tmp_file;
   EXPECT_EQ(12, write(tmp_file.fd(), "blahblahblah", 12));
 
@@ -759,7 +759,8 @@
     ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
 
     memset(&sb, 0, sizeof(sb));
-    EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Stat(tempfile_name, &sb));
+    EXPECT_EQ(-kFakeErrnoSentinel,
+              open_broker.Stat(tempfile_name, follow_links, &sb));
   }
 
   BrokerCommandSet command_set;
@@ -773,7 +774,8 @@
     ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
 
     memset(&sb, 0, sizeof(sb));
-    EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Stat(nonesuch_name, &sb));
+    EXPECT_EQ(-kFakeErrnoSentinel,
+              open_broker.Stat(nonesuch_name, follow_links, &sb));
   }
   {
     // Actual file with no permission to see file.
@@ -783,7 +785,8 @@
     ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
 
     memset(&sb, 0, sizeof(sb));
-    EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Stat(tempfile_name, &sb));
+    EXPECT_EQ(-kFakeErrnoSentinel,
+              open_broker.Stat(tempfile_name, follow_links, &sb));
   }
   {
     // Nonexistent file with permissions to see file.
@@ -794,20 +797,29 @@
     ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
 
     memset(&sb, 0, sizeof(sb));
-    EXPECT_EQ(-ENOENT, open_broker.Stat(nonesuch_name, &sb));
+    EXPECT_EQ(-ENOENT, open_broker.Stat(nonesuch_name, follow_links, &sb));
 
     // Gets denied all the way back to root since no create permission.
-    EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Stat(leading_path1, &sb));
-    EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Stat(leading_path2, &sb));
-    EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Stat(leading_path3, &sb));
+    EXPECT_EQ(-kFakeErrnoSentinel,
+              open_broker.Stat(leading_path1, follow_links, &sb));
+    EXPECT_EQ(-kFakeErrnoSentinel,
+              open_broker.Stat(leading_path2, follow_links, &sb));
+    EXPECT_EQ(-kFakeErrnoSentinel,
+              open_broker.Stat(leading_path3, follow_links, &sb));
 
     // Not fooled by substrings.
-    EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Stat(bad_leading_path1, &sb));
-    EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Stat(bad_leading_path2, &sb));
-    EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Stat(bad_leading_path3, &sb));
-    EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Stat(bad_leading_path4, &sb));
-    EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Stat(bad_leading_path5, &sb));
-    EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Stat(bad_leading_path6, &sb));
+    EXPECT_EQ(-kFakeErrnoSentinel,
+              open_broker.Stat(bad_leading_path1, follow_links, &sb));
+    EXPECT_EQ(-kFakeErrnoSentinel,
+              open_broker.Stat(bad_leading_path2, follow_links, &sb));
+    EXPECT_EQ(-kFakeErrnoSentinel,
+              open_broker.Stat(bad_leading_path3, follow_links, &sb));
+    EXPECT_EQ(-kFakeErrnoSentinel,
+              open_broker.Stat(bad_leading_path4, follow_links, &sb));
+    EXPECT_EQ(-kFakeErrnoSentinel,
+              open_broker.Stat(bad_leading_path5, follow_links, &sb));
+    EXPECT_EQ(-kFakeErrnoSentinel,
+              open_broker.Stat(bad_leading_path6, follow_links, &sb));
   }
   {
     // Nonexistent file with permissions to create file.
@@ -818,22 +830,28 @@
     ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
 
     memset(&sb, 0, sizeof(sb));
-    EXPECT_EQ(-ENOENT, open_broker.Stat(nonesuch_name, &sb));
+    EXPECT_EQ(-ENOENT, open_broker.Stat(nonesuch_name, follow_links, &sb));
 
     // Gets ENOENT all the way back to root since it has create permission.
-    EXPECT_EQ(-ENOENT, open_broker.Stat(leading_path1, &sb));
-    EXPECT_EQ(-ENOENT, open_broker.Stat(leading_path2, &sb));
+    EXPECT_EQ(-ENOENT, open_broker.Stat(leading_path1, follow_links, &sb));
+    EXPECT_EQ(-ENOENT, open_broker.Stat(leading_path2, follow_links, &sb));
 
     // But can always get the root.
-    EXPECT_EQ(0, open_broker.Stat(leading_path3, &sb));
+    EXPECT_EQ(0, open_broker.Stat(leading_path3, follow_links, &sb));
 
     // Not fooled by substrings.
-    EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Stat(bad_leading_path1, &sb));
-    EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Stat(bad_leading_path2, &sb));
-    EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Stat(bad_leading_path3, &sb));
-    EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Stat(bad_leading_path4, &sb));
-    EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Stat(bad_leading_path5, &sb));
-    EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Stat(bad_leading_path6, &sb));
+    EXPECT_EQ(-kFakeErrnoSentinel,
+              open_broker.Stat(bad_leading_path1, follow_links, &sb));
+    EXPECT_EQ(-kFakeErrnoSentinel,
+              open_broker.Stat(bad_leading_path2, follow_links, &sb));
+    EXPECT_EQ(-kFakeErrnoSentinel,
+              open_broker.Stat(bad_leading_path3, follow_links, &sb));
+    EXPECT_EQ(-kFakeErrnoSentinel,
+              open_broker.Stat(bad_leading_path4, follow_links, &sb));
+    EXPECT_EQ(-kFakeErrnoSentinel,
+              open_broker.Stat(bad_leading_path5, follow_links, &sb));
+    EXPECT_EQ(-kFakeErrnoSentinel,
+              open_broker.Stat(bad_leading_path6, follow_links, &sb));
   }
   {
     // Actual file with permissions to see file.
@@ -844,7 +862,7 @@
     ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
 
     memset(&sb, 0, sizeof(sb));
-    EXPECT_EQ(0, open_broker.Stat(tempfile_name, &sb));
+    EXPECT_EQ(0, open_broker.Stat(tempfile_name, follow_links, &sb));
 
     // Following fields may never be consistent but should be non-zero.
     // Don't trust the platform to define fields with any particular sign.
@@ -869,11 +887,13 @@
 }
 
 TEST(BrokerProcess, StatFileClient) {
-  TestStatHelper(true);
+  TestStatHelper(true, true);
+  TestStatHelper(true, false);
 }
 
 TEST(BrokerProcess, StatFileHost) {
-  TestStatHelper(false);
+  TestStatHelper(false, true);
+  TestStatHelper(false, false);
 }
 
 void TestRenameHelper(bool fast_check_in_client) {
diff --git a/services/service_manager/sandbox/linux/bpf_broker_policy_linux.cc b/services/service_manager/sandbox/linux/bpf_broker_policy_linux.cc
index 687ac78..68af74e1 100644
--- a/services/service_manager/sandbox/linux/bpf_broker_policy_linux.cc
+++ b/services/service_manager/sandbox/linux/bpf_broker_policy_linux.cc
@@ -80,6 +80,18 @@
         return Allow();
       break;
 #endif
+#if defined(__NR_lstat)
+    case __NR_lstat:
+      if (allowed_command_set_.test(sandbox::syscall_broker::COMMAND_STAT))
+        return Allow();
+      break;
+#endif
+#if defined(__NR_lstat64)
+    case __NR_lstat64:
+      if (allowed_command_set_.test(sandbox::syscall_broker::COMMAND_STAT))
+        return Allow();
+      break;
+#endif
 #if defined(__NR_fstatat)
     case __NR_fstatat:
       if (allowed_command_set_.test(sandbox::syscall_broker::COMMAND_STAT))
diff --git a/services/service_manager/sandbox/linux/bpf_network_policy_linux.cc b/services/service_manager/sandbox/linux/bpf_network_policy_linux.cc
index b0288c6..49c378f 100644
--- a/services/service_manager/sandbox/linux/bpf_network_policy_linux.cc
+++ b/services/service_manager/sandbox/linux/bpf_network_policy_linux.cc
@@ -73,6 +73,12 @@
 #if defined(__NR_stat64)
     case __NR_stat64:
 #endif
+#if defined(__NR_lstat)
+    case __NR_lstat:
+#endif
+#if defined(__NR_lstat64)
+    case __NR_lstat64:
+#endif
 #if defined(__NR_fstatat)
     case __NR_fstatat:
 #endif
diff --git a/services/ui/ws2/client_window.cc b/services/ui/ws2/client_window.cc
index a3c63d43..9239e22 100644
--- a/services/ui/ws2/client_window.cc
+++ b/services/ui/ws2/client_window.cc
@@ -161,6 +161,8 @@
       if (!client_window_->focus_owner())
         return;  // The local environment is going to process the event.
       target_client = client_window_->focus_owner();
+    } else if (client_window()->capture_owner()) {
+      target_client = client_window()->capture_owner();
     } else {
       // Prefer embedded over owner.
       target_client = !embedded ? owning : embedded;
@@ -281,6 +283,14 @@
     return pointer_press_handlers_.count(pointer_id) > 0;
   }
 
+  // Called when the capture owner changes.
+  void OnCaptureOwnerChanged() {
+    // Changing the capture owner toggles between local and the client getting
+    // the event. The |pointer_press_handlers_| are no longer applicable
+    // (because the target is purely dicatated by capture owner).
+    pointer_press_handlers_.clear();
+  }
+
   // ClientWindowEventHandler:
   void OnEvent(ui::Event* event) override {
     // This code doesn't handle PointerEvents, because they should never be
@@ -303,6 +313,17 @@
     if (ShouldIgnoreEvent(*event) || event->IsGestureEvent())
       return;
 
+    // If there is capture, send the event to the client that owns it. A null
+    // capture owner means the local environment should handle the event.
+    if (wm::CaptureController::Get()->GetCaptureWindow()) {
+      if (client_window()->capture_owner()) {
+        client_window()->capture_owner()->SendEventToClient(window(), *event);
+        event->StopPropagation();
+        return;
+      }
+      return;
+    }
+
     // This code does has two specific behaviors. It's used to ensure events
     // go to the right target (either local, or the remote client).
     // . a press-release sequence targets only one. If in non-client area then
@@ -424,6 +445,15 @@
   NOTIMPLEMENTED_LOG_ONCE();
 }
 
+void ClientWindow::SetCaptureOwner(WindowServiceClient* owner) {
+  capture_owner_ = owner;
+  if (!IsTopLevel())
+    return;
+
+  return static_cast<TopLevelEventHandler*>(event_handler_.get())
+      ->OnCaptureOwnerChanged();
+}
+
 bool ClientWindow::DoesOwnerInterceptEvents() const {
   return embedding_ && embedding_->embedding_client_intercepts_events();
 }
@@ -477,6 +507,7 @@
 }
 
 bool ClientWindow::IsHandlingPointerPressForTesting(PointerId pointer_id) {
+  DCHECK(IsTopLevel());
   return static_cast<TopLevelEventHandler*>(event_handler_.get())
       ->IsHandlingPointerPress(pointer_id);
 }
diff --git a/services/ui/ws2/client_window.h b/services/ui/ws2/client_window.h
index 3d4f8d0..ac95458 100644
--- a/services/ui/ws2/client_window.h
+++ b/services/ui/ws2/client_window.h
@@ -78,7 +78,7 @@
   void SetClientArea(const gfx::Insets& insets,
                      const std::vector<gfx::Rect>& additional_client_areas);
 
-  void set_capture_owner(WindowServiceClient* owner) { capture_owner_ = owner; }
+  void SetCaptureOwner(WindowServiceClient* owner);
   WindowServiceClient* capture_owner() const { return capture_owner_; }
 
   void set_focus_owner(WindowServiceClient* owner) { focus_owner_ = owner; }
diff --git a/services/ui/ws2/embedding_unittest.cc b/services/ui/ws2/embedding_unittest.cc
index 4c9ef53..55a7b07 100644
--- a/services/ui/ws2/embedding_unittest.cc
+++ b/services/ui/ws2/embedding_unittest.cc
@@ -23,13 +23,13 @@
 
 TEST(EmbeddingTest, DestroyingRootDestroysEmbedding) {
   WindowServiceTestSetup setup;
-  aura::Window* embed_window = setup.client_test_helper()->NewWindow(3);
+  aura::Window* embed_window = setup.client_test_helper()->NewWindow();
   ASSERT_TRUE(embed_window);
   std::unique_ptr<EmbeddingHelper> embedding_helper =
       setup.CreateEmbedding(embed_window);
   ASSERT_TRUE(embedding_helper);
   aura::Window* embed_child_window =
-      embedding_helper->client_test_helper->NewWindow(4);
+      embedding_helper->client_test_helper->NewWindow();
   ASSERT_TRUE(embed_child_window);
   aura::WindowTracker tracker;
   tracker.Add(embed_child_window);
diff --git a/services/ui/ws2/focus_handler_unittest.cc b/services/ui/ws2/focus_handler_unittest.cc
index 965e58b..bf63dce 100644
--- a/services/ui/ws2/focus_handler_unittest.cc
+++ b/services/ui/ws2/focus_handler_unittest.cc
@@ -28,7 +28,7 @@
 
 TEST(FocusHandlerTest, FocusTopLevel) {
   WindowServiceTestSetup setup;
-  aura::Window* top_level = setup.client_test_helper()->NewTopLevelWindow(1);
+  aura::Window* top_level = setup.client_test_helper()->NewTopLevelWindow();
   ASSERT_TRUE(top_level);
 
   // SetFocus() should fail as |top_level| isn't visible.
@@ -41,7 +41,7 @@
 
 TEST(FocusHandlerTest, FocusNull) {
   WindowServiceTestSetup setup;
-  aura::Window* top_level = setup.client_test_helper()->NewTopLevelWindow(1);
+  aura::Window* top_level = setup.client_test_helper()->NewTopLevelWindow();
   ASSERT_TRUE(top_level);
   top_level->Show();
   EXPECT_TRUE(setup.client_test_helper()->SetFocus(top_level));
@@ -52,10 +52,10 @@
 
 TEST(FocusHandlerTest, FocusChild) {
   WindowServiceTestSetup setup;
-  aura::Window* top_level = setup.client_test_helper()->NewTopLevelWindow(1);
+  aura::Window* top_level = setup.client_test_helper()->NewTopLevelWindow();
   ASSERT_TRUE(top_level);
   top_level->Show();
-  aura::Window* window = setup.client_test_helper()->NewWindow(3);
+  aura::Window* window = setup.client_test_helper()->NewWindow();
   ASSERT_TRUE(window);
 
   // SetFocus() should fail as |window| isn't parented yet.
@@ -76,10 +76,10 @@
 
 TEST(FocusHandlerTest, NotifyOnFocusChange) {
   WindowServiceTestSetup setup;
-  aura::Window* top_level = setup.client_test_helper()->NewTopLevelWindow(1);
+  aura::Window* top_level = setup.client_test_helper()->NewTopLevelWindow();
   ASSERT_TRUE(top_level);
   top_level->Show();
-  aura::Window* window1 = setup.client_test_helper()->NewWindow(3);
+  aura::Window* window1 = setup.client_test_helper()->NewWindow();
   ASSERT_TRUE(window1);
   top_level->AddChild(window1);
   window1->Show();
@@ -103,10 +103,10 @@
 
 TEST(FocusHandlerTest, FocusChangeFromEmbedded) {
   WindowServiceTestSetup setup;
-  aura::Window* top_level = setup.client_test_helper()->NewTopLevelWindow(1);
+  aura::Window* top_level = setup.client_test_helper()->NewTopLevelWindow();
   ASSERT_TRUE(top_level);
   top_level->Show();
-  aura::Window* embed_window = setup.client_test_helper()->NewWindow(3);
+  aura::Window* embed_window = setup.client_test_helper()->NewWindow();
   ASSERT_TRUE(embed_window);
   top_level->AddChild(embed_window);
   embed_window->Show();
@@ -150,10 +150,10 @@
 
 TEST(FocusHandlerTest, EmbedderGetsInterceptedKeyEvents) {
   WindowServiceTestSetup setup;
-  aura::Window* top_level = setup.client_test_helper()->NewTopLevelWindow(1);
+  aura::Window* top_level = setup.client_test_helper()->NewTopLevelWindow();
   ASSERT_TRUE(top_level);
   top_level->Show();
-  aura::Window* embed_window = setup.client_test_helper()->NewWindow(3);
+  aura::Window* embed_window = setup.client_test_helper()->NewWindow();
   ASSERT_TRUE(embed_window);
   top_level->AddChild(embed_window);
   embed_window->Show();
@@ -162,7 +162,7 @@
       embed_window, mojom::kEmbedFlagEmbedderInterceptsEvents);
   ASSERT_TRUE(embedding_helper);
   aura::Window* embed_child_window =
-      embedding_helper->client_test_helper->NewWindow(4);
+      embedding_helper->client_test_helper->NewWindow();
   ASSERT_TRUE(embed_child_window);
   embed_child_window->Show();
   embed_window->AddChild(embed_child_window);
diff --git a/services/ui/ws2/test_change_tracker.cc b/services/ui/ws2/test_change_tracker.cc
index 6f4e1ca..38705e63d 100644
--- a/services/ui/ws2/test_change_tracker.cc
+++ b/services/ui/ws2/test_change_tracker.cc
@@ -134,7 +134,7 @@
                                 WindowIdToString(change.window_id).c_str(),
                                 static_cast<int>(change.cursor_type));
     case CHANGE_TYPE_ON_CHANGE_COMPLETED:
-      return base::StringPrintf("ChangeCompleted id=%d sucess=%s",
+      return base::StringPrintf("ChangeCompleted id=%d success=%s",
                                 change.change_id,
                                 change.bool_value ? "true" : "false");
 
diff --git a/services/ui/ws2/window_service_client.cc b/services/ui/ws2/window_service_client.cc
index f2b53727..3b3e27c 100644
--- a/services/ui/ws2/window_service_client.cc
+++ b/services/ui/ws2/window_service_client.cc
@@ -158,7 +158,7 @@
   if (client_window->capture_owner() == this) {
     // This client will no longer know about |window|, so it should not receive
     // any events sent to the client.
-    client_window->set_capture_owner(nullptr);
+    client_window->SetCaptureOwner(nullptr);
   }
 
   // Delete the ClientRoot first, so that we don't attempt to spam the
@@ -491,14 +491,14 @@
       // Notify the current owner that it lost capture.
       if (client_window->capture_owner())
         client_window->capture_owner()->OnCaptureLost(window);
-      client_window->set_capture_owner(this);
+      client_window->SetCaptureOwner(this);
     }
     return true;
   }
 
   ClientChange change(property_change_tracker_.get(), window,
                       ClientChangeType::kCapture);
-  client_window->set_capture_owner(this);
+  client_window->SetCaptureOwner(this);
   capture_controller->SetCapture(window);
   return capture_controller->GetCaptureWindow() == window;
 }
@@ -534,7 +534,7 @@
     DVLOG(1) << "ReleaseCapture failed (client did not request capture)";
     return false;
   }
-  client_window->set_capture_owner(nullptr);
+  client_window->SetCaptureOwner(nullptr);
 
   ClientChange change(property_change_tracker_.get(), window,
                       ClientChangeType::kCapture);
@@ -769,6 +769,20 @@
   return focus_handler_.SetFocus(GetWindowByClientId(window_id));
 }
 
+bool WindowServiceClient::StackAtTopImpl(const ClientWindowId& window_id) {
+  DVLOG(3) << "StackAtTop window_id= " << window_id;
+
+  aura::Window* window = GetWindowByClientId(window_id);
+  if (!window || !IsTopLevel(window)) {
+    DVLOG(1) << "StackAtTop failed (invalid id, or invalid window)";
+    return false;
+  }
+
+  if (window->parent())
+    window->parent()->StackChildAtTop(window);
+  return true;
+}
+
 void WindowServiceClient::GetWindowTreeRecursive(
     aura::Window* window,
     std::vector<aura::Window*>* windows) {
@@ -832,7 +846,7 @@
       // One of the windows known to this client had capture. Notify the client
       // of the change. If the client does not know about the window that gained
       // capture, an invalid window id is used.
-      client_window->set_capture_owner(nullptr);
+      client_window->SetCaptureOwner(nullptr);
       const Id gained_capture_id = gained_capture &&
                                            IsWindowKnown(gained_capture) &&
                                            !IsClientRootWindow(gained_capture)
@@ -841,9 +855,6 @@
       window_tree_client_->OnCaptureChanged(gained_capture_id,
                                             TransportIdForWindow(lost_capture));
     }
-  } else {
-    DCHECK(!gained_capture || !IsClientCreatedWindow(gained_capture) ||
-           IsTopLevel(gained_capture));
   }
 }
 
@@ -1171,7 +1182,8 @@
 }
 
 void WindowServiceClient::StackAtTop(uint32_t change_id, Id window_id) {
-  NOTIMPLEMENTED_LOG_ONCE();
+  const bool result = StackAtTopImpl(MakeClientWindowId(window_id));
+  window_tree_client_->OnChangeCompleted(change_id, result);
 }
 
 void WindowServiceClient::PerformWmAction(Id window_id,
diff --git a/services/ui/ws2/window_service_client.h b/services/ui/ws2/window_service_client.h
index 485f1cf..d57272b 100644
--- a/services/ui/ws2/window_service_client.h
+++ b/services/ui/ws2/window_service_client.h
@@ -225,6 +225,7 @@
       const base::Optional<viz::LocalSurfaceId>& local_surface_id);
   std::vector<aura::Window*> GetWindowTreeImpl(const ClientWindowId& window_id);
   bool SetFocusImpl(const ClientWindowId& window_id);
+  bool StackAtTopImpl(const ClientWindowId& window_id);
 
   void GetWindowTreeRecursive(aura::Window* window,
                               std::vector<aura::Window*>* windows);
diff --git a/services/ui/ws2/window_service_client_test_helper.cc b/services/ui/ws2/window_service_client_test_helper.cc
index dca6559..89c35fa 100644
--- a/services/ui/ws2/window_service_client_test_helper.cc
+++ b/services/ui/ws2/window_service_client_test_helper.cc
@@ -29,6 +29,8 @@
 aura::Window* WindowServiceClientTestHelper::NewWindow(
     Id transport_window_id,
     base::flat_map<std::string, std::vector<uint8_t>> properties) {
+  if (transport_window_id == 0)
+    transport_window_id = next_window_id_++;
   const uint32_t change_id = 1u;
   window_service_client_->NewWindow(change_id, transport_window_id, properties);
   return window_service_client_->GetWindowByClientId(
@@ -43,6 +45,8 @@
 aura::Window* WindowServiceClientTestHelper::NewTopLevelWindow(
     Id transport_window_id,
     base::flat_map<std::string, std::vector<uint8_t>> properties) {
+  if (transport_window_id == 0)
+    transport_window_id = next_window_id_++;
   const uint32_t change_id = 1u;
   window_service_client_->NewTopLevelWindow(change_id, transport_window_id,
                                             properties);
@@ -114,6 +118,11 @@
                                                   policy);
 }
 
+bool WindowServiceClientTestHelper::StackAtTop(aura::Window* window) {
+  return window_service_client_->StackAtTopImpl(
+      window_service_client_->MakeClientWindowId(TransportIdForWindow(window)));
+}
+
 Id WindowServiceClientTestHelper::TransportIdForWindow(aura::Window* window) {
   return window ? window_service_client_->TransportIdForWindow(window)
                 : kInvalidTransportId;
diff --git a/services/ui/ws2/window_service_client_test_helper.h b/services/ui/ws2/window_service_client_test_helper.h
index c9dd64a2..5efcaee 100644
--- a/services/ui/ws2/window_service_client_test_helper.h
+++ b/services/ui/ws2/window_service_client_test_helper.h
@@ -49,11 +49,11 @@
   mojom::WindowDataPtr WindowToWindowData(aura::Window* window);
 
   aura::Window* NewWindow(
-      Id transport_window_id,
+      Id transport_window_id = 0,
       base::flat_map<std::string, std::vector<uint8_t>> properties = {});
   void DeleteWindow(aura::Window* window);
   aura::Window* NewTopLevelWindow(
-      Id transport_window_id,
+      Id transport_window_id = 0,
       base::flat_map<std::string, std::vector<uint8_t>> properties = {});
   bool SetCapture(aura::Window* window);
   bool ReleaseCapture(aura::Window* window);
@@ -83,6 +83,7 @@
                                mojom::EventTargetingPolicy policy);
   bool SetFocus(aura::Window* window);
   void SetCanFocus(aura::Window* window, bool can_focus);
+  bool StackAtTop(aura::Window* window);
 
   Id TransportIdForWindow(aura::Window* window);
 
@@ -93,6 +94,9 @@
 
   WindowServiceClient* window_service_client_;
 
+  // Next id to use for creating a window (including top-level windows).
+  Id next_window_id_ = 1;
+
   DISALLOW_COPY_AND_ASSIGN(WindowServiceClientTestHelper);
 };
 
diff --git a/services/ui/ws2/window_service_client_unittest.cc b/services/ui/ws2/window_service_client_unittest.cc
index 06116786..da1ace3d 100644
--- a/services/ui/ws2/window_service_client_unittest.cc
+++ b/services/ui/ws2/window_service_client_unittest.cc
@@ -65,9 +65,9 @@
 TEST(WindowServiceClientTest, NewWindow) {
   WindowServiceTestSetup setup;
   EXPECT_TRUE(setup.changes()->empty());
-  aura::Window* window = setup.client_test_helper()->NewWindow(1);
+  aura::Window* window = setup.client_test_helper()->NewWindow();
   ASSERT_TRUE(window);
-  EXPECT_EQ("ChangeCompleted id=1 sucess=true",
+  EXPECT_EQ("ChangeCompleted id=1 success=true",
             SingleChangeToDescription(*setup.changes()));
 }
 
@@ -79,7 +79,7 @@
   aura::Window* window = setup.client_test_helper()->NewWindow(
       1, {{ui::mojom::WindowManager::kAlwaysOnTop_Property, transport}});
   ASSERT_TRUE(window);
-  EXPECT_EQ("ChangeCompleted id=1 sucess=true",
+  EXPECT_EQ("ChangeCompleted id=1 success=true",
             SingleChangeToDescription(*setup.changes()));
   EXPECT_TRUE(window->GetProperty(aura::client::kAlwaysOnTopKey));
 }
@@ -87,7 +87,7 @@
 TEST(WindowServiceClientTest, NewTopLevelWindow) {
   WindowServiceTestSetup setup;
   EXPECT_TRUE(setup.changes()->empty());
-  aura::Window* top_level = setup.client_test_helper()->NewTopLevelWindow(1);
+  aura::Window* top_level = setup.client_test_helper()->NewTopLevelWindow();
   ASSERT_TRUE(top_level);
   EXPECT_EQ("TopLevelCreated id=1 window_id=0,1 drawn=false",
             SingleChangeToDescription(*setup.changes()));
@@ -108,7 +108,7 @@
 
 TEST(WindowServiceClientTest, SetTopLevelWindowBounds) {
   WindowServiceTestSetup setup;
-  aura::Window* top_level = setup.client_test_helper()->NewTopLevelWindow(1);
+  aura::Window* top_level = setup.client_test_helper()->NewTopLevelWindow();
   setup.changes()->clear();
 
   const gfx::Rect bounds_from_client = gfx::Rect(1, 2, 300, 400);
@@ -125,7 +125,7 @@
   }
   // See comments in WindowServiceClient::SetBoundsImpl() for why this returns
   // false.
-  EXPECT_EQ("ChangeCompleted id=2 sucess=false",
+  EXPECT_EQ("ChangeCompleted id=2 success=false",
             SingleChangeToDescription(*setup.changes()));
   setup.changes()->clear();
 
@@ -153,13 +153,13 @@
   EXPECT_EQ(restricted_bounds, (*setup.changes())[0].bounds2);
 
   // And because the layout manager changed the bounds the result is false.
-  EXPECT_EQ("ChangeCompleted id=3 sucess=false",
+  EXPECT_EQ("ChangeCompleted id=3 success=false",
             ChangeToDescription((*setup.changes())[1]));
 }
 
 TEST(WindowServiceClientTest, SetTopLevelWindowBoundsFailsForSameSize) {
   WindowServiceTestSetup setup;
-  aura::Window* top_level = setup.client_test_helper()->NewTopLevelWindow(1);
+  aura::Window* top_level = setup.client_test_helper()->NewTopLevelWindow();
   setup.changes()->clear();
   const gfx::Rect bounds = gfx::Rect(1, 2, 300, 400);
   top_level->SetBounds(bounds);
@@ -175,7 +175,7 @@
 // Tests the ability of the client to change properties on the server.
 TEST(WindowServiceClientTest, SetTopLevelWindowProperty) {
   WindowServiceTestSetup setup;
-  aura::Window* top_level = setup.client_test_helper()->NewTopLevelWindow(1);
+  aura::Window* top_level = setup.client_test_helper()->NewTopLevelWindow();
   setup.changes()->clear();
 
   EXPECT_FALSE(top_level->GetProperty(aura::client::kAlwaysOnTopKey));
@@ -185,7 +185,7 @@
   setup.client_test_helper()->SetWindowProperty(
       top_level, ui::mojom::WindowManager::kAlwaysOnTop_Property,
       client_transport_value, 2);
-  EXPECT_EQ("ChangeCompleted id=2 sucess=true",
+  EXPECT_EQ("ChangeCompleted id=2 success=true",
             SingleChangeToDescription(*setup.changes()));
   EXPECT_TRUE(top_level->GetProperty(aura::client::kAlwaysOnTopKey));
   setup.changes()->clear();
@@ -199,7 +199,7 @@
 
 TEST(WindowServiceClientTest, WindowToWindowData) {
   WindowServiceTestSetup setup;
-  aura::Window* window = setup.client_test_helper()->NewWindow(1);
+  aura::Window* window = setup.client_test_helper()->NewWindow();
   setup.changes()->clear();
 
   window->SetBounds(gfx::Rect(1, 2, 300, 400));
@@ -220,7 +220,7 @@
 TEST(WindowServiceClientTest, MovePressDragRelease) {
   WindowServiceTestSetup setup;
   TestWindowTreeClient* window_tree_client = setup.window_tree_client();
-  aura::Window* top_level = setup.client_test_helper()->NewTopLevelWindow(1);
+  aura::Window* top_level = setup.client_test_helper()->NewTopLevelWindow();
   ASSERT_TRUE(top_level);
 
   top_level->Show();
@@ -251,7 +251,7 @@
 // Used to verify destruction with a touch pointer down doesn't crash.
 TEST(WindowServiceClientTest, ShutdownWithTouchDown) {
   WindowServiceTestSetup setup;
-  aura::Window* top_level = setup.client_test_helper()->NewTopLevelWindow(1);
+  aura::Window* top_level = setup.client_test_helper()->NewTopLevelWindow();
   ASSERT_TRUE(top_level);
   top_level->Show();
   top_level->SetBounds(gfx::Rect(10, 10, 100, 100));
@@ -264,7 +264,7 @@
 TEST(WindowServiceClientTest, TouchPressDragRelease) {
   WindowServiceTestSetup setup;
   TestWindowTreeClient* window_tree_client = setup.window_tree_client();
-  aura::Window* top_level = setup.client_test_helper()->NewTopLevelWindow(1);
+  aura::Window* top_level = setup.client_test_helper()->NewTopLevelWindow();
   ASSERT_TRUE(top_level);
   top_level->Show();
   top_level->SetBounds(gfx::Rect(10, 11, 100, 100));
@@ -323,7 +323,7 @@
   WindowServiceTestSetup setup;
   TestWindowTreeClient* window_tree_client = setup.window_tree_client();
   setup.delegate()->set_delegate_for_next_top_level(&window_delegate);
-  aura::Window* top_level = setup.client_test_helper()->NewTopLevelWindow(1);
+  aura::Window* top_level = setup.client_test_helper()->NewTopLevelWindow();
   ASSERT_TRUE(top_level);
 
   top_level->Show();
@@ -400,7 +400,7 @@
   WindowServiceTestSetup setup;
   TestWindowTreeClient* window_tree_client = setup.window_tree_client();
   setup.delegate()->set_delegate_for_next_top_level(&window_delegate);
-  aura::Window* top_level = setup.client_test_helper()->NewTopLevelWindow(1);
+  aura::Window* top_level = setup.client_test_helper()->NewTopLevelWindow();
   ASSERT_TRUE(top_level);
   top_level->Show();
   top_level->SetBounds(gfx::Rect(10, 10, 100, 100));
@@ -408,7 +408,7 @@
                                             gfx::Insets(10, 0, 0, 0));
 
   // Add a child Window that is sized to fill the top-level.
-  aura::Window* window = setup.client_test_helper()->NewWindow(2);
+  aura::Window* window = setup.client_test_helper()->NewWindow();
   ASSERT_TRUE(window);
   window->Show();
   window->SetBounds(gfx::Rect(top_level->bounds().size()));
@@ -434,7 +434,7 @@
 TEST(WindowServiceClientTest, PointerWatcher) {
   WindowServiceTestSetup setup;
   TestWindowTreeClient* window_tree_client = setup.window_tree_client();
-  aura::Window* top_level = setup.client_test_helper()->NewTopLevelWindow(1);
+  aura::Window* top_level = setup.client_test_helper()->NewTopLevelWindow();
   ASSERT_TRUE(top_level);
   setup.client_test_helper()->SetEventTargetingPolicy(
       top_level, mojom::EventTargetingPolicy::NONE);
@@ -479,12 +479,12 @@
 
 TEST(WindowServiceClientTest, Capture) {
   WindowServiceTestSetup setup;
-  aura::Window* window = setup.client_test_helper()->NewWindow(1);
+  aura::Window* window = setup.client_test_helper()->NewWindow();
 
   // Setting capture on |window| should fail as it's not visible.
   EXPECT_FALSE(setup.client_test_helper()->SetCapture(window));
 
-  aura::Window* top_level = setup.client_test_helper()->NewTopLevelWindow(2);
+  aura::Window* top_level = setup.client_test_helper()->NewTopLevelWindow();
   ASSERT_TRUE(top_level);
   EXPECT_FALSE(setup.client_test_helper()->SetCapture(top_level));
   top_level->Show();
@@ -499,10 +499,90 @@
   EXPECT_TRUE(setup.client_test_helper()->ReleaseCapture(window));
 }
 
+TEST(WindowServiceClientTest, TransferCaptureToClient) {
+  EventRecordingWindowDelegate window_delegate;
+  WindowServiceTestSetup setup;
+  setup.delegate()->set_delegate_for_next_top_level(&window_delegate);
+  aura::Window* top_level = setup.client_test_helper()->NewTopLevelWindow();
+  ASSERT_TRUE(top_level);
+  top_level->Show();
+  top_level->SetBounds(gfx::Rect(0, 0, 100, 100));
+  setup.client_test_helper()->SetClientArea(top_level,
+                                            gfx::Insets(10, 0, 0, 0));
+
+  wm::CaptureController::Get()->SetCapture(top_level);
+  test::EventGenerator event_generator(setup.root());
+  event_generator.MoveMouseTo(6, 6);
+  setup.window_tree_client()->ClearInputEvents();
+  window_delegate.ClearEvents();
+  event_generator.MoveMouseTo(7, 7);
+
+  // Because capture was initiated locally event should go to |window_delegate|
+  // only (not the client).
+  EXPECT_TRUE(setup.window_tree_client()->input_events().empty());
+  EXPECT_EQ("MOUSE_MOVED", EventToEventType(window_delegate.PopEvent().get()));
+  EXPECT_TRUE(window_delegate.events().empty());
+
+  // Request capture from the client.
+  EXPECT_TRUE(setup.client_test_helper()->SetCapture(top_level));
+  event_generator.MoveMouseTo(8, 8);
+  // Now the event should go to the client and not local.
+  EXPECT_TRUE(window_delegate.events().empty());
+  EXPECT_EQ("POINTER_MOVED",
+            EventToEventType(
+                setup.window_tree_client()->PopInputEvent().event.get()));
+  EXPECT_TRUE(setup.window_tree_client()->input_events().empty());
+}
+
+TEST(WindowServiceClientTest, TransferCaptureBetweenParentAndChild) {
+  EventRecordingWindowDelegate window_delegate;
+  WindowServiceTestSetup setup;
+  setup.delegate()->set_delegate_for_next_top_level(&window_delegate);
+  aura::Window* top_level = setup.client_test_helper()->NewTopLevelWindow();
+  ASSERT_TRUE(top_level);
+  top_level->Show();
+  top_level->SetBounds(gfx::Rect(0, 0, 100, 100));
+  aura::Window* window = setup.client_test_helper()->NewWindow();
+  ASSERT_TRUE(window);
+  top_level->AddChild(window);
+  window->Show();
+  std::unique_ptr<EmbeddingHelper> embedding_helper =
+      setup.CreateEmbedding(window);
+  ASSERT_TRUE(embedding_helper);
+
+  // Move the mouse and set capture from the child.
+  test::EventGenerator event_generator(setup.root());
+  event_generator.MoveMouseTo(6, 6);
+  setup.window_tree_client()->ClearInputEvents();
+  window_delegate.ClearEvents();
+  embedding_helper->window_tree_client.ClearInputEvents();
+  EXPECT_TRUE(embedding_helper->client_test_helper->SetCapture(window));
+  event_generator.MoveMouseTo(7, 7);
+
+  // As capture was set from the child, only the child should get the event.
+  EXPECT_TRUE(setup.window_tree_client()->input_events().empty());
+  EXPECT_TRUE(window_delegate.events().empty());
+  EXPECT_EQ(
+      "POINTER_MOVED",
+      EventToEventType(
+          embedding_helper->window_tree_client.PopInputEvent().event.get()));
+  EXPECT_TRUE(embedding_helper->window_tree_client.input_events().empty());
+
+  // Set capture from the parent, only the parent should get the event now.
+  EXPECT_TRUE(setup.client_test_helper()->SetCapture(top_level));
+  event_generator.MoveMouseTo(8, 8);
+  EXPECT_EQ("POINTER_MOVED",
+            EventToEventType(
+                setup.window_tree_client()->PopInputEvent().event.get()));
+  EXPECT_TRUE(setup.window_tree_client()->input_events().empty());
+  EXPECT_TRUE(window_delegate.events().empty());
+  EXPECT_TRUE(embedding_helper->window_tree_client.input_events().empty());
+}
+
 TEST(WindowServiceClientTest, CaptureNotification) {
   WindowServiceTestSetup setup;
-  aura::Window* window = setup.client_test_helper()->NewWindow(1);
-  aura::Window* top_level = setup.client_test_helper()->NewTopLevelWindow(2);
+  aura::Window* window = setup.client_test_helper()->NewWindow();
+  aura::Window* top_level = setup.client_test_helper()->NewTopLevelWindow();
   top_level->AddChild(window);
   ASSERT_TRUE(top_level);
   top_level->Show();
@@ -518,8 +598,8 @@
 
 TEST(WindowServiceClientTest, CaptureNotificationForEmbedRoot) {
   WindowServiceTestSetup setup;
-  aura::Window* window = setup.client_test_helper()->NewWindow(1);
-  aura::Window* top_level = setup.client_test_helper()->NewTopLevelWindow(2);
+  aura::Window* window = setup.client_test_helper()->NewWindow();
+  aura::Window* top_level = setup.client_test_helper()->NewTopLevelWindow();
   top_level->AddChild(window);
   ASSERT_TRUE(top_level);
   top_level->Show();
@@ -590,8 +670,8 @@
 
 TEST(WindowServiceClientTest, EventsGoToCaptureWindow) {
   WindowServiceTestSetup setup;
-  aura::Window* window = setup.client_test_helper()->NewWindow(1);
-  aura::Window* top_level = setup.client_test_helper()->NewTopLevelWindow(2);
+  aura::Window* window = setup.client_test_helper()->NewWindow();
+  aura::Window* top_level = setup.client_test_helper()->NewTopLevelWindow();
   top_level->AddChild(window);
   ASSERT_TRUE(top_level);
   top_level->Show();
@@ -619,9 +699,9 @@
 
 TEST(WindowServiceClientTest, PointerDownResetOnCaptureChange) {
   WindowServiceTestSetup setup;
-  aura::Window* window = setup.client_test_helper()->NewWindow(1);
+  aura::Window* window = setup.client_test_helper()->NewWindow();
   ASSERT_TRUE(window);
-  aura::Window* top_level = setup.client_test_helper()->NewTopLevelWindow(2);
+  aura::Window* top_level = setup.client_test_helper()->NewTopLevelWindow();
   ASSERT_TRUE(top_level);
   top_level->AddChild(window);
   setup.client_test_helper()->SetClientArea(top_level,
@@ -650,7 +730,7 @@
 
 TEST(WindowServiceClientTest, PointerDownResetOnHide) {
   WindowServiceTestSetup setup;
-  aura::Window* top_level = setup.client_test_helper()->NewTopLevelWindow(2);
+  aura::Window* top_level = setup.client_test_helper()->NewTopLevelWindow();
   ASSERT_TRUE(top_level);
   setup.client_test_helper()->SetClientArea(top_level,
                                             gfx::Insets(10, 0, 0, 0));
@@ -675,20 +755,20 @@
 
 TEST(WindowServiceClientTest, DeleteWindow) {
   WindowServiceTestSetup setup;
-  aura::Window* window = setup.client_test_helper()->NewWindow(1);
+  aura::Window* window = setup.client_test_helper()->NewWindow();
   ASSERT_TRUE(window);
   aura::WindowTracker tracker;
   tracker.Add(window);
   setup.changes()->clear();
   setup.client_test_helper()->DeleteWindow(window);
   EXPECT_TRUE(tracker.windows().empty());
-  EXPECT_EQ("ChangeCompleted id=1 sucess=true",
+  EXPECT_EQ("ChangeCompleted id=1 success=true",
             SingleChangeToDescription(*setup.changes()));
 }
 
 TEST(WindowServiceClientTest, ExternalDeleteWindow) {
   WindowServiceTestSetup setup;
-  aura::Window* window = setup.client_test_helper()->NewWindow(1);
+  aura::Window* window = setup.client_test_helper()->NewWindow();
   ASSERT_TRUE(window);
   setup.changes()->clear();
   delete window;
@@ -698,8 +778,8 @@
 
 TEST(WindowServiceClientTest, Embed) {
   WindowServiceTestSetup setup;
-  aura::Window* window = setup.client_test_helper()->NewWindow(2);
-  aura::Window* embed_window = setup.client_test_helper()->NewWindow(3);
+  aura::Window* window = setup.client_test_helper()->NewWindow();
+  aura::Window* embed_window = setup.client_test_helper()->NewWindow();
   ASSERT_TRUE(window);
   ASSERT_TRUE(embed_window);
   window->AddChild(embed_window);
@@ -717,6 +797,33 @@
   EXPECT_NE(kInvalidTransportId, test_change.windows[0].window_id);
 }
 
+TEST(WindowServiceClientTest, StackAtTop) {
+  WindowServiceTestSetup setup;
+  aura::Window* top_level1 = setup.client_test_helper()->NewTopLevelWindow();
+  ASSERT_TRUE(top_level1);
+  setup.changes()->clear();
+  setup.client_test_helper()->window_tree()->StackAtTop(
+      10, setup.client_test_helper()->TransportIdForWindow(top_level1));
+  // This succeeds because |top_level1| is already at top. |10| is the value
+  // supplied to StackAtTop().
+  EXPECT_EQ("ChangeCompleted id=10 success=true",
+            SingleChangeToDescription(*setup.changes()));
+
+  // Create another top-level. |top_level2| should initially be above 1.
+  aura::Window* top_level2 = setup.client_test_helper()->NewTopLevelWindow();
+  ASSERT_TRUE(top_level2);
+  ASSERT_EQ(2u, top_level1->parent()->children().size());
+  EXPECT_EQ(top_level2, top_level1->parent()->children()[1]);
+
+  // Stack 1 at the top.
+  EXPECT_TRUE(setup.client_test_helper()->StackAtTop(top_level1));
+  EXPECT_EQ(top_level1, top_level1->parent()->children()[1]);
+
+  // Stacking a non-toplevel window at top should fail.
+  aura::Window* non_top_level_window = setup.client_test_helper()->NewWindow();
+  EXPECT_FALSE(setup.client_test_helper()->StackAtTop(non_top_level_window));
+}
+
 }  // namespace
 }  // namespace ws2
 }  // namespace ui
diff --git a/services/video_capture/test/fake_device_descriptor_unittest.cc b/services/video_capture/test/fake_device_descriptor_unittest.cc
index a9943cb..9baf77d 100644
--- a/services/video_capture/test/fake_device_descriptor_unittest.cc
+++ b/services/video_capture/test/fake_device_descriptor_unittest.cc
@@ -4,11 +4,11 @@
 
 #include "base/bind_helpers.h"
 #include "base/run_loop.h"
-#include "build/build_config.h"
 #include "services/video_capture/test/fake_device_descriptor_test.h"
 #include "services/video_capture/test/mock_receiver.h"
 
 using testing::_;
+using testing::AtLeast;
 using testing::InvokeWithoutArgs;
 
 namespace video_capture {
@@ -88,15 +88,8 @@
   ASSERT_FALSE(device_access_2_revoked);
 }
 
-// Flaky. crbug.com/845661
-#if defined(OS_LINUX)
-#define MAYBE_CanUseSecondRequestedProxy DISABLED_CanUseSecondRequestedProxy
-#else
-#define MAYBE_CanUseSecondRequestedProxy CanUseSecondRequestedProxy
-#endif
-
 // Tests that a second proxy requested for a device can be used successfully.
-TEST_F(FakeVideoCaptureDeviceDescriptorTest, MAYBE_CanUseSecondRequestedProxy) {
+TEST_F(FakeVideoCaptureDeviceDescriptorTest, CanUseSecondRequestedProxy) {
   mojom::DevicePtr device_proxy_1;
   factory_->CreateDevice(fake_device_info_.descriptor.device_id,
                          mojo::MakeRequest(&device_proxy_1), base::DoNothing());
@@ -123,7 +116,7 @@
   base::RunLoop wait_loop_2;
   mojom::ReceiverPtr receiver_proxy;
   MockReceiver receiver(mojo::MakeRequest(&receiver_proxy));
-  EXPECT_CALL(receiver, DoOnNewBuffer(_, _));
+  EXPECT_CALL(receiver, DoOnNewBuffer(_, _)).Times(AtLeast(1));
   EXPECT_CALL(receiver, DoOnFrameReadyInBuffer(_, _, _, _))
       .WillRepeatedly(
           InvokeWithoutArgs([&wait_loop_2]() { wait_loop_2.Quit(); }));
diff --git a/testing/buildbot/chromium.perf.json b/testing/buildbot/chromium.perf.json
index f07d5a82..3ac10e3 100644
--- a/testing/buildbot/chromium.perf.json
+++ b/testing/buildbot/chromium.perf.json
@@ -19,7 +19,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build199-b7--device3",
@@ -51,7 +51,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device4",
@@ -85,7 +85,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device4",
@@ -117,7 +117,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device4",
@@ -151,7 +151,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device4",
@@ -183,7 +183,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device5",
@@ -217,7 +217,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device5",
@@ -249,7 +249,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device4",
@@ -283,7 +283,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device4",
@@ -315,7 +315,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device2",
@@ -349,7 +349,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device2",
@@ -381,7 +381,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build199-b7--device3",
@@ -415,7 +415,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build199-b7--device3",
@@ -447,7 +447,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device5",
@@ -481,7 +481,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device5",
@@ -513,7 +513,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build199-b7--device3",
@@ -547,7 +547,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build199-b7--device3",
@@ -579,7 +579,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device6",
@@ -613,7 +613,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device6",
@@ -645,7 +645,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device5",
@@ -679,7 +679,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device5",
@@ -711,7 +711,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build199-b7--device7",
@@ -745,7 +745,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build199-b7--device7",
@@ -777,7 +777,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device6",
@@ -811,7 +811,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device6",
@@ -834,7 +834,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device5",
@@ -866,7 +866,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device5",
@@ -900,7 +900,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device5",
@@ -932,7 +932,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device2",
@@ -966,7 +966,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device2",
@@ -998,7 +998,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device2",
@@ -1032,7 +1032,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device2",
@@ -1064,7 +1064,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device2",
@@ -1098,7 +1098,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device2",
@@ -1121,7 +1121,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build199-b7--device2",
@@ -1153,7 +1153,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device2",
@@ -1187,7 +1187,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device2",
@@ -1219,7 +1219,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device6",
@@ -1253,7 +1253,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device6",
@@ -1285,7 +1285,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device1",
@@ -1317,7 +1317,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device1",
@@ -1351,7 +1351,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device1",
@@ -1383,7 +1383,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device6",
@@ -1417,7 +1417,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device6",
@@ -1449,7 +1449,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device7",
@@ -1483,7 +1483,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device7",
@@ -1515,7 +1515,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device3",
@@ -1549,7 +1549,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device3",
@@ -1581,7 +1581,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device1",
@@ -1615,7 +1615,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device1",
@@ -1647,7 +1647,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device5",
@@ -1681,7 +1681,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device5",
@@ -1713,7 +1713,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build199-b7--device7",
@@ -1745,7 +1745,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build199-b7--device6",
@@ -1779,7 +1779,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build199-b7--device6",
@@ -1811,7 +1811,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device4",
@@ -1845,7 +1845,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device4",
@@ -1877,7 +1877,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device4",
@@ -1911,7 +1911,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device4",
@@ -1943,7 +1943,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device6",
@@ -1977,7 +1977,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device6",
@@ -2009,7 +2009,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device5",
@@ -2043,7 +2043,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device5",
@@ -2075,7 +2075,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device4",
@@ -2109,7 +2109,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device4",
@@ -2141,7 +2141,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device6",
@@ -2175,7 +2175,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device6",
@@ -2207,7 +2207,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device3",
@@ -2241,7 +2241,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device3",
@@ -2273,7 +2273,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device6",
@@ -2307,7 +2307,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device6",
@@ -2339,7 +2339,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device5",
@@ -2373,7 +2373,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device5",
@@ -2405,7 +2405,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device1",
@@ -2439,7 +2439,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device1",
@@ -2471,7 +2471,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build199-b7--device7",
@@ -2505,7 +2505,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build199-b7--device7",
@@ -2537,7 +2537,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device3",
@@ -2571,7 +2571,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device3",
@@ -2603,7 +2603,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device6",
@@ -2637,7 +2637,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device6",
@@ -2669,7 +2669,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build199-b7--device2",
@@ -2703,7 +2703,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build199-b7--device2",
@@ -2735,7 +2735,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device5",
@@ -2769,7 +2769,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device5",
@@ -2801,7 +2801,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device3",
@@ -2835,7 +2835,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device3",
@@ -2867,7 +2867,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build199-b7--device2",
@@ -2901,7 +2901,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build199-b7--device2",
@@ -2933,7 +2933,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device3",
@@ -2967,7 +2967,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device3",
@@ -2999,7 +2999,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device2",
@@ -3033,7 +3033,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device2",
@@ -3065,7 +3065,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device2",
@@ -3099,7 +3099,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device2",
@@ -3131,7 +3131,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build199-b7--device6",
@@ -3165,7 +3165,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build199-b7--device6",
@@ -3197,7 +3197,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device4",
@@ -3231,7 +3231,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device4",
@@ -3263,7 +3263,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device1",
@@ -3297,7 +3297,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device1",
@@ -3329,7 +3329,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device5",
@@ -3363,7 +3363,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device5",
@@ -3395,7 +3395,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device7",
@@ -3429,7 +3429,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device7",
@@ -3461,7 +3461,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device3",
@@ -3495,7 +3495,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device3",
@@ -3527,7 +3527,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device7",
@@ -3561,7 +3561,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device7",
@@ -3593,7 +3593,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device2",
@@ -3627,7 +3627,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device2",
@@ -3659,7 +3659,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device1",
@@ -3693,7 +3693,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device1",
@@ -3725,7 +3725,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device5",
@@ -3759,7 +3759,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device5",
@@ -3791,7 +3791,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device2",
@@ -3823,7 +3823,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build199-b7--device4",
@@ -3857,7 +3857,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build199-b7--device4",
@@ -3891,7 +3891,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device2",
@@ -3923,7 +3923,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device2",
@@ -3955,7 +3955,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build199-b7--device4",
@@ -3989,7 +3989,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build199-b7--device4",
@@ -4023,7 +4023,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device2",
@@ -4055,7 +4055,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device4",
@@ -4089,7 +4089,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device4",
@@ -4121,7 +4121,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device5",
@@ -4155,7 +4155,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device5",
@@ -4187,7 +4187,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device7",
@@ -4221,7 +4221,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device7",
@@ -4253,7 +4253,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build199-b7--device1",
@@ -4287,7 +4287,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build199-b7--device1",
@@ -4319,7 +4319,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device1",
@@ -4353,7 +4353,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device1",
@@ -4385,7 +4385,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device1",
@@ -4419,7 +4419,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device1",
@@ -4451,7 +4451,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build199-b7--device6",
@@ -4485,7 +4485,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build199-b7--device6",
@@ -4517,7 +4517,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device6",
@@ -4551,7 +4551,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device6",
@@ -4583,7 +4583,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device4",
@@ -4617,7 +4617,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device4",
@@ -4649,7 +4649,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device5",
@@ -4683,7 +4683,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build200-b7--device5",
@@ -4715,7 +4715,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device3",
@@ -4749,7 +4749,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device3",
@@ -4781,7 +4781,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device3",
@@ -4815,7 +4815,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device3",
@@ -4847,7 +4847,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device6",
@@ -4881,7 +4881,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device6",
@@ -4904,7 +4904,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build199-b7--device2",
@@ -4936,7 +4936,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build199-b7--device5",
@@ -4968,7 +4968,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build199-b7--device4",
@@ -5002,7 +5002,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build199-b7--device4",
@@ -5036,7 +5036,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build199-b7--device5",
@@ -5068,7 +5068,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build201-b7--device7",
@@ -5100,7 +5100,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build199-b7--device3",
@@ -5134,7 +5134,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build199-b7--device3",
@@ -5166,7 +5166,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build199-b7--device6",
@@ -5200,7 +5200,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "device_os": "KOT49H0",
+              "device_os": "KOT49H",
               "device_os_flavor": "google",
               "device_type": "hammerhead",
               "id": "build199-b7--device6",
diff --git a/testing/buildbot/filters/mash.browser_tests.filter b/testing/buildbot/filters/mash.browser_tests.filter
index ff1e7910..90325bc 100644
--- a/testing/buildbot/filters/mash.browser_tests.filter
+++ b/testing/buildbot/filters/mash.browser_tests.filter
@@ -241,7 +241,7 @@
 # Needs EventGenerator to work across window tree hosts. crbug.com/814675
 -RoundedOmniboxPopupContentsViewTest.ClickOmnibox*
 
-# HostedAppMenu needs porting to BrowserNonClientFrameViewMus crbug.com/813666
+# HostedAppMenu needs porting to BrowserNonClientFrameViewMash crbug.com/813666
 -HostedAppPWAOnlyTest.AppInfoOpensPageInfo*
 
 # DCHECK in DelegatedFrameHost
diff --git a/testing/buildbot/filters/viz.content_browsertests.filter b/testing/buildbot/filters/viz.content_browsertests.filter
index 154c7a0..0a4671e7 100644
--- a/testing/buildbot/filters/viz.content_browsertests.filter
+++ b/testing/buildbot/filters/viz.content_browsertests.filter
@@ -8,9 +8,7 @@
 -TouchSelectionForCrossProcessFramesTests/TouchSelectionControllerClientAuraSiteIsolationTest.*
 
 # Further WaitForChildFrameSurfaceReady doesn't work http://crbug.com/787945
--SitePerProcessBrowserTest.CompositorViewportPixelSizeTest
 -SitePerProcessBrowserTest.GestureFlingStartEventsBubble
--SitePerProcessBrowserTest.NavigateCrashedSubframeToSameSite
 -SitePerProcessBrowserTest.OOPIFDetachDuringAnimation
 -SitePerProcessBrowserTest.PhysicalBackingSizeTest
 -SitePerProcessBrowserTest.ScrollBubblingFromNestedOOPIFTest
diff --git a/testing/libfuzzer/fuzzer_test.gni b/testing/libfuzzer/fuzzer_test.gni
index 9310944..fdcf6fe2 100644
--- a/testing/libfuzzer/fuzzer_test.gni
+++ b/testing/libfuzzer/fuzzer_test.gni
@@ -138,11 +138,16 @@
       if (defined(invoker.sources) && invoker.sources != []) {
         args += [ "--sources" ] + rebase_path(invoker.sources, root_build_dir)
       } else if (defined(invoker.deps) && invoker.deps != []) {
+        _full_deps = []
+        foreach(_dep, invoker.deps) {
+          _full_deps += [ get_label_info(_dep, "dir") + ":" +
+                          get_label_info(_dep, "name") ]
+        }
         args += [
                   "--build-dir",
                   rebase_path("$root_build_dir/", root_build_dir),
                   "--deps",
-                ] + invoker.deps
+                ] + _full_deps
       }
 
       outputs = [
diff --git a/testing/scripts/run_performance_tests.py b/testing/scripts/run_performance_tests.py
index 5b0ea35..6d64a08 100755
--- a/testing/scripts/run_performance_tests.py
+++ b/testing/scripts/run_performance_tests.py
@@ -218,6 +218,13 @@
         raise Exception('Shard indicators must be present for perf tests')
 
       sharding_map_path = get_sharding_map_path(args)
+
+      # Copy sharding map file to isolated_out_dir so that the collect script
+      # can collect it later.
+      shutil.copyfile(
+          sharding_map_path,
+          os.path.join(isolated_out_dir, 'benchmarks_shard_map.json'))
+
       with open(sharding_map_path) as f:
         sharding_map = json.load(f)
       sharding = sharding_map[shard_index]['benchmarks']
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index dc380e3e..99a0ef7 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -412,6 +412,24 @@
             ]
         }
     ],
+    "AutofillExpandedPopupViews": [
+        {
+            "platforms": [
+                "chromeos",
+                "linux",
+                "mac",
+                "win"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "AutofillExpandedPopupViews"
+                    ]
+                }
+            ]
+        }
+    ],
     "AutofillFieldMetadata": [
         {
             "platforms": [
@@ -1878,6 +1896,21 @@
             ]
         }
     ],
+    "MediaFoundationVideoCapture": [
+        {
+            "platforms": [
+                "win"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "MediaFoundationVideoCapture"
+                    ]
+                }
+            ]
+        }
+    ],
     "ModernMediaControls": [
         {
             "platforms": [
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
index 60d359d..e34ad36 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
@@ -689,7 +689,6 @@
 crbug.com/824918 fast/multicol/nested-one-line-in-inner.html [ Failure ]
 crbug.com/591099 fast/multicol/out-of-flow/abspos-auto-position-on-line-rtl.html [ Failure ]
 crbug.com/824918 fast/multicol/span/overflow-on-multicol.html [ Failure ]
-crbug.com/824918 fast/multicol/span/summary-split.html [ Failure Pass ]
 crbug.com/824918 fast/multicol/vertical-lr/caret-range-anonymous-block.html [ Failure ]
 crbug.com/824918 fast/multicol/vertical-rl/caret-range-anonymous-block-rtl.html [ Failure ]
 crbug.com/824918 fast/multicol/vertical-rl/caret-range-anonymous-block.html [ Failure ]
@@ -708,30 +707,12 @@
 crbug.com/591099 fast/replaced/border-radius-clip.html [ Failure Pass ]
 crbug.com/591099 fast/replaced/preferred-widths.html [ Failure ]
 crbug.com/591099 fast/replaced/table-replaced-element.html [ Failure ]
-crbug.com/591099 fast/ruby/add-text-to-block-ruby-with-after-pseudo-crash.html [ Crash ]
-crbug.com/591099 fast/ruby/base-shorter-than-text.html [ Failure ]
-crbug.com/591099 fast/ruby/float-overhang-from-ruby-text.html [ Failure ]
-crbug.com/591099 fast/ruby/list-item-marker-in-block-ruby.html [ Crash ]
-crbug.com/591099 fast/ruby/nested-ruby.html [ Failure ]
-crbug.com/591099 fast/ruby/overhang-horizontal-no-overlap1.html [ Failure ]
-crbug.com/591099 fast/ruby/overhang-horizontal-no-overlap2.html [ Failure ]
-crbug.com/591099 fast/ruby/overhang-horizontal.html [ Failure ]
-crbug.com/591099 fast/ruby/overhang-vertical-no-overlap1.html [ Failure ]
-crbug.com/591099 fast/ruby/overhang-vertical-no-overlap2.html [ Failure ]
-crbug.com/591099 fast/ruby/overhang-vertical.html [ Failure ]
-crbug.com/591099 fast/ruby/percentage-height-child-crash.html [ Crash ]
-crbug.com/591099 fast/ruby/percentage-height-child.html [ Crash ]
 crbug.com/591099 fast/ruby/position-after.html [ Failure ]
-crbug.com/591099 fast/ruby/ruby-block-style-not-updated-with-before-after-content.html [ Crash ]
-crbug.com/591099 fast/ruby/ruby-block-style-not-updated.html [ Crash ]
 crbug.com/591099 fast/ruby/ruby-empty-rt.html [ Failure ]
-crbug.com/591099 fast/ruby/ruby-length.html [ Failure ]
 crbug.com/591099 fast/ruby/ruby-run-break.html [ Failure ]
-crbug.com/591099 fast/ruby/ruby-runs-spans.html [ Failure ]
 crbug.com/591099 fast/ruby/ruby-runs.html [ Failure ]
 crbug.com/591099 fast/ruby/ruby-simple-rp.html [ Failure ]
 crbug.com/591099 fast/ruby/ruby-simple.html [ Failure ]
-crbug.com/591099 fast/ruby/ruby-text-before-after-content.html [ Failure ]
 crbug.com/591099 fast/ruby/ruby-trailing.html [ Failure ]
 crbug.com/591099 fast/ruby/rubyDOM-insert-rt.html [ Failure ]
 crbug.com/591099 fast/ruby/rubyDOM-insert-text1.html [ Failure ]
@@ -740,7 +721,6 @@
 crbug.com/591099 fast/ruby/rubyDOM-remove-rt1.html [ Failure ]
 crbug.com/591099 fast/ruby/rubyDOM-remove-rt2.html [ Failure ]
 crbug.com/591099 fast/ruby/rubyDOM-remove-text1.html [ Failure ]
-crbug.com/591099 fast/ruby/select-ruby.html [ Failure ]
 crbug.com/591099 fast/scrolling/content-box-smaller-than-scrollbar.html [ Failure ]
 crbug.com/591099 fast/scrolling/fractional-scroll-offset-fixed-position-non-composited.html [ Crash ]
 crbug.com/591099 fast/scrolling/jquery-rtl-scroll-type.html [ Failure ]
@@ -879,8 +859,6 @@
 crbug.com/591099 fast/writing-mode/flipped-blocks-inline-map-local-to-container.html [ Failure ]
 crbug.com/714962 fast/writing-mode/japanese-lr-selection.html [ Failure ]
 crbug.com/714962 fast/writing-mode/japanese-rl-selection.html [ Failure ]
-crbug.com/591099 fast/writing-mode/japanese-ruby-vertical-lr.html [ Failure ]
-crbug.com/591099 fast/writing-mode/japanese-ruby-vertical-rl.html [ Failure ]
 crbug.com/591099 fast/writing-mode/percentage-height-orthogonal-writing-modes.html [ Failure ]
 crbug.com/591099 fast/writing-mode/percentage-margins-absolute-replaced.html [ Failure ]
 crbug.com/591099 fast/writing-mode/percentage-margins-absolute.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/LeakExpectations b/third_party/WebKit/LayoutTests/LeakExpectations
index 7ca20f1..d87b3e1 100644
--- a/third_party/WebKit/LayoutTests/LeakExpectations
+++ b/third_party/WebKit/LayoutTests/LeakExpectations
@@ -75,6 +75,8 @@
 crbug.com/836278 [ Linux ] external/wpt/offscreen-canvas/convert-to-blob/offscreencanvas.convert.to.blob.html [ Pass Leak ]
 crbug.com/836278 [ Linux ] virtual/threaded/external/wpt/offscreen-canvas/convert-to-blob/offscreencanvas.convert.to.blob.html [ Pass Leak ]
 
+crbug.com/847868 [ Linux ] fast/events/drag-remove-iframe-crash.html [ Pass Leak ]
+
 ###########################################################################
 # WARNING: Memory leaks must be fixed asap. Sheriff is expected to revert #
 # culprit CLs instead of suppressing the leaks. If you have any question, #
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 3a1c7b7..acb4fa71 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -972,7 +972,6 @@
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/spanner-with-margin.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/spanner-with-margins-between-margins.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/spanner-with-relpos-child.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/summary-split.html [ Failure ]
 crbug.com/824918 virtual/layout_ng_experimental/fast/multicol/span/trailing-margin-around-spanner.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/trailing-margin-before-spanner.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/two-rows-then-spanner-then-two-rows.html [ Failure ]
@@ -4808,3 +4807,6 @@
 crbug.com/846981 fast/webgl/texImage-imageBitmap-from-imageData-resize.html [ Pass Timeout ]
 crbug.com/847114 [ Linux ] http/tests/devtools/tracing/decode-resize.js [ Pass Failure ]
 crbug.com/847205 [ Mac ] http/tests/devtools/tracing/timeline-misc/timeline-window-filter.js [ Pass Crash Timeout ]
+
+# Sheriff 2018-05-30
+crbug.com/847373 [ Mac Linux ] virtual/pwa-full-code-cache/http/tests/devtools/service-workers/service-workers-force-update-on-page-load.js [ Pass Timeout ]
diff --git a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
index c163fd90..24814508 100644
--- a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
+++ b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
@@ -3697,6 +3697,12 @@
      {}
     ]
    ],
+   "feature-policy/experimental-features/vertical-scroll-touch-block-manual.tentative.html": [
+    [
+     "/feature-policy/experimental-features/vertical-scroll-touch-block-manual.tentative.html",
+     {}
+    ]
+   ],
    "fullscreen/api/document-exit-fullscreen-manual.html": [
     [
      "/fullscreen/api/document-exit-fullscreen-manual.html",
@@ -71573,6 +71579,18 @@
      {}
     ]
    ],
+   "css/css-ui/outline-020.html": [
+    [
+     "/css/css-ui/outline-020.html",
+     [
+      [
+       "/css/css-ui/reference/outline-020-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-ui/outline-color-001.html": [
     [
      "/css/css-ui/outline-color-001.html",
@@ -128159,6 +128177,11 @@
      {}
     ]
    ],
+   "css/css-ui/reference/outline-020-ref.html": [
+    [
+     {}
+    ]
+   ],
    "css/css-ui/reference/outline-offset.html": [
     [
      {}
@@ -139584,6 +139607,11 @@
      {}
     ]
    ],
+   "feature-policy/experimental-features/resources/vertical-scroll-touch-block.html": [
+    [
+     {}
+    ]
+   ],
    "feature-policy/experimental-features/resources/vertical-scroll.js": [
     [
      {}
@@ -155079,11 +155107,21 @@
      {}
     ]
    ],
+   "interfaces/media-capabilities.idl": [
+    [
+     {}
+    ]
+   ],
    "interfaces/mediacapture-main.idl": [
     [
      {}
     ]
    ],
+   "interfaces/mediasession.idl": [
+    [
+     {}
+    ]
+   ],
    "interfaces/orientation-sensor.idl": [
     [
      {}
@@ -155919,11 +155957,6 @@
      {}
     ]
    ],
-   "mediasession/idlharness-expected.txt": [
-    [
-     {}
-    ]
-   ],
    "mimesniff/README.md": [
     [
      {}
@@ -157229,11 +157262,6 @@
      {}
     ]
    ],
-   "pointerlock/interfaces.window-expected.txt": [
-    [
-     {}
-    ]
-   ],
    "preload/OWNERS": [
     [
      {}
@@ -317669,6 +317697,10 @@
    "343127ab9431a804a3d5484c4dbaad2a2dae8c05",
    "reftest"
   ],
+  "css/css-ui/outline-020.html": [
+   "38588f278a3795effa0b77b3e65a35a344bf85f1",
+   "reftest"
+  ],
   "css/css-ui/outline-color-001.html": [
    "ce0a7c0301b2d8b8d1f521b69fb987ff39d0ff3a",
    "reftest"
@@ -317849,6 +317881,10 @@
    "c27aa0f4b78e0d388f90b1e54d19db72780b9c26",
    "support"
   ],
+  "css/css-ui/reference/outline-020-ref.html": [
+   "2491d5c730fa27be0257aa10147c249824d32edd",
+   "support"
+  ],
   "css/css-ui/reference/outline-offset.html": [
    "e2af3bbb57ba940e1dcfedc70f03c3281c259350",
    "support"
@@ -338057,6 +338093,10 @@
    "a85e54c23c9e055c959a86187d07f1c0d943f37d",
    "support"
   ],
+  "feature-policy/experimental-features/resources/vertical-scroll-touch-block.html": [
+   "b0574f00487af6997cbec51eed426bada4912bcf",
+   "support"
+  ],
   "feature-policy/experimental-features/resources/vertical-scroll.js": [
    "a62c440428fe22a7afd4d8174e47dfc483c7de90",
    "support"
@@ -338081,6 +338121,10 @@
    "32f1b7eacf4de1d7d792bb27ca439636c242c29c",
    "manual"
   ],
+  "feature-policy/experimental-features/vertical-scroll-touch-block-manual.tentative.html": [
+   "ae88b77a9d913ee830fc4a84cf1e4007942c63fb",
+   "manual"
+  ],
   "feature-policy/feature-policy-frame-policy-allowed-for-all.https.sub.html": [
    "a48c092204750e00c9aa167a9ef9d2d239445d22",
    "testharness"
@@ -361001,10 +361045,18 @@
    "ffac480912edba82886fef6d5368092d237a0c7f",
    "support"
   ],
+  "interfaces/media-capabilities.idl": [
+   "17413896d6281553091cf2c369c29de42d450962",
+   "support"
+  ],
   "interfaces/mediacapture-main.idl": [
    "3400c775504ebf32af3f8e1165a53ca60f258495",
    "support"
   ],
+  "interfaces/mediasession.idl": [
+   "7cbe73f7f563ee45772bb466ce63e16a549548d3",
+   "support"
+  ],
   "interfaces/orientation-sensor.idl": [
    "1f0698a8611726b1ba724a5d7a0961e836c7b07e",
    "support"
@@ -361450,11 +361502,11 @@
    "testharness"
   ],
   "media-capabilities/idlharness-expected.txt": [
-   "edc3a5baf961ee61a33d957f0c889c38254c8d77",
+   "b1684e483ab7ad5ea074e655966c26e6e70fa6b5",
    "support"
   ],
   "media-capabilities/idlharness.html": [
-   "396430dee8bc806e95a218e03f767b34efe8fe83",
+   "6cbda5261b98a04d1ad5c3c9816a443e67d7d523",
    "testharness"
   ],
   "media-source/OWNERS": [
@@ -362285,12 +362337,8 @@
    "5ceecb2611837e6c52a303cec32d8cb9fabe93a6",
    "support"
   ],
-  "mediasession/idlharness-expected.txt": [
-   "450305ca6e268f9304f90793f22dbb1fa5f9291e",
-   "support"
-  ],
   "mediasession/idlharness.html": [
-   "8db3276ecfd3f02e55b89171f61e45e6af540614",
+   "96a3bd3eb15a373ca1c68d528ff6514b3d7cddc1",
    "testharness"
   ],
   "mediasession/mediametadata.html": [
@@ -371353,10 +371401,6 @@
    "a5f5e073e22b8e8400bac62fd5735dca2f2655b7",
    "support"
   ],
-  "pointerlock/interfaces.window-expected.txt": [
-   "87c39d80d3aa959b1d3f08e91494aac551fd542b",
-   "support"
-  ],
   "pointerlock/interfaces.window.js": [
    "597ed15f67c4f94f15b28b167fe7ac0ce1b7d79c",
    "testharness"
@@ -390298,7 +390342,7 @@
    "testharness"
   ],
   "webrtc/RTCPeerConnection-createOffer-expected.txt": [
-   "7ae04088ab239964b6f934b747af3645f5d89d81",
+   "55cad561ac704efe1ad6a2df164d4320fe4622fe",
    "support"
   ],
   "webrtc/RTCPeerConnection-createOffer-offerToReceive-expected.txt": [
@@ -390310,7 +390354,7 @@
    "testharness"
   ],
   "webrtc/RTCPeerConnection-createOffer.html": [
-   "5b3d38072afd9bbb3e95925c3959ff3f4268a347",
+   "c3687cea8887f61fc797a7cfb73786699b97a8d7",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-generateCertificate-expected.txt": [
@@ -390358,7 +390402,7 @@
    "testharness"
   ],
   "webrtc/RTCPeerConnection-helper.js": [
-   "d579dd68118d72c06455d8ccdbeb666f8f39c58a",
+   "92d80863e0a51bf3c1b5bd9b67825100de37cfc1",
    "support"
   ],
   "webrtc/RTCPeerConnection-iceConnectionState-expected.txt": [
@@ -390426,51 +390470,51 @@
    "testharness"
   ],
   "webrtc/RTCPeerConnection-setLocalDescription-answer-expected.txt": [
-   "4b15eb005f8d0825ba66bd018d8597a2d2ff779a",
+   "23395c4322aebdcfb39359e9f33d3ec433ee8f3d",
    "support"
   ],
   "webrtc/RTCPeerConnection-setLocalDescription-answer.html": [
-   "e215aa042c67a23ae776b83d662a035a22e03810",
+   "90b1305e820a7c768f4773f7e995b04832754015",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-setLocalDescription-expected.txt": [
-   "c48d167b04378c68f29e5e89af0b86724ee6fdcd",
+   "741a1e86ce10e08e754b7eaca1982356acd9ac67",
    "support"
   ],
   "webrtc/RTCPeerConnection-setLocalDescription-offer-expected.txt": [
-   "456d511a8a15fe312d14333464b95080ef2171a8",
+   "a3fa0ad31dbdb72767bb4ad7e282b78ad1bd8341",
    "support"
   ],
   "webrtc/RTCPeerConnection-setLocalDescription-offer.html": [
-   "117fc91599d11b63f2d232a63bace8e367dbb72a",
+   "25807994834322acb7c191e03001a55deaaab4ae",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-setLocalDescription-pranswer-expected.txt": [
-   "ff21fde36d76b73bf976beec719c306ef1190c01",
+   "18a25650459a72815871c2d739a44f7d48c4fa63",
    "support"
   ],
   "webrtc/RTCPeerConnection-setLocalDescription-pranswer.html": [
-   "9653d2176c51b638b0c03dec363d2a2fb8386281",
+   "33bd3f0ba3a5065f8dcb239ad7e042dc58c93100",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-setLocalDescription-rollback-expected.txt": [
-   "da39a3ee5e6b4b0d3255bfef95601890afd80709",
+   "9f56675f14645dcaacbf84fed867400e4d1f4f17",
    "support"
   ],
   "webrtc/RTCPeerConnection-setLocalDescription-rollback.html": [
-   "293081a9ebd031cc4919a01ec323630394d401a8",
+   "90b738d362e795d472acabdc356089ab59d864bc",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-setLocalDescription.html": [
-   "b4cac26716f01ff80c902d3536dc0d2de6d85388",
+   "e1ebe9430ffde8b46630bdca30985afaba452085",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-setRemoteDescription-answer-expected.txt": [
-   "a9f67b999b427568b6fd273475811136b4d683c3",
+   "2e3744d2582d9a9781afb0df5555dcb37aaa9153",
    "support"
   ],
   "webrtc/RTCPeerConnection-setRemoteDescription-answer.html": [
-   "3f85858a7afe6a5346b2d47f5505d25caef2668b",
+   "2b263abc36ed0f2431825d47e81120503bdd988a",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-setRemoteDescription-expected.txt": [
@@ -390478,19 +390522,19 @@
    "support"
   ],
   "webrtc/RTCPeerConnection-setRemoteDescription-offer-expected.txt": [
-   "d56244cf2c4c4c4387e699a58a8f20f2e21782d3",
+   "86d4d856be4fccbe7251cffeaefe3b82658315d9",
    "support"
   ],
   "webrtc/RTCPeerConnection-setRemoteDescription-offer.html": [
-   "34337ef2126bfe276675db8884cd2a9aaa2d4432",
+   "c987426037472bcf617ee4a6212b2bfb81513369",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-setRemoteDescription-pranswer-expected.txt": [
-   "d090f3fdcb2a6f18833c5ebf22bf235baa524e29",
+   "d29efba6f7cc0344758d3719d530b698c008aa07",
    "support"
   ],
   "webrtc/RTCPeerConnection-setRemoteDescription-pranswer.html": [
-   "6ddea8760cbacc775ee99aa700fd71883cfb9bd7",
+   "90c11c58394b7b3d3c1fa1e416382c06c1b0cd61",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-setRemoteDescription-replaceTrack.https-expected.txt": [
@@ -390502,11 +390546,11 @@
    "testharness"
   ],
   "webrtc/RTCPeerConnection-setRemoteDescription-rollback-expected.txt": [
-   "da39a3ee5e6b4b0d3255bfef95601890afd80709",
+   "a75291c4ee662699d1c96cbeab99790b3bb3e429",
    "support"
   ],
   "webrtc/RTCPeerConnection-setRemoteDescription-rollback.html": [
-   "6142a9b3b95bb26634af5300bc24981f2b4cb2a4",
+   "69edabd4812f662862fc3820f56a823741fcc045",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-setRemoteDescription-tracks.https-expected.txt": [
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-ui/outline-020.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-ui/outline-020.html
new file mode 100644
index 0000000..457f10a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-ui/outline-020.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<html lang=en>
+  <meta charset=utf-8>
+  <title>CSS-UI test: outline block with outline inline</title>
+  <link rel="author" title="Aleks Totic <atotic@chromium.org>" href="atotic@">
+  <meta name=assert content="">
+  <link rel=help href="https://drafts.csswg.org/css-ui-3/#outline-props">
+  <link rel="match" href="./reference/outline-020-ref.html">
+<style>
+#container {
+  outline: green solid 5px;
+  width: 100px;
+  height: 30px;
+  padding: 5px;
+}
+#target {
+  outline: blue solid 5px;
+}
+</style>
+
+  <p>Test passes if blue outline touches top left of green outline.</p>
+  <div id="container"><span id="target">x</span></div>
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-ui/reference/outline-020-ref.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-ui/reference/outline-020-ref.html
new file mode 100644
index 0000000..194b633
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-ui/reference/outline-020-ref.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<html lang=en>
+  <meta charset=utf-8>
+  <title>CSS-UI test: outline block with outline inline reference file</title>
+  <link rel="author" title="Aleks Totic <atotic@chromium.org>">
+  <meta name=assert content="inline child displays outline correctly">
+<style>
+#container {
+  border: 5px solid green;
+  width: 110px;
+  height: 40px;
+  position: relative;
+  top: -5px;
+  left: -5px;
+}
+
+#target {
+  border: 5px solid blue;
+  position: relative;
+  top: 5px;
+}
+</style>
+
+  <p>Test passes if blue outline touches top left of green outline.</p>
+  <div id="container"><span id="target">x</span></div>
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fullscreen/api/document-onfullscreenerror.html b/third_party/WebKit/LayoutTests/external/wpt/fullscreen/api/document-onfullscreenerror.html
index 499e77d..02cb9482 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/fullscreen/api/document-onfullscreenerror.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/fullscreen/api/document-onfullscreenerror.html
@@ -7,12 +7,23 @@
 async_test(function(t)
 {
     var sync = true;
+    var promise_executed = false;
     assert_equals(document.onfullscreenerror, null, "initial onfullscreenerror");
     document.onfullscreenerror = t.step_func_done(function(event) {
+        assert_true(promise_executed);
         assert_false(sync);
     });
     var e = document.createElement('span');
-    e.requestFullscreen();
+    var promise = e.requestFullscreen();
+    if (promise) {
+        promise.catch(()=> {
+            assert_false(sync);
+            promise_executed = true;
+        });
+    } else {
+        // If promises aren't supported just treat it as already done.
+        promise_executed = true;
+    }
     sync = false;
 });
 </script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fullscreen/api/element-request-fullscreen-not-allowed.html b/third_party/WebKit/LayoutTests/external/wpt/fullscreen/api/element-request-fullscreen-not-allowed.html
index eb9e23b..3170c18 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/fullscreen/api/element-request-fullscreen-not-allowed.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/fullscreen/api/element-request-fullscreen-not-allowed.html
@@ -6,14 +6,24 @@
 <script>
 async_test(t => {
   const div = document.querySelector("div");
+  var promise_executed = false;
 
   document.addEventListener("fullscreenerror", t.step_func_done(event => {
     assert_equals(event.target, div, "event.target");
     assert_true(event.bubbles, "event.bubbles");
     assert_false(event.cancelable, "event.cancelable");
     assert_true(event.composed, "event.composed");
+    assert_true(promise_executed, "promise executed");
   }));
 
-  div.requestFullscreen();
+  var promise = div.requestFullscreen();
+  if (promise) {
+    promise.catch(()=> {
+      promise_executed = true;
+    });
+  } else {
+    // If promises aren't supported just treat it as already done.
+    promise_executed = true;
+  }
 });
 </script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fullscreen/api/element-request-fullscreen-timing-manual.html b/third_party/WebKit/LayoutTests/external/wpt/fullscreen/api/element-request-fullscreen-timing-manual.html
index 1ccfc7a..359b688 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/fullscreen/api/element-request-fullscreen-timing-manual.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/fullscreen/api/element-request-fullscreen-timing-manual.html
@@ -28,11 +28,19 @@
 }, 'Timing of fullscreenchange and resize events');
 
 async_test(t => {
-  document.createElement('a').requestFullscreen();
+  var promise = document.createElement('a').requestFullscreen();
+  var promise_executed = false;
+  if (promise) {
+    promise.catch(()=>{promise_executed = true; });
+  } else {
+    // if promises aren't supported treat it as executed.
+    promise_executed = true;
+  }
 
   // If fullscreenerror is an animation frame event, then animation frame
   // callbacks should be run after it is fired, before the timer callback.
   document.onfullscreenerror = t.step_func(() => {
+    assert_true(promise_executed, "promise executed");
     step_timeout(t.unreached_func('timer callback'));
     requestAnimationFrame(t.step_func_done());
   });
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fullscreen/api/promises-reject.html b/third_party/WebKit/LayoutTests/external/wpt/fullscreen/api/promises-reject.html
new file mode 100644
index 0000000..2f47d4c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/fullscreen/api/promises-reject.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<title>Promises#reject</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+async_test(function(t)
+{
+    var e = document.createElement('span');
+    e.requestFullscreen().catch(t.step_func_done());
+});
+async_test(function(t)
+{
+    var e = document.createElement('span');
+    document.exitFullscreen().catch(t.step_func_done());
+});
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fullscreen/api/promises-resolve-manual.html b/third_party/WebKit/LayoutTests/external/wpt/fullscreen/api/promises-resolve-manual.html
new file mode 100644
index 0000000..e58b8d90
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/fullscreen/api/promises-resolve-manual.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<title>Promises#resolve</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../trusted-click.js"></script>
+<div id="log"></div>
+<script>
+async_test(function(t)
+{
+    var div = document.querySelector("div");
+    trusted_request_with_promise(t, div, document.body,
+    () => {
+        assert_equals(document.fullscreenElement, div, "fullscreenElement before exitFullscreen()");
+        document.exitFullscreen().then(()=> {
+          assert_equals(document.fullscreenElement, null, "fullscreenElement after exiting fullscreen");
+          t.done();
+        });
+        assert_equals(document.fullscreenElement, div, "fullscreenElement after exitFullscreen()");
+    }, t.unreached_func("Request fullscreen failed"));
+});
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fullscreen/interfaces-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/fullscreen/interfaces-expected.txt
deleted file mode 100644
index 4e233a07c..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/fullscreen/interfaces-expected.txt
+++ /dev/null
@@ -1,33 +0,0 @@
-This is a testharness.js-based test.
-PASS Test driver
-PASS Document interface: attribute fullscreenEnabled
-PASS Unscopable handled correctly for fullscreenEnabled property on Document
-PASS Document interface: attribute fullscreen
-PASS Unscopable handled correctly for fullscreen property on Document
-FAIL Document interface: operation exitFullscreen() assert_unreached: Throws "TypeError: Illegal invocation" instead of rejecting promise Reached unreachable code
-PASS Unscopable handled correctly for exitFullscreen() on Document
-PASS Document interface: attribute onfullscreenchange
-PASS Unscopable handled correctly for onfullscreenchange property on Document
-PASS Document interface: attribute onfullscreenerror
-PASS Unscopable handled correctly for onfullscreenerror property on Document
-PASS Document interface: attribute fullscreenElement
-PASS Unscopable handled correctly for fullscreenElement property on Document
-PASS Document interface: new Document must inherit property "fullscreenEnabled" with the proper type
-PASS Document interface: new Document must inherit property "fullscreen" with the proper type
-PASS Document interface: new Document must inherit property "exitFullscreen()" with the proper type
-PASS Document interface: new Document must inherit property "onfullscreenchange" with the proper type
-PASS Document interface: new Document must inherit property "onfullscreenerror" with the proper type
-PASS Document interface: new Document must inherit property "fullscreenElement" with the proper type
-PASS ShadowRoot interface: attribute fullscreenElement
-PASS Unscopable handled correctly for fullscreenElement property on ShadowRoot
-FAIL Element interface: operation requestFullscreen() assert_unreached: Throws "TypeError: Illegal invocation" instead of rejecting promise Reached unreachable code
-PASS Unscopable handled correctly for requestFullscreen() on Element
-PASS Element interface: attribute onfullscreenchange
-PASS Unscopable handled correctly for onfullscreenchange property on Element
-PASS Element interface: attribute onfullscreenerror
-PASS Unscopable handled correctly for onfullscreenerror property on Element
-PASS Element interface: document.createElementNS(null, "test") must inherit property "requestFullscreen()" with the proper type
-PASS Element interface: document.createElementNS(null, "test") must inherit property "onfullscreenchange" with the proper type
-PASS Element interface: document.createElementNS(null, "test") must inherit property "onfullscreenerror" with the proper type
-Harness: the test ran to completion.
-
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fullscreen/trusted-click.js b/third_party/WebKit/LayoutTests/external/wpt/fullscreen/trusted-click.js
index cab23e3..7ee0669e 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/fullscreen/trusted-click.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/fullscreen/trusted-click.js
@@ -19,5 +19,20 @@
 // Invokes element.requestFullscreen() from a trusted click.
 function trusted_request(test, element, container)
 {
-    trusted_click(test, () => element.requestFullscreen(), container || element.parentNode);
+    trusted_click(test, () => {
+        var promise = element.requestFullscreen();
+        if (promise) {
+            // Keep the promise resolution silent. Otherwise unhandledrejection
+            // may fire for the failure test cases.
+            promise.then(() => {}, () => {});
+        }
+    }, container || element.parentNode);
+}
+
+// Invokes element.requestFullscreen() from a trusted click.
+function trusted_request_with_promise(test, element, container, resolve, reject)
+{
+    trusted_click(test, () => {
+        element.requestFullscreen().then(resolve, reject);
+    }, container || element.parentNode);
 }
diff --git a/third_party/WebKit/LayoutTests/external/wpt/interfaces/media-capabilities.idl b/third_party/WebKit/LayoutTests/external/wpt/interfaces/media-capabilities.idl
new file mode 100644
index 0000000..8e242c5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/interfaces/media-capabilities.idl
@@ -0,0 +1,78 @@
+dictionary MediaConfiguration {
+  VideoConfiguration video;
+  AudioConfiguration audio;
+};
+
+dictionary MediaDecodingConfiguration : MediaConfiguration {
+  required MediaDecodingType type;
+};
+
+dictionary MediaEncodingConfiguration : MediaConfiguration {
+  required MediaEncodingType type;
+};
+
+enum MediaDecodingType {
+  "file",
+  "media-source",
+};
+
+enum MediaEncodingType {
+  "record",
+  "transmission"
+};
+
+dictionary VideoConfiguration {
+  required DOMString contentType;
+  required unsigned long width;
+  required unsigned long height;
+  required unsigned long long bitrate;
+  required DOMString framerate;
+};
+
+dictionary AudioConfiguration {
+  required DOMString contentType;
+  DOMString channels;
+  unsigned long long bitrate;
+  unsigned long samplerate;
+};
+
+interface MediaCapabilitiesInfo {
+  readonly attribute boolean supported;
+  readonly attribute boolean smooth;
+  readonly attribute boolean powerEfficient;
+};
+
+[Exposed=(Window)]
+partial interface Navigator {
+  [SameObject] readonly attribute MediaCapabilities mediaCapabilities;
+};
+
+[Exposed=(Worker)]
+partial interface WorkerNavigator {
+  [SameObject] readonly attribute MediaCapabilities mediaCapabilities;
+};
+
+[Exposed=(Window, Worker)]
+interface MediaCapabilities {
+  Promise<MediaCapabilitiesInfo> decodingInfo(MediaDecodingConfiguration configuration);
+  Promise<MediaCapabilitiesInfo> encodingInfo(MediaEncodingConfiguration configuration);
+};
+
+interface ScreenLuminance {
+  readonly attribute double min;
+  readonly attribute double max;
+  readonly attribute double maxAverage;
+};
+
+enum ScreenColorGamut {
+  "srgb",
+  "p3",
+  "rec2020",
+};
+
+partial interface Screen {
+  readonly attribute ScreenColorGamut colorGamut;
+  readonly attribute ScreenLuminance? luminance;
+
+  attribute EventHandler onchange;
+};
diff --git a/third_party/WebKit/LayoutTests/external/wpt/interfaces/mediasession.idl b/third_party/WebKit/LayoutTests/external/wpt/interfaces/mediasession.idl
new file mode 100644
index 0000000..fded300
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/interfaces/mediasession.idl
@@ -0,0 +1,49 @@
+[Exposed=Window]
+partial interface Navigator {
+  [SameObject] readonly attribute MediaSession mediaSession;
+};
+
+enum MediaSessionPlaybackState {
+  "none",
+  "paused",
+  "playing"
+};
+
+enum MediaSessionAction {
+  "play",
+  "pause",
+  "seekbackward",
+  "seekforward",
+  "previoustrack",
+  "nexttrack",
+};
+
+callback MediaSessionActionHandler = void();
+
+[Exposed=Window]
+interface MediaSession {
+  attribute MediaMetadata? metadata;
+  attribute MediaSessionPlaybackState playbackState;
+  void setActionHandler(MediaSessionAction action, MediaSessionActionHandler? handler);
+};
+
+[Constructor(optional MediaMetadataInit init), Exposed=Window]
+interface MediaMetadata {
+  attribute DOMString title;
+  attribute DOMString artist;
+  attribute DOMString album;
+  attribute FrozenArray<MediaImage> artwork;
+};
+
+dictionary MediaMetadataInit {
+  DOMString title = "";
+  DOMString artist = "";
+  DOMString album = "";
+  sequence<MediaImage> artwork = [];
+};
+
+dictionary MediaImage {
+  required USVString src;
+  DOMString sizes = "";
+  DOMString type = "";
+};
diff --git a/third_party/WebKit/LayoutTests/external/wpt/media-capabilities/idlharness-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/media-capabilities/idlharness-expected.txt
index e99c181..64957fc 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/media-capabilities/idlharness-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/media-capabilities/idlharness-expected.txt
@@ -1,8 +1,15 @@
 This is a testharness.js-based test.
+PASS Test IDL implementation of Media Session
 PASS Navigator interface: attribute mediaCapabilities
 PASS Unscopable handled correctly for mediaCapabilities property on Navigator
 PASS Navigator interface: navigator must inherit property "mediaCapabilities" with the proper type
 PASS WorkerNavigator interface: existence and properties of interface object
+FAIL Screen interface: attribute colorGamut assert_true: The prototype object must have a property "colorGamut" expected true got false
+PASS Unscopable handled correctly for colorGamut property on Screen
+FAIL Screen interface: attribute luminance assert_true: The prototype object must have a property "luminance" expected true got false
+PASS Unscopable handled correctly for luminance property on Screen
+FAIL Screen interface: attribute onchange assert_true: The prototype object must have a property "onchange" expected true got false
+PASS Unscopable handled correctly for onchange property on Screen
 PASS MediaCapabilitiesInfo interface: existence and properties of interface object
 PASS MediaCapabilitiesInfo interface object length
 PASS MediaCapabilitiesInfo interface object name
@@ -21,13 +28,21 @@
 PASS MediaCapabilities interface: existence and properties of interface prototype object
 PASS MediaCapabilities interface: existence and properties of interface prototype object's "constructor" property
 PASS MediaCapabilities interface: existence and properties of interface prototype object's @@unscopables property
-FAIL MediaCapabilities interface: operation decodingInfo(MediaDecodingConfiguration) assert_throws: calling operation with this = null didn't throw TypeError function "function() {
-            fn.apply(obj, args);
-        }" did not throw
+PASS MediaCapabilities interface: operation decodingInfo(MediaDecodingConfiguration)
 PASS Unscopable handled correctly for decodingInfo(MediaDecodingConfiguration) on MediaCapabilities
-FAIL MediaCapabilities interface: operation encodingInfo(MediaEncodingConfiguration) assert_throws: calling operation with this = null didn't throw TypeError function "function() {
-            fn.apply(obj, args);
-        }" did not throw
+PASS MediaCapabilities interface: operation encodingInfo(MediaEncodingConfiguration)
 PASS Unscopable handled correctly for encodingInfo(MediaEncodingConfiguration) on MediaCapabilities
+FAIL ScreenLuminance interface: existence and properties of interface object assert_own_property: self does not have own property "ScreenLuminance" expected property "ScreenLuminance" missing
+FAIL ScreenLuminance interface object length assert_own_property: self does not have own property "ScreenLuminance" expected property "ScreenLuminance" missing
+FAIL ScreenLuminance interface object name assert_own_property: self does not have own property "ScreenLuminance" expected property "ScreenLuminance" missing
+FAIL ScreenLuminance interface: existence and properties of interface prototype object assert_own_property: self does not have own property "ScreenLuminance" expected property "ScreenLuminance" missing
+FAIL ScreenLuminance interface: existence and properties of interface prototype object's "constructor" property assert_own_property: self does not have own property "ScreenLuminance" expected property "ScreenLuminance" missing
+FAIL ScreenLuminance interface: existence and properties of interface prototype object's @@unscopables property assert_own_property: self does not have own property "ScreenLuminance" expected property "ScreenLuminance" missing
+FAIL ScreenLuminance interface: attribute min assert_own_property: self does not have own property "ScreenLuminance" expected property "ScreenLuminance" missing
+PASS Unscopable handled correctly for min property on ScreenLuminance
+FAIL ScreenLuminance interface: attribute max assert_own_property: self does not have own property "ScreenLuminance" expected property "ScreenLuminance" missing
+PASS Unscopable handled correctly for max property on ScreenLuminance
+FAIL ScreenLuminance interface: attribute maxAverage assert_own_property: self does not have own property "ScreenLuminance" expected property "ScreenLuminance" missing
+PASS Unscopable handled correctly for maxAverage property on ScreenLuminance
 Harness: the test ran to completion.
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/media-capabilities/idlharness.html b/third_party/WebKit/LayoutTests/external/wpt/media-capabilities/idlharness.html
index c4b0b99f..9f2fc655 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/media-capabilities/idlharness.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/media-capabilities/idlharness.html
@@ -11,81 +11,26 @@
 </head>
 <body>
 <h1>Media Session IDL tests</h1>
-<pre id='untested_idl' style='display:none'>
-[Global=Window, Exposed=Window]
-interface Window {
-};
-interface Worker {
-};
-interface Navigator {
-};
-interface WorkerNavigator {
-};
-</pre>
-<pre id='idl'>
-dictionary MediaConfiguration {
-  VideoConfiguration video;
-  AudioConfiguration audio;
-};
-
-dictionary MediaDecodingConfiguration : MediaConfiguration {
-  required MediaDecodingType type;
-};
-
-dictionary MediaEncodingConfiguration : MediaConfiguration {
-  required MediaEncodingType type;
-};
-
-enum MediaDecodingType {
-  "file",
-  "media-source",
-};
-
-dictionary VideoConfiguration {
-  required DOMString contentType;
-  required unsigned long width;
-  required unsigned long height;
-  required unsigned long bitrate;
-  required double framerate;
-};
-
-dictionary AudioConfiguration {
-  required DOMString contentType;
-  DOMString channels;
-  unsigned long bitrate;
-  unsigned long samplerate;
-};
-
-interface MediaCapabilitiesInfo {
-  readonly attribute boolean supported;
-  readonly attribute boolean smooth;
-  readonly attribute boolean powerEfficient;
-};
-
-[Exposed=(Window)]
-partial interface Navigator {
-  [SameObject] readonly attribute MediaCapabilities mediaCapabilities;
-};
-
-[Exposed=(Worker)]
-partial interface WorkerNavigator {
-  [SameObject] readonly attribute MediaCapabilities mediaCapabilities;
-};
-
-[Exposed=(Window, Worker)]
-interface MediaCapabilities {
-  Promise<MediaCapabilitiesInfo> decodingInfo(MediaDecodingConfiguration configuration);
-  Promise<MediaCapabilitiesInfo> encodingInfo(MediaEncodingConfiguration configuration);
-};
-</pre>
 <script>
-var idl_array = new IdlArray();
-idl_array.add_untested_idls(document.getElementById("untested_idl").textContent);
-idl_array.add_idls(document.getElementById("idl").textContent);
-idl_array.add_objects({
-  Navigator: ["navigator"]
-});
-idl_array.test();
+"use strict";
+function doTest([media_capabilities]) {
+    var idl_array = new IdlArray();
+    idl_array.add_untested_idls('interface Navigator {};');
+    idl_array.add_untested_idls('interface WorkerNavigator {};');
+    idl_array.add_untested_idls('interface Screen {};');
+    idl_array.add_idls(media_capabilities);
+    idl_array.add_objects({
+      Navigator: ["navigator"]
+    });
+    idl_array.test();
+}
+function fetchText(url) {
+    return fetch(url).then((response) => response.text());
+}
+promise_test(() => {
+    return Promise.all(["/interfaces/media-capabilities.idl"].map(fetchText))
+                  .then(doTest);
+}, "Test IDL implementation of Media Session");
 </script>
 <div id="log"></div>
 </body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/mediacapture-streams/MediaDevices-getUserMedia.https.html b/third_party/WebKit/LayoutTests/external/wpt/mediacapture-streams/MediaDevices-getUserMedia.https.html
index 0a2f2ff7..afa3abe 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/mediacapture-streams/MediaDevices-getUserMedia.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/mediacapture-streams/MediaDevices-getUserMedia.https.html
@@ -64,6 +64,34 @@
       }
     }));
 }, 'groupId is correctly supported by getUserMedia() for video devices');
+
+promise_test(t => {
+  return navigator.mediaDevices.enumerateDevices()
+    .then(t.step_func(async devices => {
+      for (var i in devices) {
+        await navigator.mediaDevices.getUserMedia(
+          {audio: {groupId: {exact: devices[i].groupId}}})
+            .then(
+              t.step_func(stream => {
+                var found_device = devices.find(element => {
+                  return element.deviceId ==
+                           stream.getTracks()[0].getSettings().deviceId;
+                });
+                assert_true(undefined !== found_device);
+                assert_equals(found_device.kind, "audioinput");
+                assert_equals(found_device.groupId, devices[i].groupId);
+              }),
+              t.step_func(error => {
+                assert_equals(error.name, "OverconstrainedError");
+                assert_equals(error.constraint, "groupId");
+                var found_device = devices.find(element => {
+                  return element.kind == "audioinput" &&
+                        element.groupId == devices[i].groupId});
+                assert_true(undefined === found_device);
+              }));
+      }
+    }));
+}, 'groupId is correctly supported by getUserMedia() for audio devices');
 </script>
 </body>
 </html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/mediasession/idlharness-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/mediasession/idlharness-expected.txt
deleted file mode 100644
index d5c3de3..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/mediasession/idlharness-expected.txt
+++ /dev/null
@@ -1,39 +0,0 @@
-This is a testharness.js-based test.
-FAIL Window interface: existence and properties of interface object assert_false: expected false got true
-PASS Navigator interface: attribute mediaSession
-PASS Unscopable handled correctly for mediaSession property on Navigator
-PASS Navigator interface: navigator must inherit property "mediaSession" with the proper type
-PASS MediaSession interface: existence and properties of interface object
-PASS MediaSession interface object length
-PASS MediaSession interface object name
-PASS MediaSession interface: existence and properties of interface prototype object
-PASS MediaSession interface: existence and properties of interface prototype object's "constructor" property
-PASS MediaSession interface: existence and properties of interface prototype object's @@unscopables property
-PASS MediaSession interface: attribute metadata
-PASS Unscopable handled correctly for metadata property on MediaSession
-PASS MediaSession interface: attribute playbackState
-PASS Unscopable handled correctly for playbackState property on MediaSession
-PASS MediaSession interface: operation setActionHandler(MediaSessionAction, MediaSessionActionHandler)
-PASS Unscopable handled correctly for setActionHandler(MediaSessionAction, MediaSessionActionHandler) on MediaSession
-PASS MediaMetadata interface: existence and properties of interface object
-PASS MediaMetadata interface object length
-PASS MediaMetadata interface object name
-PASS MediaMetadata interface: existence and properties of interface prototype object
-PASS MediaMetadata interface: existence and properties of interface prototype object's "constructor" property
-PASS MediaMetadata interface: existence and properties of interface prototype object's @@unscopables property
-PASS MediaMetadata interface: attribute title
-PASS Unscopable handled correctly for title property on MediaMetadata
-PASS MediaMetadata interface: attribute artist
-PASS Unscopable handled correctly for artist property on MediaMetadata
-PASS MediaMetadata interface: attribute album
-PASS Unscopable handled correctly for album property on MediaMetadata
-PASS MediaMetadata interface: attribute artwork
-PASS Unscopable handled correctly for artwork property on MediaMetadata
-PASS MediaMetadata must be primary interface of [object MediaMetadata]
-PASS Stringification of [object MediaMetadata]
-PASS MediaMetadata interface: [object MediaMetadata] must inherit property "title" with the proper type
-PASS MediaMetadata interface: [object MediaMetadata] must inherit property "artist" with the proper type
-PASS MediaMetadata interface: [object MediaMetadata] must inherit property "album" with the proper type
-FAIL MediaMetadata interface: [object MediaMetadata] must inherit property "artwork" with the proper type Unrecognized type FrozenArray
-Harness: the test ran to completion.
-
diff --git a/third_party/WebKit/LayoutTests/external/wpt/mediasession/idlharness.html b/third_party/WebKit/LayoutTests/external/wpt/mediasession/idlharness.html
index 4991404b..ead06dc 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/mediasession/idlharness.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/mediasession/idlharness.html
@@ -11,78 +11,28 @@
 </head>
 <body>
 <h1>Media Session IDL tests</h1>
-
-<pre id='untested_idl' style='display:none'>
-[Global=Window, Exposed=Global]
-interface Window {
-};
-
-interface Navigator {
-};
-</pre>
-
-<pre id='idl'>
-[Exposed=Window]
-partial interface Navigator {
-  [SameObject] readonly attribute MediaSession mediaSession;
-};
-
-enum MediaSessionPlaybackState {
-  "none",
-  "paused",
-  "playing"
-};
-
-enum MediaSessionAction {
-  "play",
-  "pause",
-  "seekbackward",
-  "seekforward",
-  "previoustrack",
-  "nexttrack",
-};
-
-callback MediaSessionActionHandler = void();
-
-[Exposed=Window]
-interface MediaSession {
-  attribute MediaMetadata? metadata;
-
-  attribute MediaSessionPlaybackState playbackState;
-
-  void setActionHandler(MediaSessionAction action, MediaSessionActionHandler? handler);
-};
-
-[Constructor(optional MediaMetadataInit init), Exposed=Window]
-interface MediaMetadata {
-  attribute DOMString title;
-  attribute DOMString artist;
-  attribute DOMString album;
-  attribute FrozenArray<MediaImage> artwork;
-};
-
-dictionary MediaMetadataInit {
-  DOMString title = "";
-  DOMString artist = "";
-  DOMString album = "";
-  sequence<MediaImage> artwork = [];
-};
-
-dictionary MediaImage {
-  required USVString src;
-  DOMString sizes = "";
-  DOMString type = "";
-};
-</pre>
 <script>
-var idl_array = new IdlArray();
-idl_array.add_untested_idls(document.getElementById("untested_idl").textContent);
-idl_array.add_idls(document.getElementById("idl").textContent);
-idl_array.add_objects({
-  MediaMetadata: [new MediaMetadata()],
-  Navigator: ["navigator"]
-});
-idl_array.test();
+"use strict";
+
+function doTest([mediasession]) {
+    var idl_array = new IdlArray();
+    idl_array.add_untested_idls('interface Navigator {};');
+    idl_array.add_idls(mediasession);
+    idl_array.add_objects({
+      MediaMetadata: ["new MediaMetadata()"],
+      Navigator: ["navigator"]
+    });
+    idl_array.test();
+}
+
+function fetchText(url) {
+    return fetch(url).then((response) => response.text());
+}
+
+promise_test(() => {
+    return Promise.all(["/interfaces/mediasession.idl"].map(fetchText))
+                  .then(doTest);
+}, "Test IDL implementation of Media Session");
 </script>
 <div id="log"></div>
 </body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/pointerlock/interfaces.window-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/pointerlock/interfaces.window-expected.txt
deleted file mode 100644
index d14cb7e..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/pointerlock/interfaces.window-expected.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-This is a testharness.js-based test.
-PASS pointerlock interfaces.
-PASS MouseEvent interface: attribute movementX
-PASS Unscopable handled correctly for movementX property on MouseEvent
-PASS MouseEvent interface: attribute movementY
-PASS Unscopable handled correctly for movementY property on MouseEvent
-PASS MouseEvent interface: new MouseEvent('foo') must inherit property "movementX" with the proper type
-PASS MouseEvent interface: new MouseEvent('foo') must inherit property "movementY" with the proper type
-PASS Document interface: attribute onpointerlockchange
-PASS Unscopable handled correctly for onpointerlockchange property on Document
-PASS Document interface: attribute onpointerlockerror
-PASS Unscopable handled correctly for onpointerlockerror property on Document
-PASS Document interface: operation exitPointerLock()
-PASS Unscopable handled correctly for exitPointerLock() on Document
-PASS Document interface: attribute pointerLockElement
-PASS Unscopable handled correctly for pointerLockElement property on Document
-FAIL Document interface: window.document must inherit property "onpointerlockchange" with the proper type Unrecognized type EventHandler
-FAIL Document interface: window.document must inherit property "onpointerlockerror" with the proper type Unrecognized type EventHandler
-PASS Document interface: window.document must inherit property "exitPointerLock()" with the proper type
-PASS Document interface: window.document must inherit property "pointerLockElement" with the proper type
-PASS Element interface: operation requestPointerLock()
-PASS Unscopable handled correctly for requestPointerLock() on Element
-PASS Element interface: window.document.documentElement must inherit property "requestPointerLock()" with the proper type
-Harness: the test ran to completion.
-
diff --git a/third_party/WebKit/LayoutTests/external/wpt/resources/idlharness.js b/third_party/WebKit/LayoutTests/external/wpt/resources/idlharness.js
index d413c51..e83c4bf6 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/resources/idlharness.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/resources/idlharness.js
@@ -258,11 +258,14 @@
     });
     this.partials.map(p => p.name).forEach(v => all_deps.add(v));
     // Add the attribute idlTypes of all the nested members of all tested idls.
-    Object.values(this.members).filter(m => !m.untested && m.members).forEach(parsed => {
-        Object.values(parsed.members).filter(m => m.type === 'attribute').forEach(m => {
-            all_deps.add(m.idlType.idlType);
-        });
-    });
+    for (const obj of [this.members, this.partials]) {
+        const tested = Object.values(obj).filter(m => !m.untested && m.members);
+        for (const parsed of tested) {
+            for (const attr of Object.values(parsed.members).filter(m => !m.untested && m.type === 'attribute')) {
+                all_deps.add(attr.idlType.idlType);
+            }
+        }
+    }
 
     if (options && options.except && options.only) {
         throw new IdlHarnessError("The only and except options can't be used together.");
diff --git a/third_party/WebKit/LayoutTests/fast/events/drag-remove-iframe-crash.html b/third_party/WebKit/LayoutTests/fast/events/drag-remove-iframe-crash.html
new file mode 100644
index 0000000..895d6c0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/events/drag-remove-iframe-crash.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<body onload="run_test()"> </body>
+<script>
+var t = async_test("Remove iframe when start dragging should not crash")
+
+function run_test() {
+    var fr = document.createElement('iframe');
+    document.body.appendChild(fr);
+    fr.contentDocument.body.innerHTML = '<div draggable="true" id="target" style="margin: 0px; width: 100px; height: 100px;background-color:red"></div>';
+
+    fr.contentDocument.getElementById("target").ondragstart = function(event) {
+        document.body.removeChild(fr);
+        t.done();
+    };
+
+    if (window.eventSender) {
+      eventSender.mouseMoveTo(50, 50);
+      eventSender.mouseDown();
+      eventSender.mouseMoveTo(80, 80);
+      eventSender.mouseUp();
+    }
+}
+
+</script>
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/accessibility/readonly-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/accessibility/readonly-expected.txt
deleted file mode 100644
index 9a336f92..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/accessibility/readonly-expected.txt
+++ /dev/null
@@ -1,123 +0,0 @@
-Link  Button A B C D E F G H I J K L M
-Focusable
-Heading
-Plain div can't be readonly
-ARIA button
-ARIA toggle button
-ARIA link
-ARIA slider
-ARIA progress meter
-Button
-This tests which elements expose themselves as readonly. Readonly here refers to whether the item is not editable, not whether a control value can be changed vs if it's unavailable.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-link1
-PASS document.activeElement == element is true
-PASS axElement.isReadOnly is false
-
-button1
-PASS document.activeElement == element is true
-PASS axElement.isReadOnly is false
-
-text1
-PASS document.activeElement == element is true
-PASS axElement.isReadOnly is false
-
-text-readonly1
-PASS document.activeElement == element is true
-PASS axElement.isReadOnly is true
-
-text-readonly2
-PASS document.activeElement == element is true
-PASS axElement.isReadOnly is false
-
-checkbox1
-PASS document.activeElement == element is true
-PASS axElement.isReadOnly is false
-
-number1
-PASS document.activeElement == element is true
-PASS axElement.isReadOnly is false
-
-radio1
-PASS document.activeElement == element is true
-PASS axElement.isReadOnly is false
-
-slider1
-PASS document.activeElement == element is true
-PASS axElement.isReadOnly is false
-
-submit1
-PASS document.activeElement == element is true
-PASS axElement.isReadOnly is false
-
-combobox1
-PASS document.activeElement == element is true
-PASS axElement.isReadOnly is false
-
-listbox1
-PASS document.activeElement == element is true
-PASS axElement.isReadOnly is false
-
-textarea1
-PASS document.activeElement == element is true
-PASS axElement.isReadOnly is false
-
-textarea-readonly1
-PASS document.activeElement == element is true
-PASS axElement.isReadOnly is true
-
-focusable1
-PASS document.activeElement == element is true
-PASS axElement.isReadOnly is false
-
-heading1
-PASS document.activeElement == element is true
-PASS axElement.isReadOnly is false
-
-div1
-PASS document.activeElement == element is true
-PASS axElement.isReadOnly is false
-
-aria-button1
-PASS document.activeElement == element is true
-PASS axElement.isReadOnly is false
-
-aria-togglebutton1
-PASS document.activeElement == element is true
-PASS axElement.isReadOnly is false
-
-aria-link1
-PASS document.activeElement == element is true
-PASS axElement.isReadOnly is false
-
-aria-slider1
-PASS document.activeElement == element is true
-PASS axElement.isReadOnly is true
-
-aria-progress1
-PASS document.activeElement == element is true
-PASS axElement.isReadOnly is false
-
-contenteditable_root1
-PASS document.activeElement == element is true
-PASS axElement.isReadOnly is false
-
-contenteditable_button1
-PASS document.activeElement == element is true
-PASS axElement.isReadOnly is false
-
-contenteditable_root-readonly1
-PASS document.activeElement == element is true
-PASS axElement.isReadOnly is false
-
-contenteditable_root-readonly2
-PASS document.activeElement == element is true
-PASS axElement.isReadOnly is true
-
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/deleting/5369009-expected.png b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/deleting/5369009-expected.png
deleted file mode 100644
index 908be8f..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/deleting/5369009-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/deleting/5369009-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/deleting/5369009-expected.txt
deleted file mode 100644
index 1252adf..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/deleting/5369009-expected.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x600
-  LayoutBlockFlow {HTML} at (0,0) size 800x600
-    LayoutBlockFlow {BODY} at (8,8) size 784x584
-      LayoutBlockFlow {P} at (0,0) size 784x40
-        LayoutText {#text} at (0,0) size 783x39
-          text run at (0,0) width 783: "This tests for a hang on delete where a style rule would cause style spans in content moved after the delete to be displayed as"
-          text run at (0,20) width 184: "blocks. You should see Hello"
-        LayoutInline {B} at (0,0) size 43x19
-          LayoutText {#text} at (184,20) size 43x19
-            text run at (184,20) width 43: "World"
-        LayoutText {#text} at (227,20) size 46x19
-          text run at (227,20) width 46: " below."
-      LayoutBlockFlow {DIV} at (0,56) size 784x20
-        LayoutBlockFlow {DIV} at (0,0) size 784x20
-          LayoutText {#text} at (0,0) size 35x19
-            text run at (0,0) width 35: "Hello"
-          LayoutInline {SPAN} at (0,0) size 43x19
-            LayoutText {#text} at (35,0) size 43x19
-              text run at (35,0) width 43: "World"
-caret: position 5 of child 0 {#text} of child 1 {DIV} of child 2 {DIV} of body
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/deleting/5433862-2-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/deleting/5433862-2-expected.txt
deleted file mode 100644
index 58c0d50..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/deleting/5433862-2-expected.txt
+++ /dev/null
@@ -1,43 +0,0 @@
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x600
-  LayoutBlockFlow {HTML} at (0,0) size 800x600
-    LayoutBlockFlow {BODY} at (8,8) size 784x584
-      LayoutBlockFlow {P} at (0,0) size 784x40
-        LayoutText {#text} at (0,0) size 783x39
-          text run at (0,0) width 579: "This tests for a bug where empty table rows well after the selection to delete were removed. "
-          text run at (579,0) width 204: "There should be five rows in the"
-          text run at (0,20) width 260: "table below, before and after the deletion."
-      LayoutBlockFlow {DIV} at (0,56) size 784x140
-        LayoutTable {TABLE} at (0,0) size 109x140 [border: (1px outset #808080)]
-          LayoutTableSection {TBODY} at (1,1) size 107x138
-            LayoutTableRow {TR} at (0,2) size 107x32
-              LayoutTableCell {TD} at (2,2) size 33x32 [border: (1px inset #808080)] [r=0 c=0 rs=1 cs=1]
-                LayoutBR {BR} at (6,6) size 0x19
-              LayoutTableCell {TD} at (37,2) size 32x32 [border: (1px inset #808080)] [r=0 c=1 rs=1 cs=1]
-              LayoutTableCell {TD} at (71,2) size 34x32 [border: (1px inset #808080)] [r=0 c=2 rs=1 cs=1]
-            LayoutTableRow {TR} at (0,36) size 107x32
-              LayoutTableCell {TD} at (2,36) size 33x32 [border: (1px inset #808080)] [r=1 c=0 rs=1 cs=1]
-                LayoutText {#text} at (6,6) size 21x19
-                  text run at (6,6) width 21: "foo"
-              LayoutTableCell {TD} at (37,36) size 32x32 [border: (1px inset #808080)] [r=1 c=1 rs=1 cs=1]
-                LayoutText {#text} at (6,6) size 20x19
-                  text run at (6,6) width 20: "bar"
-              LayoutTableCell {TD} at (71,36) size 34x32 [border: (1px inset #808080)] [r=1 c=2 rs=1 cs=1]
-                LayoutText {#text} at (6,6) size 22x19
-                  text run at (6,6) width 22: "baz"
-            LayoutTableRow {TR} at (0,70) size 107x32
-              LayoutTableCell {TD} at (2,70) size 33x32 [border: (1px inset #808080)] [r=2 c=0 rs=1 cs=1]
-                LayoutBR {BR} at (6,6) size 0x19
-              LayoutTableCell {TD} at (37,70) size 32x32 [border: (1px inset #808080)] [r=2 c=1 rs=1 cs=1]
-                LayoutBR {BR} at (6,6) size 0x19
-              LayoutTableCell {TD} at (71,70) size 34x32 [border: (1px inset #808080)] [r=2 c=2 rs=1 cs=1]
-                LayoutBR {BR} at (6,6) size 0x19
-            LayoutTableRow {TR} at (0,104) size 107x32
-              LayoutTableCell {TD} at (2,104) size 33x32 [border: (1px inset #808080)] [r=3 c=0 rs=1 cs=1]
-                LayoutBR {BR} at (6,6) size 0x19
-              LayoutTableCell {TD} at (37,104) size 32x32 [border: (1px inset #808080)] [r=3 c=1 rs=1 cs=1]
-                LayoutBR {BR} at (6,6) size 0x19
-              LayoutTableCell {TD} at (71,104) size 34x32 [border: (1px inset #808080)] [r=3 c=2 rs=1 cs=1]
-                LayoutBR {BR} at (6,6) size 0x19
-caret: position 0 of child 0 {BR} of child 0 {TD} of child 0 {TR} of child 1 {TBODY} of child 1 {TABLE} of child 3 {DIV} of body
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/deleting/5483370-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/deleting/5483370-expected.txt
deleted file mode 100644
index c291cdd..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/deleting/5483370-expected.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x600
-  LayoutBlockFlow {HTML} at (0,0) size 800x600
-    LayoutBlockFlow {BODY} at (8,8) size 784x584
-      LayoutBlockFlow {P} at (0,0) size 784x40
-        LayoutText {#text} at (0,0) size 760x39
-          text run at (0,0) width 705: "This tests for a problem where empty table rows after the selection being deleted would be removed incorrectly. "
-          text run at (705,0) width 55: "Only the"
-          text run at (0,20) width 343: "last letter in 'foo' should be removed during this delete."
-      LayoutBlockFlow {DIV} at (0,56) size 784x56
-        LayoutTable {TABLE} at (0,0) size 29x56 [border: (1px outset #808080)]
-          LayoutTableSection {TBODY} at (1,1) size 27x54
-            LayoutTableRow {TR} at (0,2) size 27x24
-              LayoutTableCell {TD} at (2,2) size 17x24 [border: (1px inset #808080)] [r=0 c=0 rs=1 cs=1]
-                LayoutText {#text} at (2,2) size 13x19
-                  text run at (2,2) width 13: "fo"
-              LayoutTableCell {TD} at (21,2) size 4x24 [border: (1px inset #808080)] [r=0 c=1 rs=1 cs=1]
-            LayoutTableRow {TR} at (0,28) size 27x24
-              LayoutTableCell {TD} at (2,28) size 17x24 [border: (1px inset #808080)] [r=1 c=0 rs=1 cs=1]
-              LayoutTableCell {TD} at (21,28) size 4x24 [border: (1px inset #808080)] [r=1 c=1 rs=1 cs=1]
-caret: position 2 of child 0 {#text} of child 0 {TD} of child 0 {TR} of child 0 {TBODY} of child 0 {TABLE} of child 2 {DIV} of body
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/deleting/delete-at-paragraph-boundaries-011-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/deleting/delete-at-paragraph-boundaries-011-expected.txt
deleted file mode 100644
index 7d6af42..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/deleting/delete-at-paragraph-boundaries-011-expected.txt
+++ /dev/null
@@ -1,39 +0,0 @@
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x600
-  LayoutBlockFlow {HTML} at (0,0) size 800x600
-    LayoutBlockFlow {BODY} at (8,8) size 784x584
-      LayoutBlockFlow {DIV} at (0,0) size 784x238 [border: (2px solid #0000FF)]
-        LayoutBlockFlow {DIV} at (14,14) size 756x83
-          LayoutText {#text} at (0,0) size 65x26
-            text run at (0,0) width 65: "Tests: "
-          LayoutBR {BR} at (0,0) size 0x0
-          LayoutText {#text} at (0,27) size 628x27
-            text run at (0,27) width 628: "Delete at the end of document when there is a BR following a P. "
-          LayoutBR {BR} at (628,48) size 0x0
-          LayoutText {#text} at (0,55) size 441x27
-            text run at (0,55) width 441: "This is a test case for rdar://problem/4110366"
-        LayoutBlockFlow {DIV} at (14,113) size 756x111
-          LayoutText {#text} at (0,0) size 189x26
-            text run at (0,0) width 189: "Expected Results: "
-          LayoutBR {BR} at (189,21) size 0x0
-          LayoutText {#text} at (0,27) size 720x55
-            text run at (0,27) width 240: "Red box with four lines. "
-            text run at (240,27) width 480: "The second line is a nested red box with the word"
-            text run at (0,55) width 81: "\"hello\". "
-            text run at (81,55) width 314: "The other three lines are empty. "
-          LayoutBR {BR} at (395,76) size 0x0
-          LayoutText {#text} at (0,83) size 468x27
-            text run at (0,83) width 468: "Selection is a caret at the start of the fourth line."
-      LayoutBlockFlow {DIV} at (0,262) size 784x140 [border: (2px solid #FF0000)]
-        LayoutBlockFlow {P} at (2,26) size 780x32 [border: (2px solid #FF0000)]
-          LayoutText {#text} at (2,2) size 49x27
-            text run at (2,2) width 49: "hello"
-        LayoutBlockFlow {P} at (2,82) size 780x32 [border: (2px solid #FF0000)]
-          LayoutBR {BR} at (2,2) size 0x27
-caret: position 0 of child 0 {BR} of child 1 {P} of child 3 {DIV} of body
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/deleting/delete-empty-table-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/deleting/delete-empty-table-expected.txt
deleted file mode 100644
index f239f2a3..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/deleting/delete-empty-table-expected.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-Radar 5565461 
-Bug 32526
-
-Executing delete command when the selection is on a cell of a table shouldn't remove the entire row.
-
-Executing delete command when the selection is on the last cell of a table shouldn't remove the entire table.
-
-
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/deleting/merge-different-styles-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/deleting/merge-different-styles-expected.txt
deleted file mode 100644
index 4ea9cc9..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/deleting/merge-different-styles-expected.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x600
-  LayoutBlockFlow {HTML} at (0,0) size 800x600
-    LayoutBlockFlow {BODY} at (8,8) size 784x584
-      LayoutBlockFlow {P} at (0,0) size 784x40
-        LayoutText {#text} at (0,0) size 749x39
-          text run at (0,0) width 345: "This places the caret before the 'b' in 'bar' and Deletes. "
-          text run at (345,0) width 404: "'foo' and 'bar' should end up on the same line, but neither should"
-          text run at (0,20) width 82: "change style."
-      LayoutBlockFlow {DIV} at (0,56) size 784x20
-        LayoutBlockFlow {DIV} at (0,0) size 784x20
-          LayoutText {#text} at (0,0) size 21x19
-            text run at (0,0) width 21: "foo"
-          LayoutInline {SPAN} at (0,0) size 24x19
-            LayoutText {#text} at (21,0) size 24x19
-              text run at (21,0) width 24: "bar"
-caret: position 3 of child 0 {#text} of child 1 {DIV} of child 2 {DIV} of body
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/deleting/merge-endOfParagraph-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/deleting/merge-endOfParagraph-expected.txt
deleted file mode 100644
index 8b3ba7a..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/deleting/merge-endOfParagraph-expected.txt
+++ /dev/null
@@ -1,29 +0,0 @@
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x600
-  LayoutBlockFlow {HTML} at (0,0) size 800x600
-    LayoutBlockFlow {BODY} at (8,8) size 784x584
-      LayoutBlockFlow {P} at (0,0) size 784x40
-        LayoutText {#text} at (0,0) size 747x39
-          text run at (0,0) width 747: "When the selection to delete ends at the end of a paragraph, that paragraph will be completely deleted, but a <br> or an"
-          text run at (0,20) width 555: "empty block will remain. Merging must happen to remove that <br> or prune that block."
-      LayoutBlockFlow {P} at (0,56) size 784x40
-        LayoutText {#text} at (0,0) size 763x39
-          text run at (0,0) width 763: "This test illustrates a case where merging wasn't allowed to happen just because the end of the selection to delete was in a"
-          text run at (0,20) width 237: "fully selected line, which is nonsense."
-      LayoutBlockFlow {P} at (0,112) size 784x60
-        LayoutText {#text} at (0,0) size 779x59
-          text run at (0,0) width 315: "Fixing that bug exposed a problem with merging. "
-          text run at (315,0) width 462: "If deletion empties out the block that contained the start of the selection to"
-          text run at (0,20) width 472: "delete, that block can collapse away and become impossible to merge into. "
-          text run at (472,20) width 307: "So we insert a placeholder to prop it open so that"
-          text run at (0,40) width 142: "the merge can happen."
-      LayoutBlockFlow {DIV} at (5,188) size 774x34 [border: (1px solid #000000)]
-        LayoutBlockFlow {DIV} at (6,6) size 762x22 [border: (1px solid #FF0000)]
-          LayoutBR {BR} at (1,1) size 0x19
-caret: position 0 of child 0 {BR} of child 0 {DIV} of child 6 {DIV} of body
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/deleting/table-cells-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/deleting/table-cells-expected.txt
deleted file mode 100644
index 761fc57..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/deleting/table-cells-expected.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x600
-  LayoutBlockFlow {HTML} at (0,0) size 800x600
-    LayoutBlockFlow {BODY} at (8,8) size 784x584
-      LayoutBlockFlow {P} at (0,0) size 784x20
-        LayoutText {#text} at (0,0) size 717x19
-          text run at (0,0) width 361: "This tests deletion of a selection that spans multiple cells. "
-          text run at (361,0) width 356: "Just table content should be removed, not table structure."
-      LayoutBlockFlow {DIV} at (0,36) size 784x30
-        LayoutTable {TABLE} at (0,0) size 343x30 [border: (1px outset #808080)]
-          LayoutTableSection {TBODY} at (1,1) size 341x28
-            LayoutTableRow {TR} at (0,2) size 341x24
-              LayoutTableCell {TD} at (2,2) size 157x24 [border: (1px inset #808080)] [r=0 c=0 rs=1 cs=1]
-                LayoutBlockFlow {DIV} at (2,2) size 153x20
-                  LayoutText {#text} at (0,0) size 153x19
-                    text run at (0,0) width 153: "These two pieces of text"
-              LayoutTableCell {TD} at (161,2) size 178x24 [border: (1px inset #808080)] [r=0 c=1 rs=1 cs=1]
-                LayoutBlockFlow {DIV} at (2,2) size 174x20
-                  LayoutText {#text} at (0,0) size 174x19
-                    text run at (0,0) width 174: " should be in different cells."
-caret: position 24 of child 0 {#text} of child 0 {DIV} of child 0 {TD} of child 0 {TR} of child 0 {TBODY} of child 0 {TABLE} of child 2 {DIV} of body
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/execCommand/4916541-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/execCommand/4916541-expected.txt
deleted file mode 100644
index 4be6df3..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/execCommand/4916541-expected.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x600
-  LayoutBlockFlow {HTML} at (0,0) size 800x600
-    LayoutBlockFlow {BODY} at (8,8) size 784x584
-      LayoutBlockFlow {P} at (0,0) size 784x40
-        LayoutText {#text} at (0,0) size 783x39
-          text run at (0,0) width 377: "This tests selection preservation during an indent operation. "
-          text run at (377,0) width 406: "The selection should start and end in the same parts of the words"
-          text run at (0,20) width 320: "'foo' and 'bar' before and after the indent operation."
-      LayoutBlockFlow {DIV} at (0,56) size 784x40
-        LayoutBlockFlow {BLOCKQUOTE} at (40,0) size 744x40
-          LayoutInline {SPAN} at (0,0) size 21x19
-            LayoutText {#text} at (0,0) size 21x19
-              text run at (0,0) width 21: "foo"
-            LayoutBR {BR} at (21,15) size 0x0
-          LayoutInline {SPAN} at (0,0) size 20x19
-            LayoutText {#text} at (0,20) size 20x19
-              text run at (0,20) width 20: "bar"
-selection start: position 1 of child 0 {#text} of child 0 {SPAN} of child 0 {BLOCKQUOTE} of child 2 {DIV} of body
-selection end:   position 2 of child 0 {#text} of child 1 {SPAN} of child 0 {BLOCKQUOTE} of child 2 {DIV} of body
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/execCommand/5190926-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/execCommand/5190926-expected.txt
deleted file mode 100644
index 8a16113..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/execCommand/5190926-expected.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x600
-  LayoutBlockFlow {HTML} at (0,0) size 800x600
-    LayoutBlockFlow {BODY} at (8,8) size 784x576
-      LayoutBlockFlow {OL} at (0,0) size 784x60
-        LayoutListItem {LI} at (40,0) size 744x20
-          LayoutListMarker (anonymous) at (-16,0) size 16x19: "1"
-          LayoutInline {U} at (0,0) size 497x19
-            LayoutText {#text} at (0,0) size 497x19
-              text run at (0,0) width 497: "This tests for a crash when making and removing lists from underlined content."
-        LayoutListItem {LI} at (40,20) size 744x20
-          LayoutListMarker (anonymous) at (-16,0) size 16x19: "2"
-          LayoutInline {U} at (0,0) size 272x19
-            LayoutText {#text} at (0,0) size 272x19
-              text run at (0,0) width 272: "All three paragraphs should be in list items."
-        LayoutListItem {LI} at (40,40) size 744x20
-          LayoutListMarker (anonymous) at (-16,0) size 16x19: "3"
-          LayoutInline {U} at (0,0) size 222x19
-            LayoutText {#text} at (0,0) size 222x19
-              text run at (0,0) width 222: "And all three should be underlined."
-selection start: position 0 of child 0 {#text} of child 0 {U} of child 0 {LI} of child 0 {OL} of body
-selection end:   position 35 of child 0 {#text} of child 0 {U} of child 2 {LI} of child 0 {OL} of body
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/execCommand/insertImage-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/execCommand/insertImage-expected.txt
deleted file mode 100644
index 45da801..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/execCommand/insertImage-expected.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x600
-  LayoutBlockFlow {HTML} at (0,0) size 800x600
-    LayoutBlockFlow {BODY} at (8,8) size 784x576
-      LayoutBlockFlow {P} at (0,0) size 784x40
-        LayoutText {#text} at (0,0) size 748x39
-          text run at (0,0) width 306: "This is a test of execCommand(InsertImage, ...). "
-          text run at (306,0) width 442: "The first test passes execCommand a path to a valid image, the second"
-          text run at (0,20) width 369: "passes execCommand a path where no image should exist."
-      LayoutBlockFlow {DIV} at (0,56) size 784x103
-        LayoutImage {IMG} at (0,0) size 76x103
-        LayoutBlockFlow {IMG} at (76,87) size 16x16
-          LayoutInline {SPAN} at (0,0) size 0x0
-            LayoutImage (floating) {IMG} at (0,0) size 16x16
-            LayoutInline {SPAN} at (0,0) size 0x0
-      LayoutBlockFlow {UL} at (0,175) size 784x0
-caret: position 1 of child 1 {IMG} of child 3 {DIV} of body
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/execCommand/outdent-multiparagraph-list-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/execCommand/outdent-multiparagraph-list-expected.txt
deleted file mode 100644
index 844aa59..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/execCommand/outdent-multiparagraph-list-expected.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-This tests outdenting different selections in the lists. The test should not hang.
-Bugzilla bug 
-Radar bug
-
-hello world
-ciao
-how are you?
-good
-
-hello world
-ciao
-how are you?
-good
-
-hello world
-ciao
-how are you?
-good
-
-hello world
-ciao
-how are you?
-good
-
-
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/execCommand/queryCommandState-02-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/execCommand/queryCommandState-02-expected.txt
deleted file mode 100644
index 7ee0ba8..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/execCommand/queryCommandState-02-expected.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-This tests queryCommandState.
-Bug 32285 
-Radar 7442065
-
-one
-two
-Success
-Success
-Success
-Success
-Success
-Success
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/inserting/4840662-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/inserting/4840662-expected.txt
deleted file mode 100644
index 0be127b..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/inserting/4840662-expected.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x600
-  LayoutBlockFlow {HTML} at (0,0) size 800x600
-    LayoutBlockFlow {BODY} at (8,8) size 784x584
-      LayoutBlockFlow {P} at (0,0) size 784x40
-        LayoutText {#text} at (0,0) size 762x39
-          text run at (0,0) width 402: "This tests insertion before/after links that are also display:block. "
-          text run at (402,0) width 360: "Insertion before/after display:block links should go inside"
-          text run at (0,20) width 362: "the links so that text is inserted in the expected paragraph."
-      LayoutBlockFlow {DIV} at (0,56) size 784x20
-        LayoutBlockFlow {A} at (0,0) size 784x20 [color=#0000EE]
-          LayoutText {#text} at (0,0) size 248x19
-            text run at (0,0) width 248: "This sentence should all be on one line."
-caret: position 40 of child 0 {#text} of child 0 {A} of child 2 {DIV} of body
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/inserting/5549929-2-expected.png b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/inserting/5549929-2-expected.png
deleted file mode 100644
index 23ae191..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/inserting/5549929-2-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/inserting/5549929-2-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/inserting/5549929-2-expected.txt
deleted file mode 100644
index 7504d88..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/inserting/5549929-2-expected.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x600
-  LayoutBlockFlow {HTML} at (0,0) size 800x600
-    LayoutBlockFlow {BODY} at (8,8) size 784x584
-      LayoutBlockFlow {P} at (0,0) size 784x40
-        LayoutText {#text} at (0,0) size 733x39
-          text run at (0,0) width 616: "This tests to make sure that a br isn't inserted into a tab span during an InsertLineBreak operation. "
-          text run at (616,0) width 117: "You can test for its"
-          text run at (0,20) width 432: "existence with the DOM inspector or you can look at the render tree."
-      LayoutBlockFlow {DIV} at (0,56) size 784x40
-        LayoutBlockFlow {DIV} at (0,0) size 784x40
-          LayoutBR {BR} at (0,0) size 0x19
-          LayoutInline {SPAN} at (0,0) size 32x19
-            LayoutText {#text} at (0,20) size 32x19
-              text run at (0,20) width 32: "\x{9}"
-          LayoutText {#text} at (32,20) size 20x19
-            text run at (32,20) width 20: "bar"
-caret: position 0 of child 0 {#text} of child 1 {SPAN} of child 1 {DIV} of child 2 {DIV} of body
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/inserting/5549929-3-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/inserting/5549929-3-expected.txt
deleted file mode 100644
index 8ac2d1b..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/inserting/5549929-3-expected.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x600
-  LayoutBlockFlow {HTML} at (0,0) size 800x600
-    LayoutBlockFlow {BODY} at (8,8) size 784x584
-      LayoutBlockFlow {P} at (0,0) size 784x40
-        LayoutText {#text} at (0,0) size 767x39
-          text run at (0,0) width 351: "This tests inserting a line break at the end of a tab span. "
-          text run at (351,0) width 416: "Below you should see 'foo' followed by an empty paragraph, with"
-          text run at (0,20) width 85: "the caret in it."
-      LayoutBlockFlow {DIV} at (0,56) size 784x40
-        LayoutText {#text} at (0,0) size 21x19
-          text run at (0,0) width 21: "foo"
-        LayoutInline {SPAN} at (0,0) size 11x19
-          LayoutText {#text} at (21,0) size 11x19
-            text run at (21,0) width 11: "\x{9}"
-        LayoutBR {BR} at (32,15) size 0x0
-        LayoutBR {BR} at (0,20) size 0x19
-caret: position 0 of child 3 {BR} of child 2 {DIV} of body
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/inserting/caret-position-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/inserting/caret-position-expected.txt
deleted file mode 100644
index ccf64e2..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/inserting/caret-position-expected.txt
+++ /dev/null
@@ -1,26 +0,0 @@
-
-Right aligned div. 
-x
-
-RTL div. 
-a
-
-0px right padding RTL textarea. 
- 
-LTR div and textarea. 
-a
-
- 
-NO WRAPPING 
-No wrapping right aligned div. 
-
-No wrapping RTL div. 
-a
-
-No wrapping 0px right padding RTL textarea. 
- 
-No wrapping LTR div and textarea. 
-a
-
- 
-
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/inserting/line-break-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/inserting/line-break-expected.txt
deleted file mode 100644
index 01a0a36..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/inserting/line-break-expected.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x600
-  LayoutBlockFlow {HTML} at (0,0) size 800x600
-    LayoutBlockFlow {BODY} at (8,8) size 784x584
-      LayoutBlockFlow {P} at (0,0) size 784x40
-        LayoutText {#text} at (0,0) size 753x39
-          text run at (0,0) width 282: "This testcase used to break InsertLineBreak. "
-          text run at (282,0) width 471: "The editable region below should have two empty paragraphs in it, and the"
-          text run at (0,20) width 186: "caret should be in the second."
-      LayoutBlockFlow {DIV} at (0,56) size 784x40
-        LayoutBlockFlow {DIV} at (0,0) size 784x40
-          LayoutBR {BR} at (0,0) size 0x19
-          LayoutBR {BR} at (0,20) size 0x19
-caret: position 0 of child 2 {BR} of child 0 {DIV} of child 2 {DIV} of body
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/inserting/paragraph-separator-in-table-1-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/inserting/paragraph-separator-in-table-1-expected.txt
deleted file mode 100644
index 6295f7c..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/inserting/paragraph-separator-in-table-1-expected.txt
+++ /dev/null
@@ -1,27 +0,0 @@
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x600
-  LayoutBlockFlow {HTML} at (0,0) size 800x600
-    LayoutBlockFlow {BODY} at (8,8) size 784x584
-      LayoutBlockFlow {P} at (0,0) size 784x20
-        LayoutText {#text} at (0,0) size 612x19
-          text run at (0,0) width 337: "This tests InsertParagraphSeparator inside table cells. "
-          text run at (337,0) width 275: "'Cell' and 'Two' should be on separate lines."
-      LayoutBlockFlow {DIV} at (0,36) size 784x50
-        LayoutTable {TABLE} at (0,0) size 103x50 [border: (1px outset #808080)]
-          LayoutTableSection {TBODY} at (1,1) size 101x48
-            LayoutTableRow {TR} at (0,2) size 101x44
-              LayoutTableCell {TD} at (2,12) size 61x24 [border: (1px inset #808080)] [r=0 c=0 rs=1 cs=1]
-                LayoutText {#text} at (2,2) size 57x19
-                  text run at (2,2) width 57: "Cell One"
-              LayoutTableCell {TD} at (65,2) size 34x44 [border: (1px inset #808080)] [r=0 c=1 rs=1 cs=1]
-                LayoutText {#text} at (2,2) size 30x19
-                  text run at (2,2) width 30: "Cell "
-                LayoutBR {BR} at (32,17) size 0x0
-                LayoutInline {SPAN} at (0,0) size 29x19
-                  LayoutText {#text} at (2,22) size 29x19
-                    text run at (2,22) width 29: "Two"
-caret: position 0 of child 0 {#text} of child 2 {SPAN} of child 1 {TD} of child 0 {TR} of child 0 {TBODY} of child 0 {TABLE} of child 2 {DIV} of body
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/inserting/paragraph-separator-in-table-2-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/inserting/paragraph-separator-in-table-2-expected.txt
deleted file mode 100644
index 2a9545d..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/inserting/paragraph-separator-in-table-2-expected.txt
+++ /dev/null
@@ -1,26 +0,0 @@
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x600
-  LayoutBlockFlow {HTML} at (0,0) size 800x600
-    LayoutBlockFlow {BODY} at (8,8) size 784x584
-      LayoutBlockFlow {P} at (0,0) size 784x40
-        LayoutText {#text} at (0,0) size 768x39
-          text run at (0,0) width 337: "This tests InsertParagraphSeparator inside table cells. "
-          text run at (337,0) width 431: "The first cell should contain 'Cell' and a newline, and the second cell"
-          text run at (0,20) width 135: "should contain 'Two'."
-      LayoutBlockFlow {DIV} at (0,56) size 784x50
-        LayoutTable {TABLE} at (0,0) size 79x50 [border: (1px outset #808080)]
-          LayoutTableSection {TBODY} at (1,1) size 77x48
-            LayoutTableRow {TR} at (0,2) size 77x44
-              LayoutTableCell {TD} at (2,2) size 34x44 [border: (1px inset #808080)] [r=0 c=0 rs=1 cs=1]
-                LayoutText {#text} at (2,2) size 30x19
-                  text run at (2,2) width 30: "Cell "
-                LayoutBR {BR} at (32,17) size 0x0
-                LayoutBR {BR} at (2,22) size 0x19
-              LayoutTableCell {TD} at (38,12) size 37x24 [border: (1px inset #808080)] [r=0 c=1 rs=1 cs=1]
-                LayoutText {#text} at (2,2) size 33x19
-                  text run at (2,2) width 33: " Two"
-caret: position 0 of child 2 {BR} of child 0 {TD} of child 0 {TR} of child 0 {TBODY} of child 0 {TABLE} of child 2 {DIV} of body
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/pasteboard/4631972-expected.png b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/pasteboard/4631972-expected.png
deleted file mode 100644
index 9c6f9fa..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/pasteboard/4631972-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/pasteboard/4806874-expected.png b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/pasteboard/4806874-expected.png
deleted file mode 100644
index 051ca213..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/pasteboard/4806874-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/pasteboard/4806874-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/pasteboard/4806874-expected.txt
deleted file mode 100644
index 969ad06..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/pasteboard/4806874-expected.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x600
-  LayoutBlockFlow {HTML} at (0,0) size 800x600
-    LayoutBlockFlow {BODY} at (8,8) size 784x584
-      LayoutBlockFlow {P} at (0,0) size 784x20
-        LayoutText {#text} at (0,0) size 543x19
-          text run at (0,0) width 245: "This tests for an infinite loop on Paste. "
-          text run at (245,0) width 298: "You should see 'Hello: ' and then an input field."
-      LayoutBlockFlow {DIV} at (0,36) size 784x22
-        LayoutText {#text} at (0,1) size 35x19
-          text run at (0,1) width 35: "Hello"
-        LayoutTextControl {INPUT} at (35,0) size 181x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
-        LayoutText {#text} at (216,1) size 4x19
-          text run at (216,1) width 4: ":"
-layer at (45,47) size 177x16
-  LayoutBlockFlow {DIV} at (2,3) size 177x16
-caret: position 1 of child 1 {INPUT} of child 2 {DIV} of body
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/pasteboard/4947130-expected.png b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/pasteboard/4947130-expected.png
deleted file mode 100644
index 1984af1..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/pasteboard/4947130-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/pasteboard/4947130-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/pasteboard/4947130-expected.txt
deleted file mode 100644
index b05d98702..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/pasteboard/4947130-expected.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x600
-  LayoutBlockFlow {HTML} at (0,0) size 800x600
-    LayoutBlockFlow {BODY} at (8,8) size 784x584
-      LayoutBlockFlow {P} at (0,0) size 784x40
-        LayoutText {#text} at (0,0) size 778x39
-          text run at (0,0) width 491: "This tests to see if dragging an image is a move drag by default (it should be). "
-          text run at (491,0) width 287: "You should only see one picture and it should"
-          text run at (0,20) width 200: "be somewhere near the middle. "
-        LayoutInline {B} at (0,0) size 492x19
-          LayoutText {#text} at (200,20) size 492x19
-            text run at (200,20) width 492: "This demonstrates a bug, the ghost of the drag caret is left after the drop."
-      LayoutBlockFlow {DIV} at (0,56) size 784x108
-        LayoutText {#text} at (0,88) size 48x19
-          text run at (0,88) width 48: "xxxxxx"
-        LayoutImage {IMG} at (48,0) size 76x103
-        LayoutText {#text} at (124,88) size 32x19
-          text run at (124,88) width 32: "xxxx"
-selection start: position 0 of child 1 {IMG} of child 2 {DIV} of body
-selection end:   position 1 of child 1 {IMG} of child 2 {DIV} of body
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/pasteboard/5134759-expected.png b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/pasteboard/5134759-expected.png
deleted file mode 100644
index dd7aa45f..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/pasteboard/5134759-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/pasteboard/5601583-1-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/pasteboard/5601583-1-expected.txt
deleted file mode 100644
index 85e9c0a..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/pasteboard/5601583-1-expected.txt
+++ /dev/null
@@ -1,26 +0,0 @@
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x600
-  LayoutBlockFlow {HTML} at (0,0) size 800x600
-    LayoutBlockFlow {BODY} at (8,8) size 784x584
-      LayoutBlockFlow {P} at (0,0) size 784x40
-        LayoutText {#text} at (0,0) size 719x39
-          text run at (0,0) width 407: "This tests for a bug where a copied link wouldn't paste as a link. "
-          text run at (407,0) width 312: "Both editable regions below should contain a link"
-          text run at (0,20) width 104: "\"Hello\\nWorld\"."
-      LayoutBlockFlow {DIV} at (0,56) size 784x40
-        LayoutInline {A} at (0,0) size 39x39 [color=#0000EE]
-          LayoutText {#text} at (0,0) size 35x19
-            text run at (0,0) width 35: "Hello"
-          LayoutBR {BR} at (35,15) size 0x0
-          LayoutText {#text} at (0,20) size 39x19
-            text run at (0,20) width 39: "World"
-        LayoutText {#text} at (0,0) size 0x0
-      LayoutBlockFlow {DIV} at (0,96) size 784x40
-        LayoutInline {A} at (0,0) size 39x39 [color=#0000EE]
-          LayoutText {#text} at (0,0) size 35x19
-            text run at (0,0) width 35: "Hello"
-          LayoutBR {BR} at (35,15) size 0x0
-          LayoutText {#text} at (0,20) size 39x19
-            text run at (0,20) width 39: "World"
-caret: position 5 of child 2 {#text} of child 0 {A} of child 4 {DIV} of body
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/pasteboard/5761530-1-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/pasteboard/5761530-1-expected.txt
deleted file mode 100644
index 371a9906..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/pasteboard/5761530-1-expected.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-This tests to see that tabs are put into tab spans when they are copied individually. The pasted tab should be inside of a tab span, not a style span. To run the test manually, paste and then inspect the editable region, and ensure that there is a tab span at the beginning of the editable div.
-
-<span style="white-space: pre;">	</span><span style="white-space:pre;">	</span>xxx
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/pasteboard/bad-placeholder-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/pasteboard/bad-placeholder-expected.txt
deleted file mode 100644
index af643eb..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/pasteboard/bad-placeholder-expected.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x600
-  LayoutBlockFlow {HTML} at (0,0) size 800x600
-    LayoutBlockFlow {BODY} at (8,8) size 784x584
-      LayoutBlockFlow {P} at (0,0) size 784x40
-        LayoutText {#text} at (0,0) size 750x39
-          text run at (0,0) width 750: "This is a test to make sure that a placeholder, even one without our special class on it, is removed only when it has been"
-          text run at (0,20) width 405: "collapsed away or when it has been displaced by pasted content."
-      LayoutBlockFlow {P} at (0,56) size 784x40
-        LayoutText {#text} at (0,0) size 763x39
-          text run at (0,0) width 694: "Placeholders with our special class can, through bugs in deletion, be inserted into the document unnecessarily. "
-          text run at (694,0) width 61: "Once that"
-          text run at (0,20) width 763: "happens, we don't want to make matters worse by removing them if they are acting as line breaks instead of placeholders."
-      LayoutBlockFlow {DIV} at (0,112) size 784x40
-        LayoutText {#text} at (0,0) size 99x19
-          text run at (0,0) width 99: "First paragraph."
-        LayoutBR {BR} at (99,15) size 0x0
-        LayoutText {#text} at (0,20) size 118x19
-          text run at (0,20) width 118: "Second paragraph."
-caret: position 16 of child 0 {#text} of child 4 {DIV} of body
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/pasteboard/drop-inputtext-acquires-style-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/pasteboard/drop-inputtext-acquires-style-expected.txt
deleted file mode 100644
index 5022ddf..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/pasteboard/drop-inputtext-acquires-style-expected.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-This test checks that the plain text dropped into a styled text region will acquire the appropriate style.
-To run this test manually, drag the text in the input element below into the bold text region. The dropped text should be bold. Click the verify button to check.
-
- 
- 
-Drag the text from the above iDrag this textnput element into this bold text
-<b contenteditable="true" id="destination">Drag the text from the above iDrag this textnput element into this bold text</b>
-SUCCESS
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/pasteboard/input-field-1-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/pasteboard/input-field-1-expected.txt
deleted file mode 100644
index ccc0a915..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/pasteboard/input-field-1-expected.txt
+++ /dev/null
@@ -1,26 +0,0 @@
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x600
-  LayoutBlockFlow {HTML} at (0,0) size 800x600
-    LayoutBlockFlow {BODY} at (8,8) size 784x576
-      LayoutBlockFlow {P} at (0,0) size 784x20
-        LayoutText {#text} at (0,0) size 235x19
-          text run at (0,0) width 235: "This tests Copy/Paste of a input field."
-      LayoutBlockFlow {DIV} at (0,36) size 784x22
-        LayoutTextControl {INPUT} at (0,0) size 181x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
-        LayoutTextControl {INPUT} at (181,0) size 181x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
-      LayoutBlockFlow {UL} at (0,74) size 784x20
-        LayoutListItem {LI} at (40,0) size 744x20
-          LayoutListMarker (anonymous) at (-18,0) size 7x19: bullet
-          LayoutText {#text} at (0,0) size 43x19
-            text run at (0,0) width 43: "Passed"
-layer at (10,47) size 177x16
-  LayoutBlockFlow {DIV} at (2,3) size 177x16
-layer at (191,47) size 177x16
-  LayoutBlockFlow {DIV} at (2,3) size 177x16
-caret: position 1 of child 1 {INPUT} of child 2 {DIV} of body
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/pasteboard/paste-line-endings-001-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/pasteboard/paste-line-endings-001-expected.txt
deleted file mode 100644
index 8bdbc6e..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/pasteboard/paste-line-endings-001-expected.txt
+++ /dev/null
@@ -1,45 +0,0 @@
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x600
-  LayoutBlockFlow {HTML} at (0,0) size 800x600
-    LayoutBlockFlow {BODY} at (8,8) size 784x584
-      LayoutBlockFlow {DIV} at (0,0) size 784x210 [border: (2px solid #0000FF)]
-        LayoutBlockFlow {DIV} at (14,14) size 756x83
-          LayoutText {#text} at (0,0) size 65x26
-            text run at (0,0) width 65: "Tests: "
-          LayoutBR {BR} at (0,0) size 0x0
-          LayoutText {#text} at (0,27) size 162x27
-            text run at (0,27) width 162: "Fix for this bug: "
-          LayoutInline {A} at (0,0) size 259x27 [color=#0000EE]
-            LayoutText {#text} at (162,27) size 259x27
-              text run at (162,27) width 259: "<rdar://problem/4045511>"
-          LayoutText {#text} at (421,27) size 704x55
-            text run at (421,27) width 283: " Copying and pasting end-of-"
-            text run at (0,55) width 533: "paragraph selection puts insertion point in wrong place"
-        LayoutBlockFlow {DIV} at (14,113) size 756x83
-          LayoutText {#text} at (0,0) size 189x26
-            text run at (0,0) width 189: "Expected Results: "
-          LayoutBR {BR} at (189,21) size 0x0
-          LayoutText {#text} at (0,27) size 715x55
-            text run at (0,27) width 706: "Should see two lines of text below, self-documenting themselves as \"line"
-            text run at (0,55) width 715: "one\" and \"line two\". The insertion point must be at the start of \"line two\"."
-      LayoutBlockFlow {DIV} at (0,234) size 784x60
-        LayoutBlockFlow {DIV} at (0,0) size 784x60 [border: (2px solid #FF0000)]
-          LayoutText {#text} at (2,2) size 78x27
-            text run at (2,2) width 78: "line one"
-          LayoutBR {BR} at (80,23) size 0x0
-          LayoutText {#text} at (2,30) size 79x27
-            text run at (2,30) width 79: "line two"
-caret: position 0 of child 2 {#text} of child 1 {DIV} of child 3 {DIV} of body
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/pasteboard/paste-line-endings-003-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/pasteboard/paste-line-endings-003-expected.txt
deleted file mode 100644
index d1888d5..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/pasteboard/paste-line-endings-003-expected.txt
+++ /dev/null
@@ -1,46 +0,0 @@
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x600
-  LayoutBlockFlow {HTML} at (0,0) size 800x600
-    LayoutBlockFlow {BODY} at (8,8) size 784x584
-      LayoutBlockFlow {DIV} at (0,0) size 784x210 [border: (2px solid #0000FF)]
-        LayoutBlockFlow {DIV} at (14,14) size 756x83
-          LayoutText {#text} at (0,0) size 65x26
-            text run at (0,0) width 65: "Tests: "
-          LayoutBR {BR} at (0,0) size 0x0
-          LayoutText {#text} at (0,27) size 162x27
-            text run at (0,27) width 162: "Fix for this bug: "
-          LayoutInline {A} at (0,0) size 259x27 [color=#0000EE]
-            LayoutText {#text} at (162,27) size 259x27
-              text run at (162,27) width 259: "<rdar://problem/4045511>"
-          LayoutText {#text} at (421,27) size 704x55
-            text run at (421,27) width 283: " Copying and pasting end-of-"
-            text run at (0,55) width 533: "paragraph selection puts insertion point in wrong place"
-        LayoutBlockFlow {DIV} at (14,113) size 756x83
-          LayoutText {#text} at (0,0) size 189x26
-            text run at (0,0) width 189: "Expected Results: "
-          LayoutBR {BR} at (189,21) size 0x0
-          LayoutText {#text} at (0,27) size 715x55
-            text run at (0,27) width 706: "Should see two lines of text below, self-documenting themselves as \"line"
-            text run at (0,55) width 715: "one\" and \"line two\". The insertion point must be at the start of \"line two\"."
-      LayoutBlockFlow {DIV} at (0,234) size 784x60
-        LayoutBlockFlow {DIV} at (0,0) size 784x60 [border: (2px solid #FF0000)]
-          LayoutBlockFlow {DIV} at (2,2) size 780x28
-            LayoutText {#text} at (0,0) size 78x27
-              text run at (0,0) width 78: "line one"
-          LayoutBlockFlow {DIV} at (2,30) size 780x28
-            LayoutText {#text} at (0,0) size 79x27
-              text run at (0,0) width 79: "line two"
-caret: position 0 of child 0 {#text} of child 2 {DIV} of child 1 {DIV} of child 3 {DIV} of body
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/pasteboard/paste-pre-001-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/pasteboard/paste-pre-001-expected.txt
deleted file mode 100644
index 334b868..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/pasteboard/paste-pre-001-expected.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-Tests: 
-Copying and pasting content inside of a PRE tag. This test was created after fixing 3918056.
-Expected Results: 
-The PRE tag and the formatting of the text inside of the PRE should be maintained. Should see this content in the red box below:
-foo
-bar
-foo
-bar
-execCutCommand: <div id="test" class="editing"> <pre><br></pre> </div>
-execPasteCommand: <div id="test" class="editing"> <pre>foo bar</pre> </div>
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/pasteboard/styled-element-markup-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/pasteboard/styled-element-markup-expected.txt
deleted file mode 100644
index d6b8f8b3..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/pasteboard/styled-element-markup-expected.txt
+++ /dev/null
@@ -1,26 +0,0 @@
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x600
-  LayoutBlockFlow {HTML} at (0,0) size 800x600
-    LayoutBlockFlow {BODY} at (8,8) size 784x584
-      LayoutBlockFlow {P} at (0,0) size 784x40
-        LayoutText {#text} at (0,0) size 772x39
-          text run at (0,0) width 334: "This tests copy/paste of styled elements, like images. "
-          text run at (334,0) width 438: "The image in the region below should be centered after its copied and"
-          text run at (0,20) width 44: "pasted."
-      LayoutBlockFlow {P} at (0,56) size 784x40
-        LayoutInline {B} at (0,0) size 778x39
-          LayoutText {#text} at (0,0) size 778x39
-            text run at (0,0) width 778: "This demonstrates a bug: createMarkup puts the text-align property on the image, which doesn't center it, so its left"
-            text run at (0,20) width 113: "aligned on paste."
-      LayoutBlockFlow {DIV} at (0,112) size 784x103
-        LayoutBlockFlow {CENTER} at (0,0) size 784x103
-          LayoutImage {IMG} at (354,0) size 76x103
-      LayoutBlockFlow {DIV} at (0,215) size 784x103
-        LayoutImage {IMG} at (0,0) size 76x103
-caret: position 1 of child 0 {IMG} of child 6 {DIV} of body
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/5099303-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/5099303-expected.txt
deleted file mode 100644
index d6b9c9d..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/5099303-expected.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x600
-  LayoutBlockFlow {HTML} at (0,0) size 800x600
-    LayoutBlockFlow {BODY} at (8,8) size 784x584
-      LayoutBlockFlow {P} at (0,0) size 784x20
-        LayoutText {#text} at (0,0) size 758x19
-          text run at (0,0) width 439: "This tests extending an editable selection across non-editable content. "
-          text run at (439,0) width 319: "Everything in the region below should be selected."
-      LayoutBlockFlow {DIV} at (0,36) size 784x20
-        LayoutText {#text} at (0,0) size 8x19
-          text run at (0,0) width 8: "x"
-        LayoutInline {SPAN} at (0,0) size 16x19
-          LayoutText {#text} at (8,0) size 16x19
-            text run at (8,0) width 16: "xx"
-        LayoutText {#text} at (24,0) size 8x19
-          text run at (24,0) width 8: "x"
-selection start: position 0 of child 0 {#text} of child 2 {DIV} of body
-selection end:   position 1 of child 2 {#text} of child 2 {DIV} of body
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/6476-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/6476-expected.txt
deleted file mode 100644
index e9c9b36..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/6476-expected.txt
+++ /dev/null
@@ -1,36 +0,0 @@
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x600
-  LayoutBlockFlow {HTML} at (0,0) size 800x600
-    LayoutBlockFlow {BODY} at (8,8) size 784x576
-      LayoutBlockFlow {DIV} at (0,0) size 250x40
-        LayoutText {#text} at (0,0) size 202x19
-          text run at (0,0) width 202: "Try to create a caret after this lin"
-        LayoutInline {SPAN} at (0,0) size 7x19
-          LayoutText {#text} at (202,0) size 7x19
-            text run at (202,0) width 7: "e"
-        LayoutText {#text} at (209,0) size 4x19
-          text run at (209,0) width 4: " "
-        LayoutInline {SPAN} at (0,0) size 0x19
-        LayoutText {#text} at (0,20) size 181x19
-          text run at (0,20) width 181: "thisshouldbeonthesecondline"
-      LayoutBlockFlow {P} at (0,66) size 784x40
-        LayoutText {#text} at (0,0) size 131x19
-          text run at (0,0) width 131: "This is a testcase for "
-        LayoutInline {A} at (0,0) size 343x19 [color=#0000EE]
-          LayoutText {#text} at (131,0) size 343x19
-            text run at (131,0) width 343: "http://bugzilla.opendarwin.org/show_bug.cgi?id=6476"
-        LayoutText {#text} at (474,0) size 779x39
-          text run at (474,0) width 8: ". "
-          text run at (482,0) width 297: "Creating an upstream caret with the mouse was"
-          text run at (0,20) width 591: "impossible if the upstream and downstream carets were separated only by unrendered content."
-      LayoutBlockFlow {P} at (0,122) size 784x40
-        LayoutText {#text} at (0,0) size 764x39
-          text run at (0,0) width 318: "This test uses the eventSender to do mouse clicks. "
-          text run at (318,0) width 446: "To run it manually, click in the space after all the text on the first line in"
-          text run at (0,20) width 166: "the above editable region. "
-          text run at (166,20) width 384: "Clicking should create a caret after the first space after the 'e'."
-layer at (8,56) size 784x2 clip at (0,0) size 0x0
-  LayoutBlockFlow {HR} at (0,48) size 784x2 [border: (1px inset #EEEEEE)]
-caret: position 1 of child 2 {#text} of child 1 {DIV} of body
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/caret-and-focus-ring-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/caret-and-focus-ring-expected.txt
deleted file mode 100644
index 28053ca..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/caret-and-focus-ring-expected.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x600
-  LayoutBlockFlow {HTML} at (0,0) size 800x600
-    LayoutBlockFlow {BODY} at (8,8) size 784x584
-      LayoutBlockFlow {P} at (0,0) size 784x40
-        LayoutText {#text} at (0,0) size 772x39
-          text run at (0,0) width 772: "This tests that the caret and the focus halo are not displayed when the WebView is not focused (using the pixel output from"
-          text run at (0,20) width 123: "DumpRenderTree)."
-      LayoutBlockFlow {DIV} at (0,56) size 784x20
-        LayoutText {#text} at (0,0) size 65x19
-          text run at (0,0) width 65: "This is an "
-        LayoutInline {SPAN} at (0,0) size 49x19
-          LayoutText {#text} at (65,0) size 49x19
-            text run at (65,0) width 49: "editable"
-        LayoutText {#text} at (114,0) size 404x19
-          text run at (114,0) width 404: " region. No caret or focus ring should appear in the pixel results."
-caret: position 11 of child 0 {#text} of child 3 {DIV} of body
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/caret-ltr-2-expected.png b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/caret-ltr-2-expected.png
deleted file mode 100644
index 9980886..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/caret-ltr-2-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/caret-ltr-2-left-expected.png b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/caret-ltr-2-left-expected.png
deleted file mode 100644
index 2677329..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/caret-ltr-2-left-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/caret-rtl-2-expected.png b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/caret-rtl-2-expected.png
deleted file mode 100644
index 101db49d..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/caret-rtl-2-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/caret-rtl-2-left-expected.png b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/caret-rtl-2-left-expected.png
deleted file mode 100644
index 231bc4c..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/caret-rtl-2-left-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/caret-rtl-expected.png b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/caret-rtl-expected.png
deleted file mode 100644
index 606f3b2..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/caret-rtl-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/caret-rtl-right-expected.png b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/caret-rtl-right-expected.png
deleted file mode 100644
index 077f221..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/caret-rtl-right-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/click-start-of-line-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/click-start-of-line-expected.txt
deleted file mode 100644
index 373c8f1..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/click-start-of-line-expected.txt
+++ /dev/null
@@ -1,31 +0,0 @@
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x600
-  LayoutBlockFlow {HTML} at (0,0) size 800x600
-    LayoutBlockFlow {BODY} at (8,8) size 784x584
-      LayoutBlockFlow {P} at (0,0) size 784x40
-        LayoutText {#text} at (0,0) size 104x19
-          text run at (0,0) width 104: "This is a test for "
-        LayoutInline {I} at (0,0) size 775x39
-          LayoutInline {A} at (0,0) size 347x19 [color=#0000EE]
-            LayoutText {#text} at (104,0) size 347x19
-              text run at (104,0) width 347: "http://bugzilla.opendarwin.org/show_bug.cgi?id=9978"
-          LayoutText {#text} at (451,0) size 775x39
-            text run at (451,0) width 4: " "
-            text run at (455,0) width 320: "REGRESSION (r12949-r12988): Clicking the first"
-            text run at (0,20) width 382: "letter on a line places the caret at the end of the previous line"
-        LayoutText {#text} at (382,20) size 4x19
-          text run at (382,20) width 4: "."
-      LayoutBlockFlow {P} at (0,56) size 784x20
-        LayoutText {#text} at (0,0) size 728x19
-          text run at (0,0) width 549: "To test interactively, click the left side of the W on the second line. The caret should be "
-          text run at (549,0) width 179: "move to the start of that line."
-      LayoutBlockFlow {DIV} at (0,102) size 250x114
-        LayoutText {#text} at (0,1) size 232x112
-          text run at (0,1) width 152: "Alice in"
-          text run at (152,1) width 12: " "
-          text run at (0,58) width 232: "Wonderland"
-layer at (8,100) size 784x2 clip at (0,0) size 0x0
-  LayoutBlockFlow {HR} at (0,92) size 784x2 [border: (1px inset #EEEEEE)]
-caret: position 10 of child 0 {#text} of child 7 {DIV} of body
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/contains-boundaries-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/contains-boundaries-expected.txt
deleted file mode 100644
index 9bbceda..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/contains-boundaries-expected.txt
+++ /dev/null
@@ -1,29 +0,0 @@
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x600
-  LayoutBlockFlow {HTML} at (0,0) size 800x600
-    LayoutBlockFlow {BODY} at (8,8) size 784x584
-      LayoutBlockFlow {P} at (0,0) size 784x40
-        LayoutText {#text} at (0,0) size 52x19
-          text run at (0,0) width 52: "Test for "
-        LayoutInline {I} at (0,0) size 742x39
-          LayoutInline {A} at (0,0) size 300x19 [color=#0000EE]
-            LayoutText {#text} at (52,0) size 300x19
-              text run at (52,0) width 300: "http://bugs.webkit.org/show_bug.cgi?id=14347"
-          LayoutText {#text} at (352,0) size 742x39
-            text run at (352,0) width 4: " "
-            text run at (356,0) width 386: "REGRESSION (r21291): Initiating a drag near the edge of a"
-            text run at (0,20) width 127: "selection deselects it"
-        LayoutText {#text} at (127,20) size 4x19
-          text run at (127,20) width 4: "."
-      LayoutBlockFlow {P} at (0,56) size 784x20
-        LayoutText {#text} at (0,0) size 326x19
-          text run at (0,0) width 326: "Drag the selected letter W into the yellow rectangle."
-      LayoutBlockFlow {DIV} at (0,92) size 784x84
-        LayoutText {#text} at (0,1) size 68x81
-          text run at (0,1) width 68: "W"
-      LayoutBlockFlow {DIV} at (0,176) size 784x84 [bgcolor=#FFFFCC]
-        LayoutText {#text} at (0,1) size 68x81
-          text run at (0,1) width 68: "W"
-selection start: position 0 of child 0 {#text} of child 6 {DIV} of body
-selection end:   position 1 of child 0 {#text} of child 6 {DIV} of body
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/editable-div-clear-on-keydown-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/editable-div-clear-on-keydown-expected.txt
deleted file mode 100644
index 16e8c02d..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/editable-div-clear-on-keydown-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-Tests behavior of code that clears the text from a focused editable div. 
-
-To run manually press any key to clear the text in the div.
-The key that was typed should replace the text in the editable div and div should still have the focus and a blinking caret.
-
-a
-PASS
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/leave-requested-block-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/leave-requested-block-expected.txt
deleted file mode 100644
index e652e840..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/leave-requested-block-expected.txt
+++ /dev/null
@@ -1,30 +0,0 @@
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x600
-  LayoutBlockFlow {HTML} at (0,0) size 800x600
-    LayoutBlockFlow {BODY} at (8,8) size 784x576
-      LayoutBlockFlow {P} at (0,0) size 784x20
-        LayoutText {#text} at (0,0) size 126x19
-          text run at (0,0) width 126: "Test case for fix for "
-        LayoutInline {A} at (0,0) size 343x19 [color=#0000EE]
-          LayoutText {#text} at (126,0) size 343x19
-            text run at (126,0) width 343: "http://bugzilla.opendarwin.org/show_bug.cgi?id=5354"
-        LayoutText {#text} at (469,0) size 4x19
-          text run at (469,0) width 4: "."
-      LayoutBlockFlow {P} at (0,36) size 784x40
-        LayoutText {#text} at (0,0) size 771x39
-          text run at (0,0) width 771: "Changes were made to VisiblePosition's so that init() will not leave the block containing the requested position unless there"
-          text run at (0,20) width 230: "are no VisiblePositions in that block."
-      LayoutBlockFlow {P} at (0,92) size 784x0
-      LayoutBlockFlow {DIV} at (0,92) size 784x56 [border: (2px solid #AAAAFF)]
-        LayoutBlockFlow {P} at (2,18) size 780x20
-          LayoutText {#text} at (0,0) size 682x19
-            text run at (0,0) width 682: "Select All while inside this editable block should create a selection that does not extend outside of this block."
-layer at (8,172) size 784x20
-  LayoutBlockFlow {P} at (0,164) size 784x20
-    LayoutText {#text} at (0,0) size 424x19
-      text run at (0,0) width 424: "This is a paragraph outside the editable block with overflow:hidden"
-selection start: position 0 of child 0 {#text} of child 1 {P} of child 5 {DIV} of body
-selection end:   position 113 of child 0 {#text} of child 1 {P} of child 5 {DIV} of body
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/move-by-line-002-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/move-by-line-002-expected.txt
deleted file mode 100644
index 3de5f4c7..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/move-by-line-002-expected.txt
+++ /dev/null
@@ -1,31 +0,0 @@
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x600
-  LayoutBlockFlow {HTML} at (0,0) size 800x600
-    LayoutBlockFlow {BODY} at (8,8) size 784x584
-      LayoutBlockFlow {DIV} at (0,0) size 784x216 [border: (2px solid #FF0000)]
-        LayoutBlockFlow {DIV} at (14,38) size 756x140
-          LayoutBlockFlow {OL} at (0,0) size 756x140
-            LayoutListItem {LI} at (40,0) size 716x28
-              LayoutListMarker (anonymous) at (-24,0) size 24x27: "1"
-              LayoutText {#text} at (0,0) size 35x27
-                text run at (0,0) width 35: "one"
-            LayoutListItem {LI} at (40,28) size 716x28
-              LayoutListMarker (anonymous) at (-24,0) size 24x27: "2"
-              LayoutText {#text} at (0,0) size 36x27
-                text run at (0,0) width 36: "two"
-            LayoutListItem {LI} at (40,56) size 716x28
-              LayoutListMarker (anonymous) at (-24,0) size 24x27: "3"
-              LayoutBR {BR} at (0,0) size 0x27
-            LayoutListItem {LI} at (40,84) size 716x28
-              LayoutListMarker (anonymous) at (-24,0) size 24x27: "4"
-              LayoutText {#text} at (0,0) size 40x27
-                text run at (0,0) width 40: "four"
-            LayoutListItem {LI} at (40,112) size 716x28
-              LayoutListMarker (anonymous) at (-24,0) size 24x27: "5"
-              LayoutText {#text} at (0,0) size 38x27
-                text run at (0,0) width 38: "five"
-caret: position 0 of child 0 {BR} of child 2 {LI} of child 1 {OL} of child 1 {DIV} of child 1 {DIV} of body
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/previous-line-position-expected.png b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/previous-line-position-expected.png
deleted file mode 100644
index d340897..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/previous-line-position-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/select-missing-image-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/select-missing-image-expected.txt
deleted file mode 100644
index a83e687..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/select-missing-image-expected.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x600
-  LayoutBlockFlow {HTML} at (0,0) size 800x600
-    LayoutBlockFlow {BODY} at (8,8) size 784x584
-      LayoutBlockFlow {DIV} at (0,0) size 784x128 [border: (2px solid #FF0000)]
-        LayoutBlockFlow {IMG} at (14,14) size 550x100
-        LayoutText {#text} at (0,0) size 0x0
-layer at (22,22) size 550x100 clip at (23,23) size 548x98
-  LayoutBlockFlow {SPAN} at (0,0) size 550x100 [border: (1px solid #C0C0C0)]
-    LayoutImage (floating) {IMG} at (2,2) size 16x16
-    LayoutInline {SPAN} at (0,0) size 480x27
-      LayoutText {#text} at (18,2) size 480x27
-        text run at (18,2) width 480: "Should see selection tint over this missing image."
-selection start: position 0 of child 1 {IMG} of child 1 {DIV} of body
-selection end:   position 1 of child 1 {IMG} of child 1 {DIV} of body
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/triple-click-in-pre-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/triple-click-in-pre-expected.txt
deleted file mode 100644
index fa64d264..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/selection/triple-click-in-pre-expected.txt
+++ /dev/null
@@ -1,27 +0,0 @@
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x600
-  LayoutBlockFlow {HTML} at (0,0) size 800x600
-    LayoutBlockFlow {BODY} at (8,8) size 784x584
-      LayoutBlockFlow {P} at (0,0) size 784x20
-        LayoutText {#text} at (0,0) size 677x19
-          text run at (0,0) width 379: "This tests modifying a selection created with a double click. "
-          text run at (379,0) width 298: "The new selection should use word granularity."
-      LayoutBlockFlow {P} at (0,36) size 784x40 [color=#008000]
-        LayoutText {#text} at (0,0) size 784x39
-          text run at (0,0) width 223: "This test does not run interactively. "
-          text run at (223,0) width 352: "It uses the testRunners eventSender to do mouse clicks. "
-          text run at (575,0) width 209: "To run it manually, triple click on"
-          text run at (0,20) width 432: "the second line in the box below (anywhere after \"whitespace:pre\"). "
-          text run at (432,20) width 273: "The triple click should select the entire line."
-      LayoutBlockFlow {DIV} at (0,92) size 784x60
-        LayoutBlockFlow {DIV} at (0,0) size 784x60 [border: (1px solid #0000FF)]
-          LayoutBlockFlow {PRE} at (1,14) size 782x32
-            LayoutText {#text} at (0,0) size 120x32
-              text run at (0,0) width 120: "this text is in"
-              text run at (120,0) width 0: " "
-              text run at (0,16) width 112: "whitespace:pre"
-selection start: position 16 of child 0 {#text} of child 1 {PRE} of child 1 {DIV} of child 5 {DIV} of body
-selection end:   position 30 of child 0 {#text} of child 1 {PRE} of child 1 {DIV} of child 5 {DIV} of body
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/undo/crash-redo-with-iframes-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/undo/crash-redo-with-iframes-expected.txt
deleted file mode 100644
index 742c47e..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/undo/crash-redo-with-iframes-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-PASS if Blink doesn't crash. 
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/undo/undo-deleteWord-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/undo/undo-deleteWord-expected.txt
deleted file mode 100644
index ca38bddb..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/undo/undo-deleteWord-expected.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-Tests: 
-Undo a delete word after a series of insertText does not wipe out the entire content.
-one two three four
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/undo/undo-smart-delete-reversed-selection-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/undo/undo-smart-delete-reversed-selection-expected.txt
deleted file mode 100644
index 0dc77db..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/undo/undo-smart-delete-reversed-selection-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-Tests: 
-Double-click the green "a" and drag backwards to select "bar baz" with word granularity. Delete, then undo the delete. The space that got smart deleted should now be selected and the anchor of the selection should be at the end of the selection.
-
-foo bar baz biz
-PASSED
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/unsupported-content/list-delete-001-expected.png b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/unsupported-content/list-delete-001-expected.png
deleted file mode 100644
index 4e8786fe..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/unsupported-content/list-delete-001-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/unsupported-content/list-delete-003-expected.png b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/unsupported-content/list-delete-003-expected.png
deleted file mode 100644
index 7144ac6..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/unsupported-content/list-delete-003-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/unsupported-content/list-type-after-expected.png b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/unsupported-content/list-type-after-expected.png
deleted file mode 100644
index 31bcc56..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/unsupported-content/list-type-after-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/unsupported-content/list-type-before-expected.png b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/unsupported-content/list-type-before-expected.png
deleted file mode 100644
index b9969c87..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/unsupported-content/list-type-before-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/unsupported-content/table-type-after-expected.png b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/unsupported-content/table-type-after-expected.png
deleted file mode 100644
index 211f93af..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/unsupported-content/table-type-after-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/unsupported-content/table-type-before-expected.png b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/unsupported-content/table-type-before-expected.png
deleted file mode 100644
index 81dd5b2..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/editing/unsupported-content/table-type-before-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/block/float/rubybase-children-moved-crash-2-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/block/float/rubybase-children-moved-crash-2-expected.txt
deleted file mode 100644
index 6c350ea5..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/block/float/rubybase-children-moved-crash-2-expected.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-crbug.com/683104: Passes if it does not crash.
-
-Text 
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/css/first-letter-capitalized-edit-select-crash-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/css/first-letter-capitalized-edit-select-crash-expected.txt
deleted file mode 100644
index 13df36d..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/css/first-letter-capitalized-edit-select-crash-expected.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-Pass If No Crash Or Assert In Debug
- 
-
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/dom/52776-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/dom/52776-expected.txt
deleted file mode 100644
index 93eb2d8..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/dom/52776-expected.txt
+++ /dev/null
@@ -1,272 +0,0 @@
-layer at (0,0) size 800x600 clip at (0,0) size 785x600 scrollHeight 1812
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 785x1812 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600
-  LayoutBlockFlow {HTML} at (0,0) size 785x1812
-    LayoutBlockFlow {BODY} at (8,16) size 769x1780
-      LayoutBlockFlow {DIV} at (0,0) size 769x864
-        LayoutBlockFlow {P} at (0,0) size 769x20
-          LayoutText {#text} at (683,0) size 86x19
-            text run at (683,0) width 86: "Right To Left"
-        LayoutBlockFlow {P} at (0,36) size 769x20
-          LayoutText {#text} at (444,0) size 325x19
-            text run at (444,0) width 4 RTL: "."
-            text run at (448,0) width 321: "Well-formed pair of unicode bidi control characters"
-        LayoutBlockFlow {DIV} at (0,72) size 769x20
-          LayoutText {#text} at (756,0) size 13x19
-            text run at (756,0) width 0 RTL: "\x{202C}"
-            text run at (756,0) width 5 RTL: "!"
-            text run at (761,0) width 8: "\x{202B}b"
-        LayoutBlockFlow {DIV} at (0,92) size 769x20
-          LayoutText {#text} at (756,0) size 13x19
-            text run at (756,0) width 0 RTL: "\x{202C}"
-            text run at (756,0) width 13: "\x{202A}b!"
-        LayoutBlockFlow {P} at (0,128) size 769x20
-          LayoutText {#text} at (509,0) size 260x19
-            text run at (509,0) width 4 RTL: "."
-            text run at (513,0) width 256: "Unpaired unicode bidi control characters"
-        LayoutBlockFlow {DIV} at (0,164) size 769x20
-          LayoutText {#text} at (756,0) size 13x19
-            text run at (756,0) width 5 RTL: "!"
-            text run at (761,0) width 8: "\x{202B}b"
-        LayoutBlockFlow {DIV} at (0,184) size 769x20
-          LayoutText {#text} at (756,0) size 13x19
-            text run at (756,0) width 13: "\x{202A}b!"
-        LayoutBlockFlow {P} at (0,220) size 769x20
-          LayoutText {#text} at (435,0) size 334x19
-            text run at (435,0) width 4 RTL: "."
-            text run at (439,0) width 330: "Empty content inside unicode bidi control characters"
-        LayoutBlockFlow {DIV} at (0,256) size 769x20
-          LayoutText {#text} at (769,0) size 0x19
-            text run at (769,0) width 0 RTL: "\x{202C}\x{202C}"
-        LayoutBlockFlow {DIV} at (0,276) size 769x20
-          LayoutText {#text} at (769,0) size 0x19
-            text run at (769,0) width 0: "\x{202A}"
-            text run at (769,0) width 0 RTL: "\x{202A}"
-        LayoutBlockFlow {DIV} at (0,296) size 769x20
-          LayoutText {#text} at (769,0) size 0x19
-            text run at (769,0) width 0 RTL: "\x{202B}"
-            text run at (769,0) width 0 RTL: "\x{202B}"
-        LayoutBlockFlow {P} at (0,332) size 769x20
-          LayoutText {#text} at (489,0) size 280x19
-            text run at (489,0) width 4 RTL: "."
-            text run at (493,0) width 276: "String inside unicode bidi control characters"
-        LayoutBlockFlow {DIV} at (0,368) size 769x20
-          LayoutText {#text} at (747,0) size 22x19
-            text run at (747,0) width 0 RTL: "\x{202C}"
-            text run at (747,0) width 22: "\x{202C}abc"
-        LayoutBlockFlow {DIV} at (0,388) size 769x20
-          LayoutText {#text} at (747,0) size 22x19
-            text run at (747,0) width 22: "\x{202A}abc"
-            text run at (769,0) width 0 RTL: "\x{202A}"
-        LayoutBlockFlow {DIV} at (0,408) size 769x20
-          LayoutText {#text} at (747,0) size 22x19
-            text run at (747,0) width 22: "\x{202B}abc"
-            text run at (769,0) width 0 RTL: "\x{202B}"
-        LayoutBlockFlow {P} at (0,444) size 769x20
-          LayoutText {#text} at (482,0) size 287x19
-            text run at (482,0) width 4 RTL: "."
-            text run at (486,0) width 283: "String around unicode bidi control characters"
-        LayoutBlockFlow {DIV} at (0,480) size 769x20
-          LayoutText {#text} at (727,0) size 42x19
-            text run at (727,0) width 0 RTL: "\x{202C}"
-            text run at (727,0) width 42: "def\x{202C}abc"
-        LayoutBlockFlow {DIV} at (0,500) size 769x20
-          LayoutText {#text} at (727,0) size 42x19
-            text run at (727,0) width 42: "\x{202C}abc\x{202C}def"
-        LayoutBlockFlow {DIV} at (0,520) size 769x20
-          LayoutText {#text} at (704,0) size 65x19
-            text run at (704,0) width 65: "xyz\x{202C}abc\x{202C}def"
-        LayoutBlockFlow {P} at (0,556) size 769x20
-          LayoutText {#text} at (524,0) size 245x19
-            text run at (524,0) width 4 RTL: "."
-            text run at (528,0) width 241: "Nested unicode bidi control characters"
-        LayoutBlockFlow {DIV} at (0,592) size 769x20
-          LayoutText {#text} at (756,0) size 13x19
-            text run at (756,0) width 0: "\x{202A}"
-            text run at (756,0) width 0 RTL: "\x{202C}"
-            text run at (756,0) width 13: "\x{202A}b!"
-        LayoutBlockFlow {DIV} at (0,612) size 769x20
-          LayoutText {#text} at (756,0) size 13x19
-            text run at (756,0) width 0 RTL: "\x{202C}"
-            text run at (756,0) width 5 RTL: "!"
-            text run at (761,0) width 8: "\x{202B}b"
-            text run at (769,0) width 0 RTL: "\x{202B}"
-        LayoutBlockFlow {DIV} at (0,632) size 769x20
-          LayoutText {#text} at (756,0) size 13x19
-            text run at (756,0) width 5 RTL: "!\x{202C}"
-            text run at (761,0) width 8: "\x{202C}\x{202C}b"
-        LayoutBlockFlow {DIV} at (0,652) size 769x20
-          LayoutText {#text} at (749,0) size 20x19
-            text run at (749,0) width 0 RTL: "\x{202C}"
-            text run at (749,0) width 8: "1"
-            text run at (757,0) width 5 RTL: "!"
-            text run at (762,0) width 7: "\x{202B}c"
-            text run at (769,0) width 0 RTL: "\x{202C}"
-        LayoutBlockFlow {P} at (0,688) size 769x20
-          LayoutText {#text} at (494,0) size 275x19
-            text run at (494,0) width 4 RTL: "."
-            text run at (498,0) width 271: "Start with different directionality characters"
-        LayoutBlockFlow {DIV} at (0,724) size 769x20
-          LayoutText {#text} at (745,0) size 24x19
-            text run at (745,0) width 0 RTL: "\x{202B}"
-            text run at (745,0) width 0 RTL: "\x{202C}"
-            text run at (745,0) width 8: "\x{202A}1"
-            text run at (753,0) width 0 RTL: "\x{202C}"
-            text run at (753,0) width 16: "12"
-        LayoutBlockFlow {DIV} at (0,744) size 769x20
-          LayoutText {#text} at (753,0) size 16x19
-            text run at (753,0) width 0 RTL: "\x{202A}"
-            text run at (753,0) width 0 RTL: "\x{202C}"
-            text run at (753,0) width 16: "12"
-        LayoutBlockFlow {DIV} at (0,764) size 769x20
-          LayoutText {#text} at (751,0) size 18x19
-            text run at (751,0) width 0 RTL: "\x{202A}"
-            text run at (751,0) width 0 RTL: "\x{202C}"
-            text run at (751,0) width 18: "\x{660}\x{661}"
-        LayoutBlockFlow {DIV} at (0,784) size 769x20
-          LayoutText {#text} at (749,0) size 20x19
-            text run at (749,0) width 0 RTL: "\x{202A}"
-            text run at (749,0) width 20 RTL: "\x{683}\x{684}\x{202C}"
-        LayoutBlockFlow {DIV} at (0,804) size 769x20
-          LayoutText {#text} at (759,0) size 10x19
-            text run at (759,0) width 0 RTL: "\x{202A}"
-            text run at (759,0) width 10 RTL: "\x{1}\x{202C}"
-        LayoutBlockFlow {DIV} at (0,824) size 769x20
-          LayoutText {#text} at (747,0) size 22x19
-            text run at (747,0) width 22: "abc\x{202C}"
-            text run at (769,0) width 0 RTL: "\x{202A}"
-        LayoutBlockFlow {DIV} at (0,844) size 769x20
-          LayoutText {#text} at (751,0) size 18x19
-            text run at (751,0) width 0 RTL: "\x{202A}"
-            text run at (751,0) width 18 RTL: "\x{5D0}\x{5D1}\x{202C}"
-      LayoutBlockFlow {DIV} at (0,880) size 769x864
-        LayoutBlockFlow {P} at (0,0) size 769x20
-          LayoutText {#text} at (0,0) size 86x19
-            text run at (0,0) width 86: "Left To Right"
-        LayoutBlockFlow {P} at (0,36) size 769x20
-          LayoutText {#text} at (0,0) size 325x19
-            text run at (0,0) width 325: "Well-formed pair of unicode bidi control characters."
-        LayoutBlockFlow {DIV} at (0,72) size 769x20
-          LayoutText {#text} at (0,0) size 13x19
-            text run at (0,0) width 5 RTL: "!"
-            text run at (5,0) width 8: "\x{202B}b"
-            text run at (13,0) width 0: "\x{202C}"
-        LayoutBlockFlow {DIV} at (0,92) size 769x20
-          LayoutText {#text} at (0,0) size 13x19
-            text run at (0,0) width 13: "\x{202A}b!"
-            text run at (13,0) width 0: "\x{202C}"
-        LayoutBlockFlow {P} at (0,128) size 769x20
-          LayoutText {#text} at (0,0) size 260x19
-            text run at (0,0) width 260: "Unpaired unicode bidi control characters."
-        LayoutBlockFlow {DIV} at (0,164) size 769x20
-          LayoutText {#text} at (0,0) size 13x19
-            text run at (0,0) width 13: "\x{202B}b!"
-        LayoutBlockFlow {DIV} at (0,184) size 769x20
-          LayoutText {#text} at (0,0) size 13x19
-            text run at (0,0) width 13: "\x{202A}b!"
-        LayoutBlockFlow {P} at (0,220) size 769x20
-          LayoutText {#text} at (0,0) size 334x19
-            text run at (0,0) width 334: "Empty content inside unicode bidi control characters."
-        LayoutBlockFlow {DIV} at (0,256) size 769x20
-          LayoutText {#text} at (0,0) size 0x19
-            text run at (0,0) width 0: "\x{202C}\x{202C}"
-        LayoutBlockFlow {DIV} at (0,276) size 769x20
-          LayoutText {#text} at (0,0) size 0x19
-            text run at (0,0) width 0: "\x{202A}"
-            text run at (0,0) width 0: "\x{202A}"
-        LayoutBlockFlow {DIV} at (0,296) size 769x20
-          LayoutText {#text} at (0,0) size 0x19
-            text run at (0,0) width 0: "\x{202B}"
-            text run at (0,0) width 0 RTL: "\x{202B}"
-        LayoutBlockFlow {P} at (0,332) size 769x20
-          LayoutText {#text} at (0,0) size 280x19
-            text run at (0,0) width 280: "String inside unicode bidi control characters."
-        LayoutBlockFlow {DIV} at (0,368) size 769x20
-          LayoutText {#text} at (0,0) size 22x19
-            text run at (0,0) width 22: "\x{202C}abc\x{202C}"
-        LayoutBlockFlow {DIV} at (0,388) size 769x20
-          LayoutText {#text} at (0,0) size 22x19
-            text run at (0,0) width 22: "\x{202A}abc"
-            text run at (22,0) width 0: "\x{202A}"
-        LayoutBlockFlow {DIV} at (0,408) size 769x20
-          LayoutText {#text} at (0,0) size 22x19
-            text run at (0,0) width 22: "\x{202B}abc"
-            text run at (22,0) width 0: "\x{202B}"
-        LayoutBlockFlow {P} at (0,444) size 769x20
-          LayoutText {#text} at (0,0) size 287x19
-            text run at (0,0) width 287: "String around unicode bidi control characters."
-        LayoutBlockFlow {DIV} at (0,480) size 769x20
-          LayoutText {#text} at (0,0) size 42x19
-            text run at (0,0) width 42: "def\x{202C}abc\x{202C}"
-        LayoutBlockFlow {DIV} at (0,500) size 769x20
-          LayoutText {#text} at (0,0) size 42x19
-            text run at (0,0) width 42: "\x{202C}abc\x{202C}def"
-        LayoutBlockFlow {DIV} at (0,520) size 769x20
-          LayoutText {#text} at (0,0) size 65x19
-            text run at (0,0) width 65: "xyz\x{202C}abc\x{202C}def"
-        LayoutBlockFlow {P} at (0,556) size 769x20
-          LayoutText {#text} at (0,0) size 245x19
-            text run at (0,0) width 245: "Nested unicode bidi control characters."
-        LayoutBlockFlow {DIV} at (0,592) size 769x20
-          LayoutText {#text} at (0,0) size 13x19
-            text run at (0,0) width 0: "\x{202A}"
-            text run at (0,0) width 13: "\x{202A}b!"
-            text run at (13,0) width 0: "\x{202C}"
-        LayoutBlockFlow {DIV} at (0,612) size 769x20
-          LayoutText {#text} at (0,0) size 13x19
-            text run at (0,0) width 5 RTL: "!"
-            text run at (5,0) width 8: "\x{202B}b"
-            text run at (13,0) width 0: "\x{202C}"
-            text run at (13,0) width 0 RTL: "\x{202B}"
-        LayoutBlockFlow {DIV} at (0,632) size 769x20
-          LayoutText {#text} at (0,0) size 13x19
-            text run at (0,0) width 13: "\x{202C}\x{202C}b!\x{202C}"
-        LayoutBlockFlow {DIV} at (0,652) size 769x20
-          LayoutText {#text} at (0,0) size 20x19
-            text run at (0,0) width 8: "\x{202C}1"
-            text run at (8,0) width 5 RTL: "!"
-            text run at (13,0) width 7: "\x{202B}c"
-            text run at (20,0) width 0: "\x{202C}"
-        LayoutBlockFlow {P} at (0,688) size 769x20
-          LayoutText {#text} at (0,0) size 275x19
-            text run at (0,0) width 275: "Start with different directionality characters."
-        LayoutBlockFlow {DIV} at (0,724) size 769x20
-          LayoutText {#text} at (0,0) size 24x19
-            text run at (0,0) width 16: "12\x{202C}"
-            text run at (16,0) width 8: "\x{202A}1"
-            text run at (24,0) width 0: "\x{202C}"
-            text run at (24,0) width 0: "\x{202B}"
-        LayoutBlockFlow {DIV} at (0,744) size 769x20
-          LayoutText {#text} at (0,0) size 16x19
-            text run at (0,0) width 16: "12\x{202C}"
-            text run at (16,0) width 0: "\x{202A}"
-        LayoutBlockFlow {DIV} at (0,764) size 769x20
-          LayoutText {#text} at (0,0) size 18x19
-            text run at (0,0) width 18: "\x{660}\x{661}"
-            text run at (18,0) width 0: "\x{202C}"
-            text run at (18,0) width 0: "\x{202A}"
-        LayoutBlockFlow {DIV} at (0,784) size 769x20
-          LayoutText {#text} at (0,0) size 20x19
-            text run at (0,0) width 20 RTL: "\x{683}\x{684}"
-            text run at (20,0) width 0: "\x{202C}"
-            text run at (20,0) width 0: "\x{202A}"
-        LayoutBlockFlow {DIV} at (0,804) size 769x20
-          LayoutText {#text} at (0,0) size 10x19
-            text run at (0,0) width 10: "\x{1}\x{202C}"
-            text run at (10,0) width 0: "\x{202A}"
-        LayoutBlockFlow {DIV} at (0,824) size 769x20
-          LayoutText {#text} at (0,0) size 22x19
-            text run at (0,0) width 22: "abc\x{202C}"
-            text run at (22,0) width 0: "\x{202A}"
-        LayoutBlockFlow {DIV} at (0,844) size 769x20
-          LayoutText {#text} at (0,0) size 18x19
-            text run at (0,0) width 18 RTL: "\x{5D0}\x{5D1}"
-            text run at (18,0) width 0: "\x{202C}"
-            text run at (18,0) width 0: "\x{202A}"
-      LayoutBlockFlow {UL} at (0,1760) size 769x20
-        LayoutListItem {LI} at (40,0) size 729x20
-          LayoutListMarker (anonymous) at (-18,0) size 7x19: bullet
-          LayoutText {#text} at (0,0) size 704x19
-            text run at (0,0) width 704: "test id=test: the right-most character of rendering result of <PDF>abc<PDF> in RTL block should be c: Success"
-selection start: position 3 of child 0 {#text} of child 20 {DIV} of child 1 {DIV} of body
-selection end:   position 4 of child 0 {#text} of child 20 {DIV} of child 1 {DIV} of body
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/dom/shadow/event-path-for-user-agent-shadow-tree-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/dom/shadow/event-path-for-user-agent-shadow-tree-expected.txt
deleted file mode 100644
index 50b8df5..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/dom/shadow/event-path-for-user-agent-shadow-tree-expected.txt
+++ /dev/null
@@ -1,29 +0,0 @@
- 
-Dispaching a click event on #details-child
-
-event.path on node #details-child
-#details-child, #details, #sandbox, [object HTMLBodyElement], [object HTMLHtmlElement], [object HTMLDocument], [object Window], length: 7
-
-event.path on node #details
-#details-child, #details, #sandbox, [object HTMLBodyElement], [object HTMLHtmlElement], [object HTMLDocument], [object Window], length: 7
-
-event.path on node #sandbox
-#details-child, #details, #sandbox, [object HTMLBodyElement], [object HTMLHtmlElement], [object HTMLDocument], [object Window], length: 7
-
-Dispaching a click event on #summary-child
-
-event.path on node #summary-child
-#summary-child, #summary, #details, #sandbox, [object HTMLBodyElement], [object HTMLHtmlElement], [object HTMLDocument], [object Window], length: 8
-
-event.path on node #summary
-#summary-child, #summary, #details, #sandbox, [object HTMLBodyElement], [object HTMLHtmlElement], [object HTMLDocument], [object Window], length: 8
-
-event.path on node #details
-#summary-child, #summary, #details, #sandbox, [object HTMLBodyElement], [object HTMLHtmlElement], [object HTMLDocument], [object Window], length: 8
-
-event.path on node #sandbox
-#summary-child, #summary, #details, #sandbox, [object HTMLBodyElement], [object HTMLHtmlElement], [object HTMLDocument], [object Window], length: 8
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/dom/shadow/querySelector-for-useragent-shadowroot-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/dom/shadow/querySelector-for-useragent-shadowroot-expected.txt
deleted file mode 100644
index b4f96f2..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/dom/shadow/querySelector-for-useragent-shadowroot-expected.txt
+++ /dev/null
@@ -1,26 +0,0 @@
-
-  
-crbug.com/337616: test for querySelectorAll with ::shadow and /deep/
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-PASS document.querySelectorAll("form /deep/ *").length is 1
-PASS document.querySelectorAll("form /deep/ *")[0].id is "input"
-PASS document.querySelectorAll("form::shadow *").length is 0
-PASS document.querySelectorAll("input /deep/ *").length is 0
-PASS document.querySelectorAll("input::shadow *").length is 0
-PASS document.querySelectorAll("details /deep/ *").length is 2
-PASS document.querySelectorAll("details /deep/ *")[0].id is "summary"
-PASS document.querySelectorAll("details /deep/ *")[1].id is "p"
-PASS document.querySelectorAll("details::shadow *").length is 0
-PASS document.querySelectorAll("summary /deep/ *").length is 0
-PASS document.querySelectorAll("summary::shadow *").length is 0
-PASS document.querySelectorAll("meter /deep/ *").length is 0
-PASS document.querySelectorAll("meter::shadow *").length is 0
-PASS document.querySelectorAll("progress /deep/ *").length is 0
-PASS document.querySelectorAll("progress::shadow *").length is 0
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/events/drag-selects-image-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/events/drag-selects-image-expected.txt
deleted file mode 100644
index cecab69..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/events/drag-selects-image-expected.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-Non-editable area
-
-Editable area
-
-This tests that images are properly left selected or unselected when an image drag is started. Only the image in the editable area should be selected when an image drag is started.
-
-Starting test...
-0 range(s) selected
-Dragging image in non-editable area...
-0 range(s) selected
-Dragging image in editable area...
-1 range(s) selected
-imageTwo is selected
-
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/forms/form-radio-node-list-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/forms/form-radio-node-list-expected.txt
deleted file mode 100644
index f6f06066..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/forms/form-radio-node-list-expected.txt
+++ /dev/null
@@ -1,26 +0,0 @@
-Test RadioNodeLists returned by the HTMLFormElement named-getter.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-PASS form1.elements.length is 9
-Check that only 'listed elements' are included in the list, if any.
-PASS radioNodeList.length is 9
-PASS radioNodeList[0] instanceof HTMLButtonElement is true
-PASS radioNodeList[1] instanceof HTMLFieldSetElement is true
-PASS radioNodeList[2] instanceof HTMLInputElement is true
-PASS radioNodeList[2].type is "hidden"
-PASS radioNodeList[3] instanceof HTMLInputElement is true
-PASS radioNodeList[3].type is "text"
-PASS radioNodeList[4] instanceof HTMLInputElement is true
-PASS radioNodeList[4].type is "radio"
-PASS radioNodeList[5] instanceof HTMLOutputElement is true
-PASS radioNodeList[6] instanceof HTMLObjectElement is true
-PASS radioNodeList[7] instanceof HTMLSelectElement is true
-PASS radioNodeList[8] instanceof HTMLTextAreaElement is true
-PASS successfullyParsed is true
-
-TEST COMPLETE
- 
-          
-
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/forms/image/image-alt-text-expected.png b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/forms/image/image-alt-text-expected.png
deleted file mode 100644
index 9b9f2ed4..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/forms/image/image-alt-text-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/forms/image/image-error-event-modifies-type-crash-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/forms/image/image-error-event-modifies-type-crash-expected.txt
deleted file mode 100644
index 10d9c73a..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/forms/image/image-error-event-modifies-type-crash-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-   Test Passes if it does not crash.
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/floating-ruby-text-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/floating-ruby-text-expected.txt
deleted file mode 100644
index fe80082..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/floating-ruby-text-expected.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x36
-  LayoutNGBlockFlow {HTML} at (0,0) size 800x36 [color=#FFFFFF]
-    LayoutNGBlockFlow {BODY} at (8,8) size 784x20
-      LayoutRuby (inline) {RUBY} at (0,0) size 784x20
-        LayoutRubyRun (anonymous) at (0,0) size 784x20
-          LayoutRubyText {RT} at (0,-5) size 784x5
-            LayoutText {#text} at (372,0) size 40x5
-              text run at (372,0) width 40: "rubytext"
-          LayoutRubyBase (anonymous) at (0,0) size 784x20
-            LayoutText {#text} at (0,0) size 784x20
-              text run at (0,0) width 784: "Attempt to create a floating ruby text element. Float is not supported for"
-              text run at (0,10) width 784: "ruby text, which should be apparent from the resulting render tree."
-      LayoutText {#text} at (0,0) size 0x0
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/positioned-ruby-text-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/positioned-ruby-text-expected.txt
deleted file mode 100644
index 8cbc458..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/positioned-ruby-text-expected.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x46
-  LayoutNGBlockFlow {HTML} at (0,0) size 800x46 [color=#FFFFFF]
-    LayoutNGBlockFlow {BODY} at (8,8) size 784x30
-      LayoutRuby (inline) {RUBY} at (0,0) size 784x30
-        LayoutRubyRun (anonymous) at (0,0) size 784x30
-          LayoutRubyText {RT} at (0,-5) size 784x5
-            LayoutText {#text} at (372,0) size 40x5
-              text run at (372,0) width 40: "rubytext"
-          LayoutRubyBase (anonymous) at (0,0) size 784x30
-            LayoutText {#text} at (0,0) size 784x30
-              text run at (0,0) width 784: "Attempt to create a positioned ruby text element. Non-static position is not"
-              text run at (0,10) width 784: "supported for ruby text, which should be apparent from the resulting render"
-              text run at (0,20) width 50: "tree."
-      LayoutText {#text} at (0,0) size 0x0
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/ruby-inline-style-not-updated-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/ruby-inline-style-not-updated-expected.txt
new file mode 100644
index 0000000..05be069
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/ruby-inline-style-not-updated-expected.txt
@@ -0,0 +1,15 @@
+layer at (0,0) size 800x600
+  LayoutView at (0,0) size 800x600
+layer at (0,0) size 800x600
+  LayoutBlockFlow {HTML} at (0,0) size 800x600
+    LayoutBlockFlow {BODY} at (8,8) size 784x584
+      LayoutNGBlockFlow (anonymous) at (0,0) size 769x128
+        LayoutRuby (inline) {RUBY} at (0,0) size 512x128 [color=#0000FF]
+          LayoutRubyRun (anonymous) at (0,0) size 512x128
+            LayoutRubyBase (anonymous) at (0,0) size 512x128
+              LayoutText {#text} at (0,0) size 512x128
+                text run at (0,0) width 512: "ABCD"
+        LayoutText {#text} at (0,0) size 0x0
+      LayoutBlockFlow {DIV} at (0,128) size 784x128 [color=#008000]
+        LayoutText {#text} at (0,0) size 512x128
+          text run at (0,0) width 512: "EFGH"
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/ruby-text-before-child-split-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/ruby-text-before-child-split-expected.txt
index ed51721..1a4a17e 100644
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/ruby-text-before-child-split-expected.txt
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/ruby-text-before-child-split-expected.txt
@@ -1,8 +1,8 @@
 layer at (0,0) size 800x600
   LayoutView at (0,0) size 800x600
 layer at (0,0) size 800x26
-  LayoutNGBlockFlow {HTML} at (0,0) size 800x26 [color=#FFFFFF]
-    LayoutNGBlockFlow {BODY} at (8,8) size 784x10
+  LayoutBlockFlow {HTML} at (0,0) size 800x26 [color=#FFFFFF]
+    LayoutBlockFlow {BODY} at (8,8) size 784x10
       LayoutRuby (inline) {RUBY} at (0,0) size 20x10
         LayoutRubyRun (anonymous) at (0,0) size 10x10
           LayoutRubyText {RT} at (0,0) size 10x0
@@ -10,9 +10,9 @@
             LayoutNGBlockFlow (anonymous) at (0,0) size 10x10
               LayoutText {#text} at (0,0) size 10x10
                 text run at (0,0) width 10: "A"
-              LayoutInline {I} at (0,0) size 0x0
+              LayoutInline {I} at (0,0) size 0x10
             LayoutNGBlockFlow (anonymous) at (0,10) size 10x0
-              LayoutNGBlockFlow {DIV} at (0,0) size 10x0
+              LayoutBlockFlow {DIV} at (0,0) size 10x0
             LayoutNGBlockFlow (anonymous) at (0,10) size 10x0
               LayoutInline {I} at (0,0) size 0x0
         LayoutRubyRun (anonymous) at (10,0) size 10x10
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/rubyDOM-insert-rt-block-1-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/rubyDOM-insert-rt-block-1-expected.txt
deleted file mode 100644
index cb77987..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/rubyDOM-insert-rt-block-1-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-SUCCESS!
-
-
-textnew ruby text
-block
-more textruby text
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/rubyDOM-remove-text2-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/rubyDOM-remove-text2-expected.txt
index 62dec7b..be8fc405 100644
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/rubyDOM-remove-text2-expected.txt
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/rubyDOM-remove-text2-expected.txt
@@ -1,21 +1,21 @@
 layer at (0,0) size 800x600
   LayoutView at (0,0) size 800x600
 layer at (0,0) size 800x600
-  LayoutNGBlockFlow {HTML} at (0,0) size 800x600
-    LayoutNGBlockFlow {BODY} at (8,8) size 784x576
-      LayoutNGBlockFlow {P} at (0,0) size 784x20
+  LayoutBlockFlow {HTML} at (0,0) size 800x600
+    LayoutBlockFlow {BODY} at (8,8) size 784x576
+      LayoutBlockFlow {P} at (0,0) size 784x20
         LayoutText {#text} at (0,0) size 732x19
           text run at (0,0) width 732: "The following is a test for DOM manipulation within <ruby>: Removing a ruby base object, leaving the base empty."
-      LayoutNGBlockFlow {P} at (0,36) size 784x20
+      LayoutBlockFlow {P} at (0,36) size 784x20
         LayoutText {#text} at (0,0) size 436x19
           text run at (0,0) width 436: "Both lines should look identical (the first line is the one manipulated)."
-      LayoutNGBlockFlow (anonymous) at (0,72) size 784x40
-        LayoutBR {BR} at (0,0) size 0x0
-        LayoutBR {BR} at (0,20) size 0x0
-      LayoutNGBlockFlow {P} at (0,128) size 784x20
+      LayoutNGBlockFlow (anonymous) at (0,72) size 769x40
+        LayoutBR {BR} at (0,0) size 0x19
+        LayoutBR {BR} at (0,20) size 0x19
+      LayoutBlockFlow {P} at (0,128) size 784x20
         LayoutText {#text} at (0,0) size 155x19
           text run at (0,0) width 155: "<ruby> is defined in the "
-        LayoutRuby (inline) {RUBY} at (0,0) size 8x20
+        LayoutRuby (inline) {RUBY} at (0,0) size 122x19
           LayoutRubyRun (anonymous) at (155,17) size 114x0
             LayoutRubyText {RT} at (0,-12) size 114x12
               LayoutText {#text} at (0,0) size 114x12
@@ -26,10 +26,10 @@
                 text run at (0,0) width 8: "5"
         LayoutText {#text} at (277,0) size 36x19
           text run at (277,0) width 36: " spec."
-      LayoutNGBlockFlow {P} at (0,164) size 784x20
+      LayoutBlockFlow {P} at (0,164) size 784x20
         LayoutText {#text} at (0,0) size 155x19
           text run at (0,0) width 155: "<ruby> is defined in the "
-        LayoutRuby (inline) {RUBY} at (0,0) size 8x20
+        LayoutRuby (inline) {RUBY} at (0,0) size 122x19
           LayoutRubyRun (anonymous) at (155,17) size 114x0
             LayoutRubyText {RT} at (0,-12) size 114x12
               LayoutText {#text} at (0,0) size 114x12
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/split-ruby-run-percentage-height-descendant-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/split-ruby-run-percentage-height-descendant-expected.txt
deleted file mode 100644
index 9a99fc3..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/split-ruby-run-percentage-height-descendant-expected.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-This test checks that <ruby> doesn't leave any pointer in the percentage height descendant when it splits a ruby run.
-For this test to pass it should not crash or ASSERT!
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/text/stroking-decorations-expected.png b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/text/stroking-decorations-expected.png
deleted file mode 100644
index 7f5d136..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/text/stroking-decorations-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/http/tests/misc/empty-urls-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/http/tests/misc/empty-urls-expected.txt
deleted file mode 100644
index 7efef2f..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/http/tests/misc/empty-urls-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-PASS: img onerror fired
-PASS: input type=image onerror fired
-PASS: script onerror fired
-PASS: video src onerror fired
-PASS: audio onerror fired
-PASS: source onerror fired
-       
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/4776765-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/4776765-expected.txt
deleted file mode 100644
index 1898615..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/4776765-expected.txt
+++ /dev/null
@@ -1,69 +0,0 @@
-{
-  "layers": [
-    {
-      "name": "LayoutView #document",
-      "bounds": [800, 600],
-      "drawsContent": false,
-      "backgroundColor": "#FFFFFF"
-    },
-    {
-      "name": "Scrolling Layer",
-      "bounds": [800, 600],
-      "drawsContent": false
-    },
-    {
-      "name": "Scrolling Contents Layer",
-      "bounds": [800, 600],
-      "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
-      "paintInvalidations": [
-        {
-          "object": "LayoutBlockFlow DIV",
-          "rect": [7, 43, 786, 62],
-          "reason": "geometry"
-        },
-        {
-          "object": "Caret",
-          "rect": [8, 84, 1, 19],
-          "reason": "caret"
-        },
-        {
-          "object": "Caret",
-          "rect": [8, 64, 1, 19],
-          "reason": "caret"
-        }
-      ]
-    }
-  ],
-  "objectPaintInvalidations": [
-    {
-      "object": "LayoutBlockFlow DIV",
-      "reason": "geometry"
-    },
-    {
-      "object": "Caret",
-      "reason": "caret"
-    },
-    {
-      "object": "LayoutBlockFlow DIV id='div'",
-      "reason": "appeared"
-    },
-    {
-      "object": "RootInlineBox",
-      "reason": "appeared"
-    },
-    {
-      "object": "Caret",
-      "reason": "caret"
-    },
-    {
-      "object": "LayoutBR BR",
-      "reason": "appeared"
-    },
-    {
-      "object": "InlineTextBox '\n'",
-      "reason": "appeared"
-    }
-  ]
-}
-
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/delete-into-nested-block-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/delete-into-nested-block-expected.txt
deleted file mode 100644
index 59a0999..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/delete-into-nested-block-expected.txt
+++ /dev/null
@@ -1,71 +0,0 @@
-{
-  "layers": [
-    {
-      "name": "LayoutView #document",
-      "bounds": [800, 600],
-      "drawsContent": false,
-      "backgroundColor": "#FFFFFF"
-    },
-    {
-      "name": "Scrolling Layer",
-      "bounds": [800, 600],
-      "drawsContent": false
-    },
-    {
-      "name": "Scrolling Contents Layer",
-      "bounds": [800, 600],
-      "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
-      "paintInvalidations": [
-        {
-          "object": "InlineTextBox 'three'",
-          "rect": [8, 167, 31, 20],
-          "reason": "disappeared"
-        },
-        {
-          "object": "InlineTextBox 'three'",
-          "rect": [8, 127, 31, 20],
-          "reason": "appeared"
-        },
-        {
-          "object": "InlineTextBox 'one'",
-          "rect": [8, 127, 27, 20],
-          "reason": "disappeared"
-        },
-        {
-          "object": "InlineTextBox 'two'",
-          "rect": [8, 147, 24, 20],
-          "reason": "disappeared"
-        },
-        {
-          "object": "Caret",
-          "rect": [8, 127, 1, 20],
-          "reason": "appeared"
-        }
-      ]
-    }
-  ],
-  "objectPaintInvalidations": [
-    {
-      "object": "LayoutBlockFlow DIV id='one'",
-      "reason": "full"
-    },
-    {
-      "object": "RootInlineBox",
-      "reason": "full"
-    },
-    {
-      "object": "Caret",
-      "reason": "caret"
-    },
-    {
-      "object": "LayoutText #text",
-      "reason": "appeared"
-    },
-    {
-      "object": "InlineTextBox 'three'",
-      "reason": "appeared"
-    }
-  ]
-}
-
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/outline/inline-outline-repaint-expected.png b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/outline/inline-outline-repaint-expected.png
deleted file mode 100644
index 18ed59d..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/outline/inline-outline-repaint-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/outline/inline-outline-repaint-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/outline/inline-outline-repaint-expected.txt
deleted file mode 100644
index f628045..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/outline/inline-outline-repaint-expected.txt
+++ /dev/null
@@ -1,106 +0,0 @@
-{
-  "layers": [
-    {
-      "name": "LayoutView #document",
-      "bounds": [800, 600],
-      "drawsContent": false,
-      "backgroundColor": "#FFFFFF"
-    },
-    {
-      "name": "Scrolling Layer",
-      "bounds": [800, 600],
-      "drawsContent": false
-    },
-    {
-      "name": "Scrolling Contents Layer",
-      "bounds": [800, 600],
-      "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
-      "paintInvalidations": [
-        {
-          "object": "LayoutInline SPAN id='test'",
-          "rect": [5, 173, 95, 45],
-          "reason": "geometry"
-        },
-        {
-          "object": "InlineTextBox ' '",
-          "rect": [8, 176, 89, 39],
-          "reason": "disappeared"
-        },
-        {
-          "object": "InlineTextBox 'Lorem ipsum'",
-          "rect": [8, 176, 89, 39],
-          "reason": "disappeared"
-        },
-        {
-          "object": "InlineTextBox 'dolor sit amet\u00A0'",
-          "rect": [8, 176, 89, 39],
-          "reason": "disappeared"
-        },
-        {
-          "object": "InlineTextBox ' '",
-          "rect": [8, 176, 88, 39],
-          "reason": "appeared"
-        },
-        {
-          "object": "InlineTextBox 'Lorem ipsum'",
-          "rect": [8, 176, 88, 39],
-          "reason": "appeared"
-        },
-        {
-          "object": "InlineTextBox 'dolor\u00A0'",
-          "rect": [8, 176, 88, 39],
-          "reason": "appeared"
-        },
-        {
-          "object": "Caret",
-          "rect": [45, 196, 1, 19],
-          "reason": "appeared"
-        }
-      ]
-    }
-  ],
-  "objectPaintInvalidations": [
-    {
-      "object": "LayoutBlockFlow DIV",
-      "reason": "geometry"
-    },
-    {
-      "object": "RootInlineBox",
-      "reason": "geometry"
-    },
-    {
-      "object": "Caret",
-      "reason": "caret"
-    },
-    {
-      "object": "LayoutInline SPAN id='test'",
-      "reason": "geometry"
-    },
-    {
-      "object": "InlineFlowBox",
-      "reason": "geometry"
-    },
-    {
-      "object": "InlineFlowBox",
-      "reason": "geometry"
-    },
-    {
-      "object": "LayoutText #text",
-      "reason": "full"
-    },
-    {
-      "object": "InlineTextBox 'Lorem ipsum'",
-      "reason": "full"
-    },
-    {
-      "object": "InlineTextBox ' '",
-      "reason": "full"
-    },
-    {
-      "object": "InlineTextBox 'dolor\u00A0'",
-      "reason": "full"
-    }
-  ]
-}
-
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/ruby-flipped-blocks-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/ruby-flipped-blocks-expected.txt
deleted file mode 100644
index 63a9c3b..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/ruby-flipped-blocks-expected.txt
+++ /dev/null
@@ -1,95 +0,0 @@
-{
-  "layers": [
-    {
-      "name": "LayoutView #document",
-      "bounds": [800, 600],
-      "drawsContent": false,
-      "backgroundColor": "#FFFFFF"
-    },
-    {
-      "name": "Scrolling Layer",
-      "bounds": [800, 600],
-      "drawsContent": false
-    },
-    {
-      "name": "Scrolling Contents Layer",
-      "bounds": [800, 600],
-      "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
-      "paintInvalidations": [
-        {
-          "object": "InlineTextBox 'b'",
-          "rect": [8, 28, 20, 20],
-          "reason": "appeared"
-        },
-        {
-          "object": "InlineTextBox 'b'",
-          "rect": [8, 28, 20, 20],
-          "reason": "disappeared"
-        },
-        {
-          "object": "NGPaintFragment",
-          "rect": [8, 8, 20, 20],
-          "reason": "appeared"
-        },
-        {
-          "object": "NGPaintFragment",
-          "rect": [8, 8, 20, 20],
-          "reason": "disappeared"
-        },
-        {
-          "object": "InlineTextBox 'c'",
-          "rect": [28, 33, 10, 10],
-          "reason": "subtree"
-        }
-      ]
-    }
-  ],
-  "objectPaintInvalidations": [
-    {
-      "object": "NGPaintFragment",
-      "reason": "subtree"
-    },
-    {
-      "object": "NGPaintFragment",
-      "reason": "subtree"
-    },
-    {
-      "object": "LayoutRubyRun (anonymous)",
-      "reason": "subtree"
-    },
-    {
-      "object": "LayoutRubyText RT id='inner'",
-      "reason": "subtree"
-    },
-    {
-      "object": "RootInlineBox",
-      "reason": "subtree"
-    },
-    {
-      "object": "LayoutText #text",
-      "reason": "subtree"
-    },
-    {
-      "object": "InlineTextBox 'c'",
-      "reason": "subtree"
-    },
-    {
-      "object": "LayoutRubyBase (anonymous)",
-      "reason": "subtree"
-    },
-    {
-      "object": "RootInlineBox",
-      "reason": "subtree"
-    },
-    {
-      "object": "LayoutText #text",
-      "reason": "subtree"
-    },
-    {
-      "object": "InlineTextBox 'b'",
-      "reason": "subtree"
-    }
-  ]
-}
-
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/selection/selection-after-delete-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/selection/selection-after-delete-expected.txt
deleted file mode 100644
index b1bf88dd..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/selection/selection-after-delete-expected.txt
+++ /dev/null
@@ -1,403 +0,0 @@
-{
-  "layers": [
-    {
-      "name": "LayoutView #document",
-      "bounds": [800, 600],
-      "drawsContent": false,
-      "backgroundColor": "#FFFFFF"
-    },
-    {
-      "name": "Scrolling Layer",
-      "bounds": [800, 600],
-      "drawsContent": false
-    },
-    {
-      "name": "Scrolling Contents Layer",
-      "bounds": [800, 600],
-      "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
-      "paintInvalidations": [
-        {
-          "object": "InlineTextBox ' '",
-          "rect": [39, 79, 154, 99],
-          "reason": "disappeared"
-        },
-        {
-          "object": "InlineTextBox ' '",
-          "rect": [39, 79, 154, 99],
-          "reason": "disappeared"
-        },
-        {
-          "object": "InlineTextBox ' '",
-          "rect": [39, 79, 154, 99],
-          "reason": "disappeared"
-        },
-        {
-          "object": "InlineTextBox ' '",
-          "rect": [39, 79, 154, 99],
-          "reason": "disappeared"
-        },
-        {
-          "object": "InlineTextBox 'hello world hello world'",
-          "rect": [39, 79, 154, 99],
-          "reason": "disappeared"
-        },
-        {
-          "object": "InlineTextBox 'hello world hello world'",
-          "rect": [39, 79, 154, 99],
-          "reason": "disappeared"
-        },
-        {
-          "object": "InlineTextBox 'hello world hello world'",
-          "rect": [39, 79, 154, 99],
-          "reason": "disappeared"
-        },
-        {
-          "object": "InlineTextBox 'hello world hello world'",
-          "rect": [39, 79, 154, 99],
-          "reason": "disappeared"
-        },
-        {
-          "object": "InlineTextBox 'hello'",
-          "rect": [39, 79, 154, 99],
-          "reason": "disappeared"
-        },
-        {
-          "object": "LayoutBlockFlow DIV id='test'",
-          "rect": [38, 78, 152, 102],
-          "reason": "geometry"
-        },
-        {
-          "object": "Caret",
-          "rect": [39, 79, 1, 19],
-          "reason": "appeared"
-        }
-      ],
-      "underPaintInvalidations": [
-        {
-          "x": 194,
-          "y": 79,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 80,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 81,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 82,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 83,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 84,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 85,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 86,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 87,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 88,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 89,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 90,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 91,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 92,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 93,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 94,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 95,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 96,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 97,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 98,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 99,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 100,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 101,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 102,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 103,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 104,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 105,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 106,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 107,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 108,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 109,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 110,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 111,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 112,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 113,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 114,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 115,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 116,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 117,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 118,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 119,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 120,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 121,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 122,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 123,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 124,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 125,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 126,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 127,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 128,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        }
-      ]
-    }
-  ],
-  "objectPaintInvalidations": [
-    {
-      "object": "LayoutBlockFlow DIV id='test'",
-      "reason": "geometry"
-    },
-    {
-      "object": "RootInlineBox",
-      "reason": "geometry"
-    },
-    {
-      "object": "Caret",
-      "reason": "caret"
-    },
-    {
-      "object": "LayoutBR BR",
-      "reason": "geometry"
-    },
-    {
-      "object": "InlineTextBox '\n'",
-      "reason": "geometry"
-    }
-  ]
-}
-
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/selection/selection-after-remove-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/selection/selection-after-remove-expected.txt
deleted file mode 100644
index 477833c..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/selection/selection-after-remove-expected.txt
+++ /dev/null
@@ -1,430 +0,0 @@
-{
-  "layers": [
-    {
-      "name": "LayoutView #document",
-      "bounds": [800, 600],
-      "drawsContent": false,
-      "backgroundColor": "#FFFFFF"
-    },
-    {
-      "name": "Scrolling Layer",
-      "bounds": [800, 600],
-      "drawsContent": false
-    },
-    {
-      "name": "Scrolling Contents Layer",
-      "bounds": [800, 600],
-      "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
-      "paintInvalidations": [
-        {
-          "object": "InlineTextBox ' '",
-          "rect": [39, 79, 154, 99],
-          "reason": "disappeared"
-        },
-        {
-          "object": "InlineTextBox ' '",
-          "rect": [39, 79, 154, 99],
-          "reason": "disappeared"
-        },
-        {
-          "object": "InlineTextBox ' '",
-          "rect": [39, 79, 154, 99],
-          "reason": "disappeared"
-        },
-        {
-          "object": "InlineTextBox ' '",
-          "rect": [39, 79, 154, 99],
-          "reason": "disappeared"
-        },
-        {
-          "object": "InlineTextBox 'hello world hello world'",
-          "rect": [39, 79, 154, 99],
-          "reason": "disappeared"
-        },
-        {
-          "object": "InlineTextBox 'hello world hello world'",
-          "rect": [39, 79, 154, 99],
-          "reason": "disappeared"
-        },
-        {
-          "object": "InlineTextBox 'hello world hello world'",
-          "rect": [39, 79, 154, 99],
-          "reason": "disappeared"
-        },
-        {
-          "object": "InlineTextBox 'hello'",
-          "rect": [39, 79, 154, 99],
-          "reason": "disappeared"
-        },
-        {
-          "object": "InlineTextBox 'world hello world'",
-          "rect": [39, 79, 154, 99],
-          "reason": "disappeared"
-        },
-        {
-          "object": "LayoutBlockFlow DIV id='test'",
-          "rect": [38, 78, 152, 102],
-          "reason": "geometry"
-        },
-        {
-          "object": "InlineTextBox ' world'",
-          "rect": [70, 158, 41, 20],
-          "reason": "disappeared"
-        },
-        {
-          "object": "InlineTextBox 'world'",
-          "rect": [74, 79, 37, 19],
-          "reason": "appeared"
-        },
-        {
-          "object": "InlineTextBox 'hello '",
-          "rect": [39, 79, 35, 19],
-          "reason": "appeared"
-        },
-        {
-          "object": "InlineTextBox 'hello '",
-          "rect": [39, 79, 35, 19],
-          "reason": "disappeared"
-        }
-      ],
-      "underPaintInvalidations": [
-        {
-          "x": 194,
-          "y": 79,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 80,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 81,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 82,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 83,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 84,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 85,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 86,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 87,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 88,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 89,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 90,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 91,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 92,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 93,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 94,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 95,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 96,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 97,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 98,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 99,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 100,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 101,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 102,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 103,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 104,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 105,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 106,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 107,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 108,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 109,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 110,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 111,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 112,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 113,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 114,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 115,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 116,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 117,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 118,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 119,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 120,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 121,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 122,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 123,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 124,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 125,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 126,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 127,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        },
-        {
-          "x": 194,
-          "y": 128,
-          "oldPixel": "#3333FF",
-          "newPixel": "#FFFFFF"
-        }
-      ]
-    }
-  ],
-  "objectPaintInvalidations": [
-    {
-      "object": "LayoutBlockFlow DIV id='test'",
-      "reason": "geometry"
-    },
-    {
-      "object": "RootInlineBox",
-      "reason": "geometry"
-    },
-    {
-      "object": "LayoutText #text",
-      "reason": "geometry"
-    },
-    {
-      "object": "InlineTextBox 'hello '",
-      "reason": "geometry"
-    },
-    {
-      "object": "LayoutText #text",
-      "reason": "geometry"
-    },
-    {
-      "object": "InlineTextBox 'world'",
-      "reason": "geometry"
-    },
-    {
-      "object": "LayoutBR BR",
-      "reason": "geometry"
-    },
-    {
-      "object": "InlineTextBox '\n'",
-      "reason": "geometry"
-    }
-  ]
-}
-
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/markers/suggestion-marker-split-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/markers/suggestion-marker-split-expected.txt
deleted file mode 100644
index e0b85f30..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/paint/markers/suggestion-marker-split-expected.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x56
-  LayoutBlockFlow {HTML} at (0,0) size 800x56
-    LayoutBlockFlow {BODY} at (8,8) size 784x40
-      LayoutBlockFlow {DIV} at (0,0) size 784x20
-        LayoutText {#text} at (0,0) size 22x19
-          text run at (0,0) width 22: "abc"
-        LayoutText {#text} at (22,0) size 32x19
-          text run at (22,0) width 32: " xxx "
-        LayoutText {#text} at (54,0) size 20x19
-          text run at (54,0) width 20: "def"
-layer at (8,28) size 128x20 scrollWidth 160
-  LayoutBlockFlow {DIV} at (0,20) size 128x20
-    LayoutText {#text} at (0,0) size 62x19
-      text run at (0,0) width 62: "abcdefghi"
-    LayoutText {#text} at (62,0) size 32x19
-      text run at (62,0) width 32: " xxx "
-    LayoutText {#text} at (94,0) size 65x19
-      text run at (94,0) width 65: "jklmnopqr"
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/plugins/focus-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/plugins/focus-expected.txt
deleted file mode 100644
index 595e5bb..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/plugins/focus-expected.txt
+++ /dev/null
@@ -1,29 +0,0 @@
-CONSOLE MESSAGE: Blink Test Plugin: initializing
-CONSOLE MESSAGE: Blink Test Plugin: initializing
-CONSOLE MESSAGE: Blink Test Plugin: initializing
-CONSOLE MESSAGE: Blink Test Plugin: initializing
-Test for Embed and Object for bug 32292: Unable to focus on embedded plugins such as Flash via javascript focus().
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-PASS "embedElem"; document.activeElement === pluginElement is true
-PASS "objectElem"; document.activeElement === pluginElement is true
-PASS "embedElemWithFallbackContents"; document.activeElement === pluginElement is true
-PASS "objectElemWithFallbackContents"; document.activeElement === pluginElement is true
-PASS "noPluginEmbedElem"; document.activeElement === pluginElement is false
-PASS "noPluginObjectElem"; document.activeElement === pluginElement is false
-PASS "noPluginEmbedElemWithFallbackContents"; document.activeElement === pluginElement is false
-PASS "noPluginObjectElemWithFallbackContents"; document.activeElement === pluginElement is false
-PASS "noPluginEmbedElemWithTabindex"; document.activeElement === pluginElement is true
-PASS "noPluginObjectElemWithTabindex"; document.activeElement === pluginElement is true
-PASS "noPluginEmbedElemWithContenteditable"; document.activeElement === pluginElement is true
-PASS "noPluginObjectElemWithContenteditable"; document.activeElement === pluginElement is true
-PASS successfullyParsed is true
-
-TEST COMPLETE
-Test for bug 32292: "Unable to focus on embedded plugins such as Flash via javascript focus()"
-
-This tests focusing Embeds and Objects. See LayoutTests/java for Applet elements.
-
-   Fallback contents.     Fallback contents. Fallback contents.      
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/plugins/tabindex-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/plugins/tabindex-expected.txt
deleted file mode 100644
index 2305e11..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/plugins/tabindex-expected.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-CONSOLE MESSAGE: Blink Test Plugin: initializing
-CONSOLE MESSAGE: Blink Test Plugin: initializing
-CONSOLE MESSAGE: Blink Test Plugin: initializing
-CONSOLE MESSAGE: Blink Test Plugin: initializing
-Test for the tabIndex of embedded plugins. Plugins may be focusable so their tabIndex should be 0 by default.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-PASS "embedElem"; pluginElement.tabIndex is 0
-PASS "objectElem"; pluginElement.tabIndex is 1
-PASS "embedElemWithFallbackContents"; pluginElement.tabIndex is -1
-PASS "objectElemWithFallbackContents"; pluginElement.tabIndex is 0
-PASS "noPluginEmbedElem"; pluginElement.tabIndex is 1
-PASS "noPluginObjectElem"; pluginElement.tabIndex is 0
-PASS "noPluginEmbedElemWithFallbackContents"; pluginElement.tabIndex is 0
-PASS "noPluginObjectElemWithFallbackContents"; pluginElement.tabIndex is 0
-PASS "noPluginEmbedElemWithDisplayNone"; pluginElement.tabIndex is -1
-PASS "noPluginObjectElemWithDisplayNone"; pluginElement.tabIndex is 0
-PASS "noPluginEmbedElemWithContenteditable"; pluginElement.tabIndex is 0
-PASS "noPluginObjectElemWithContenteditable"; pluginElement.tabIndex is -1
-PASS successfullyParsed is true
-
-TEST COMPLETE
-   Fallback contents.     Fallback contents. Fallback contents.   
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/transforms/transformed-caret-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/transforms/transformed-caret-expected.txt
deleted file mode 100644
index 575b741..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/transforms/transformed-caret-expected.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x418
-  LayoutBlockFlow {HTML} at (0,0) size 800x418
-    LayoutBlockFlow {BODY} at (8,16) size 784x322
-      LayoutBlockFlow {P} at (0,0) size 784x40
-        LayoutText {#text} at (0,0) size 362x19
-          text run at (0,0) width 362: "The caret should render correctly in transformed elements"
-        LayoutBR {BR} at (362,15) size 0x0
-        LayoutInline {A} at (0,0) size 305x19 [color=#0000EE]
-          LayoutText {#text} at (0,20) size 305x19
-            text run at (0,20) width 305: "https://bugs.webkit.org/show_bug.cgi?id=15671"
-layer at (88,136) size 302x202
-  LayoutBlockFlow {DIV} at (80,120) size 302x202 [border: (1px solid #000000)]
-    LayoutText {#text} at (1,2) size 174x90
-      text run at (1,2) width 174: "Caret"
-caret: position 6 of child 0 {#text} of child 3 {DIV} of body
diff --git a/third_party/WebKit/LayoutTests/fullscreen/api/element-request-fullscreen-two-elements.html b/third_party/WebKit/LayoutTests/fullscreen/api/element-request-fullscreen-two-elements.html
index 2d0a4ee..8644e18c 100644
--- a/third_party/WebKit/LayoutTests/fullscreen/api/element-request-fullscreen-two-elements.html
+++ b/third_party/WebKit/LayoutTests/fullscreen/api/element-request-fullscreen-two-elements.html
@@ -23,8 +23,8 @@
   });
 
   trusted_click(t, () => {
-    b.requestFullscreen();
-    a.requestFullscreen();
+    silence_rejection(b.requestFullscreen());
+    silence_rejection(a.requestFullscreen());
   }, document.body);
 });
 </script>
diff --git a/third_party/WebKit/LayoutTests/fullscreen/api/element-request-fullscreen-vs-exit.html b/third_party/WebKit/LayoutTests/fullscreen/api/element-request-fullscreen-vs-exit.html
index 419673b..ce9c2b2c 100644
--- a/third_party/WebKit/LayoutTests/fullscreen/api/element-request-fullscreen-vs-exit.html
+++ b/third_party/WebKit/LayoutTests/fullscreen/api/element-request-fullscreen-vs-exit.html
@@ -17,9 +17,9 @@
     document.onfullscreenchange = t.step_func_done();
     document.onfullscreenerror = t.unreached_func("fullscreenerror event");
 
-    target.requestFullscreen();
+    silence_rejection(target.requestFullscreen());
     assert_equals(document.fullscreenElement, null, "fullscreenElement after requestFullscreen()");
-    document.exitFullscreen();
+    silence_rejection(document.exitFullscreen());
     assert_equals(document.fullscreenElement, null, "fullscreenElement after exitFullscreen()");
   }, document.body);
 });
diff --git a/third_party/WebKit/LayoutTests/fullscreen/trusted-click.js b/third_party/WebKit/LayoutTests/fullscreen/trusted-click.js
index 67b6a2c..ac20227a 100644
--- a/third_party/WebKit/LayoutTests/fullscreen/trusted-click.js
+++ b/third_party/WebKit/LayoutTests/fullscreen/trusted-click.js
@@ -38,9 +38,17 @@
     }
 }
 
+function silence_rejection(promise) {
+  // Keep the promise resolution silent. Otherwise unhandledrejection
+  // may fire for the failure test cases.
+  if (promise) promise.catch(() => {});
+}
+
 // Invokes element.requestFullscreen() from a trusted click.
 function trusted_request(test, element, container, clickRectInRootFrameCoordinate)
 {
-    trusted_click(test, () => element.requestFullscreen(), container || element.parentNode,
+    trusted_click(test, () => {
+        silence_rejection(element.requestFullscreen());
+    }, container || element.parentNode,
                   clickRectInRootFrameCoordinate);
 }
diff --git a/third_party/WebKit/LayoutTests/media/controls/overflow-menu-always-visible.html b/third_party/WebKit/LayoutTests/media/controls/overflow-menu-always-visible.html
index 2dad67e..ffcc86f 100644
--- a/third_party/WebKit/LayoutTests/media/controls/overflow-menu-always-visible.html
+++ b/third_party/WebKit/LayoutTests/media/controls/overflow-menu-always-visible.html
@@ -17,6 +17,7 @@
     testRunner.waitUntilDone();
 
   var video = document.querySelector('video');
+  enableTestMode(video);
   video.src = '../content/test.ogv';
   video.addTextTrack('captions', 'foo');
   video.addTextTrack('captions', 'bar');
diff --git a/third_party/WebKit/LayoutTests/virtual/layout_ng/fast/block/float/rubybase-children-moved-crash-2-expected.txt b/third_party/WebKit/LayoutTests/virtual/layout_ng/fast/block/float/rubybase-children-moved-crash-2-expected.txt
index 6c350ea5..0bfa3ce 100644
--- a/third_party/WebKit/LayoutTests/virtual/layout_ng/fast/block/float/rubybase-children-moved-crash-2-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/layout_ng/fast/block/float/rubybase-children-moved-crash-2-expected.txt
@@ -1,3 +1,3 @@
 crbug.com/683104: Passes if it does not crash.
 
-Text 
+Text  
diff --git a/third_party/afl/BUILD.gn b/third_party/afl/BUILD.gn
index af09c3d..01e4ce1 100644
--- a/third_party/afl/BUILD.gn
+++ b/third_party/afl/BUILD.gn
@@ -95,6 +95,8 @@
   configs -= [ "//build/config/sanitizers:default_sanitizer_flags" ]
   configs += [ ":afl-tool" ]
 
+  no_default_deps = true
+
   sources = [
     "src/afl-fuzz.c",
   ]
@@ -105,6 +107,8 @@
   configs -= [ "//build/config/sanitizers:default_sanitizer_flags" ]
   configs += [ ":afl-tool" ]
 
+  no_default_deps = true
+
   sources = [
     "src/afl-tmin.c",
   ]
@@ -115,6 +119,8 @@
   configs -= [ "//build/config/sanitizers:default_sanitizer_flags" ]
   configs += [ ":afl-tool" ]
 
+  no_default_deps = true
+
   sources = [
     "src/afl-showmap.c",
   ]
diff --git a/third_party/blink/public/platform/modules/mediastream/media_devices.mojom b/third_party/blink/public/platform/modules/mediastream/media_devices.mojom
index a096ea6..85e82eb 100644
--- a/third_party/blink/public/platform/modules/mediastream/media_devices.mojom
+++ b/third_party/blink/public/platform/modules/mediastream/media_devices.mojom
@@ -35,6 +35,7 @@
 
 struct AudioInputDeviceCapabilities {
   string device_id;
+  string group_id;
   media.mojom.AudioParameters parameters;
 };
 
diff --git a/third_party/blink/public/platform/web_runtime_features.h b/third_party/blink/public/platform/web_runtime_features.h
index 10e35b9..2245bb92 100644
--- a/third_party/blink/public/platform/web_runtime_features.h
+++ b/third_party/blink/public/platform/web_runtime_features.h
@@ -95,6 +95,7 @@
       bool);
   BLINK_PLATFORM_EXPORT static void EnableLayeredAPI(bool);
   BLINK_PLATFORM_EXPORT static void EnableLazyFrameLoading(bool);
+  BLINK_PLATFORM_EXPORT static void EnableLazyFrameVisibleLoadTimeMetrics(bool);
   BLINK_PLATFORM_EXPORT static void EnableLazyParseCSS(bool);
   BLINK_PLATFORM_EXPORT static void EnableMediaCapture(bool);
   BLINK_PLATFORM_EXPORT static void EnableMediaSession(bool);
diff --git a/third_party/blink/renderer/core/css/BUILD.gn b/third_party/blink/renderer/core/css/BUILD.gn
index 3f260d5..ce138a9 100644
--- a/third_party/blink/renderer/core/css/BUILD.gn
+++ b/third_party/blink/renderer/core/css/BUILD.gn
@@ -856,9 +856,10 @@
     "resolver/selector_filter_parent_scope.h",
     "resolver/style_adjuster.cc",
     "resolver/style_adjuster.h",
+    "resolver/style_builder.cc",
+    "resolver/style_builder.h",
     "resolver/style_builder_converter.cc",
     "resolver/style_builder_converter.h",
-    "resolver/style_builder_custom.cc",
     "resolver/style_resolver.cc",
     "resolver/style_resolver.h",
     "resolver/style_resolver_state.cc",
diff --git a/third_party/blink/renderer/core/css/resolver/style_builder_custom.cc b/third_party/blink/renderer/core/css/resolver/style_builder.cc
similarity index 100%
rename from third_party/blink/renderer/core/css/resolver/style_builder_custom.cc
rename to third_party/blink/renderer/core/css/resolver/style_builder.cc
diff --git a/third_party/blink/renderer/core/fileapi/file_reader_loader.cc b/third_party/blink/renderer/core/fileapi/file_reader_loader.cc
index 5543f63..63a0c52 100644
--- a/third_party/blink/renderer/core/fileapi/file_reader_loader.cc
+++ b/third_party/blink/renderer/core/fileapi/file_reader_loader.cc
@@ -93,7 +93,7 @@
   MojoResult result =
       CreateDataPipe(nullptr, &producer_handle, &consumer_handle_);
   if (result != MOJO_RESULT_OK) {
-    Failed(FileError::kNotReadableErr);
+    Failed(FileError::kNotReadableErr, FailureType::kMojoPipeCreation);
     return;
   }
 
@@ -108,14 +108,16 @@
     if (received_on_complete_)
       return;
     if (!received_all_data_) {
-      Failed(FileError::kNotReadableErr);
+      Failed(FileError::kNotReadableErr, FailureType::kSyncDataNotAllLoaded);
       return;
     }
 
     // Wait for OnComplete
     binding_.WaitForIncomingMethodCall();
-    if (!received_on_complete_)
-      Failed(FileError::kNotReadableErr);
+    if (!received_on_complete_) {
+      Failed(FileError::kNotReadableErr,
+             FailureType::kSyncOnCompleteNotReceived);
+    }
   }
 }
 
@@ -198,11 +200,16 @@
   }
 }
 
-void FileReaderLoader::Failed(FileError::ErrorCode error_code) {
+void FileReaderLoader::Failed(FileError::ErrorCode error_code,
+                              FailureType type) {
+  DEFINE_THREAD_SAFE_STATIC_LOCAL(EnumerationHistogram, failure_histogram,
+                                  ("Storage.Blob.FileReaderLoader.FailureType",
+                                   static_cast<int>(FailureType::kCount)));
   // If an error was already reported, don't report this error again.
   if (error_code_ != FileError::kOK)
     return;
   error_code_ = error_code;
+  failure_histogram.Count(static_cast<int>(type));
   Cleanup();
   if (client_)
     client_->DidFail(error_code_);
@@ -218,13 +225,14 @@
     // so to call ArrayBuffer's create function.
     // FIXME: Support reading more than the current size limit of ArrayBuffer.
     if (total_bytes > std::numeric_limits<unsigned>::max()) {
-      Failed(FileError::kNotReadableErr);
+      Failed(FileError::kNotReadableErr, FailureType::kTotalBytesTooLarge);
       return;
     }
 
     raw_data_ = std::make_unique<ArrayBufferBuilder>(total_bytes);
     if (!raw_data_->IsValid()) {
-      Failed(FileError::kNotReadableErr);
+      Failed(FileError::kNotReadableErr,
+             FailureType::kArrayBufferBuilderCreation);
       return;
     }
     raw_data_->SetVariableCapacity(false);
@@ -253,7 +261,7 @@
   if (!bytes_appended) {
     raw_data_.reset();
     bytes_loaded_ = 0;
-    Failed(FileError::kNotReadableErr);
+    Failed(FileError::kNotReadableErr, FailureType::kArrayBufferBuilderAppend);
     return;
   }
   bytes_loaded_ += bytes_appended;
@@ -305,14 +313,16 @@
   DEFINE_THREAD_SAFE_STATIC_LOCAL(SparseHistogram,
                                   file_reader_loader_read_errors_histogram,
                                   ("Storage.Blob.FileReaderLoader.ReadError"));
-  if (status != net::OK || data_length != total_bytes_) {
-    net_error_ = status;
-    if (net_error_ != net::OK)
-      file_reader_loader_read_errors_histogram.Sample(std::max(0, -net_error_));
+  if (status != net::OK) {
+    file_reader_loader_read_errors_histogram.Sample(std::max(0, -net_error_));
     Failed(status == net::ERR_FILE_NOT_FOUND ? FileError::kNotFoundErr
-                                             : FileError::kNotReadableErr);
+                                             : FileError::kNotReadableErr,
+           FailureType::kBackendReadError);
     return;
   }
+  if (data_length != total_bytes_) {
+    Failed(FileError::kNotReadableErr, FailureType::kReadSizesIncorrect);
+  }
 
   received_on_complete_ = true;
   if (received_all_data_)
@@ -321,8 +331,10 @@
 
 void FileReaderLoader::OnDataPipeReadable(MojoResult result) {
   if (result != MOJO_RESULT_OK) {
-    if (!received_all_data_)
-      Failed(FileError::kNotReadableErr);
+    if (!received_all_data_) {
+      Failed(FileError::kNotReadableErr,
+             FailureType::kDataPipeNotReadableWithBytesLeft);
+    }
     return;
   }
 
@@ -341,12 +353,14 @@
     }
     if (result == MOJO_RESULT_FAILED_PRECONDITION) {
       // Pipe closed.
-      if (!received_all_data_)
-        Failed(FileError::kNotReadableErr);
+      if (!received_all_data_) {
+        Failed(FileError::kNotReadableErr, FailureType::kMojoPipeClosedEarly);
+      }
       return;
     }
     if (result != MOJO_RESULT_OK) {
-      Failed(FileError::kNotReadableErr);
+      Failed(FileError::kNotReadableErr,
+             FailureType::kMojoPipeUnexpectedReadError);
       return;
     }
     OnReceivedData(static_cast<const char*>(buffer), num_bytes);
diff --git a/third_party/blink/renderer/core/fileapi/file_reader_loader.h b/third_party/blink/renderer/core/fileapi/file_reader_loader.h
index 0f1bfdbf..6b5d8b3e 100644
--- a/third_party/blink/renderer/core/fileapi/file_reader_loader.h
+++ b/third_party/blink/renderer/core/fileapi/file_reader_loader.h
@@ -107,8 +107,28 @@
   bool HasFinishedLoading() const { return finished_loading_; }
 
  private:
+  // These values are persisted to logs. Entries should not be renumbered and
+  // numeric values should never be reused.
+  enum class FailureType {
+    kMojoPipeCreation = 0,
+    kSyncDataNotAllLoaded = 1,
+    kSyncOnCompleteNotReceived = 2,
+    kTotalBytesTooLarge = 3,
+    kArrayBufferBuilderCreation = 4,
+    kArrayBufferBuilderAppend = 5,
+    kBackendReadError = 6,
+    kReadSizesIncorrect = 7,
+    kDataPipeNotReadableWithBytesLeft = 8,
+    kMojoPipeClosedEarly = 9,
+    // Any MojoResult error we aren't expecting during data pipe reading falls
+    // into this bucket. If there are a large number of errors reported here,
+    // then there can be a new enumeration reported for mojo pipe errors.
+    kMojoPipeUnexpectedReadError = 10,
+    kCount
+  };
+
   void Cleanup();
-  void Failed(FileError::ErrorCode);
+  void Failed(FileError::ErrorCode, FailureType type);
 
   void OnStartLoading(uint64_t total_bytes);
   void OnReceivedData(const char* data, unsigned data_length);
diff --git a/third_party/blink/renderer/core/fullscreen/document_fullscreen.cc b/third_party/blink/renderer/core/fullscreen/document_fullscreen.cc
index e82c548..9ae4c22 100644
--- a/third_party/blink/renderer/core/fullscreen/document_fullscreen.cc
+++ b/third_party/blink/renderer/core/fullscreen/document_fullscreen.cc
@@ -37,8 +37,14 @@
   return Fullscreen::FullscreenElementForBindingFrom(document);
 }
 
-void DocumentFullscreen::exitFullscreen(Document& document) {
-  Fullscreen::ExitFullscreen(document);
+ScriptPromise DocumentFullscreen::exitFullscreen(ScriptState* script_state,
+                                                 Document& document) {
+  return Fullscreen::ExitFullscreen(document, script_state);
+}
+
+void DocumentFullscreen::webkitExitFullscreen(Document& document) {
+  ScriptPromise promise = Fullscreen::ExitFullscreen(document);
+  DCHECK(promise.IsEmpty());
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/fullscreen/document_fullscreen.h b/third_party/blink/renderer/core/fullscreen/document_fullscreen.h
index 7b4895e..1b73f7b 100644
--- a/third_party/blink/renderer/core/fullscreen/document_fullscreen.h
+++ b/third_party/blink/renderer/core/fullscreen/document_fullscreen.h
@@ -26,7 +26,9 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FULLSCREEN_DOCUMENT_FULLSCREEN_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_FULLSCREEN_DOCUMENT_FULLSCREEN_H_
 
+#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/core/dom/events/event_target.h"
+#include "third_party/blink/renderer/platform/bindings/script_state.h"
 #include "third_party/blink/renderer/platform/wtf/allocator.h"
 
 namespace blink {
@@ -40,7 +42,8 @@
  public:
   static bool fullscreenEnabled(Document&);
   static Element* fullscreenElement(Document&);
-  static void exitFullscreen(Document&);
+  static ScriptPromise exitFullscreen(ScriptState*, Document&);
+  static void webkitExitFullscreen(Document&);
 
   DEFINE_STATIC_ATTRIBUTE_EVENT_LISTENER(fullscreenchange);
   DEFINE_STATIC_ATTRIBUTE_EVENT_LISTENER(fullscreenerror);
diff --git a/third_party/blink/renderer/core/fullscreen/document_fullscreen.idl b/third_party/blink/renderer/core/fullscreen/document_fullscreen.idl
index 4de9b66..c99b475 100644
--- a/third_party/blink/renderer/core/fullscreen/document_fullscreen.idl
+++ b/third_party/blink/renderer/core/fullscreen/document_fullscreen.idl
@@ -27,7 +27,7 @@
     [LenientSetter, RuntimeEnabled=FullscreenUnprefixed] readonly attribute boolean fullscreenEnabled;
     [LenientSetter, Unscopable, RuntimeEnabled=FullscreenUnprefixed, ImplementedAs=fullscreenElement] readonly attribute boolean fullscreen;
 
-    [RuntimeEnabled=FullscreenUnprefixed] void exitFullscreen();
+    [CallWith=ScriptState, RuntimeEnabled=FullscreenUnprefixed] Promise<void> exitFullscreen();
 
     [RuntimeEnabled=FullscreenUnprefixed] attribute EventHandler onfullscreenchange;
     [RuntimeEnabled=FullscreenUnprefixed] attribute EventHandler onfullscreenerror;
@@ -35,12 +35,12 @@
     // Mozilla version
     [MeasureAs=PrefixedDocumentIsFullscreen, ImplementedAs=fullscreenElement] readonly attribute boolean webkitIsFullScreen;
     [MeasureAs=PrefixedDocumentCurrentFullScreenElement, ImplementedAs=fullscreenElement] readonly attribute Element webkitCurrentFullScreenElement;
-    [MeasureAs=PrefixedDocumentCancelFullScreen, ImplementedAs=exitFullscreen] void webkitCancelFullScreen();
+    [MeasureAs=PrefixedDocumentCancelFullScreen, ImplementedAs=webkitExitFullscreen] void webkitCancelFullScreen();
 
     // W3C version
     [MeasureAs=PrefixedDocumentFullscreenEnabled, ImplementedAs=fullscreenEnabled] readonly attribute boolean webkitFullscreenEnabled;
     [MeasureAs=PrefixedDocumentFullscreenElement, ImplementedAs=fullscreenElement] readonly attribute Element webkitFullscreenElement;
-    [MeasureAs=PrefixedDocumentExitFullscreen, ImplementedAs=exitFullscreen] void webkitExitFullscreen();
+    [MeasureAs=PrefixedDocumentExitFullscreen, ImplementedAs=webkitExitFullscreen] void webkitExitFullscreen();
 
     attribute EventHandler onwebkitfullscreenchange;
     attribute EventHandler onwebkitfullscreenerror;
diff --git a/third_party/blink/renderer/core/fullscreen/element_fullscreen.cc b/third_party/blink/renderer/core/fullscreen/element_fullscreen.cc
index 8d0509b9..2f38e20 100644
--- a/third_party/blink/renderer/core/fullscreen/element_fullscreen.cc
+++ b/third_party/blink/renderer/core/fullscreen/element_fullscreen.cc
@@ -9,10 +9,12 @@
 
 namespace blink {
 
-void ElementFullscreen::requestFullscreen(Element& element,
-                                          const FullscreenOptions& options) {
-  Fullscreen::RequestFullscreen(element, options,
-                                Fullscreen::RequestType::kUnprefixed);
+ScriptPromise ElementFullscreen::requestFullscreen(
+    ScriptState* script_state,
+    Element& element,
+    const FullscreenOptions& options) {
+  return Fullscreen::RequestFullscreen(
+      element, options, Fullscreen::RequestType::kUnprefixed, script_state);
 }
 
 void ElementFullscreen::webkitRequestFullscreen(Element& element) {
diff --git a/third_party/blink/renderer/core/fullscreen/element_fullscreen.h b/third_party/blink/renderer/core/fullscreen/element_fullscreen.h
index 0d1446d..3a7ad9d 100644
--- a/third_party/blink/renderer/core/fullscreen/element_fullscreen.h
+++ b/third_party/blink/renderer/core/fullscreen/element_fullscreen.h
@@ -5,8 +5,10 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FULLSCREEN_ELEMENT_FULLSCREEN_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_FULLSCREEN_ELEMENT_FULLSCREEN_H_
 
+#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/core/dom/events/event_target.h"
 #include "third_party/blink/renderer/core/fullscreen/fullscreen_options.h"
+#include "third_party/blink/renderer/platform/bindings/script_state.h"
 #include "third_party/blink/renderer/platform/wtf/allocator.h"
 
 namespace blink {
@@ -17,7 +19,9 @@
   STATIC_ONLY(ElementFullscreen);
 
  public:
-  static void requestFullscreen(Element&, const FullscreenOptions&);
+  static ScriptPromise requestFullscreen(ScriptState*,
+                                         Element&,
+                                         const FullscreenOptions&);
 
   DEFINE_STATIC_ATTRIBUTE_EVENT_LISTENER(fullscreenchange);
   DEFINE_STATIC_ATTRIBUTE_EVENT_LISTENER(fullscreenerror);
diff --git a/third_party/blink/renderer/core/fullscreen/element_fullscreen.idl b/third_party/blink/renderer/core/fullscreen/element_fullscreen.idl
index 36c45b32..76d2d9c7 100644
--- a/third_party/blink/renderer/core/fullscreen/element_fullscreen.idl
+++ b/third_party/blink/renderer/core/fullscreen/element_fullscreen.idl
@@ -9,7 +9,7 @@
 ] partial interface Element {
     // TODO(dtapuska): If RuntimeEnabled=FullscreenUnprefixed is shipped before RuntimeEnabled=FullscreenOptions
     // we need to split the optional FullscreenOptions out controlled by RuntimeEnabled=FullscreenOptions
-    [RuntimeEnabled=FullscreenUnprefixed] void requestFullscreen(optional FullscreenOptions options);
+    [CallWith=ScriptState, RuntimeEnabled=FullscreenUnprefixed] Promise<void> requestFullscreen(optional FullscreenOptions options);
 
     [RuntimeEnabled=FullscreenUnprefixed] attribute EventHandler onfullscreenchange;
     [RuntimeEnabled=FullscreenUnprefixed] attribute EventHandler onfullscreenerror;
diff --git a/third_party/blink/renderer/core/fullscreen/fullscreen.cc b/third_party/blink/renderer/core/fullscreen/fullscreen.cc
index 365e652..26368f4 100644
--- a/third_party/blink/renderer/core/fullscreen/fullscreen.cc
+++ b/third_party/blink/renderer/core/fullscreen/fullscreen.cc
@@ -31,6 +31,7 @@
 
 #include "base/macros.h"
 #include "third_party/blink/public/platform/task_type.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/core/css/style_engine.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/element_traversal.h"
@@ -419,6 +420,7 @@
     full_screen_layout_object_->Destroy();
 
   pending_requests_.clear();
+  pending_exits_.clear();
   fullscreen_element_stack_.clear();
 }
 
@@ -429,9 +431,10 @@
   RequestFullscreen(pending, FullscreenOptions(), RequestType::kPrefixed);
 }
 
-void Fullscreen::RequestFullscreen(Element& pending,
-                                   const FullscreenOptions& options,
-                                   RequestType request_type) {
+ScriptPromise Fullscreen::RequestFullscreen(Element& pending,
+                                            const FullscreenOptions& options,
+                                            RequestType request_type,
+                                            ScriptState* script_state) {
   RequestFullscreenScope scope;
 
   // 1. Let |pending| be the context object.
@@ -440,12 +443,24 @@
   Document& document = pending.GetDocument();
 
   // 3. Let |promise| be a new promise.
-  // TODO(foolip): Promises. https://crbug.com/644637
+  // For optimization allocate the ScriptPromiseResolver just after step 4.
+  ScriptPromiseResolver* resolver = nullptr;
 
   // 4. If |pendingDoc| is not fully active, then reject |promise| with a
   // TypeError exception and return |promise|.
-  if (!document.IsActive() || !document.GetFrame())
-    return;
+  if (!document.IsActive() || !document.GetFrame()) {
+    if (!script_state)
+      return ScriptPromise();
+    return ScriptPromise::Reject(
+        script_state, V8ThrowException::CreateTypeError(
+                          script_state->GetIsolate(), "Document not active"));
+  }
+
+  if (script_state) {
+    // We should only be creating promises for unprefixed variants.
+    DCHECK_EQ(Fullscreen::RequestType::kUnprefixed, request_type);
+    resolver = ScriptPromiseResolver::Create(script_state);
+  }
 
   bool for_cross_process_descendant =
       request_type == RequestType::kPrefixedForCrossProcessDescendant;
@@ -474,6 +489,7 @@
     error = true;
 
   // 7. Return |promise|, and run the remaining steps in parallel.
+  ScriptPromise promise = resolver ? resolver->Promise() : ScriptPromise();
 
   // 8. If |error| is false: Resize |pendingDoc|'s top-level browsing context's
   // document's viewport's dimensions to match the dimensions of the screen of
@@ -486,7 +502,7 @@
     }
 
     From(document).pending_requests_.push_back(
-        std::make_pair(&pending, request_type));
+        new PendingRequest(&pending, request_type, resolver));
     LocalFrame& frame = *document.GetFrame();
     frame.GetChromeClient().EnterFullscreen(frame, options);
   } else {
@@ -494,9 +510,11 @@
     // synchronously because when |error| is true, |ContinueRequestFullscreen()|
     // will only queue a task and return. This is indistinguishable from, e.g.,
     // enqueueing a microtask to continue at step 9.
-    ContinueRequestFullscreen(document, pending, request_type,
+    ContinueRequestFullscreen(document, pending, request_type, resolver,
                               true /* error */);
   }
+
+  return promise;
 }
 
 void Fullscreen::DidEnterFullscreen() {
@@ -513,10 +531,11 @@
     return;
   }
 
-  ElementStack requests;
+  PendingRequests requests;
   requests.swap(pending_requests_);
-  for (const ElementStackEntry& request : requests) {
-    ContinueRequestFullscreen(*GetDocument(), *request.first, request.second,
+  for (const Member<PendingRequest>& request : requests) {
+    ContinueRequestFullscreen(*GetDocument(), *request->element(),
+                              request->type(), request->resolver(),
                               false /* error */);
   }
 }
@@ -524,6 +543,7 @@
 void Fullscreen::ContinueRequestFullscreen(Document& document,
                                            Element& pending,
                                            RequestType request_type,
+                                           ScriptPromiseResolver* resolver,
                                            bool error) {
   DCHECK(document.IsActive());
   DCHECK(document.GetFrame());
@@ -543,6 +563,13 @@
 
     // 10.2. Reject |promise| with a TypeError exception and terminate these
     // steps.
+    if (resolver) {
+      ScriptState::Scope scope(resolver->GetScriptState());
+      // TODO(dtapuska): Change error to be something useful instead of just a
+      // boolean and return this to the user.
+      resolver->Reject(V8ThrowException::CreateTypeError(
+          resolver->GetScriptState()->GetIsolate(), "fullscreen error"));
+    }
     return;
   }
 
@@ -594,6 +621,10 @@
   }
 
   // 14. Resolve |promise| with undefined.
+  if (resolver) {
+    ScriptState::Scope scope(resolver->GetScriptState());
+    resolver->Resolve();
+  }
 }
 
 // https://fullscreen.spec.whatwg.org/#fully-exit-fullscreen
@@ -619,14 +650,24 @@
 }
 
 // https://fullscreen.spec.whatwg.org/#exit-fullscreen
-void Fullscreen::ExitFullscreen(Document& doc) {
+ScriptPromise Fullscreen::ExitFullscreen(Document& doc,
+                                         ScriptState* script_state) {
   // 1. Let |promise| be a new promise.
-  // TODO(foolip): Promises. https://crbug.com/644637
+  // ScriptPromiseResolver is allocated after step 2.
+  ScriptPromiseResolver* resolver = nullptr;
 
   // 2. If |doc| is not fully active or |doc|'s fullscreen element is null, then
   // reject |promise| with a TypeError exception and return |promise|.
-  if (!doc.IsActive() || !doc.GetFrame() || !FullscreenElementFrom(doc))
-    return;
+  if (!doc.IsActive() || !doc.GetFrame() || !FullscreenElementFrom(doc)) {
+    if (!script_state)
+      return ScriptPromise();
+    return ScriptPromise::Reject(
+        script_state, V8ThrowException::CreateTypeError(
+                          script_state->GetIsolate(), "Document not active"));
+  }
+
+  if (script_state)
+    resolver = ScriptPromiseResolver::Create(script_state);
 
   // 3. Let |resize| be false.
   bool resize = false;
@@ -656,27 +697,48 @@
   }
 
   // 7. Return |promise|, and run the remaining steps in parallel.
+  ScriptPromise promise = resolver ? resolver->Promise() : ScriptPromise();
 
   // 8. If |resize| is true, resize |doc|'s viewport to its "normal" dimensions.
   if (resize) {
+    From(doc).pending_exits_.push_back(resolver);
     LocalFrame& frame = *doc.GetFrame();
     frame.GetChromeClient().ExitFullscreen(frame);
   } else {
     // Note: We are past the "in parallel" point, and |ContinueExitFullscreen()|
     // will change script-observable state (document.fullscreenElement)
     // synchronously, so we have to continue asynchronously.
-    Microtask::EnqueueMicrotask(WTF::Bind(
-        ContinueExitFullscreen, WrapWeakPersistent(&doc), false /* resize */));
+    Microtask::EnqueueMicrotask(
+        WTF::Bind(ContinueExitFullscreen, WrapPersistent(&doc),
+                  WrapPersistent(resolver), false /* resize */));
   }
+  return promise;
 }
 
 void Fullscreen::DidExitFullscreen() {
-  ContinueExitFullscreen(GetDocument(), true /* resize */);
+  Document* document = GetDocument();
+  PendingExits exits;
+  exits.swap(pending_exits_);
+  // Allow a UA originated fullscreen exit to complete.
+  if (exits.IsEmpty()) {
+    ContinueExitFullscreen(document, nullptr, true /* resize */);
+  } else {
+    for (const Member<PendingExit>& exit : exits)
+      ContinueExitFullscreen(document, exit, true /* resize */);
+  }
 }
 
-void Fullscreen::ContinueExitFullscreen(Document* doc, bool resize) {
-  if (!doc || !doc->IsActive() || !doc->GetFrame())
+void Fullscreen::ContinueExitFullscreen(Document* doc,
+                                        ScriptPromiseResolver* resolver,
+                                        bool resize) {
+  if (!doc || !doc->IsActive() || !doc->GetFrame()) {
+    if (resolver) {
+      ScriptState::Scope scope(resolver->GetScriptState());
+      resolver->Reject(V8ThrowException::CreateTypeError(
+          resolver->GetScriptState()->GetIsolate(), "Document is not active"));
+    }
     return;
+  }
 
   if (resize) {
     // See comment for step 6.
@@ -685,8 +747,13 @@
 
   // 9. If |doc|'s fullscreen element is null, then resolve |promise| with
   // undefined and terminate these steps.
-  if (!FullscreenElementFrom(*doc))
+  if (!FullscreenElementFrom(*doc)) {
+    if (resolver) {
+      ScriptState::Scope scope(resolver->GetScriptState());
+      resolver->Resolve();
+    }
     return;
+  }
 
   // 10. Let |exitDocs| be the result of collecting documents to unfullscreen
   // given |doc|.
@@ -743,6 +810,10 @@
   }
 
   // 14. Resolve |promise| with undefined.
+  if (resolver) {
+    ScriptState::Scope scope(resolver->GetScriptState());
+    resolver->Resolve();
+  }
 }
 
 // https://fullscreen.spec.whatwg.org/#dom-document-fullscreenenabled
@@ -910,9 +981,22 @@
 
 void Fullscreen::Trace(blink::Visitor* visitor) {
   visitor->Trace(pending_requests_);
+  visitor->Trace(pending_exits_);
   visitor->Trace(fullscreen_element_stack_);
   Supplement<Document>::Trace(visitor);
   ContextLifecycleObserver::Trace(visitor);
 }
 
+Fullscreen::PendingRequest::PendingRequest(Element* element,
+                                           RequestType type,
+                                           ScriptPromiseResolver* resolver)
+    : element_(element), type_(type), resolver_(resolver) {}
+
+Fullscreen::PendingRequest::~PendingRequest() = default;
+
+void Fullscreen::PendingRequest::Trace(blink::Visitor* visitor) {
+  visitor->Trace(element_);
+  visitor->Trace(resolver_);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/fullscreen/fullscreen.h b/third_party/blink/renderer/core/fullscreen/fullscreen.h
index 24b75842..309f14b 100644
--- a/third_party/blink/renderer/core/fullscreen/fullscreen.h
+++ b/third_party/blink/renderer/core/fullscreen/fullscreen.h
@@ -45,6 +45,7 @@
 class ComputedStyle;
 class FullscreenOptions;
 class LayoutFullScreen;
+class ScriptPromiseResolver;
 
 // The Fullscreen class implements most of the Fullscreen API Standard,
 // https://fullscreen.spec.whatwg.org/, especially its algorithms. It is a
@@ -80,12 +81,13 @@
   };
 
   static void RequestFullscreen(Element&);
-  static void RequestFullscreen(Element&,
-                                const FullscreenOptions&,
-                                RequestType);
+  static ScriptPromise RequestFullscreen(Element&,
+                                         const FullscreenOptions&,
+                                         RequestType,
+                                         ScriptState* state = nullptr);
 
   static void FullyExitFullscreen(Document&);
-  static void ExitFullscreen(Document&);
+  static ScriptPromise ExitFullscreen(Document&, ScriptState* state = nullptr);
 
   static bool FullscreenEnabled(Document&);
   Element* FullscreenElement() const {
@@ -122,9 +124,12 @@
   static void ContinueRequestFullscreen(Document&,
                                         Element&,
                                         RequestType,
+                                        ScriptPromiseResolver* resolver,
                                         bool error);
 
-  static void ContinueExitFullscreen(Document*, bool resize);
+  static void ContinueExitFullscreen(Document*,
+                                     ScriptPromiseResolver* resolver,
+                                     bool resize);
 
   void ClearFullscreenElementStack();
   void PopFullscreenElementStack();
@@ -135,12 +140,39 @@
 
   using ElementStackEntry = std::pair<Member<Element>, RequestType>;
   using ElementStack = HeapVector<ElementStackEntry>;
-  ElementStack pending_requests_;
   ElementStack fullscreen_element_stack_;
 
   LayoutFullScreen* full_screen_layout_object_;
   LayoutRect saved_placeholder_frame_rect_;
   scoped_refptr<ComputedStyle> saved_placeholder_computed_style_;
+
+  // Stores the pending request, promise and the type for executing
+  // the asynchronous portion of the request.
+  class PendingRequest : public GarbageCollectedFinalized<PendingRequest> {
+   public:
+    PendingRequest(Element* element,
+                   RequestType type,
+                   ScriptPromiseResolver* resolver);
+    virtual ~PendingRequest();
+    virtual void Trace(blink::Visitor* visitor);
+
+    Element* element() { return element_; }
+    RequestType type() { return type_; }
+    ScriptPromiseResolver* resolver() { return resolver_; }
+
+   private:
+    Member<Element> element_;
+    RequestType type_;
+    Member<ScriptPromiseResolver> resolver_;
+
+    DISALLOW_COPY_AND_ASSIGN(PendingRequest);
+  };
+  using PendingRequests = HeapVector<Member<PendingRequest>>;
+  PendingRequests pending_requests_;
+
+  using PendingExit = ScriptPromiseResolver;
+  using PendingExits = HeapVector<Member<PendingExit>>;
+  PendingExits pending_exits_;
 };
 
 inline Fullscreen* Fullscreen::FromIfExists(Document& document) {
diff --git a/third_party/blink/renderer/core/html/html_frame_owner_element.cc b/third_party/blink/renderer/core/html/html_frame_owner_element.cc
index d6a16ac..48e8b93 100644
--- a/third_party/blink/renderer/core/html/html_frame_owner_element.cc
+++ b/third_party/blink/renderer/core/html/html_frame_owner_element.cc
@@ -22,7 +22,9 @@
 
 #include <limits>
 
+#include "base/metrics/histogram_macros.h"
 #include "third_party/blink/public/platform/modules/fetch/fetch_api_request.mojom-shared.h"
+#include "third_party/blink/public/platform/web_effective_connection_type.h"
 #include "third_party/blink/renderer/bindings/core/v8/exception_messages.h"
 #include "third_party/blink/renderer/bindings/core/v8/exception_state.h"
 #include "third_party/blink/renderer/core/css/style_change_reason.h"
@@ -270,6 +272,11 @@
 }
 
 void HTMLFrameOwnerElement::DispatchLoad() {
+  if (time_when_first_load_finished_.is_null()) {
+    time_when_first_load_finished_ = CurrentTimeTicks();
+    RecordVisibilityMetricsIfLoadedAndVisible();
+  }
+
   DispatchScopedEvent(Event::Create(EventTypeNames::load));
 }
 
@@ -399,8 +406,7 @@
   if (IsPlugin())
     request.SetSkipServiceWorker(true);
 
-  if (RuntimeEnabledFeatures::LazyFrameLoadingEnabled() &&
-      should_lazy_load_children_ &&
+  if (should_lazy_load_children_ &&
       // Only http:// or https:// URLs are eligible for lazy loading, excluding
       // URLs like invalid or empty URLs, "about:blank", local file URLs, etc.
       // that it doesn't make sense to lazily load.
@@ -418,17 +424,37 @@
     // the viewport or visible.
     should_lazy_load_children_ = false;
 
-    lazy_load_intersection_observer_ = IntersectionObserver::Create(
-        {Length(kLazyLoadRootMarginPx, kFixed)},
-        {std::numeric_limits<float>::min()}, &GetDocument(),
-        WTF::BindRepeating(&HTMLFrameOwnerElement::LoadIfHiddenOrNearViewport,
-                           WrapWeakPersistent(this), request, child_load_type));
+    if (RuntimeEnabledFeatures::LazyFrameVisibleLoadTimeMetricsEnabled()) {
+      DCHECK(time_when_first_visible_.is_null());
+      DCHECK(!lazy_load_visibility_metrics_observer_);
 
-    lazy_load_intersection_observer_->observe(this);
-  } else {
-    child_frame->Loader().StartNavigation(
-        FrameLoadRequest(&GetDocument(), request), child_load_type);
+      lazy_load_visibility_metrics_observer_ = IntersectionObserver::Create(
+          {}, {std::numeric_limits<float>::min()}, &GetDocument(),
+          WTF::BindRepeating(
+              &HTMLFrameOwnerElement::RecordMetricsOnVisibilityChanged,
+              WrapWeakPersistent(this)));
+
+      lazy_load_visibility_metrics_observer_->observe(this);
+    }
+
+    if (RuntimeEnabledFeatures::LazyFrameLoadingEnabled()) {
+      DCHECK(!lazy_load_intersection_observer_);
+
+      lazy_load_intersection_observer_ = IntersectionObserver::Create(
+          {Length(kLazyLoadRootMarginPx, kFixed)},
+          {std::numeric_limits<float>::min()}, &GetDocument(),
+          WTF::BindRepeating(&HTMLFrameOwnerElement::LoadIfHiddenOrNearViewport,
+                             WrapWeakPersistent(this), request,
+                             child_load_type));
+
+      lazy_load_intersection_observer_->observe(this);
+      return true;
+    }
   }
+
+  child_frame->Loader().StartNavigation(
+      FrameLoadRequest(&GetDocument(), request), child_load_type);
+
   return true;
 }
 
@@ -457,6 +483,103 @@
                        frame_load_type);
 }
 
+void HTMLFrameOwnerElement::RecordMetricsOnVisibilityChanged(
+    const HeapVector<Member<IntersectionObserverEntry>>& entries) {
+  DCHECK(!entries.IsEmpty());
+  DCHECK_EQ(this, entries.back()->target());
+
+  if (IsFrameProbablyHidden(*entries.back()->boundingClientRect())) {
+    lazy_load_visibility_metrics_observer_->disconnect();
+    lazy_load_visibility_metrics_observer_.Clear();
+    return;
+  }
+
+  if (!has_above_the_fold_been_set_) {
+    is_initially_above_the_fold_ = entries.back()->isIntersecting();
+    has_above_the_fold_been_set_ = true;
+  }
+
+  if (!entries.back()->isIntersecting())
+    return;
+
+  DCHECK(time_when_first_visible_.is_null());
+  time_when_first_visible_ = CurrentTimeTicks();
+  RecordVisibilityMetricsIfLoadedAndVisible();
+
+  lazy_load_visibility_metrics_observer_->disconnect();
+  lazy_load_visibility_metrics_observer_.Clear();
+}
+
+void HTMLFrameOwnerElement::RecordVisibilityMetricsIfLoadedAndVisible() {
+  if (time_when_first_load_finished_.is_null() ||
+      time_when_first_visible_.is_null() || !GetDocument().GetFrame()) {
+    return;
+  }
+
+  DCHECK(has_above_the_fold_been_set_);
+
+  TimeDelta visible_load_delay =
+      time_when_first_load_finished_ - time_when_first_visible_;
+  if (visible_load_delay < TimeDelta())
+    visible_load_delay = TimeDelta();
+
+  switch (GetDocument().GetFrame()->Client()->GetEffectiveConnectionType()) {
+    case WebEffectiveConnectionType::kTypeSlow2G:
+      if (is_initially_above_the_fold_) {
+        UMA_HISTOGRAM_MEDIUM_TIMES(
+            "Blink.VisibleLoadTime.LazyLoadEligibleFrames.AboveTheFold.Slow2G",
+            visible_load_delay);
+      } else {
+        UMA_HISTOGRAM_MEDIUM_TIMES(
+            "Blink.VisibleLoadTime.LazyLoadEligibleFrames.BelowTheFold.Slow2G",
+            visible_load_delay);
+      }
+      break;
+
+    case WebEffectiveConnectionType::kType2G:
+      if (is_initially_above_the_fold_) {
+        UMA_HISTOGRAM_MEDIUM_TIMES(
+            "Blink.VisibleLoadTime.LazyLoadEligibleFrames.AboveTheFold.2G",
+            visible_load_delay);
+      } else {
+        UMA_HISTOGRAM_MEDIUM_TIMES(
+            "Blink.VisibleLoadTime.LazyLoadEligibleFrames.BelowTheFold.2G",
+            visible_load_delay);
+      }
+      break;
+
+    case WebEffectiveConnectionType::kType3G:
+      if (is_initially_above_the_fold_) {
+        UMA_HISTOGRAM_MEDIUM_TIMES(
+            "Blink.VisibleLoadTime.LazyLoadEligibleFrames.AboveTheFold.3G",
+            visible_load_delay);
+      } else {
+        UMA_HISTOGRAM_MEDIUM_TIMES(
+            "Blink.VisibleLoadTime.LazyLoadEligibleFrames.BelowTheFold.3G",
+            visible_load_delay);
+      }
+      break;
+
+    case WebEffectiveConnectionType::kType4G:
+      if (is_initially_above_the_fold_) {
+        UMA_HISTOGRAM_MEDIUM_TIMES(
+            "Blink.VisibleLoadTime.LazyLoadEligibleFrames.AboveTheFold.4G",
+            visible_load_delay);
+      } else {
+        UMA_HISTOGRAM_MEDIUM_TIMES(
+            "Blink.VisibleLoadTime.LazyLoadEligibleFrames.BelowTheFold.4G",
+            visible_load_delay);
+      }
+      break;
+
+    case WebEffectiveConnectionType::kTypeUnknown:
+    case WebEffectiveConnectionType::kTypeOffline:
+      // No VisibleLoadTime histograms are recorded for these effective
+      // connection types.
+      break;
+  }
+}
+
 void HTMLFrameOwnerElement::CancelPendingLazyLoad() {
   if (!lazy_load_intersection_observer_)
     return;
@@ -472,6 +595,7 @@
   visitor->Trace(content_frame_);
   visitor->Trace(embedded_content_view_);
   visitor->Trace(lazy_load_intersection_observer_);
+  visitor->Trace(lazy_load_visibility_metrics_observer_);
   HTMLElement::Trace(visitor);
   FrameOwner::Trace(visitor);
 }
diff --git a/third_party/blink/renderer/core/html/html_frame_owner_element.h b/third_party/blink/renderer/core/html/html_frame_owner_element.h
index 41b4d00..0bdba39 100644
--- a/third_party/blink/renderer/core/html/html_frame_owner_element.h
+++ b/third_party/blink/renderer/core/html/html_frame_owner_element.h
@@ -32,6 +32,7 @@
 #include "third_party/blink/renderer/platform/scroll/scroll_types.h"
 #include "third_party/blink/renderer/platform/weborigin/security_policy.h"
 #include "third_party/blink/renderer/platform/wtf/hash_counted_set.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
 
 namespace blink {
 
@@ -173,6 +174,11 @@
       FrameLoadType,
       const HeapVector<Member<IntersectionObserverEntry>>&);
 
+  void RecordMetricsOnVisibilityChanged(
+      const HeapVector<Member<IntersectionObserverEntry>>&);
+
+  void RecordVisibilityMetricsIfLoadedAndVisible();
+
   Member<Frame> content_frame_;
   Member<EmbeddedContentView> embedded_content_view_;
   SandboxFlags sandbox_flags_;
@@ -181,6 +187,19 @@
 
   Member<IntersectionObserver> lazy_load_intersection_observer_;
   bool should_lazy_load_children_;
+
+  // Keeps track of whether this frame was initially visible on the page.
+  bool is_initially_above_the_fold_ = false;
+  bool has_above_the_fold_been_set_ = false;
+
+  // Used to record visibility-related metrics related to lazy load. This is an
+  // IntersectionObserver instead of just an ElementVisibilityObserver so that
+  // hidden frames can be detected in order to avoid recording metrics for them.
+  Member<IntersectionObserver> lazy_load_visibility_metrics_observer_;
+  // Set when the frame first becomes visible (i.e. appears in the viewport).
+  TimeTicks time_when_first_visible_;
+  // Set when the first load event is dispatched for this frame.
+  TimeTicks time_when_first_load_finished_;
 };
 
 DEFINE_ELEMENT_TYPE_CASTS(HTMLFrameOwnerElement, IsFrameOwnerElement());
diff --git a/third_party/blink/renderer/core/html/html_frame_owner_element_test.cc b/third_party/blink/renderer/core/html/html_frame_owner_element_test.cc
index d2d41673..72b88fd 100644
--- a/third_party/blink/renderer/core/html/html_frame_owner_element_test.cc
+++ b/third_party/blink/renderer/core/html/html_frame_owner_element_test.cc
@@ -5,13 +5,17 @@
 #include "third_party/blink/renderer/core/html/html_frame_owner_element.h"
 
 #include <algorithm>
+#include <tuple>
 
+#include "base/optional.h"
+#include "third_party/blink/public/platform/web_effective_connection_type.h"
 #include "third_party/blink/renderer/core/exported/web_view_impl.h"
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
 #include "third_party/blink/renderer/core/testing/sim/sim_compositor.h"
 #include "third_party/blink/renderer/core/testing/sim/sim_request.h"
 #include "third_party/blink/renderer/core/testing/sim/sim_test.h"
 #include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/testing/histogram_tester.h"
 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
@@ -21,23 +25,100 @@
 
 namespace {
 
-class LazyLoadFramesTest : public SimTest {
+constexpr std::pair<WebEffectiveConnectionType, const char*>
+    kVisibleLoadTimeAboveTheFoldHistogramNames[] = {
+        {WebEffectiveConnectionType::kTypeSlow2G,
+         "Blink.VisibleLoadTime.LazyLoadEligibleFrames.AboveTheFold.Slow2G"},
+        {WebEffectiveConnectionType::kType2G,
+         "Blink.VisibleLoadTime.LazyLoadEligibleFrames.AboveTheFold.2G"},
+        {WebEffectiveConnectionType::kType3G,
+         "Blink.VisibleLoadTime.LazyLoadEligibleFrames.AboveTheFold.3G"},
+        {WebEffectiveConnectionType::kType4G,
+         "Blink.VisibleLoadTime.LazyLoadEligibleFrames.AboveTheFold.4G"},
+};
+
+constexpr std::pair<WebEffectiveConnectionType, const char*>
+    kVisibleLoadTimeBelowTheFoldHistogramNames[] = {
+        {WebEffectiveConnectionType::kTypeSlow2G,
+         "Blink.VisibleLoadTime.LazyLoadEligibleFrames.BelowTheFold.Slow2G"},
+        {WebEffectiveConnectionType::kType2G,
+         "Blink.VisibleLoadTime.LazyLoadEligibleFrames.BelowTheFold.2G"},
+        {WebEffectiveConnectionType::kType3G,
+         "Blink.VisibleLoadTime.LazyLoadEligibleFrames.BelowTheFold.3G"},
+        {WebEffectiveConnectionType::kType4G,
+         "Blink.VisibleLoadTime.LazyLoadEligibleFrames.BelowTheFold.4G"},
+};
+
+// Convenience enums to make it easy to access the appropriate value of the
+// tuple parameters in the parameterized tests below, e.g. so that
+// std::get<LazyFrameLoadingFeatureStatus>(GetParam()) can be used instead of
+// std::get<0>(GetParam()) if they were just booleans.
+enum class LazyFrameLoadingFeatureStatus { kDisabled, kEnabled };
+enum class LazyFrameVisibleLoadTimeFeatureStatus { kDisabled, kEnabled };
+
+class LazyLoadFramesTest : public SimTest,
+                           public ::testing::WithParamInterface<
+                               std::tuple<LazyFrameLoadingFeatureStatus,
+                                          LazyFrameVisibleLoadTimeFeatureStatus,
+                                          WebEffectiveConnectionType>> {
  public:
   static constexpr int kViewportWidth = 800;
   static constexpr int kViewportHeight = 600;
 
-  LazyLoadFramesTest() : scoped_lazy_frame_loading_for_test_(true) {}
+  LazyLoadFramesTest()
+      : scoped_lazy_frame_loading_for_test_(
+            std::get<LazyFrameLoadingFeatureStatus>(GetParam()) ==
+            LazyFrameLoadingFeatureStatus::kEnabled),
+        scoped_lazy_frame_visible_load_time_metrics_for_test_(
+            std::get<LazyFrameVisibleLoadTimeFeatureStatus>(GetParam()) ==
+            LazyFrameVisibleLoadTimeFeatureStatus::kEnabled) {}
 
   void SetUp() override {
+    SetEffectiveConnectionTypeForTesting(
+        std::get<WebEffectiveConnectionType>(GetParam()));
+
     SimTest::SetUp();
     WebView().Resize(WebSize(kViewportWidth, kViewportHeight));
   }
 
+  void ExpectVisibleLoadTimeHistogramSamplesIfApplicable(
+      int expected_above_the_fold_count,
+      int expected_below_the_fold_count) {
+    if (std::get<LazyFrameVisibleLoadTimeFeatureStatus>(GetParam()) !=
+        LazyFrameVisibleLoadTimeFeatureStatus::kEnabled) {
+      // Expect zero samples if the visible load time metrics feature is
+      // disabled.
+      expected_above_the_fold_count = 0;
+      expected_below_the_fold_count = 0;
+    }
+
+    for (const auto& pair : kVisibleLoadTimeAboveTheFoldHistogramNames) {
+      histogram_tester()->ExpectTotalCount(
+          pair.second,
+          std::get<WebEffectiveConnectionType>(GetParam()) == pair.first
+              ? expected_above_the_fold_count
+              : 0);
+    }
+    for (const auto& pair : kVisibleLoadTimeBelowTheFoldHistogramNames) {
+      histogram_tester()->ExpectTotalCount(
+          pair.second,
+          std::get<WebEffectiveConnectionType>(GetParam()) == pair.first
+              ? expected_below_the_fold_count
+              : 0);
+    }
+  }
+
+  HistogramTester* histogram_tester() { return &histogram_tester_; }
+
  private:
   ScopedLazyFrameLoadingForTest scoped_lazy_frame_loading_for_test_;
+  ScopedLazyFrameVisibleLoadTimeMetricsForTest
+      scoped_lazy_frame_visible_load_time_metrics_for_test_;
+
+  HistogramTester histogram_tester_;
 };
 
-TEST_F(LazyLoadFramesTest, SameOriginFrameIsNotDeferred) {
+TEST_P(LazyLoadFramesTest, SameOriginFrameIsNotDeferred) {
   SimRequest main_resource("https://example.com/", "text/html");
   SimRequest child_frame_resource("https://example.com/subframe.html",
                                   "text/html");
@@ -61,9 +142,11 @@
 
   EXPECT_TRUE(ConsoleMessages().Contains("main body onload"));
   EXPECT_TRUE(ConsoleMessages().Contains("child frame element onload"));
+
+  ExpectVisibleLoadTimeHistogramSamplesIfApplicable(0, 0);
 }
 
-TEST_F(LazyLoadFramesTest, AboveTheFoldFrameIsNotDeferred) {
+TEST_P(LazyLoadFramesTest, AboveTheFoldFrameIsNotDeferred) {
   SimRequest main_resource("https://example.com/", "text/html");
   SimRequest child_frame_resource("https://crossorigin.com/subframe.html",
                                   "text/html");
@@ -82,16 +165,17 @@
 
   Compositor().BeginFrame();
   test::RunPendingTasks();
-
   child_frame_resource.Complete("");
-
+  Compositor().BeginFrame();
   test::RunPendingTasks();
 
   EXPECT_TRUE(ConsoleMessages().Contains("main body onload"));
   EXPECT_TRUE(ConsoleMessages().Contains("child frame element onload"));
+
+  ExpectVisibleLoadTimeHistogramSamplesIfApplicable(1, 0);
 }
 
-TEST_F(LazyLoadFramesTest, BelowTheFoldButNearViewportFrameIsNotDeferred) {
+TEST_P(LazyLoadFramesTest, BelowTheFoldButNearViewportFrameIsNotDeferred) {
   SimRequest main_resource("https://example.com/", "text/html");
   SimRequest child_frame_resource("https://crossorigin.com/subframe.html",
                                   "text/html");
@@ -110,16 +194,29 @@
 
   Compositor().BeginFrame();
   test::RunPendingTasks();
-
   child_frame_resource.Complete("");
-
+  Compositor().BeginFrame();
   test::RunPendingTasks();
 
   EXPECT_TRUE(ConsoleMessages().Contains("main body onload"));
   EXPECT_TRUE(ConsoleMessages().Contains("child frame element onload"));
+
+  // The frame is below the fold, but hasn't been scrolled down to yet, so there
+  // should be no samples in any of the below the fold visible load time
+  // histograms yet.
+  ExpectVisibleLoadTimeHistogramSamplesIfApplicable(0, 0);
+
+  // Scroll down until the child frame is visible.
+  GetDocument().View()->LayoutViewportScrollableArea()->SetScrollOffset(
+      ScrollOffset(0, 150), kProgrammaticScroll);
+
+  Compositor().BeginFrame();
+  test::RunPendingTasks();
+
+  ExpectVisibleLoadTimeHistogramSamplesIfApplicable(0, 1);
 }
 
-TEST_F(LazyLoadFramesTest, HiddenAndTinyFramesAreNotDeferred) {
+TEST_P(LazyLoadFramesTest, HiddenAndTinyFramesAreNotDeferred) {
   SimRequest main_resource("https://example.com/", "text/html");
 
   SimRequest display_none_frame_resource(
@@ -181,6 +278,7 @@
   off_screen_left_frame_resource.Complete("");
   off_screen_top_frame_resource.Complete("");
 
+  Compositor().BeginFrame();
   test::RunPendingTasks();
 
   EXPECT_TRUE(ConsoleMessages().Contains("main body onload"));
@@ -190,10 +288,27 @@
   EXPECT_TRUE(ConsoleMessages().Contains("tiny height element onload"));
   EXPECT_TRUE(ConsoleMessages().Contains("off screen left element onload"));
   EXPECT_TRUE(ConsoleMessages().Contains("off screen top element onload"));
+
+  // All of the frames on the page are hidden or tiny, so no visible load time
+  // samples should have been recorded for them.
+  ExpectVisibleLoadTimeHistogramSamplesIfApplicable(0, 0);
 }
 
-TEST_F(LazyLoadFramesTest, CrossOriginFrameIsDeferredUntilNearViewport) {
+TEST_P(LazyLoadFramesTest, CrossOriginFrameIsDeferredUntilNearViewport) {
   SimRequest main_resource("https://example.com/", "text/html");
+  base::Optional<SimRequest> child_frame_resource;
+  base::Optional<SimRequest> nested_child_frame_resource;
+
+  if (std::get<LazyFrameLoadingFeatureStatus>(GetParam()) !=
+      LazyFrameLoadingFeatureStatus::kEnabled) {
+    // These SimRequests need to be defined now if the frames won't actually be
+    // lazily loaded. Otherwise, they'll be defined later to ensure that the
+    // subframe resources aren't requested until the page is scrolled down.
+    child_frame_resource.emplace("https://crossorigin.com/subframe.html",
+                                 "text/html");
+    nested_child_frame_resource.emplace("https://test.com/", "text/html");
+  }
+
   LoadURL("https://example.com/");
 
   main_resource.Complete(String::Format(
@@ -209,40 +324,69 @@
   Compositor().BeginFrame();
   test::RunPendingTasks();
 
-  EXPECT_TRUE(ConsoleMessages().Contains("main body onload"));
+  // If the child frame is being lazy loaded, then the body's load event should
+  // have already fired.
+  EXPECT_EQ(std::get<LazyFrameLoadingFeatureStatus>(GetParam()) ==
+                LazyFrameLoadingFeatureStatus::kEnabled,
+            ConsoleMessages().Contains("main body onload"));
   EXPECT_FALSE(ConsoleMessages().Contains("child frame element onload"));
 
-  SimRequest child_frame_resource("https://crossorigin.com/subframe.html",
-                                  "text/html");
+  ExpectVisibleLoadTimeHistogramSamplesIfApplicable(0, 0);
+
+  if (!child_frame_resource.has_value()) {
+    child_frame_resource.emplace("https://crossorigin.com/subframe.html",
+                                 "text/html");
+  }
 
   // Scroll down near the child frame. This should cause the child frame to get
   // loaded.
   GetDocument().View()->LayoutViewportScrollableArea()->SetScrollOffset(
-      ScrollOffset(0, kViewportHeight + 150), kProgrammaticScroll);
+      ScrollOffset(0, HTMLFrameOwnerElement::kLazyLoadRootMarginPx + 150),
+      kProgrammaticScroll);
 
   Compositor().BeginFrame();
   test::RunPendingTasks();
 
-  SimRequest nested_child_frame_resource("https://test.com/", "text/html");
+  EXPECT_FALSE(ConsoleMessages().Contains("child frame element onload"));
+
+  // The child frame is visible, but it hasn't finished loading yet, so no
+  // visible load time samples should have been recorded yet.
+  ExpectVisibleLoadTimeHistogramSamplesIfApplicable(0, 0);
+
+  if (!nested_child_frame_resource.has_value())
+    nested_child_frame_resource.emplace("https://test.com/", "text/html");
 
   // There's another nested cross origin iframe inside the first child frame,
   // partway down such that it's not near the viewport. It should still be
   // loaded immediately, and not deferred, since it's nested inside a frame that
   // was previously deferred.
-  child_frame_resource.Complete(
+  child_frame_resource->Complete(
       "<div style='height: 200px;'></div>"
       "<iframe src='https://test.com/' style='width: 200px; height: 200px;'>"
       "</iframe>");
 
+  Compositor().BeginFrame();
   test::RunPendingTasks();
-  nested_child_frame_resource.Complete("");
+
+  EXPECT_FALSE(ConsoleMessages().Contains("child frame element onload"));
+
+  // The child frame is visible, but it hasn't finished loading (due to the
+  // nested iframe inside it), so no visible load time samples should have been
+  // recorded yet.
+  ExpectVisibleLoadTimeHistogramSamplesIfApplicable(0, 0);
+
+  test::RunPendingTasks();
+  nested_child_frame_resource->Complete("");
+
+  Compositor().BeginFrame();
   test::RunPendingTasks();
 
   EXPECT_TRUE(ConsoleMessages().Contains("main body onload"));
   EXPECT_TRUE(ConsoleMessages().Contains("child frame element onload"));
+  ExpectVisibleLoadTimeHistogramSamplesIfApplicable(0, 1);
 }
 
-TEST_F(LazyLoadFramesTest, AboutBlankNavigationIsNotDeferred) {
+TEST_P(LazyLoadFramesTest, AboutBlankNavigationIsNotDeferred) {
   SimRequest main_resource("https://example.com/", "text/html");
   SimRequest child_frame_resource("https://crossorigin.com/subframe.html",
                                   "text/html");
@@ -283,9 +427,11 @@
   EXPECT_EQ(2, static_cast<int>(std::count(ConsoleMessages().begin(),
                                            ConsoleMessages().end(),
                                            "child frame element onload")));
+
+  ExpectVisibleLoadTimeHistogramSamplesIfApplicable(0, 0);
 }
 
-TEST_F(LazyLoadFramesTest, JavascriptStringUrlIsNotDeferred) {
+TEST_P(LazyLoadFramesTest, JavascriptStringUrlIsNotDeferred) {
   SimRequest main_resource("https://example.com/", "text/html");
   LoadURL("https://example.com/");
 
@@ -301,8 +447,27 @@
 
   EXPECT_TRUE(ConsoleMessages().Contains("main body onload"));
   EXPECT_TRUE(ConsoleMessages().Contains("child frame element onload"));
+
+  Compositor().BeginFrame();
+  test::RunPendingTasks();
+  ExpectVisibleLoadTimeHistogramSamplesIfApplicable(0, 0);
 }
 
+INSTANTIATE_TEST_CASE_P(
+    LazyFrameLoading,
+    LazyLoadFramesTest,
+    ::testing::Combine(
+        ::testing::Values(LazyFrameLoadingFeatureStatus::kDisabled,
+                          LazyFrameLoadingFeatureStatus::kEnabled),
+        ::testing::Values(LazyFrameVisibleLoadTimeFeatureStatus::kDisabled,
+                          LazyFrameVisibleLoadTimeFeatureStatus::kEnabled),
+        ::testing::Values(WebEffectiveConnectionType::kTypeUnknown,
+                          WebEffectiveConnectionType::kTypeOffline,
+                          WebEffectiveConnectionType::kTypeSlow2G,
+                          WebEffectiveConnectionType::kType2G,
+                          WebEffectiveConnectionType::kType3G,
+                          WebEffectiveConnectionType::kType4G)));
+
 }  // namespace
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/html_ruby_element.h b/third_party/blink/renderer/core/html/html_ruby_element.h
index 4181a0d3..f0cad9ff 100644
--- a/third_party/blink/renderer/core/html/html_ruby_element.h
+++ b/third_party/blink/renderer/core/html/html_ruby_element.h
@@ -19,6 +19,7 @@
   explicit HTMLRubyElement(Document&);
 
   LayoutObject* CreateLayoutObject(const ComputedStyle&) override;
+  bool ShouldForceLegacyLayout() const final { return true; }
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/input/mouse_event_manager.cc b/third_party/blink/renderer/core/input/mouse_event_manager.cc
index c2f51dae..2207b5d 100644
--- a/third_party/blink/renderer/core/input/mouse_event_manager.cc
+++ b/third_party/blink/renderer/core/input/mouse_event_manager.cc
@@ -970,40 +970,44 @@
                                                 mouse_down_pos_))
     return false;
 
+  if (DispatchDragSrcEvent(EventTypeNames::dragstart, mouse_down_) !=
+      WebInputEventResult::kNotHandled)
+    return false;
+
+  // Dispatching the event could cause |frame_| to be detached.
+  if (!frame_->GetPage())
+    return false;
+
   // If dispatching dragstart brings about another mouse down -- one way
   // this will happen is if a DevTools user breaks within a dragstart
   // handler and then clicks on the suspended page -- the drag state is
   // reset. Hence, need to check if this particular drag operation can
   // continue even if dispatchEvent() indicates no (direct) cancellation.
   // Do that by checking if m_dragSrc is still set.
-  mouse_down_may_start_drag_ = false;
-  if (DispatchDragSrcEvent(EventTypeNames::dragstart, mouse_down_) ==
-          WebInputEventResult::kNotHandled &&
-      GetDragState().drag_src_) {
-    // TODO(editing-dev): The use of
-    // updateStyleAndLayoutIgnorePendingStylesheets needs to be audited.  See
-    // http://crbug.com/590369 for more details.
-    frame_->GetDocument()->UpdateStyleAndLayoutIgnorePendingStylesheets();
-    mouse_down_may_start_drag_ = !IsInPasswordField(
-        frame_->Selection().ComputeVisibleSelectionInDOMTree().Start());
-  }
+  if (!GetDragState().drag_src_)
+    return false;
 
-  // Invalidate clipboard here against anymore pasteboard writing for security.
-  // The drag image can still be changed as we drag, but not the pasteboard
-  // data.
+  // Do not start dragging in password field.
+  // TODO(editing-dev): The use of
+  // updateStyleAndLayoutIgnorePendingStylesheets needs to be audited.  See
+  // http://crbug.com/590369 for more details.
+  frame_->GetDocument()->UpdateStyleAndLayoutIgnorePendingStylesheets();
+  if (IsInPasswordField(
+          frame_->Selection().ComputeVisibleSelectionInDOMTree().Start()))
+    return false;
+
+  // Invalidate clipboard here against anymore pasteboard writing for
+  // security. The drag image can still be changed as we drag, but not
+  // the pasteboard data.
   GetDragState().drag_data_transfer_->SetAccessPolicy(
       DataTransferAccessPolicy::kImageWritable);
 
-  if (mouse_down_may_start_drag_) {
-    // Dispatching the event could cause Page to go away. Make sure it's still
-    // valid before trying to use DragController.
-    if (frame_->GetPage() &&
-        drag_controller.StartDrag(frame_, GetDragState(), event.Event(),
-                                  mouse_down_pos_))
-      return true;
-    // Drag was canned at the last minute - we owe m_dragSrc a DRAGEND event
-    DispatchDragSrcEvent(EventTypeNames::dragend, event.Event());
-  }
+  if (drag_controller.StartDrag(frame_, GetDragState(), event.Event(),
+                                mouse_down_pos_))
+    return true;
+
+  // Drag was canned at the last minute - we owe m_dragSrc a DRAGEND event
+  DispatchDragSrcEvent(EventTypeNames::dragend, event.Event());
 
   return false;
 }
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc
index 087b58b..b2057c6f 100644
--- a/third_party/blink/renderer/core/layout/layout_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -5105,7 +5105,12 @@
     AddOutlineRects(outline_rects, LayoutPoint(),
                     OutlineRectsShouldIncludeBlockVisualOverflow());
     LayoutRect rect = UnionRectEvenIfEmpty(outline_rects);
-    SetOutlineMayBeAffectedByDescendants(rect.Size() != Size());
+    bool outline_affected = rect.Size() != Size();
+    // LayoutNG will set this flag for inline descendants
+    // which are not visible to Legacy code.
+    if (IsLayoutNGMixin())
+      outline_affected |= OutlineMayBeAffectedByDescendants();
+    SetOutlineMayBeAffectedByDescendants(outline_affected);
     rect.Inflate(style.OutlineOutsetExtent());
     outsets.Unite(rect.ToOutsets(Size()));
   }
diff --git a/third_party/blink/renderer/core/testing/sim/sim_test.cc b/third_party/blink/renderer/core/testing/sim/sim_test.cc
index 312b4310..7327418 100644
--- a/third_party/blink/renderer/core/testing/sim/sim_test.cc
+++ b/third_party/blink/renderer/core/testing/sim/sim_test.cc
@@ -80,6 +80,12 @@
   return compositor_;
 }
 
+void SimTest::SetEffectiveConnectionTypeForTesting(
+    WebEffectiveConnectionType effective_connection_type) {
+  web_frame_client_.SetEffectiveConnectionTypeForTesting(
+      effective_connection_type);
+}
+
 void SimTest::AddConsoleMessage(const String& message) {
   console_messages_.push_back(message);
 }
diff --git a/third_party/blink/renderer/core/testing/sim/sim_test.h b/third_party/blink/renderer/core/testing/sim/sim_test.h
index 125a4a9..ed38fac 100644
--- a/third_party/blink/renderer/core/testing/sim/sim_test.h
+++ b/third_party/blink/renderer/core/testing/sim/sim_test.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_TESTING_SIM_SIM_TEST_H_
 
 #include <gtest/gtest.h>
+#include "third_party/blink/public/platform/web_effective_connection_type.h"
 #include "third_party/blink/renderer/core/frame/frame_test_helpers.h"
 #include "third_party/blink/renderer/core/testing/sim/sim_compositor.h"
 #include "third_party/blink/renderer/core/testing/sim/sim_network.h"
@@ -42,6 +43,8 @@
 
   Vector<String>& ConsoleMessages() { return console_messages_; }
 
+  void SetEffectiveConnectionTypeForTesting(WebEffectiveConnectionType);
+
  private:
   friend class SimWebFrameClient;
 
diff --git a/third_party/blink/renderer/core/testing/sim/sim_web_frame_client.cc b/third_party/blink/renderer/core/testing/sim/sim_web_frame_client.cc
index 987844c..9ede2eb 100644
--- a/third_party/blink/renderer/core/testing/sim/sim_web_frame_client.cc
+++ b/third_party/blink/renderer/core/testing/sim/sim_web_frame_client.cc
@@ -9,7 +9,9 @@
 
 namespace blink {
 
-SimWebFrameClient::SimWebFrameClient(SimTest& test) : test_(&test) {}
+SimWebFrameClient::SimWebFrameClient(SimTest& test)
+    : test_(&test),
+      effective_connection_type_(WebEffectiveConnectionType::kTypeUnknown) {}
 
 void SimWebFrameClient::DidAddMessageToConsole(const WebConsoleMessage& message,
                                                const WebString& source_name,
@@ -18,4 +20,13 @@
   test_->AddConsoleMessage(message.text);
 }
 
+WebEffectiveConnectionType SimWebFrameClient::GetEffectiveConnectionType() {
+  return effective_connection_type_;
+}
+
+void SimWebFrameClient::SetEffectiveConnectionTypeForTesting(
+    WebEffectiveConnectionType effective_connection_type) {
+  effective_connection_type_ = effective_connection_type;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/testing/sim/sim_web_frame_client.h b/third_party/blink/renderer/core/testing/sim/sim_web_frame_client.h
index da4e78e..bc29eb3 100644
--- a/third_party/blink/renderer/core/testing/sim/sim_web_frame_client.h
+++ b/third_party/blink/renderer/core/testing/sim/sim_web_frame_client.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_TESTING_SIM_SIM_WEB_FRAME_CLIENT_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_TESTING_SIM_SIM_WEB_FRAME_CLIENT_H_
 
+#include "third_party/blink/public/platform/web_effective_connection_type.h"
 #include "third_party/blink/renderer/core/frame/frame_test_helpers.h"
 
 namespace blink {
@@ -21,8 +22,13 @@
                               unsigned source_line,
                               const WebString& stack_trace) override;
 
+  WebEffectiveConnectionType GetEffectiveConnectionType() override;
+  void SetEffectiveConnectionTypeForTesting(
+      WebEffectiveConnectionType) override;
+
  private:
   SimTest* test_;
+  WebEffectiveConnectionType effective_connection_type_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc b/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc
index a7a4bb97..008f30d 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc
@@ -55,13 +55,9 @@
 
   // Add entries to reverse map.
   for (const String& target_id : target_ids) {
-    HashSet<AXID>* source_axids = id_attr_to_related_mapping_.at(target_id);
-    if (!source_axids) {
-      source_axids = new HashSet<AXID>();
-      id_attr_to_related_mapping_.Set(target_id,
-                                      base::WrapUnique(source_axids));
-    }
-    source_axids->insert(relation_source_axid);
+    auto result =
+        id_attr_to_related_mapping_.insert(target_id, HashSet<AXID>());
+    result.stored_value->value.insert(relation_source_axid);
   }
 }
 
@@ -207,12 +203,11 @@
   if (!element->HasID())
     return;
 
-  String id = element->GetIdAttribute();
-  HashSet<AXID>* source_axids = id_attr_to_related_mapping_.at(id);
-  if (!source_axids)
+  auto it = id_attr_to_related_mapping_.find(element->GetIdAttribute());
+  if (it == id_attr_to_related_mapping_.end())
     return;
 
-  for (const auto& source_axid : *source_axids) {
+  for (const auto& source_axid : it->value) {
     AXObject* source_object = ObjectFromAXID(source_axid);
     if (source_object)
       source_objects.push_back(source_object);
diff --git a/third_party/blink/renderer/modules/accessibility/ax_relation_cache.h b/third_party/blink/renderer/modules/accessibility/ax_relation_cache.h
index d11cf08a..1959d1ad 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_relation_cache.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_relation_cache.h
@@ -109,7 +109,7 @@
   //   quickly determine if it affects an aria-owns relationship.
   // - When text changes, we can recompute any label or description based on it
   //   and fire the appropriate change events.
-  HashMap<String, std::unique_ptr<HashSet<AXID>>> id_attr_to_related_mapping_;
+  HashMap<String, HashSet<AXID>> id_attr_to_related_mapping_;
 
   // Helpers that call back into object cache
   AXObject* ObjectFromAXID(AXID) const;
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_loading_panel_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_loading_panel_element.cc
index 37e3b1b..8348f62 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_loading_panel_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_loading_panel_element.cc
@@ -11,7 +11,6 @@
 #include "third_party/blink/renderer/core/html/html_div_element.h"
 #include "third_party/blink/renderer/core/html/html_style_element.h"
 #include "third_party/blink/renderer/core/html/media/html_media_element.h"
-#include "third_party/blink/renderer/core/html/media/html_video_element.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
 #include "third_party/blink/renderer/modules/media_controls/elements/media_control_elements_helper.h"
 #include "third_party/blink/renderer/modules/media_controls/media_controls_impl.h"
@@ -20,9 +19,7 @@
 namespace {
 
 static const char kAnimationIterationCountName[] = "animation-iteration-count";
-static const char kClassAttributeName[] = "class";
 static const char kInfinite[] = "infinite";
-static const char kNoFrameAvailableSpinnerClass[] = "dark";
 
 }  // namespace
 
@@ -48,11 +45,6 @@
 //     | | +- #spinner-mask-1-background
 //     \ +- #spinner-mask-2
 //         +- #spinner-mask-2-background
-// +- #cutoff-1
-// +- #cutoff-2
-// +- #cutoff-3
-// +- #cutoff-4
-// +- #fake-timeline
 void MediaControlLoadingPanelElement::PopulateShadowDOM() {
   ShadowRoot* shadow_root = GetShadowRoot();
   DCHECK(!shadow_root->HasChildren());
@@ -74,13 +66,12 @@
 
   // The spinner is responsible for rotating the elements below. The square
   // edges will be cut off by the frame above.
-  spinner_ =
+  HTMLDivElement* spinner =
       MediaControlElementsHelper::CreateDivWithId("spinner", spinner_frame);
-  SetSpinnerClassIfNecessary();
 
   // The layer performs a secondary "fill-unfill-rotate" animation.
   HTMLDivElement* layer =
-      MediaControlElementsHelper::CreateDivWithId("layer", spinner_);
+      MediaControlElementsHelper::CreateDivWithId("layer", spinner);
 
   // The spinner is split into two halves, one on the left (1) and the other
   // on the right (2). The mask elements stop the background from overlapping
@@ -99,18 +90,6 @@
       "spinner-mask-2-background", mask2);
 
   event_listener_ = new MediaControlAnimationEventListener(this);
-
-  // The four cutoffs are responsible for filling the background of the loading
-  // panel with white, whilst leaving a small box in the middle that is
-  // transparent. This is where the spinner will be.
-  MediaControlElementsHelper::CreateDivWithId("cutoff-1", shadow_root);
-  MediaControlElementsHelper::CreateDivWithId("cutoff-2", shadow_root);
-  MediaControlElementsHelper::CreateDivWithId("cutoff-3", shadow_root);
-  MediaControlElementsHelper::CreateDivWithId("cutoff-4", shadow_root);
-
-  // The fake timeline creates a fake bar at the bottom to look like the
-  // timeline.
-  MediaControlElementsHelper::CreateDivWithId("fake-timeline", shadow_root);
 }
 
 void MediaControlLoadingPanelElement::RemovedFrom(
@@ -133,7 +112,6 @@
   }
   shadow_root->RemoveChildren();
 
-  spinner_.Clear();
   mask1_background_.Clear();
   mask2_background_.Clear();
 }
@@ -221,24 +199,6 @@
 
 void MediaControlLoadingPanelElement::OnAnimationIteration() {
   animation_count_ += 1;
-  SetSpinnerClassIfNecessary();
-}
-
-void MediaControlLoadingPanelElement::SetSpinnerClassIfNecessary() {
-  if (!spinner_)
-    return;
-
-  HTMLVideoElement& video_element =
-      static_cast<HTMLVideoElement&>(MediaElement());
-  if (!video_element.ShouldDisplayPosterImage() &&
-      !video_element.HasAvailableVideoFrame()) {
-    if (!spinner_->hasAttribute(kClassAttributeName)) {
-      spinner_->setAttribute(kClassAttributeName,
-                             kNoFrameAvailableSpinnerClass);
-    }
-  } else {
-    spinner_->removeAttribute(kClassAttributeName);
-  }
 }
 
 Element& MediaControlLoadingPanelElement::WatchedAnimationElement() const {
@@ -250,7 +210,6 @@
   MediaControlAnimationEventListener::Observer::Trace(visitor);
   MediaControlDivElement::Trace(visitor);
   visitor->Trace(event_listener_);
-  visitor->Trace(spinner_);
   visitor->Trace(mask1_background_);
   visitor->Trace(mask2_background_);
 }
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_loading_panel_element.h b/third_party/blink/renderer/modules/media_controls/elements/media_control_loading_panel_element.h
index 9f9ef1d..15b30a4 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_loading_panel_element.h
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_loading_panel_element.h
@@ -63,10 +63,6 @@
   // background elements.
   void SetAnimationIterationCount(const String&);
 
-  // Sets the background color of the spinner to black if there is no poster
-  // image or video frame available.
-  void SetSpinnerClassIfNecessary();
-
   // The loading panel is only used once and has a lot of DOM elements so these
   // two functions will populate the shadow DOM or clean it if the panel is
   // hidden.
@@ -85,7 +81,6 @@
   bool controls_hidden_ = false;
 
   Member<MediaControlAnimationEventListener> event_listener_;
-  Member<HTMLDivElement> spinner_;
   Member<HTMLDivElement> mask1_background_;
   Member<HTMLDivElement> mask2_background_;
 };
diff --git a/third_party/blink/renderer/modules/media_controls/resources/default_100_percent/modern/loading_mask_1.svg b/third_party/blink/renderer/modules/media_controls/resources/default_100_percent/modern/loading_mask_1.svg
index bbbde1e..03a1b04 100644
--- a/third_party/blink/renderer/modules/media_controls/resources/default_100_percent/modern/loading_mask_1.svg
+++ b/third_party/blink/renderer/modules/media_controls/resources/default_100_percent/modern/loading_mask_1.svg
@@ -2,10 +2,7 @@
 <!-- Generator: Adobe Illustrator 19.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
 <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
 	 viewBox="0 0 196 196" style="enable-background:new 0 0 196 196;" xml:space="preserve">
-<style type="text/css">
-	.st0{fill:#FFFFFF;}
-</style>
-<path class="st0" d="M0,0l0,49l0,98l0,49h49h98h49v-49V49V0l-49,0L49,0L0,0z M98,49v4c-24.9,0-45,20.1-45,45
+<path class="st0" d="M98,49v4c-24.9,0-45,20.1-45,45
 	c0,18,10.6,33.6,25.9,40.8l-1.7,3.6c0.1,0,0.2,0.1,0.3,0.1c-0.1,0-0.2-0.1-0.3-0.1l0,0C60.5,134.5,49,117.6,49,98
 	C49,70.9,70.9,49,98,49z"/>
 </svg>
diff --git a/third_party/blink/renderer/modules/media_controls/resources/default_100_percent/modern/loading_mask_2.svg b/third_party/blink/renderer/modules/media_controls/resources/default_100_percent/modern/loading_mask_2.svg
index 380e62d4f..a8763b0 100644
--- a/third_party/blink/renderer/modules/media_controls/resources/default_100_percent/modern/loading_mask_2.svg
+++ b/third_party/blink/renderer/modules/media_controls/resources/default_100_percent/modern/loading_mask_2.svg
@@ -2,9 +2,6 @@
 <!-- Generator: Adobe Illustrator 19.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
 <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
 	 viewBox="0 0 196 196" style="enable-background:new 0 0 196 196;" xml:space="preserve">
-<style type="text/css">
-	.st0{fill:#FFFFFF;enable-background:new    ;}
-</style>
-<path class="st0" d="M147,0H49H0v49v98v49h49h98h49v-49V49V0H147z M147,98c0,19.6-11.5,36.5-28.2,44.4l0,0c-0.1,0-0.2,0.1-0.3,0.1
+<path class="st0" d="M147,98c0,19.6-11.5,36.5-28.2,44.4l0,0c-0.1,0-0.2,0.1-0.3,0.1
 	c0.1,0,0.2-0.1,0.3-0.1l-1.7-3.6C132.4,131.6,143,116,143,98c0-24.9-20.1-45-45-45v-4C125.1,49,147,70.9,147,98z"/>
 </svg>
diff --git a/third_party/blink/renderer/modules/media_controls/resources/modernMediaControls.css b/third_party/blink/renderer/modules/media_controls/resources/modernMediaControls.css
index 2cd56fb..16549ba 100644
--- a/third_party/blink/renderer/modules/media_controls/resources/modernMediaControls.css
+++ b/third_party/blink/renderer/modules/media_controls/resources/modernMediaControls.css
@@ -685,7 +685,8 @@
   right: 0;
   bottom: 0;
   overflow: hidden;
-  opacity: .5;
+  z-index: 1;
+  pointer-events: none;
 }
 
 audio::-internal-media-controls-loading-panel,
@@ -693,16 +694,6 @@
   display: none;
 }
 
-.state-loading-metadata div[pseudo="-webkit-media-controls-panel" i] {
-  background-image: none;
-}
-
-.state-loading-metadata div[pseudo="-internal-media-controls-button-panel" i],
-.state-loading-metadata input[pseudo="-webkit-media-controls-timeline" i],
-.state-loading-metadata input[pseudo="-webkit-media-controls-overlay-play-button" i]::-internal-media-controls-overlay-play-button-internal {
-  opacity: 0;
-}
-
 /**
  * Text Tracks
  */
diff --git a/third_party/blink/renderer/modules/media_controls/resources/modernMediaControls_loading.css b/third_party/blink/renderer/modules/media_controls/resources/modernMediaControls_loading.css
index e9f59a6..b5cdfed 100644
--- a/third_party/blink/renderer/modules/media_controls/resources/modernMediaControls_loading.css
+++ b/third_party/blink/renderer/modules/media_controls/resources/modernMediaControls_loading.css
@@ -19,10 +19,6 @@
   width: 100%;
 }
 
-#spinner.dark {
-  background-color: black;
-}
-
 #spinner:after {
   position: absolute;
   content: ' ';
@@ -30,9 +26,6 @@
   left: -25%;
   width: 150%;
   height: 150%;
-  background-image: -webkit-image-set(
-    url(default_100_percent/modern/loading_mask_overlay.svg) 1x);
-  background-position: center;
 }
 
 #spinner:before {
@@ -42,7 +35,6 @@
   left: 2px;
   right: 2px;
   bottom: 2px;
-  background: white;
   border-radius: 100%;
 }
 
@@ -128,57 +120,3 @@
   50% { transform: rotate(-30deg) }
   to { transform: rotate(-155deg) }
 }
-
-#cutoff-1,
-#cutoff-2,
-#cutoff-3,
-#cutoff-4 {
-  position: absolute;
-  background: white;
-}
-
-#cutoff-1,
-#cutoff-2 {
-  left: 0;
-  right: 0;
-}
-
-#cutoff-1 {
-  top: 0;
-  bottom: 50%;
-  margin-bottom: 37px;
-}
-
-#cutoff-2 {
-  top: 50%;
-  bottom: 16px;
-  margin-top: 19px;
-}
-
-#cutoff-3,
-#cutoff-4 {
-  top: 0;
-  bottom: 16px;
-}
-
-#cutoff-3 {
-  left: 0;
-  right: 50%;
-  margin-right: 28px;
-}
-
-#cutoff-4 {
-  left: 50%;
-  right: 0;
-  margin-left: 28px;
-}
-
-#fake-timeline {
-  bottom: 0;
-  position: absolute;
-  left: 0;
-  right: 0;
-  border: solid white;
-  border-width: 0 16px 12px;
-  height: 4px;
-}
diff --git a/third_party/blink/renderer/modules/serviceworkers/service_worker_global_scope_proxy.cc b/third_party/blink/renderer/modules/serviceworkers/service_worker_global_scope_proxy.cc
index 91f1dbc..69ab797 100644
--- a/third_party/blink/renderer/modules/serviceworkers/service_worker_global_scope_proxy.cc
+++ b/third_party/blink/renderer/modules/serviceworkers/service_worker_global_scope_proxy.cc
@@ -150,6 +150,9 @@
   ScriptState* script_state =
       WorkerGlobalScope()->ScriptController()->GetScriptState();
 
+  // Do not remove this, it modifies V8 state.
+  ScriptState::Scope scope(script_state);
+
   BackgroundFetchSettledEventInit init;
   init.setId(developer_id);
   init.setFetches(BackgroundFetchSettledFetches::Create(script_state, fetches));
@@ -201,6 +204,9 @@
   ScriptState* script_state =
       WorkerGlobalScope()->ScriptController()->GetScriptState();
 
+  // Do not remove this, it modifies V8 state.
+  ScriptState::Scope scope(script_state);
+
   BackgroundFetchSettledEventInit init;
   init.setId(developer_id);
   init.setFetches(BackgroundFetchSettledFetches::Create(script_state, fetches));
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index ad3935a2..90fcfc6 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -601,8 +601,6 @@
     "file_metadata.cc",
     "file_metadata.h",
     "file_system_type.h",
-    "fonts/accept_languages_resolver.cc",
-    "fonts/accept_languages_resolver.h",
     "fonts/alternate_font_family.h",
     "fonts/android/font_cache_android.cc",
     "fonts/bitmap_glyphs_blacklist.cc",
@@ -1751,7 +1749,6 @@
     "exported/web_cors_test.cc",
     "exported/web_string_test.cc",
     "feature_policy/feature_policy_test.cc",
-    "fonts/accept_languages_resolver_test.cc",
     "fonts/android/font_cache_android_test.cc",
     "fonts/bitmap_glyphs_blacklist_test.cc",
     "fonts/font_cache_test.cc",
diff --git a/third_party/blink/renderer/platform/DEPS b/third_party/blink/renderer/platform/DEPS
index 2da4a9c..b013420f 100644
--- a/third_party/blink/renderer/platform/DEPS
+++ b/third_party/blink/renderer/platform/DEPS
@@ -5,6 +5,7 @@
     "+base/bind.h",
     "+base/bind_helpers.h",
     "+base/bit_cast.h",
+    "+base/compiler_specific.h",
     "+base/cpu.h",
     "+base/feature_list.h",
     "+base/files",
diff --git a/third_party/blink/renderer/platform/exported/web_runtime_features.cc b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
index 66674cfe..ed8d99b 100644
--- a/third_party/blink/renderer/platform/exported/web_runtime_features.cc
+++ b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
@@ -180,6 +180,10 @@
   RuntimeEnabledFeatures::SetLazyFrameLoadingEnabled(enable);
 }
 
+void WebRuntimeFeatures::EnableLazyFrameVisibleLoadTimeMetrics(bool enable) {
+  RuntimeEnabledFeatures::SetLazyFrameVisibleLoadTimeMetricsEnabled(enable);
+}
+
 void WebRuntimeFeatures::EnableLazyParseCSS(bool enable) {
   RuntimeEnabledFeatures::SetLazyParseCSSEnabled(enable);
 }
diff --git a/third_party/blink/renderer/platform/fonts/accept_languages_resolver.cc b/third_party/blink/renderer/platform/fonts/accept_languages_resolver.cc
deleted file mode 100644
index 3939b0c..0000000
--- a/third_party/blink/renderer/platform/fonts/accept_languages_resolver.cc
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "third_party/blink/renderer/platform/fonts/accept_languages_resolver.h"
-
-#include "third_party/blink/renderer/platform/fonts/font_global_context.h"
-#include "third_party/blink/renderer/platform/layout_locale.h"
-
-namespace blink {
-
-void AcceptLanguagesResolver::AcceptLanguagesChanged(
-    const String& accept_languages) {
-  String& current_value = FontGlobalContext::CurrentAcceptLanguages();
-  if (current_value == accept_languages)
-    return;
-
-  current_value = accept_languages;
-  FontGlobalContext::InvalidateLocaleForHan();
-}
-
-const LayoutLocale* AcceptLanguagesResolver::LocaleForHan() {
-  return LocaleForHanFromAcceptLanguages(
-      FontGlobalContext::CurrentAcceptLanguages());
-}
-
-const LayoutLocale* AcceptLanguagesResolver::LocaleForHanFromAcceptLanguages(
-    const String& accept_languages) {
-  // Use the first acceptLanguages that can disambiguate.
-  Vector<String> languages;
-  accept_languages.Split(',', languages);
-  for (String token : languages) {
-    token = token.StripWhiteSpace();
-    const LayoutLocale* locale = LayoutLocale::Get(AtomicString(token));
-    if (locale->HasScriptForHan())
-      return locale;
-  }
-
-  return nullptr;
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/platform/fonts/accept_languages_resolver.h b/third_party/blink/renderer/platform/fonts/accept_languages_resolver.h
deleted file mode 100644
index f664ecbf..0000000
--- a/third_party/blink/renderer/platform/fonts/accept_languages_resolver.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_ACCEPT_LANGUAGES_RESOLVER_H_
-#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_ACCEPT_LANGUAGES_RESOLVER_H_
-
-#include "third_party/blink/renderer/platform/platform_export.h"
-#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
-
-#include <unicode/uscript.h>
-
-namespace blink {
-
-class LayoutLocale;
-
-class PLATFORM_EXPORT AcceptLanguagesResolver {
- public:
-  static void AcceptLanguagesChanged(const String&);
-
-  static const LayoutLocale* LocaleForHan();
-  static const LayoutLocale* LocaleForHanFromAcceptLanguages(const String&);
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_ACCEPT_LANGUAGES_RESOLVER_H_
diff --git a/third_party/blink/renderer/platform/fonts/accept_languages_resolver_test.cc b/third_party/blink/renderer/platform/fonts/accept_languages_resolver_test.cc
deleted file mode 100644
index ef0be29..0000000
--- a/third_party/blink/renderer/platform/fonts/accept_languages_resolver_test.cc
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "third_party/blink/renderer/platform/fonts/accept_languages_resolver.h"
-
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/renderer/platform/layout_locale.h"
-
-namespace blink {
-
-TEST(AcceptLanguagesResolverTest, AcceptLanguagesChanged) {
-  struct {
-    const char* accept_languages;
-    UScriptCode script;
-    const char* locale;
-  } tests[] = {
-      // Non-Han script cases.
-      {nullptr, USCRIPT_COMMON, nullptr},
-      {"", USCRIPT_COMMON, nullptr},
-      {"en-US", USCRIPT_COMMON, nullptr},
-      {",en-US", USCRIPT_COMMON, nullptr},
-
-      // Single value cases.
-      {"ja-JP", USCRIPT_KATAKANA_OR_HIRAGANA, "ja-jp"},
-      {"ko-KR", USCRIPT_HANGUL, "ko-kr"},
-      {"zh-CN", USCRIPT_SIMPLIFIED_HAN, "zh-Hans"},
-      {"zh-HK", USCRIPT_TRADITIONAL_HAN, "zh-Hant"},
-      {"zh-TW", USCRIPT_TRADITIONAL_HAN, "zh-Hant"},
-
-      // Language only.
-      {"ja", USCRIPT_KATAKANA_OR_HIRAGANA, "ja-jp"},
-      {"ko", USCRIPT_HANGUL, "ko-kr"},
-      {"zh", USCRIPT_SIMPLIFIED_HAN, "zh-Hans"},
-
-      // Unusual combinations.
-      {"en-JP", USCRIPT_KATAKANA_OR_HIRAGANA, "ja-jp"},
-
-      // Han scripts not in the first item.
-      {"en-US,ja-JP", USCRIPT_KATAKANA_OR_HIRAGANA, "ja-jp"},
-      {"en-US,en-JP", USCRIPT_KATAKANA_OR_HIRAGANA, "ja-jp"},
-
-      // Multiple Han scripts. The first one wins.
-      {"ja-JP,zh-CN", USCRIPT_KATAKANA_OR_HIRAGANA, "ja-jp"},
-      {"zh-TW,ja-JP", USCRIPT_TRADITIONAL_HAN, "zh-Hant"},
-  };
-
-  for (const auto& test : tests) {
-    const LayoutLocale* locale =
-        AcceptLanguagesResolver::LocaleForHanFromAcceptLanguages(
-            test.accept_languages);
-
-    if (test.script == USCRIPT_COMMON) {
-      EXPECT_EQ(nullptr, locale) << test.accept_languages;
-      continue;
-    }
-
-    ASSERT_NE(nullptr, locale) << test.accept_languages;
-    EXPECT_EQ(test.script, locale->GetScriptForHan()) << test.accept_languages;
-    EXPECT_STRCASEEQ(test.locale, locale->LocaleForHanForSkFontMgr())
-        << test.accept_languages;
-  }
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/platform/fonts/font_cache.cc b/third_party/blink/renderer/platform/fonts/font_cache.cc
index 12c6bfa..fc9fefa3 100644
--- a/third_party/blink/renderer/platform/fonts/font_cache.cc
+++ b/third_party/blink/renderer/platform/fonts/font_cache.cc
@@ -38,7 +38,6 @@
 #include "build/build_config.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/platform/font_family_names.h"
-#include "third_party/blink/renderer/platform/fonts/accept_languages_resolver.h"
 #include "third_party/blink/renderer/platform/fonts/alternate_font_family.h"
 #include "third_party/blink/renderer/platform/fonts/font_cache_client.h"
 #include "third_party/blink/renderer/platform/fonts/font_cache_key.h"
@@ -53,6 +52,7 @@
 #include "third_party/blink/renderer/platform/histogram.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/web_memory_allocator_dump.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/web_process_memory_dump.h"
+#include "third_party/blink/renderer/platform/layout_locale.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/wtf/hash_map.h"
 #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
@@ -209,7 +209,7 @@
 }
 
 void FontCache::AcceptLanguagesChanged(const String& accept_languages) {
-  AcceptLanguagesResolver::AcceptLanguagesChanged(accept_languages);
+  LayoutLocale::AcceptLanguagesChanged(accept_languages);
   GetFontCache()->InvalidateShapeCache();
 }
 
diff --git a/third_party/blink/renderer/platform/fonts/font_global_context.cc b/third_party/blink/renderer/platform/fonts/font_global_context.cc
index e90f6d7..7745d5a0 100644
--- a/third_party/blink/renderer/platform/fonts/font_global_context.cc
+++ b/third_party/blink/renderer/platform/fonts/font_global_context.cc
@@ -5,7 +5,6 @@
 #include "third_party/blink/renderer/platform/fonts/font_global_context.h"
 
 #include "third_party/blink/renderer/platform/fonts/font_cache.h"
-#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
 #include "third_party/blink/renderer/platform/wtf/thread_specific.h"
 
 namespace blink {
@@ -19,12 +18,7 @@
   return *font_persistent;
 }
 
-FontGlobalContext::FontGlobalContext()
-    : harfbuzz_font_funcs_(nullptr),
-      default_locale_(nullptr),
-      system_locale_(nullptr),
-      default_locale_for_han_(nullptr),
-      has_default_locale_for_han_(false) {}
+FontGlobalContext::FontGlobalContext() : harfbuzz_font_funcs_(nullptr) {}
 
 void FontGlobalContext::ClearMemory() {
   if (!Get(kDoNotCreate))
@@ -35,11 +29,7 @@
 
 void FontGlobalContext::ClearForTesting() {
   FontGlobalContext* ctx = Get();
-  ctx->default_locale_ = nullptr;
-  ctx->system_locale_ = nullptr;
-  ctx->default_locale_for_han_ = nullptr;
-  ctx->has_default_locale_for_han_ = false;
-  ctx->layout_locale_map_.clear();
+  ctx->layout_locale_data_ = LayoutLocale::PerThreadData();
   ctx->font_cache_.Invalidate();
 }
 
diff --git a/third_party/blink/renderer/platform/fonts/font_global_context.h b/third_party/blink/renderer/platform/fonts/font_global_context.h
index 256d435..4c08765 100644
--- a/third_party/blink/renderer/platform/fonts/font_global_context.h
+++ b/third_party/blink/renderer/platform/fonts/font_global_context.h
@@ -7,22 +7,15 @@
 
 #include "third_party/blink/renderer/platform/fonts/font_cache.h"
 #include "third_party/blink/renderer/platform/fonts/shaping/harf_buzz_font_cache.h"
-#include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
+#include "third_party/blink/renderer/platform/layout_locale.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
-#include "third_party/blink/renderer/platform/wtf/hash_map.h"
-#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
-#include "third_party/blink/renderer/platform/wtf/text/atomic_string_hash.h"
 
 struct hb_font_funcs_t;
 
 namespace blink {
 
-class LayoutLocale;
 class FontCache;
 
-using LayoutLocaleMap =
-    HashMap<AtomicString, scoped_refptr<LayoutLocale>, CaseFoldingHash>;
-
 enum CreateIfNeeded { kDoNotCreate, kCreate };
 
 // FontGlobalContext contains non-thread-safe, thread-specific data used for
@@ -47,44 +40,8 @@
     Get()->harfbuzz_font_funcs_ = funcs;
   }
 
-  static inline LayoutLocaleMap& GetLayoutLocaleMap() {
-    return Get()->layout_locale_map_;
-  }
-
-  static inline const LayoutLocale* GetDefaultLayoutLocale() {
-    return Get()->default_locale_;
-  }
-  static inline void SetDefaultLayoutLocale(const LayoutLocale* locale) {
-    Get()->default_locale_ = locale;
-  }
-  static inline const LayoutLocale* GetSystemLayoutLocale() {
-    return Get()->system_locale_;
-  }
-  static inline void SetSystemLayoutLocale(const LayoutLocale* locale) {
-    Get()->system_locale_ = locale;
-  }
-
-  static inline const LayoutLocale* GetDefaultLocaleForHan() {
-    FontGlobalContext* ctx = Get();
-    DCHECK(ctx->has_default_locale_for_han_);
-    return ctx->default_locale_for_han_;
-  }
-  static inline void SetDefaultLocaleForHan(const LayoutLocale* locale) {
-    FontGlobalContext* ctx = Get();
-    ctx->default_locale_for_han_ = locale;
-    ctx->has_default_locale_for_han_ = true;
-  }
-  static inline void InvalidateLocaleForHan() {
-    FontGlobalContext* ctx = Get();
-    ctx->default_locale_for_han_ = nullptr;
-    ctx->has_default_locale_for_han_ = false;
-  }
-  static inline bool HasDefaultLocaleForHan() {
-    return Get()->has_default_locale_for_han_;
-  }
-
-  static inline String& CurrentAcceptLanguages() {
-    return Get()->current_accept_languages_;
+  static LayoutLocale::PerThreadData& GetLayoutLocaleData() {
+    return Get()->layout_locale_data_;
   }
 
   // Called by MemoryCoordinator to clear memory.
@@ -99,18 +56,9 @@
   ~FontGlobalContext() = default;
 
   FontCache font_cache_;
-
   HarfBuzzFontCache harf_buzz_font_cache_;
-
   hb_font_funcs_t* harfbuzz_font_funcs_;
-
-  LayoutLocaleMap layout_locale_map_;
-  const LayoutLocale* default_locale_;
-  const LayoutLocale* system_locale_;
-  const LayoutLocale* default_locale_for_han_;
-  bool has_default_locale_for_han_;
-
-  String current_accept_languages_;
+  LayoutLocale::PerThreadData layout_locale_data_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/fonts/script_run_iterator.cc b/third_party/blink/renderer/platform/fonts/script_run_iterator.cc
index 96d5277..df6bc30 100644
--- a/third_party/blink/renderer/platform/fonts/script_run_iterator.cc
+++ b/third_party/blink/renderer/platform/fonts/script_run_iterator.cc
@@ -12,11 +12,12 @@
 
 typedef ScriptData::PairedBracketType PairedBracketType;
 
-const int ScriptData::kMaxScriptCount = 20;
+constexpr int ScriptRunIterator::kMaxScriptCount;
+constexpr int ScriptData::kMaxScriptCount;
 
 ScriptData::~ScriptData() = default;
 
-void ICUScriptData::GetScripts(UChar32 ch, Vector<UScriptCode>& dst) const {
+void ICUScriptData::GetScripts(UChar32 ch, UScriptCodeList& dst) const {
   ICUError status;
   // Leave room to insert primary script. It's not strictly necessary but
   // it ensures that the result won't ever be greater than kMaxScriptCount,
diff --git a/third_party/blink/renderer/platform/fonts/script_run_iterator.h b/third_party/blink/renderer/platform/fonts/script_run_iterator.h
index 7c4ce650..919863e1 100644
--- a/third_party/blink/renderer/platform/fonts/script_run_iterator.h
+++ b/third_party/blink/renderer/platform/fonts/script_run_iterator.h
@@ -33,6 +33,9 @@
 
   bool Consume(unsigned& limit, UScriptCode&);
 
+  static constexpr int kMaxScriptCount = 20;
+  using UScriptCodeList = Vector<UScriptCode, kMaxScriptCount>;
+
  private:
   struct BracketRec {
     DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
@@ -56,9 +59,9 @@
   // excessively large when processing long runs of text.
   static const int kMaxBrackets = 32;
 
-  Vector<UScriptCode> current_set_;
-  Vector<UScriptCode> next_set_;
-  Vector<UScriptCode> ahead_set_;
+  UScriptCodeList current_set_;
+  UScriptCodeList next_set_;
+  UScriptCodeList ahead_set_;
 
   UChar32 ahead_character_;
   size_t ahead_pos_;
@@ -90,9 +93,10 @@
     kBracketTypeCount
   };
 
-  static const int kMaxScriptCount;
+  static constexpr int kMaxScriptCount = ScriptRunIterator::kMaxScriptCount;
+  using UScriptCodeList = ScriptRunIterator::UScriptCodeList;
 
-  virtual void GetScripts(UChar32, Vector<UScriptCode>& dst) const = 0;
+  virtual void GetScripts(UChar32, UScriptCodeList& dst) const = 0;
 
   virtual UChar32 GetPairedBracket(UChar32) const = 0;
 
@@ -105,7 +109,7 @@
 
   static const ICUScriptData* Instance();
 
-  void GetScripts(UChar32, Vector<UScriptCode>& dst) const override;
+  void GetScripts(UChar32, UScriptCodeList& dst) const override;
 
   UChar32 GetPairedBracket(UChar32) const override;
 
diff --git a/third_party/blink/renderer/platform/fonts/script_run_iterator_test.cc b/third_party/blink/renderer/platform/fonts/script_run_iterator_test.cc
index bf289de..a8ffd21 100644
--- a/third_party/blink/renderer/platform/fonts/script_run_iterator_test.cc
+++ b/third_party/blink/renderer/platform/fonts/script_run_iterator_test.cc
@@ -34,7 +34,7 @@
     return &mock_script_data;
   }
 
-  void GetScripts(UChar32 ch, Vector<UScriptCode>& dst) const override {
+  void GetScripts(UChar32 ch, UScriptCodeList& dst) const override {
     DCHECK_GE(ch, kMockCharMin);
     DCHECK_LT(ch, kMockCharLimit);
 
@@ -663,7 +663,7 @@
 TEST_F(ScriptRunIteratorICUDataTest, ICUDataGetScriptsReturnsAllExtensions) {
   int max_extensions;
   UChar32 cp = GetACharWithMaxExtensions(&max_extensions);
-  Vector<UScriptCode> extensions;
+  ScriptData::UScriptCodeList extensions;
   ICUScriptData::Instance()->GetScripts(cp, extensions);
 
   // It's possible that GetScripts adds the primary script to the list of
@@ -673,7 +673,7 @@
 }
 
 TEST_F(ScriptRunIteratorICUDataTest, CommonHaveNoMoreThanOneExtension) {
-  Vector<UScriptCode> extensions;
+  ScriptData::UScriptCodeList extensions;
   for (UChar32 cp = 0; cp < 0x110000; ++cp) {
     ICUScriptData::Instance()->GetScripts(cp, extensions);
     UScriptCode primary = extensions.at(0);
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc b/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc
index 50e7d57..d26ae7e9 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc
@@ -832,7 +832,7 @@
   const RunInfo& run = *runs_.front();
   const bool is_horizontal_run = run.IsHorizontal();
   const SimpleFontData& font_data = *run.font_data_;
-  DCHECK(!run.glyph_data_.IsEmpty()) << ToString();
+  DCHECK(!run.glyph_data_.IsEmpty()) << *this;
   const unsigned character_index = run.glyph_data_.front().character_index;
   GlyphBoundsAccumulator bounds(0.f);
   for (const auto& glyph : run.glyph_data_) {
@@ -853,7 +853,7 @@
   const RunInfo& run = *runs_.back();
   const bool is_horizontal_run = run.IsHorizontal();
   const SimpleFontData& font_data = *run.font_data_;
-  DCHECK(!run.glyph_data_.IsEmpty()) << ToString();
+  DCHECK(!run.glyph_data_.IsEmpty()) << *this;
   const unsigned character_index = run.glyph_data_.back().character_index;
   GlyphBoundsAccumulator bounds(width_);
   for (auto glyph_it = run.glyph_data_.rbegin();
@@ -1088,4 +1088,9 @@
   return output.ToString();
 }
 
+std::ostream& operator<<(std::ostream& ostream,
+                         const ShapeResult& shape_result) {
+  return ostream << shape_result.ToString();
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shape_result.h b/third_party/blink/renderer/platform/fonts/shaping/shape_result.h
index 2505db6c7..229bb41b 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/shape_result.h
+++ b/third_party/blink/renderer/platform/fonts/shaping/shape_result.h
@@ -255,6 +255,8 @@
   friend class ShapeResultBloberizer;
 };
 
+PLATFORM_EXPORT std::ostream& operator<<(std::ostream&, const ShapeResult&);
+
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_SHAPE_RESULT_H_
diff --git a/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc b/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc
index 3895015..87d1ecf 100644
--- a/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc
+++ b/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc
@@ -65,7 +65,8 @@
   // need to construct VideoFrameSubmitter later in order to do this.
   frame_sink_id_ = id;
   frame_sink_destroyed_callback_ = frame_sink_destroyed_callback;
-  StartSubmitting();
+  if (resource_provider_->IsInitialized())
+    StartSubmitting();
 }
 
 void VideoFrameSubmitter::StopUsingProvider() {
diff --git a/third_party/blink/renderer/platform/layout_locale.cc b/third_party/blink/renderer/platform/layout_locale.cc
index 7fb1f9c..4c381f6 100644
--- a/third_party/blink/renderer/platform/layout_locale.cc
+++ b/third_party/blink/renderer/platform/layout_locale.cc
@@ -4,9 +4,10 @@
 
 #include "third_party/blink/renderer/platform/layout_locale.h"
 
-#include "third_party/blink/renderer/platform/fonts/accept_languages_resolver.h"
+#include "base/compiler_specific.h"
 #include "third_party/blink/renderer/platform/fonts/font_global_context.h"
 #include "third_party/blink/renderer/platform/language.h"
+#include "third_party/blink/renderer/platform/text/hyphenation.h"
 #include "third_party/blink/renderer/platform/text/icu_error.h"
 #include "third_party/blink/renderer/platform/text/locale_to_script_mapping.h"
 #include "third_party/blink/renderer/platform/wtf/hash_map.h"
@@ -77,25 +78,38 @@
   return has_script_for_han_;
 }
 
+// static
 const LayoutLocale* LayoutLocale::LocaleForHan(
     const LayoutLocale* content_locale) {
   if (content_locale && content_locale->HasScriptForHan())
     return content_locale;
 
-  if (FontGlobalContext::HasDefaultLocaleForHan())
-    return FontGlobalContext::GetDefaultLocaleForHan();
-
-  const LayoutLocale* default_for_han;
-  if (const LayoutLocale* locale = AcceptLanguagesResolver::LocaleForHan())
-    default_for_han = locale;
-  else if (GetDefault().HasScriptForHan())
-    default_for_han = &GetDefault();
-  else if (GetSystem().HasScriptForHan())
-    default_for_han = &GetSystem();
-  else
-    default_for_han = nullptr;
-  FontGlobalContext::SetDefaultLocaleForHan(default_for_han);
-  return default_for_han;
+  PerThreadData& data = FontGlobalContext::GetLayoutLocaleData();
+  if (UNLIKELY(!data.default_locale_for_han_computed)) {
+    // Use the first acceptLanguages that can disambiguate.
+    Vector<String> languages;
+    data.current_accept_languages.Split(',', languages);
+    for (String token : languages) {
+      token = token.StripWhiteSpace();
+      const LayoutLocale* locale = LayoutLocale::Get(AtomicString(token));
+      if (locale->HasScriptForHan()) {
+        data.default_locale_for_han = locale;
+        break;
+      }
+    }
+    if (!data.default_locale_for_han) {
+      const LayoutLocale& default_locale = GetDefault();
+      if (default_locale.HasScriptForHan())
+        data.default_locale_for_han = &default_locale;
+    }
+    if (!data.default_locale_for_han) {
+      const LayoutLocale& system_locale = GetSystem();
+      if (system_locale.HasScriptForHan())
+        data.default_locale_for_han = &system_locale;
+    }
+    data.default_locale_for_han_computed = true;
+  }
+  return data.default_locale_for_han;
 }
 
 const char* LayoutLocale::LocaleForHanForSkFontMgr() const {
@@ -112,38 +126,40 @@
       has_script_for_han_(false),
       hyphenation_computed_(false) {}
 
+// static
 const LayoutLocale* LayoutLocale::Get(const AtomicString& locale) {
   if (locale.IsNull())
     return nullptr;
 
-  auto result = FontGlobalContext::GetLayoutLocaleMap().insert(locale, nullptr);
+  auto result = FontGlobalContext::GetLayoutLocaleData().locale_map.insert(
+      locale, nullptr);
   if (result.is_new_entry)
     result.stored_value->value = base::AdoptRef(new LayoutLocale(locale));
   return result.stored_value->value.get();
 }
 
+// static
 const LayoutLocale& LayoutLocale::GetDefault() {
-  if (const LayoutLocale* locale = FontGlobalContext::GetDefaultLayoutLocale())
-    return *locale;
-
-  AtomicString language = DefaultLanguage();
-  const LayoutLocale* locale =
-      LayoutLocale::Get(!language.IsEmpty() ? language : "en");
-  FontGlobalContext::SetDefaultLayoutLocale(locale);
-  return *locale;
+  PerThreadData& data = FontGlobalContext::GetLayoutLocaleData();
+  if (UNLIKELY(!data.default_locale)) {
+    AtomicString language = DefaultLanguage();
+    data.default_locale =
+        LayoutLocale::Get(!language.IsEmpty() ? language : "en");
+  }
+  return *data.default_locale;
 }
 
+// static
 const LayoutLocale& LayoutLocale::GetSystem() {
-  if (const LayoutLocale* locale = FontGlobalContext::GetSystemLayoutLocale())
-    return *locale;
-
-  // Platforms such as Windows can give more information than the default
-  // locale, such as "en-JP" for English speakers in Japan.
-  String name = icu::Locale::getDefault().getName();
-  const LayoutLocale* locale =
-      LayoutLocale::Get(AtomicString(name.Replace('_', '-')));
-  FontGlobalContext::SetSystemLayoutLocale(locale);
-  return *locale;
+  PerThreadData& data = FontGlobalContext::GetLayoutLocaleData();
+  if (UNLIKELY(!data.system_locale)) {
+    // Platforms such as Windows can give more information than the default
+    // locale, such as "en-JP" for English speakers in Japan.
+    String name = icu::Locale::getDefault().getName();
+    data.system_locale =
+        LayoutLocale::Get(AtomicString(name.Replace('_', '-')));
+  }
+  return *data.system_locale;
 }
 
 scoped_refptr<LayoutLocale> LayoutLocale::CreateForTesting(
@@ -223,4 +239,15 @@
   return string_;
 }
 
+// static
+void LayoutLocale::AcceptLanguagesChanged(const String& accept_languages) {
+  PerThreadData& data = FontGlobalContext::GetLayoutLocaleData();
+  if (data.current_accept_languages == accept_languages)
+    return;
+
+  data.current_accept_languages = accept_languages;
+  data.default_locale_for_han = nullptr;
+  data.default_locale_for_han_computed = false;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/layout_locale.h b/third_party/blink/renderer/platform/layout_locale.h
index ffbcea9..9eb9207b 100644
--- a/third_party/blink/renderer/platform/layout_locale.h
+++ b/third_party/blink/renderer/platform/layout_locale.h
@@ -8,8 +8,11 @@
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/text/hyphenation.h"
 #include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
 #include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
 #include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string_hash.h"
 
 #include <unicode/uscript.h>
 
@@ -17,8 +20,6 @@
 
 namespace blink {
 
-class Hyphenation;
-
 enum class LineBreakIteratorMode { kDefault, kNormal, kStrict, kLoose };
 
 class PLATFORM_EXPORT LayoutLocale : public RefCounted<LayoutLocale> {
@@ -64,6 +65,18 @@
   static void SetHyphenationForTesting(const AtomicString&,
                                        scoped_refptr<Hyphenation>);
 
+  struct PerThreadData {
+    HashMap<AtomicString, scoped_refptr<LayoutLocale>, CaseFoldingHash>
+        locale_map;
+    const LayoutLocale* default_locale = nullptr;
+    const LayoutLocale* system_locale = nullptr;
+    const LayoutLocale* default_locale_for_han = nullptr;
+    bool default_locale_for_han_computed = false;
+    String current_accept_languages;
+  };
+
+  static void AcceptLanguagesChanged(const String&);
+
  private:
   explicit LayoutLocale(const AtomicString&);
 
diff --git a/third_party/blink/renderer/platform/layout_locale_test.cc b/third_party/blink/renderer/platform/layout_locale_test.cc
index 2791c114..f3006b5 100644
--- a/third_party/blink/renderer/platform/layout_locale_test.cc
+++ b/third_party/blink/renderer/platform/layout_locale_test.cc
@@ -152,4 +152,56 @@
   }
 }
 
+TEST(LayoutLocaleTest, AcceptLanguagesChanged) {
+  struct {
+    const char* accept_languages;
+    UScriptCode script;
+    const char* locale;
+  } tests[] = {
+      // Non-Han script cases.
+      {nullptr, USCRIPT_COMMON, nullptr},
+      {"", USCRIPT_COMMON, nullptr},
+      {"en-US", USCRIPT_COMMON, nullptr},
+      {",en-US", USCRIPT_COMMON, nullptr},
+
+      // Single value cases.
+      {"ja-JP", USCRIPT_KATAKANA_OR_HIRAGANA, "ja-jp"},
+      {"ko-KR", USCRIPT_HANGUL, "ko-kr"},
+      {"zh-CN", USCRIPT_SIMPLIFIED_HAN, "zh-Hans"},
+      {"zh-HK", USCRIPT_TRADITIONAL_HAN, "zh-Hant"},
+      {"zh-TW", USCRIPT_TRADITIONAL_HAN, "zh-Hant"},
+
+      // Language only.
+      {"ja", USCRIPT_KATAKANA_OR_HIRAGANA, "ja-jp"},
+      {"ko", USCRIPT_HANGUL, "ko-kr"},
+      {"zh", USCRIPT_SIMPLIFIED_HAN, "zh-Hans"},
+
+      // Unusual combinations.
+      {"en-JP", USCRIPT_KATAKANA_OR_HIRAGANA, "ja-jp"},
+
+      // Han scripts not in the first item.
+      {"en-US,ja-JP", USCRIPT_KATAKANA_OR_HIRAGANA, "ja-jp"},
+      {"en-US,en-JP", USCRIPT_KATAKANA_OR_HIRAGANA, "ja-jp"},
+
+      // Multiple Han scripts. The first one wins.
+      {"ja-JP,zh-CN", USCRIPT_KATAKANA_OR_HIRAGANA, "ja-jp"},
+      {"zh-TW,ja-JP", USCRIPT_TRADITIONAL_HAN, "zh-Hant"},
+  };
+
+  for (const auto& test : tests) {
+    LayoutLocale::AcceptLanguagesChanged(test.accept_languages);
+    const LayoutLocale* locale = LayoutLocale::LocaleForHan(nullptr);
+
+    if (test.script == USCRIPT_COMMON) {
+      EXPECT_EQ(nullptr, locale) << test.accept_languages;
+      continue;
+    }
+
+    ASSERT_NE(nullptr, locale) << test.accept_languages;
+    EXPECT_EQ(test.script, locale->GetScriptForHan()) << test.accept_languages;
+    EXPECT_STRCASEEQ(test.locale, locale->LocaleForHanForSkFontMgr())
+        << test.accept_languages;
+  }
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 4f2404b7..61144cd 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -658,6 +658,9 @@
       name: "LazyFrameLoading",
     },
     {
+      name: "LazyFrameVisibleLoadTimeMetrics",
+    },
+    {
       name: "LazyInitializeMediaControls",
       // This is enabled by features::kLazyInitializeMediaControls.
     },
diff --git a/third_party/blink/renderer/platform/scheduler/base/task_queue_impl.cc b/third_party/blink/renderer/platform/scheduler/base/task_queue_impl.cc
index a335614..f8447ca 100644
--- a/third_party/blink/renderer/platform/scheduler/base/task_queue_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/base/task_queue_impl.cc
@@ -341,10 +341,11 @@
   main_thread_only().immediate_work_queue->ReloadEmptyImmediateQueue();
 }
 
-TaskQueueImpl::TaskDeque TaskQueueImpl::TakeImmediateIncomingQueue() {
+void TaskQueueImpl::ReloadEmptyImmediateQueue(TaskDeque* queue) {
+  DCHECK(queue->empty());
+
   AutoLock immediate_incoming_queue_lock(immediate_incoming_queue_lock_);
-  TaskQueueImpl::TaskDeque queue;
-  queue.swap(immediate_incoming_queue());
+  queue->swap(immediate_incoming_queue());
 
   // Activate delayed fence if necessary. This is ideologically similar to
   // ActivateDelayedFenceIfNeeded, but due to immediate tasks being posted
@@ -352,7 +353,7 @@
   // so we have to check all immediate tasks and use their enqueue order for
   // a fence.
   if (main_thread_only().delayed_fence) {
-    for (const Task& task : queue) {
+    for (const Task& task : *queue) {
       if (task.delayed_run_time >= main_thread_only().delayed_fence.value()) {
         main_thread_only().delayed_fence = nullopt;
         DCHECK_EQ(main_thread_only().current_fence,
@@ -368,8 +369,6 @@
       }
     }
   }
-
-  return queue;
 }
 
 bool TaskQueueImpl::IsEmpty() const {
diff --git a/third_party/blink/renderer/platform/scheduler/base/task_queue_impl.h b/third_party/blink/renderer/platform/scheduler/base/task_queue_impl.h
index 471e60b..34a7e90 100644
--- a/third_party/blink/renderer/platform/scheduler/base/task_queue_impl.h
+++ b/third_party/blink/renderer/platform/scheduler/base/task_queue_impl.h
@@ -35,6 +35,12 @@
 class WorkQueue;
 class WorkQueueSets;
 
+struct IncomingImmediateWorkList {
+  IncomingImmediateWorkList* next = nullptr;
+  TaskQueueImpl* queue = nullptr;
+  internal::EnqueueOrder order;
+};
+
 // TaskQueueImpl has four main queues:
 //
 // Immediate (non-delayed) tasks:
@@ -238,6 +244,11 @@
     return main_thread_only().immediate_work_queue.get();
   }
 
+  // Protected by TaskQueueManagerImpl's AnyThread lock.
+  IncomingImmediateWorkList* immediate_work_list_storage() {
+    return &immediate_work_list_storage_;
+  }
+
   // Enqueues any delayed tasks which should be run now on the
   // |delayed_work_queue|.
   // Must be called from the main thread.
@@ -383,9 +394,10 @@
   // PartitionAlloc.
   using TaskDeque = circular_deque<Task>;
 
-  // Extracts all the tasks from the immediate incoming queue and clears it.
+  // Extracts all the tasks from the immediate incoming queue and swaps it with
+  // |queue| which must be empty.
   // Can be called from any thread.
-  TaskDeque TakeImmediateIncomingQueue();
+  void ReloadEmptyImmediateQueue(TaskDeque* queue);
 
   void TraceQueueSize() const;
   static void QueueAsValueInto(const TaskDeque& queue,
@@ -447,6 +459,9 @@
     return immediate_incoming_queue_;
   }
 
+  // Protected by TaskQueueManagerImpl's AnyThread lock.
+  IncomingImmediateWorkList immediate_work_list_storage_;
+
   const bool should_monitor_quiescence_;
   const bool should_notify_observers_;
 
diff --git a/third_party/blink/renderer/platform/scheduler/base/task_queue_manager_impl.cc b/third_party/blink/renderer/platform/scheduler/base/task_queue_manager_impl.cc
index 78f0064..8c5b62f 100644
--- a/third_party/blink/renderer/platform/scheduler/base/task_queue_manager_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/base/task_queue_manager_impl.cc
@@ -153,6 +153,41 @@
   main_thread_only().observer = observer;
 }
 
+bool TaskQueueManagerImpl::AddToIncomingImmediateWorkList(
+    internal::TaskQueueImpl* task_queue,
+    internal::EnqueueOrder enqueue_order) {
+  AutoLock lock(any_thread_lock_);
+  // Check if |task_queue| is already in the linked list.
+  if (task_queue->immediate_work_list_storage()->queue)
+    return false;
+
+  // Insert into the linked list.
+  task_queue->immediate_work_list_storage()->queue = task_queue;
+  task_queue->immediate_work_list_storage()->order = enqueue_order;
+  task_queue->immediate_work_list_storage()->next =
+      any_thread().incoming_immediate_work_list;
+  any_thread().incoming_immediate_work_list =
+      task_queue->immediate_work_list_storage();
+  return true;
+}
+
+void TaskQueueManagerImpl::RemoveFromIncomingImmediateWorkList(
+    internal::TaskQueueImpl* task_queue) {
+  AutoLock lock(any_thread_lock_);
+  internal::IncomingImmediateWorkList** prev =
+      &any_thread().incoming_immediate_work_list;
+  while (*prev) {
+    if ((*prev)->queue == task_queue) {
+      *prev = (*prev)->next;
+      break;
+    }
+    prev = &(*prev)->next;
+  }
+
+  task_queue->immediate_work_list_storage()->next = nullptr;
+  task_queue->immediate_work_list_storage()->queue = nullptr;
+}
+
 void TaskQueueManagerImpl::UnregisterTaskQueueImpl(
     std::unique_ptr<internal::TaskQueueImpl> task_queue) {
   TRACE_EVENT1("sequence_manager", "TaskQueueManagerImpl::UnregisterTaskQueue",
@@ -161,10 +196,9 @@
 
   main_thread_only().selector.RemoveQueue(task_queue.get());
 
-  {
-    AutoLock lock(any_thread_lock_);
-    any_thread().has_incoming_immediate_work.erase(task_queue.get());
-  }
+  // Remove |task_queue| from the linked list if present.
+  // This is O(n).  We assume this will be a relatively infrequent operation.
+  RemoveFromIncomingImmediateWorkList(task_queue.get());
 
   task_queue->UnregisterTaskQueue();
 
@@ -175,14 +209,13 @@
   main_thread_only().queues_to_delete[task_queue.get()] = std::move(task_queue);
 }
 
-void TaskQueueManagerImpl::ReloadEmptyWorkQueues(
-    const IncomingImmediateWorkMap& queues_to_reload) const {
+void TaskQueueManagerImpl::ReloadEmptyWorkQueues() {
   // There are two cases where a queue needs reloading.  First, it might be
   // completely empty and we've just posted a task (this method handles that
   // case). Secondly if the work queue becomes empty in when calling
   // WorkQueue::TakeTaskFromWorkQueue (handled there).
-  for (const auto& pair : queues_to_reload) {
-    pair.first->ReloadImmediateWorkQueueIfEmpty();
+  for (internal::TaskQueueImpl* queue : main_thread_only().queues_to_reload) {
+    queue->ReloadImmediateWorkQueueIfEmpty();
   }
 }
 
@@ -230,13 +263,7 @@
     internal::TaskQueueImpl* queue,
     internal::EnqueueOrder enqueue_order,
     bool queue_is_blocked) {
-  {
-    AutoLock lock(any_thread_lock_);
-    any_thread().has_incoming_immediate_work.insert(
-        std::make_pair(queue, enqueue_order));
-  }
-
-  if (!queue_is_blocked)
+  if (AddToIncomingImmediateWorkList(queue, enqueue_order) && !queue_is_blocked)
     controller_->ScheduleWork();
 }
 
@@ -267,16 +294,23 @@
   DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
   TRACE_EVENT0("sequence_manager", "TaskQueueManagerImpl::TakeTask");
 
-  IncomingImmediateWorkMap queues_to_reload;
-
   {
     AutoLock lock(any_thread_lock_);
-    std::swap(queues_to_reload, any_thread().has_incoming_immediate_work);
+    main_thread_only().queues_to_reload.clear();
+
+    for (internal::IncomingImmediateWorkList* iter =
+             any_thread().incoming_immediate_work_list;
+         iter; iter = iter->next) {
+      main_thread_only().queues_to_reload.push_back(iter->queue);
+      iter->queue = nullptr;
+    }
+
+    any_thread().incoming_immediate_work_list = nullptr;
   }
 
   // It's important we call ReloadEmptyWorkQueues out side of the lock to
   // avoid a lock order inversion.
-  ReloadEmptyWorkQueues(queues_to_reload);
+  ReloadEmptyWorkQueues();
   LazyNow lazy_now(main_thread_only().real_time_domain->CreateLazyNow());
   WakeUpReadyDelayedQueues(&lazy_now);
 
@@ -346,8 +380,10 @@
   // hasn't been called yet. This check catches the case of fresh incoming work.
   {
     AutoLock lock(any_thread_lock_);
-    for (const auto& pair : any_thread().has_incoming_immediate_work) {
-      if (pair.first->CouldTaskRun(pair.second))
+    for (const internal::IncomingImmediateWorkList* iter =
+             any_thread().incoming_immediate_work_list;
+         iter; iter = iter->next) {
+      if (iter->queue->CouldTaskRun(iter->order))
         return TimeDelta();
     }
   }
@@ -571,8 +607,10 @@
   {
     AutoLock lock(any_thread_lock_);
     state->BeginArray("has_incoming_immediate_work");
-    for (const auto& pair : any_thread().has_incoming_immediate_work) {
-      state->AppendString(pair.first->GetName());
+    for (const internal::IncomingImmediateWorkList* iter =
+             any_thread().incoming_immediate_work_list;
+         iter; iter = iter->next) {
+      state->AppendString(iter->queue->GetName());
     }
     state->EndArray();
   }
diff --git a/third_party/blink/renderer/platform/scheduler/base/task_queue_manager_impl.h b/third_party/blink/renderer/platform/scheduler/base/task_queue_manager_impl.h
index ba348f2..35b6cd1 100644
--- a/third_party/blink/renderer/platform/scheduler/base/task_queue_manager_impl.h
+++ b/third_party/blink/renderer/platform/scheduler/base/task_queue_manager_impl.h
@@ -164,15 +164,12 @@
     kTaskQueueManagerDeleted,
   };
 
-  using IncomingImmediateWorkMap =
-      std::unordered_map<internal::TaskQueueImpl*, internal::EnqueueOrder>;
-
   struct AnyThread {
     AnyThread();
     ~AnyThread();
 
     // Task queues with newly available work on the incoming queue.
-    IncomingImmediateWorkMap has_incoming_immediate_work;
+    internal::IncomingImmediateWorkList* incoming_immediate_work_list = nullptr;
   };
 
   // TaskQueueManager maintains a queue of non-nestable tasks since they're
@@ -236,6 +233,12 @@
     std::map<internal::TaskQueueImpl*, std::unique_ptr<internal::TaskQueueImpl>>
         queues_to_delete;
 
+    // Scratch space used to store the contents of
+    // any_thread().incoming_immediate_work_list for use by
+    // ReloadEmptyWorkQueues.  We keep hold of this vector to avoid unnecessary
+    // memory allocations.
+    std::vector<internal::TaskQueueImpl*> queues_to_reload;
+
     bool task_was_run_on_quiescence_monitored_queue = false;
 
     // Due to nested runloops more than one task can be executing concurrently.
@@ -275,10 +278,16 @@
                                        internal::EnqueueOrder enqueue_order,
                                        bool queue_is_blocked);
 
+  // Returns true if |task_queue| was added to the list, or false if it was
+  // already in the list.  If |task_queue| was inserted, the |order| is set
+  // with |enqueue_order|.
+  bool AddToIncomingImmediateWorkList(internal::TaskQueueImpl* task_queue,
+                                      internal::EnqueueOrder enqueue_order);
+  void RemoveFromIncomingImmediateWorkList(internal::TaskQueueImpl* task_queue);
+
   // Calls |ReloadImmediateWorkQueueIfEmpty| on all queues in
-  // |queues_to_reload|.
-  void ReloadEmptyWorkQueues(
-      const IncomingImmediateWorkMap& queues_to_reload) const;
+  // |main_thread_only().queues_to_reload|.
+  void ReloadEmptyWorkQueues();
 
   std::unique_ptr<internal::TaskQueueImpl> CreateTaskQueueImpl(
       const TaskQueue::Spec& spec) override;
diff --git a/third_party/blink/renderer/platform/scheduler/base/task_queue_manager_perftest.cc b/third_party/blink/renderer/platform/scheduler/base/task_queue_manager_perftest.cc
index a80845f7..c4876e2 100644
--- a/third_party/blink/renderer/platform/scheduler/base/task_queue_manager_perftest.cc
+++ b/third_party/blink/renderer/platform/scheduler/base/task_queue_manager_perftest.cc
@@ -258,7 +258,7 @@
 
   max_tasks_in_flight_ = 200;
   Benchmark(
-      "run 10000 delayed tasks with eight queues",
+      "run 10000 delayed tasks with thirty two queues",
       BindRepeating(&TaskQueueManagerPerfTest::ResetAndCallTestDelayedTask,
                     Unretained(this), 10000));
 }
@@ -306,7 +306,7 @@
 
   max_tasks_in_flight_ = 200;
   Benchmark(
-      "run 10000 immediate tasks with eight queues",
+      "run 10000 immediate tasks with thirty two queues",
       BindRepeating(&TaskQueueManagerPerfTest::ResetAndCallTestImmediateTask,
                     Unretained(this), 10000));
 }
diff --git a/third_party/blink/renderer/platform/scheduler/base/work_queue.cc b/third_party/blink/renderer/platform/scheduler/base/work_queue.cc
index f2319bc..b67f9163 100644
--- a/third_party/blink/renderer/platform/scheduler/base/work_queue.cc
+++ b/third_party/blink/renderer/platform/scheduler/base/work_queue.cc
@@ -117,7 +117,7 @@
 void WorkQueue::ReloadEmptyImmediateQueue() {
   DCHECK(tasks_.empty());
 
-  tasks_ = task_queue_->TakeImmediateIncomingQueue();
+  task_queue_->ReloadEmptyImmediateQueue(&tasks_);
   if (tasks_.empty())
     return;
 
@@ -135,7 +135,7 @@
   // NB immediate tasks have a different pipeline to delayed ones.
   if (queue_type_ == QueueType::kImmediate && tasks_.empty()) {
     // Short-circuit the queue reload so that OnPopQueue does the right thing.
-    tasks_ = task_queue_->TakeImmediateIncomingQueue();
+    task_queue_->ReloadEmptyImmediateQueue(&tasks_);
   }
   // OnPopQueue calls GetFrontTaskEnqueueOrder which checks BlockedByFence() so
   // we don't need to here.
@@ -156,7 +156,7 @@
     // NB immediate tasks have a different pipeline to delayed ones.
     if (queue_type_ == QueueType::kImmediate && tasks_.empty()) {
       // Short-circuit the queue reload so that OnPopQueue does the right thing.
-      tasks_ = task_queue_->TakeImmediateIncomingQueue();
+      task_queue_->ReloadEmptyImmediateQueue(&tasks_);
     }
     work_queue_sets_->OnPopQueue(this);
     task_queue_->TraceQueueSize();
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc
index 54651255..4cfa325 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc
@@ -62,6 +62,17 @@
   }
 }
 
+// Used to update the priority of task_queue. Note that this function is
+// used for queues associated with a frame.
+void UpdatePriority(MainThreadTaskQueue* task_queue) {
+  if (!task_queue)
+    return;
+
+  FrameSchedulerImpl* frame_scheduler = task_queue->GetFrameScheduler();
+  DCHECK(frame_scheduler);
+  task_queue->SetQueuePriority(frame_scheduler->ComputePriority(task_queue));
+}
+
 }  // namespace
 
 FrameSchedulerImpl::ActiveConnectionHandleImpl::ActiveConnectionHandleImpl(
@@ -465,6 +476,15 @@
     parent_page_scheduler_->OnConnectionUpdated();
 }
 
+void FrameSchedulerImpl::UpdateQueuePriorities() {
+  UpdatePriority(loading_task_queue_.get());
+  UpdatePriority(loading_control_task_queue_.get());
+  UpdatePriority(throttleable_task_queue_.get());
+  UpdatePriority(deferrable_task_queue_.get());
+  UpdatePriority(pausable_task_queue_.get());
+  UpdatePriority(unpausable_task_queue_.get());
+}
+
 void FrameSchedulerImpl::AsValueInto(
     base::trace_event::TracedValue* state) const {
   state->SetBoolean("frame_visible", frame_visible_);
@@ -633,5 +653,28 @@
   return has_active_connection();
 }
 
+TaskQueue::QueuePriority FrameSchedulerImpl::ComputePriority(
+    MainThreadTaskQueue* task_queue) const {
+  DCHECK(task_queue);
+
+  FrameScheduler* frame_scheduler = task_queue->GetFrameScheduler();
+
+  // Checks the task queue is associated with this frame scheduler.
+  DCHECK_EQ(frame_scheduler, this);
+
+  base::Optional<TaskQueue::QueuePriority> fixed_priority =
+      task_queue->FixedPriority();
+
+  if (fixed_priority)
+    return fixed_priority.value();
+
+  // TODO(farahcharab) Add further logic depending on frame origin/type,
+  // |use_case|, and which finch experiment is enabled.
+  return task_queue->queue_type() ==
+                 MainThreadTaskQueue::QueueType::kFrameLoadingControl
+             ? TaskQueue::QueuePriority::kHighestPriority
+             : TaskQueue::QueuePriority::kNormalPriority;
+}
+
 }  // namespace scheduler
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h
index ac8df80..811a9f92 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h
@@ -94,6 +94,12 @@
   void SetPageKeepActiveForTracing(bool keep_active);
   void SetPageFrozenForTracing(bool frozen);
 
+  // Computes the priority of |task_queue| if it is associated to this frame
+  // scheduler. Note that the main's thread policy should be upto date to
+  // compute the correct priority.
+  base::sequence_manager::TaskQueue::QueuePriority ComputePriority(
+      MainThreadTaskQueue* task_queue) const;
+
  protected:
   // This will construct a subframe that is not linked to any main thread or
   // page scheduler. Should be used only for testing purposes.
@@ -130,6 +136,10 @@
   void DidOpenActiveConnection();
   void DidCloseActiveConnection();
 
+  // Updates the priorities of all the task queues associated with this
+  // frame scheduler.
+  void UpdateQueuePriorities();
+
   scoped_refptr<base::sequence_manager::TaskQueue> LoadingTaskQueue();
   scoped_refptr<base::sequence_manager::TaskQueue> LoadingControlTaskQueue();
   scoped_refptr<base::sequence_manager::TaskQueue> ThrottleableTaskQueue();
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_helper.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_helper.cc
index ea65849..f60b445f 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_helper.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_helper.cc
@@ -62,8 +62,6 @@
           params.spec, params, main_thread_scheduler_);
   if (params.fixed_priority)
     task_queue->SetQueuePriority(params.fixed_priority.value());
-  if (params.used_for_important_tasks)
-    task_queue->SetQueuePriority(TaskQueue::QueuePriority::kHighestPriority);
   return task_queue;
 }
 
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
index 028f814..b181305 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
@@ -751,6 +751,8 @@
       task_queue.get(), insert_result.first->second.get(), TaskQueuePolicy(),
       main_thread_only().current_policy.GetQueuePolicy(queue_class));
 
+  task_queue->SetQueuePriority(ComputePriority(task_queue.get()));
+
   if (task_queue->CanBeThrottled())
     AddQueueToWakeUpBudgetPool(task_queue.get());
 
@@ -777,9 +779,6 @@
           .SetCanBeFrozen(
               RuntimeEnabledFeatures::StopLoadingInBackgroundEnabled())
           .SetCanBeDeferred(true)
-          .SetUsedForImportantTasks(
-              queue_type ==
-              MainThreadTaskQueue::QueueType::kFrameLoadingControl)
           .SetFrameScheduler(frame_scheduler));
 }
 
@@ -1390,8 +1389,8 @@
   policy_may_need_update_.SetWhileLocked(false);
 
   base::TimeDelta expected_use_case_duration;
-  UseCase use_case = ComputeCurrentUseCase(now, &expected_use_case_duration);
-  main_thread_only().current_use_case = use_case;
+  main_thread_only().current_use_case =
+      ComputeCurrentUseCase(now, &expected_use_case_duration);
 
   base::TimeDelta touchstart_expected_flag_valid_for_duration;
   // TODO(skyostil): Consider handlers for all types of blocking gestures (e.g.,
@@ -1470,27 +1469,29 @@
 
   ExpensiveTaskPolicy expensive_task_policy = ExpensiveTaskPolicy::kRun;
   new_policy.rail_mode() = v8::PERFORMANCE_ANIMATION;
+  new_policy.use_case() = main_thread_only().current_use_case;
 
-  switch (use_case) {
+  switch (new_policy.use_case()) {
     case UseCase::kCompositorGesture:
       if (touchstart_expected_soon) {
         new_policy.rail_mode() = v8::PERFORMANCE_RESPONSE;
         expensive_task_policy = ExpensiveTaskPolicy::kBlock;
-        new_policy.compositor_queue_policy().priority =
-            TaskQueue::kHighestPriority;
+        new_policy.compositor_priority() =
+            TaskQueue::QueuePriority::kHighestPriority;
       } else {
         // What we really want to do is priorize loading tasks, but that doesn't
         // seem to be safe. Instead we do that by proxy by deprioritizing
         // compositor tasks. This should be safe since we've already gone to the
         // pain of fixing ordering issues with them.
-        new_policy.compositor_queue_policy().priority = TaskQueue::kLowPriority;
+        new_policy.compositor_priority() =
+            TaskQueue::QueuePriority::kLowPriority;
       }
       break;
 
     case UseCase::kSynchronizedGesture:
-      new_policy.compositor_queue_policy().priority =
-          main_thread_compositing_is_fast ? TaskQueue::kHighestPriority
-                                          : TaskQueue::kNormalPriority;
+      new_policy.compositor_priority() = main_thread_compositing_is_fast
+                                             ? TaskQueue::kHighestPriority
+                                             : TaskQueue::kNormalPriority;
       if (touchstart_expected_soon) {
         new_policy.rail_mode() = v8::PERFORMANCE_RESPONSE;
         expensive_task_policy = ExpensiveTaskPolicy::kBlock;
@@ -1504,9 +1505,10 @@
       // about which things we should be prioritizing, so we don't attempt to
       // block expensive tasks because we don't know whether they were integral
       // to the page's functionality or not.
-      new_policy.compositor_queue_policy().priority =
-          main_thread_compositing_is_fast ? TaskQueue::kHighestPriority
-                                          : TaskQueue::kNormalPriority;
+      new_policy.compositor_priority() =
+          main_thread_compositing_is_fast
+              ? TaskQueue::QueuePriority::kHighestPriority
+              : TaskQueue::QueuePriority::kNormalPriority;
       break;
 
     case UseCase::kMainThreadGesture:
@@ -1514,8 +1516,8 @@
       // by the main thread. Since we know the established gesture type, we can
       // be a little more aggressive about prioritizing compositing and input
       // handling over other tasks.
-      new_policy.compositor_queue_policy().priority =
-          TaskQueue::kHighestPriority;
+      new_policy.compositor_priority() =
+          TaskQueue::QueuePriority::kHighestPriority;
       if (touchstart_expected_soon) {
         new_policy.rail_mode() = v8::PERFORMANCE_RESPONSE;
         expensive_task_policy = ExpensiveTaskPolicy::kBlock;
@@ -1525,9 +1527,9 @@
       break;
 
     case UseCase::kTouchstart:
+      new_policy.compositor_priority() =
+          TaskQueue::QueuePriority::kHighestPriority;
       new_policy.rail_mode() = v8::PERFORMANCE_RESPONSE;
-      new_policy.compositor_queue_policy().priority =
-          TaskQueue::kHighestPriority;
       new_policy.loading_queue_policy().is_blocked = true;
       new_policy.timer_queue_policy().is_blocked = true;
       // NOTE this is a nop due to the above.
@@ -1656,8 +1658,17 @@
   }
 
   DCHECK(compositor_task_queue_->IsQueueEnabled());
+
+  Policy old_policy = main_thread_only().current_policy;
   main_thread_only().current_policy = new_policy;
 
+  if (ShouldUpdateTaskQueuePriorities(old_policy)) {
+    for (const auto& pair : task_runners_) {
+      MainThreadTaskQueue* task_queue = pair.first.get();
+      task_queue->SetQueuePriority(ComputePriority(task_queue));
+    }
+  }
+
   if (newly_frozen)
     Platform::Current()->RequestPurgeMemory();
 }
@@ -1678,8 +1689,6 @@
   DCHECK(task_queue_enabled_voter ||
          old_task_queue_policy.IsQueueEnabled(task_queue));
 
-  task_queue->SetQueuePriority(new_task_queue_policy.GetPriority(task_queue));
-
   TimeDomainType old_time_domain_type =
       old_task_queue_policy.GetTimeDomainType(task_queue);
   TimeDomainType new_time_domain_type =
@@ -2207,16 +2216,6 @@
   return true;
 }
 
-TaskQueue::QueuePriority MainThreadSchedulerImpl::TaskQueuePolicy::GetPriority(
-    MainThreadTaskQueue* task_queue) const {
-  base::Optional<TaskQueue::QueuePriority> fixed_priority =
-      task_queue->FixedPriority();
-  if (fixed_priority)
-    return fixed_priority.value();
-  return task_queue->UsedForImportantTasks() ? TaskQueue::kHighestPriority
-                                             : priority;
-}
-
 MainThreadSchedulerImpl::TimeDomainType
 MainThreadSchedulerImpl::TaskQueuePolicy::GetTimeDomainType(
     MainThreadTaskQueue* task_queue) const {
@@ -2234,7 +2233,6 @@
   state->SetBoolean("is_throttled", is_throttled);
   state->SetBoolean("is_blocked", is_blocked);
   state->SetBoolean("use_virtual_time", use_virtual_time);
-  state->SetString("priority", TaskQueue::PriorityToString(priority));
 }
 
 void MainThreadSchedulerImpl::Policy::AsValueInto(
@@ -2256,6 +2254,10 @@
   state->EndDictionary();
 
   state->SetString("rail_mode", RAILModeToString(rail_mode()));
+  state->SetString("compositor_priority",
+                   TaskQueue::PriorityToString(compositor_priority()));
+  state->SetString("use_case", UseCaseToString(use_case()));
+
   state->SetBoolean("should_disable_throttling", should_disable_throttling());
   state->SetBoolean("frozen_when_backgrounded", frozen_when_backgrounded());
 }
@@ -2608,6 +2610,33 @@
   builder.Record(ukm_recorder);
 }
 
+TaskQueue::QueuePriority MainThreadSchedulerImpl::ComputePriority(
+    MainThreadTaskQueue* task_queue) const {
+  DCHECK(task_queue);
+
+  // If |task_queue| is associated to a frame, the the frame scheduler computes
+  // the priority.
+  FrameSchedulerImpl* frame_scheduler = task_queue->GetFrameScheduler();
+
+  if (frame_scheduler) {
+    return frame_scheduler->ComputePriority(task_queue);
+  }
+
+  base::Optional<TaskQueue::QueuePriority> fixed_priority =
+      task_queue->FixedPriority();
+  if (fixed_priority) {
+    return fixed_priority.value();
+  }
+
+  if (task_queue->queue_class() ==
+      MainThreadTaskQueue::QueueClass::kCompositor) {
+    return main_thread_only().current_policy.compositor_priority();
+  }
+
+  // Default priority.
+  return TaskQueue::QueuePriority::kNormalPriority;
+}
+
 void MainThreadSchedulerImpl::OnBeginNestedRunLoop() {
   queueing_time_estimator_.OnBeginNestedRunLoop();
 
@@ -2758,6 +2787,14 @@
              main_thread_only().random_generator) < sampling_rate;
 }
 
+bool MainThreadSchedulerImpl::ShouldUpdateTaskQueuePriorities(
+    Policy old_policy) const {
+  return old_policy.use_case() !=
+             main_thread_only().current_policy.use_case() ||
+         old_policy.compositor_priority() !=
+             main_thread_only().current_policy.compositor_priority();
+}
+
 // static
 const char* MainThreadSchedulerImpl::UseCaseToString(UseCase use_case) {
   switch (use_case) {
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h
index a0f46ed..c18a1dc 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h
@@ -274,6 +274,11 @@
                                 bool is_reload,
                                 bool is_main_frame);
 
+  // Note that the main's thread policy should be upto date to compute
+  // the correct priority.
+  base::sequence_manager::TaskQueue::QueuePriority ComputePriority(
+      MainThreadTaskQueue* task_queue) const;
+
   // Test helpers.
   MainThreadSchedulerHelper* GetSchedulerHelperForTesting();
   TaskCostEstimator* GetLoadingTaskCostEstimatorForTesting();
@@ -373,29 +378,23 @@
           is_paused(false),
           is_throttled(false),
           is_blocked(false),
-          use_virtual_time(false),
-          priority(base::sequence_manager::TaskQueue::kNormalPriority) {}
+          use_virtual_time(false) {}
 
     bool is_enabled;
     bool is_paused;
     bool is_throttled;
     bool is_blocked;
     bool use_virtual_time;
-    base::sequence_manager::TaskQueue::QueuePriority priority;
 
     bool IsQueueEnabled(MainThreadTaskQueue* task_queue) const;
 
-    base::sequence_manager::TaskQueue::QueuePriority GetPriority(
-        MainThreadTaskQueue* task_queue) const;
-
     TimeDomainType GetTimeDomainType(MainThreadTaskQueue* task_queue) const;
 
     bool operator==(const TaskQueuePolicy& other) const {
       return is_enabled == other.is_enabled && is_paused == other.is_paused &&
              is_throttled == other.is_throttled &&
              is_blocked == other.is_blocked &&
-             use_virtual_time == other.use_virtual_time &&
-             priority == other.priority;
+             use_virtual_time == other.use_virtual_time;
     }
 
     void AsValueInto(base::trace_event::TracedValue* state) const;
@@ -406,7 +405,11 @@
     Policy()
         : rail_mode_(v8::PERFORMANCE_ANIMATION),
           should_disable_throttling_(false),
-          frozen_when_backgrounded_(false) {}
+          frozen_when_backgrounded_(false),
+          compositor_priority_(base::sequence_manager::TaskQueue::
+                                   QueuePriority::kNormalPriority),
+          use_case_(UseCase::kNone) {}
+
     ~Policy() = default;
 
     TaskQueuePolicy& compositor_queue_policy() {
@@ -461,10 +464,23 @@
     bool& frozen_when_backgrounded() { return frozen_when_backgrounded_; }
     bool frozen_when_backgrounded() const { return frozen_when_backgrounded_; }
 
+    base::sequence_manager::TaskQueue::QueuePriority& compositor_priority() {
+      return compositor_priority_;
+    }
+    base::sequence_manager::TaskQueue::QueuePriority compositor_priority()
+        const {
+      return compositor_priority_;
+    }
+
+    UseCase& use_case() { return use_case_; }
+    UseCase use_case() const { return use_case_; }
+
     bool operator==(const Policy& other) const {
       return policies_ == other.policies_ && rail_mode_ == other.rail_mode_ &&
              should_disable_throttling_ == other.should_disable_throttling_ &&
-             frozen_when_backgrounded_ == other.frozen_when_backgrounded_;
+             frozen_when_backgrounded_ == other.frozen_when_backgrounded_ &&
+             compositor_priority_ == other.compositor_priority_ &&
+             use_case_ == other.use_case_;
     }
 
     void AsValueInto(base::trace_event::TracedValue* state) const;
@@ -474,6 +490,12 @@
     bool should_disable_throttling_;
     bool frozen_when_backgrounded_;
 
+    // Priority of task queues belonging to the compositor class (Check
+    // MainThread::QueueClass).
+    base::sequence_manager::TaskQueue::QueuePriority compositor_priority_;
+
+    UseCase use_case_;
+
     std::array<TaskQueuePolicy,
                static_cast<size_t>(MainThreadTaskQueue::QueueClass::kCount)>
         policies_;
@@ -637,6 +659,10 @@
   // Returns true with probability of kSamplingRateForTaskUkm.
   bool ShouldRecordTaskUkm(bool has_thread_time);
 
+  // Returns true if there is a change in the main thread's policy that should
+  // trigger a priority update.
+  bool ShouldUpdateTaskQueuePriorities(Policy new_policy) const;
+
   static void RunIdleTask(WebThread::IdleTask, base::TimeTicks deadline);
 
   // Probabilistically record all task metadata for the current task.
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc
index 77ecbcbf..754958c 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc
@@ -212,14 +212,14 @@
 
 class MainThreadSchedulerImplForTest : public MainThreadSchedulerImpl {
  public:
-  using MainThreadSchedulerImpl::EstimateLongestJankFreeTaskDuration;
-  using MainThreadSchedulerImpl::OnIdlePeriodEnded;
-  using MainThreadSchedulerImpl::OnIdlePeriodStarted;
-  using MainThreadSchedulerImpl::OnPendingTasksChanged;
   using MainThreadSchedulerImpl::CompositorTaskQueue;
   using MainThreadSchedulerImpl::ControlTaskQueue;
   using MainThreadSchedulerImpl::DefaultTaskQueue;
+  using MainThreadSchedulerImpl::EstimateLongestJankFreeTaskDuration;
   using MainThreadSchedulerImpl::InputTaskQueue;
+  using MainThreadSchedulerImpl::OnIdlePeriodEnded;
+  using MainThreadSchedulerImpl::OnIdlePeriodStarted;
+  using MainThreadSchedulerImpl::OnPendingTasksChanged;
   using MainThreadSchedulerImpl::V8TaskQueue;
   using MainThreadSchedulerImpl::VirtualTimeControlTaskQueue;
 
@@ -308,29 +308,35 @@
 
   void Initialize(std::unique_ptr<MainThreadSchedulerImplForTest> scheduler) {
     scheduler_ = std::move(scheduler);
+
     if (kLaunchingProcessIsBackgrounded) {
       scheduler_->SetRendererBackgrounded(false);
       // Reset the policy count as foregrounding would force an initial update.
       scheduler_->update_policy_count_ = 0;
       scheduler_->use_cases_.clear();
     }
+
     default_task_runner_ = scheduler_->DefaultTaskQueue();
     compositor_task_runner_ = scheduler_->CompositorTaskQueue();
     input_task_runner_ = scheduler_->InputTaskQueue();
-    loading_task_runner_ = scheduler_->NewLoadingTaskQueue(
-        MainThreadTaskQueue::QueueType::kFrameLoading, nullptr);
-    loading_control_task_runner_ = scheduler_->NewLoadingTaskQueue(
-        MainThreadTaskQueue::QueueType::kFrameLoadingControl, nullptr);
     idle_task_runner_ = scheduler_->IdleTaskRunner();
-    timer_task_runner_ = scheduler_->NewTimerTaskQueue(
-        MainThreadTaskQueue::QueueType::kFrameThrottleable, nullptr);
     v8_task_runner_ = scheduler_->V8TaskQueue();
-    fake_queue_ = scheduler_->NewLoadingTaskQueue(
-        MainThreadTaskQueue::QueueType::kFrameLoading, nullptr);
+
+    page_scheduler_ =
+        std::make_unique<PageSchedulerImpl>(nullptr, scheduler_.get(), false);
+    main_frame_scheduler_ = page_scheduler_->CreateFrameSchedulerImpl(
+        nullptr, FrameScheduler::FrameType::kMainFrame);
+
+    loading_task_runner_ = main_frame_scheduler_->LoadingTaskQueue();
+    loading_control_task_runner_ =
+        main_frame_scheduler_->LoadingControlTaskQueue();
+    timer_task_runner_ = main_frame_scheduler_->ThrottleableTaskQueue();
   }
 
   void TearDown() override {
     DCHECK(!mock_task_runner_.get() || !message_loop_.get());
+    main_frame_scheduler_.reset();
+    page_scheduler_.reset();
     scheduler_->Shutdown();
     if (mock_task_runner_.get()) {
       // Check that all tests stop posting tasks.
@@ -592,11 +598,15 @@
   }
 
   void AdvanceTimeWithTask(double duration) {
+    scoped_refptr<MainThreadTaskQueue> fake_queue =
+        scheduler_->NewLoadingTaskQueue(
+            MainThreadTaskQueue::QueueType::kFrameLoading, nullptr);
+
     base::TimeTicks start = clock_.NowTicks();
-    scheduler_->OnTaskStarted(fake_queue_.get(), fake_task_, start);
+    scheduler_->OnTaskStarted(fake_queue.get(), fake_task_, start);
     clock_.Advance(base::TimeDelta::FromSecondsD(duration));
     base::TimeTicks end = clock_.NowTicks();
-    scheduler_->OnTaskCompleted(fake_queue_.get(), fake_task_, start, end,
+    scheduler_->OnTaskCompleted(fake_queue.get(), fake_task_, start, end,
                                 base::nullopt);
   }
 
@@ -729,12 +739,14 @@
   base::test::ScopedFeatureList feature_list_;
   base::SimpleTestTickClock clock_;
   TaskQueue::Task fake_task_;
-  scoped_refptr<MainThreadTaskQueue> fake_queue_;
   // Only one of mock_task_runner_ or message_loop_ will be set.
   scoped_refptr<cc::OrderedSimpleTaskRunner> mock_task_runner_;
   std::unique_ptr<base::MessageLoop> message_loop_;
 
   std::unique_ptr<MainThreadSchedulerImplForTest> scheduler_;
+  std::unique_ptr<PageSchedulerImpl> page_scheduler_;
+  std::unique_ptr<FrameSchedulerImpl> main_frame_scheduler_;
+
   scoped_refptr<base::SingleThreadTaskRunner> default_task_runner_;
   scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner_;
   scoped_refptr<base::SingleThreadTaskRunner> input_task_runner_;
@@ -2511,6 +2523,8 @@
 }
 
 TEST_F(MainThreadSchedulerImplTest, ShutdownPreventsPostingOfNewTasks) {
+  main_frame_scheduler_.reset();
+  page_scheduler_.reset();
   scheduler_->Shutdown();
   std::vector<std::string> run_order;
   PostTestTasks(&run_order, "D1 C1");
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.cc
index a4bf4ca..5aa3904f 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.cc
@@ -108,7 +108,6 @@
       can_be_paused_(params.can_be_paused),
       can_be_frozen_(params.can_be_frozen),
       freeze_when_keep_active_(params.freeze_when_keep_active),
-      used_for_important_tasks_(params.used_for_important_tasks),
       main_thread_scheduler_(main_thread_scheduler),
       frame_scheduler_(params.frame_scheduler) {
   if (GetTaskQueueImpl()) {
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h
index c3f72e9f..5fa3a74 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h
@@ -88,8 +88,7 @@
           can_be_throttled(false),
           can_be_paused(false),
           can_be_frozen(false),
-          freeze_when_keep_active(false),
-          used_for_important_tasks(false) {}
+          freeze_when_keep_active(false) {}
 
     QueueCreationParams SetFixedPriority(
         base::Optional<base::sequence_manager::TaskQueue::QueuePriority>
@@ -123,11 +122,6 @@
       return *this;
     }
 
-    QueueCreationParams SetUsedForImportantTasks(bool value) {
-      used_for_important_tasks = value;
-      return *this;
-    }
-
     // Forwarded calls to |spec|.
 
     QueueCreationParams SetFrameScheduler(FrameSchedulerImpl* scheduler) {
@@ -160,7 +154,6 @@
     bool can_be_paused;
     bool can_be_frozen;
     bool freeze_when_keep_active;
-    bool used_for_important_tasks;
   };
 
   ~MainThreadTaskQueue() override;
@@ -184,8 +177,6 @@
 
   bool FreezeWhenKeepActive() const { return freeze_when_keep_active_; }
 
-  bool UsedForImportantTasks() const { return used_for_important_tasks_; }
-
   void OnTaskStarted(const base::sequence_manager::TaskQueue::Task& task,
                      base::TimeTicks start);
 
@@ -228,7 +219,6 @@
   const bool can_be_paused_;
   const bool can_be_frozen_;
   const bool freeze_when_keep_active_;
-  const bool used_for_important_tasks_;
 
   // Needed to notify renderer scheduler about completed tasks.
   MainThreadSchedulerImpl* main_thread_scheduler_;  // NOT OWNED
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc
index 9b687edb..85c2e2ab 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc
@@ -315,7 +315,8 @@
 }
 
 void PageSchedulerImpl::RequestBeginMainFrameNotExpected(bool new_state) {
-  delegate_->RequestBeginMainFrameNotExpected(new_state);
+  if (delegate_)
+    delegate_->RequestBeginMainFrameNotExpected(new_state);
 }
 
 bool PageSchedulerImpl::IsAudioPlaying() const {
diff --git a/third_party/iaccessible2/ia2_api_all.idl b/third_party/iaccessible2/ia2_api_all.idl
index 14ebaf04..c64bbf9 100644
--- a/third_party/iaccessible2/ia2_api_all.idl
+++ b/third_party/iaccessible2/ia2_api_all.idl
@@ -995,7 +995,17 @@
    * A bar that serves as a level indicator to, for instance, show
    * the strength of a password or the charge of a battery.
    */
-  IA2_ROLE_LEVEL_BAR
+  IA2_ROLE_LEVEL_BAR,
+
+  /** Content previously deleted or proposed for deletion, e.g. in revision
+   history or a content view providing suggestions from reviewers.
+   */
+  IA2_ROLE_CONTENT_DELETION,
+
+  /** Content previously inserted or proposed for insertion, e.g. in revision
+   history or a content view providing suggestions from reviewers.
+   */
+  IA2_ROLE_CONTENT_INSERTION
 };
 
 /*************************************************************************
diff --git a/third_party/libaom/BUILD.gn.cmake b/third_party/libaom/BUILD.gn.cmake
index be86c77..d80671d0 100644
--- a/third_party/libaom/BUILD.gn.cmake
+++ b/third_party/libaom/BUILD.gn.cmake
@@ -107,9 +107,6 @@
       sources = aom_dsp_common_asm_sse2
       sources += aom_dsp_common_asm_ssse3
       sources += aom_ports_asm_x86
-      if (current_cpu == "x64") {
-      }
-
       defines = [ "CHROMIUM" ]
       if (is_android) {
         # TODO(johannkoenig): this was for vp8 assembly. May no longer apply.
@@ -119,9 +116,7 @@
       }
       include_dirs = libaom_include_dirs
     }
-  }
 
-  if (current_cpu == "x86" || (current_cpu == "x64" && !is_msan)) {
     # The following targets are deliberately source_set rather than
     # static_library. The :libaom target exposes these intrinsic implementations
     # via global function pointer symbols, which hides the object dependency at
@@ -204,12 +199,10 @@
       sources += aom_dsp_common_intrin_neon
     }
 
-    arm_assembly_sources = aom_dsp_common_asm_neon
-
     # Converts ARM assembly files to GAS style.
     action_foreach("convert_arm_assembly") {
       script = "//third_party/libvpx/run_perl.py"
-      sources = arm_assembly_sources
+      sources = aom_dsp_common_asm_neon
       gen_file =
           get_label_info("//third_party/libaom/source/libaom", "root_gen_dir") +
           "/{{source_root_relative_dir}}/{{source_file_part}}.S"
@@ -257,44 +250,18 @@
       configs -= [ "//build/config/compiler:default_optimization" ]
       configs += [ "//build/config/compiler:optimize_max" ]
     }
-
-    if (is_nacl) {
-      sources = libaom_srcs_generic
-    } else if (current_cpu == "x86") {
-      sources = libaom_srcs_x86
-    } else if (current_cpu == "x64") {
-      if (is_msan) {
-        sources = libaom_srcs_generic
-      } else {
-        sources = aom_av1_common_sources
-        sources += aom_av1_decoder_sources
-        sources += aom_dsp_common_sources
-        sources += aom_dsp_decoder_sources
-        sources += aom_mem_sources
-        sources += aom_rtcd_sources
-        sources += aom_scale_sources
-        sources += aom_sources
-        sources += aom_util_sources
-      }
-    } else if (current_cpu == "mipsel" || current_cpu == "mips64el") {
-      sources = libaom_srcs_mips
-    } else if (current_cpu == "arm") {
-      sources = aom_av1_common_sources
-      sources += aom_av1_decoder_sources
-      sources += aom_dsp_common_sources
-      sources += aom_dsp_decoder_sources
-      sources += aom_mem_sources
-      sources += aom_rtcd_sources
-      sources += aom_scale_sources
-      sources += aom_sources
-      sources += aom_util_sources
-    } else if (current_cpu == "arm64") {
-      sources = libaom_srcs_arm64
-    }
-
     configs -= [ "//build/config/compiler:chromium_code" ]
     configs += [ "//build/config/compiler:no_chromium_code" ]
     configs += [ ":libaom_config" ]
+    sources = aom_av1_common_sources
+    sources += aom_av1_decoder_sources
+    sources += aom_dsp_common_sources
+    sources += aom_dsp_decoder_sources
+    sources += aom_mem_sources
+    sources += aom_rtcd_sources
+    sources += aom_scale_sources
+    sources += aom_sources
+    sources += aom_util_sources
     deps = []
     if (current_cpu == "x86" || (current_cpu == "x64" && !is_msan)) {
       deps += [
@@ -309,14 +276,13 @@
     }
     if (cpu_arch_full == "arm-neon" || cpu_arch_full == "arm-neon-cpu-detect") {
       deps += [
-        ":libaom_intrinsics_neon",
         ":libaom_assembly_arm",
+        ":libaom_intrinsics_neon",
       ]
     }
     if (is_android) {
       deps += [ "//third_party/android_tools:cpu_features" ]
     }
-
     public_configs = [ ":libaom_external_config" ]
   }
 }
diff --git a/third_party/libaom/cmake_update.sh b/third_party/libaom/cmake_update.sh
index 8ba3792..6a5f403 100755
--- a/third_party/libaom/cmake_update.sh
+++ b/third_party/libaom/cmake_update.sh
@@ -43,6 +43,7 @@
   mkdir "${TMP}"
   cd "${TMP}"
 
+  echo "Generate ${1} config files."
   rm -fr "${CFG}/${1}"
   mkdir -p "${CFG}/${1}/config"
 }
@@ -166,16 +167,18 @@
 #all_platforms+=" -DENABLE_AVX2=0"
 toolchain="-DCMAKE_TOOLCHAIN_FILE=${SRC}/build/cmake/toolchains"
 
-echo "Generate linux/ia32 config files."
 reset_dirs linux/ia32
 gen_config_files linux/ia32 "${toolchain}/x86-linux.cmake ${all_platforms}"
 # libaom_srcs.gni and aom_version.h are shared.
 cp libaom_srcs.gni "${BASE}"
-rm -f "${CFG}/aom_version.h"
-cp config/aom_version.h "${CFG}/config"
+if [ ! -f "${CFG}/config/aom_version.h" ]; then
+  # These steps can be removed after the first run.
+  rm -f "${CFG}/aom_version.h"
+  mkdir -p "${CFG}/config/"
+fi
+cp config/aom_version.h "${CFG}/config/"
 gen_rtcd_header linux/ia32 x86 #--disable-avx2
 
-echo "Generate linux/x64 config files."
 reset_dirs linux/x64
 gen_config_files linux/x64 "${all_platforms}"
 gen_rtcd_header linux/x64 x86_64 #--disable-avx2
@@ -183,7 +186,6 @@
 # Windows looks like linux but with some minor tweaks. Cmake doesn't generate VS
 # project files on linux otherwise we would not resort to these hacks.
 
-echo "Generate win/x64 config files"
 reset_dirs win/x64
 cp "${CFG}/linux/x64/config"/* "${CFG}/win/x64/config/"
 sed -i.bak \
@@ -197,18 +199,15 @@
 egrep "#define [A-Z0-9_]+ [01]" "${CFG}/win/x64/config/aom_config.h" \
   | awk '{print "%define " $2 " " $3}' > "${CFG}/win/x64/config/aom_config.asm"
 
-echo "Generate linux/arm config files."
 reset_dirs linux/arm
 gen_config_files linux/arm \
   "${toolchain}/armv7-linux-gcc.cmake -DENABLE_NEON=0 -DENABLE_NEON_ASM=0 ${all_platforms}"
 gen_rtcd_header linux/arm armv7 --disable-neon
 
-echo "Generate linux/arm-neon config files."
 reset_dirs linux/arm-neon
 gen_config_files linux/arm-neon "${toolchain}/armv7-linux-gcc.cmake ${all_platforms}"
 gen_rtcd_header linux/arm-neon armv7
 
-echo "Generate linux/arm-neon-cpu-detect config files."
 reset_dirs linux/arm-neon-cpu-detect
 gen_config_files linux/arm-neon-cpu-detect \
   "${toolchain}/armv7-linux-gcc.cmake -DCONFIG_RUNTIME_CPU_DETECT=1 ${all_platforms}"
diff --git a/third_party/win_build_output/midl/third_party/iaccessible2/x64/ia2_api_all.h b/third_party/win_build_output/midl/third_party/iaccessible2/x64/ia2_api_all.h
index 1774166..0be70deeb 100644
--- a/third_party/win_build_output/midl/third_party/iaccessible2/x64/ia2_api_all.h
+++ b/third_party/win_build_output/midl/third_party/iaccessible2/x64/ia2_api_all.h
@@ -735,7 +735,9 @@
         IA2_ROLE_VIEW_PORT	= ( IA2_ROLE_TOGGLE_BUTTON + 1 ) ,

         IA2_ROLE_COMPLEMENTARY_CONTENT	= ( IA2_ROLE_VIEW_PORT + 1 ) ,

         IA2_ROLE_LANDMARK	= ( IA2_ROLE_COMPLEMENTARY_CONTENT + 1 ) ,

-        IA2_ROLE_LEVEL_BAR	= ( IA2_ROLE_LANDMARK + 1 ) 

+        IA2_ROLE_LEVEL_BAR	= ( IA2_ROLE_LANDMARK + 1 ) ,

+        IA2_ROLE_CONTENT_DELETION	= ( IA2_ROLE_LEVEL_BAR + 1 ) ,

+        IA2_ROLE_CONTENT_INSERTION	= ( IA2_ROLE_CONTENT_DELETION + 1 ) 

     } ;

 typedef long AccessibleStates;

 

diff --git a/third_party/win_build_output/midl/third_party/iaccessible2/x64/ia2_api_all.tlb b/third_party/win_build_output/midl/third_party/iaccessible2/x64/ia2_api_all.tlb
index fac69326..544ad09 100644
--- a/third_party/win_build_output/midl/third_party/iaccessible2/x64/ia2_api_all.tlb
+++ b/third_party/win_build_output/midl/third_party/iaccessible2/x64/ia2_api_all.tlb
Binary files differ
diff --git a/third_party/win_build_output/midl/third_party/iaccessible2/x86/ia2_api_all.h b/third_party/win_build_output/midl/third_party/iaccessible2/x86/ia2_api_all.h
index ba25b5c..44f8d28 100644
--- a/third_party/win_build_output/midl/third_party/iaccessible2/x86/ia2_api_all.h
+++ b/third_party/win_build_output/midl/third_party/iaccessible2/x86/ia2_api_all.h
@@ -735,7 +735,9 @@
         IA2_ROLE_VIEW_PORT	= ( IA2_ROLE_TOGGLE_BUTTON + 1 ) ,

         IA2_ROLE_COMPLEMENTARY_CONTENT	= ( IA2_ROLE_VIEW_PORT + 1 ) ,

         IA2_ROLE_LANDMARK	= ( IA2_ROLE_COMPLEMENTARY_CONTENT + 1 ) ,

-        IA2_ROLE_LEVEL_BAR	= ( IA2_ROLE_LANDMARK + 1 ) 

+        IA2_ROLE_LEVEL_BAR	= ( IA2_ROLE_LANDMARK + 1 ) ,

+        IA2_ROLE_CONTENT_DELETION	= ( IA2_ROLE_LEVEL_BAR + 1 ) ,

+        IA2_ROLE_CONTENT_INSERTION	= ( IA2_ROLE_CONTENT_DELETION + 1 ) 

     } ;

 typedef long AccessibleStates;

 

diff --git a/third_party/win_build_output/midl/third_party/iaccessible2/x86/ia2_api_all.tlb b/third_party/win_build_output/midl/third_party/iaccessible2/x86/ia2_api_all.tlb
index 70b443fc..ec9e72c 100644
--- a/third_party/win_build_output/midl/third_party/iaccessible2/x86/ia2_api_all.tlb
+++ b/third_party/win_build_output/midl/third_party/iaccessible2/x86/ia2_api_all.tlb
Binary files differ
diff --git a/tools/gn/bootstrap/bootstrap.py b/tools/gn/bootstrap/bootstrap.py
index 7ae06c3..74967b0 100755
--- a/tools/gn/bootstrap/bootstrap.py
+++ b/tools/gn/bootstrap/bootstrap.py
@@ -634,10 +634,6 @@
       'base/trace_event/heap_profiler_allocation_context.cc',
       'base/trace_event/heap_profiler_allocation_context_tracker.cc',
       'base/trace_event/heap_profiler_event_filter.cc',
-      'base/trace_event/heap_profiler_heap_dump_writer.cc',
-      'base/trace_event/heap_profiler_serialization_state.cc',
-      'base/trace_event/heap_profiler_stack_frame_deduplicator.cc',
-      'base/trace_event/heap_profiler_type_name_deduplicator.cc',
       'base/trace_event/malloc_dump_provider.cc',
       'base/trace_event/memory_allocator_dump.cc',
       'base/trace_event/memory_allocator_dump_guid.cc',
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 139770c..5bad7f5 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -1035,7 +1035,7 @@
     ],
 
     'asan_lsan_release_trybot': [
-      'asan', 'lsan', 'release_trybot',
+      'asan', 'libfuzzer', 'lsan', 'release_trybot',
     ],
 
     # Cast Linux takes very long in linking, possibly due to being on GCE
diff --git a/tools/metrics/actions/README.md b/tools/metrics/actions/README.md
index 1cfb2840..07e443f 100644
--- a/tools/metrics/actions/README.md
+++ b/tools/metrics/actions/README.md
@@ -162,6 +162,9 @@
 tag entry.  If your user action is being replaced by a new version, we suggest
 noting that in the previous user action's description.
 
+A changelist that marks a user action as obsolete should be reviewed by all
+current owners.
+
 Deleting user action entries would be bad if someone accidentally reused your
 old user action name and which therefore corrupts new data with whatever old
 data is still coming in.  It's also useful to keep obsolete user action
diff --git a/tools/metrics/histograms/README.md b/tools/metrics/histograms/README.md
index 669cc21..49cf1fa7 100644
--- a/tools/metrics/histograms/README.md
+++ b/tools/metrics/histograms/README.md
@@ -340,6 +340,9 @@
 milestone in the obsolete tag entry.  If your histogram is being replaced by a
 new version, we suggest noting that in the previous histogram's description.
 
+A changelist that marks a histogram as obsolete should be reviewed by all
+current owners.
+
 Deleting histogram entries would be bad if someone to accidentally reused your
 old histogram name and thereby corrupts new data with whatever old data is still
 coming in.  It's also useful to keep obsolete histogram descriptions in
@@ -358,6 +361,9 @@
 tools not to treat the partial base name as a distinct histogram. Note that
 suffixes can be applied recursively.
 
+You can also declare ownership of `<histogram_suffixes>`. If there's no owner
+specified, the generated histograms will inherit owners from the parents.
+
 ### Enum labels
 
 _All_ histograms, including boolean and sparse histograms, may have enum labels
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index bfb09d2..4bda71a2 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -984,6 +984,10 @@
   </int>
   <int value="7" label="All GPU process crashes"/>
   <int value="8" label="All renderer process crashes"/>
+  <int value="9"
+      label="Visible main frame renderer foreground intentional kill"/>
+  <int value="10"
+      label="Visible renderer foreground normal termination without minidump"/>
 </enum>
 
 <enum name="AndroidResourceExtractionStatus">
@@ -19845,6 +19849,22 @@
   <int value="14" label="Async DeleteFile Failed"/>
 </enum>
 
+<enum name="FileReaderLoaderFailureType">
+  <int value="0" label="Mojo pipe creation failed"/>
+  <int value="1" label="Data incomplete after synchronous reading"/>
+  <int value="2" label="OnComplete never called after synchronous reading"/>
+  <int value="3" label="The blob's total size too large for FileReaderLoader"/>
+  <int value="4" label="Failed to create the ArrayBufferBuilder"/>
+  <int value="5" label="Failed to append to the ArrayBufferBuilder"/>
+  <int value="6"
+      label="Backend read error, see Storage.Blob.FileReaderLoader.ReadError"/>
+  <int value="7" label="The size read does not match the size reported"/>
+  <int value="8"
+      label="The data pipe is not readable and there are bytes left to read"/>
+  <int value="9" label="The data pipe closed early"/>
+  <int value="10" label="Data pipe reading failed with an unexpected error"/>
+</enum>
+
 <enum name="FileReaderSyncWorkerType">
   <int value="0" label="Other"/>
   <int value="1" label="Dedicated Worker"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 1ae3dcdb..7be99c7d 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -8483,6 +8483,26 @@
   </details>
 </histogram>
 
+<histogram name="Blink.VisibleLoadTime.LazyLoadEligibleFrames.AboveTheFold"
+    units="ms">
+  <owner>sclittle@chromium.org</owner>
+  <summary>
+    Milliseconds spent waiting for an above the fold iframe to load. Only fires
+    for iframes that would be eligible for lazy loading, i.e. cross-origin and
+    non-hidden.
+  </summary>
+</histogram>
+
+<histogram name="Blink.VisibleLoadTime.LazyLoadEligibleFrames.BelowTheFold"
+    units="ms">
+  <owner>sclittle@chromium.org</owner>
+  <summary>
+    Milliseconds spent waiting for a below the fold iframe to load. Only fires
+    for iframes that would be eligible for lazy loading, i.e. cross-origin and
+    non-hidden.
+  </summary>
+</histogram>
+
 <histogram name="Blink.XHR.setRequestHeader.HeaderValueCategoryInRFC7230"
     enum="XMLHttpRequestHeaderValueCategoryInRFC7230">
   <obsolete>
@@ -93583,6 +93603,19 @@
   </summary>
 </histogram>
 
+<histogram name="Storage.Blob.FileReaderLoader.FailureType"
+    enum="FileReaderLoaderFailureType">
+  <owner>dmurph@chromium.org</owner>
+  <summary>
+    Recorded when an error occurs in the FileReaderLoader, which is used to load
+    blobs in the Renderer. FileReaderUser is mostly used for Javascript's
+    'FileReader', but can also be used to read blobs for the IndexedDB
+    renderer-side implementation. For the read error category, see
+    Storage.Blob.FileReaderLoader.ReadError for a breakdown of the specific read
+    error reasons.
+  </summary>
+</histogram>
+
 <histogram name="Storage.Blob.FileReaderLoader.ReadError" enum="NetErrorCodes">
   <owner>dmurph@chromium.org</owner>
   <summary>
@@ -111184,6 +111217,17 @@
   <affected-histogram name="BlinkGC.CollectionRate"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="BlinkVisibleLoadTimeSuffixes" separator=".">
+  <suffix name="2G" label="2G effective connection type"/>
+  <suffix name="3G" label="3G effective connection type"/>
+  <suffix name="4G" label="4G effective connection type"/>
+  <suffix name="Slow2G" label="Slow-2G effective connection type"/>
+  <affected-histogram
+      name="Blink.VisibleLoadTime.LazyLoadEligibleFrames.AboveTheFold"/>
+  <affected-histogram
+      name="Blink.VisibleLoadTime.LazyLoadEligibleFrames.BelowTheFold"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="CachedResourceType" separator=".">
   <suffix name="Audio"
       label="Showing cache patterns only for audio resources."/>
diff --git a/tools/perf/core/perf_data_generator.py b/tools/perf/core/perf_data_generator.py
index 63bb12a..3647bae 100755
--- a/tools/perf/core/perf_data_generator.py
+++ b/tools/perf/core/perf_data_generator.py
@@ -177,7 +177,7 @@
       {
        'os': 'Android',
        'pool': 'chrome.tests.perf',
-       'device_os': 'KOT49H0',
+       'device_os': 'KOT49H',
        'device_type': 'hammerhead',
        'device_os_flavor': 'google',
        'device_ids': [
diff --git a/tools/perf/expectations.config b/tools/perf/expectations.config
index 6b84a25..014af581 100644
--- a/tools/perf/expectations.config
+++ b/tools/perf/expectations.config
@@ -15,9 +15,6 @@
 crbug.com/764868 [ Android_One ] blink_perf.bindings/structured-clone-json-serialize.html [ Skip ]
 crbug.com/764868 [ Android_One ] blink_perf.bindings/structured-clone-long-string-serialize.html [ Skip ]
 crbug.com/764868 [ Android_One ] blink_perf.bindings/structured-clone-json-deserialize.html [ Skip ]
-crbug.com/845555 [ Nexus_5X ] blink_perf.bindings/structured-clone-json-serialize.html [ Skip ]
-crbug.com/845555 [ Nexus_5X ] blink_perf.bindings/structured-clone-json-deserialize.html [ Skip ]
-
 
 # Benchmark: blink_perf.canvas
 crbug.com/593973 [ Android_Svelte ] blink_perf.canvas/* [ Skip ]
@@ -50,7 +47,6 @@
 
 # Benchmark: dromaeo
 crbug.com/844541 [ Android_One ] dromaeo/http://dromaeo.com?dom-modify [ Skip ]
-crbug.com/845511 [ Nexus_5X ] dromaeo/http://dromaeo.com?dom-modify [ Skip ]
 
 # Benchmark: blink_perf.svg
 crbug.com/736817 [ Nexus_5X ] blink_perf.svg/SvgCubics.html [ Skip ]
diff --git a/tools/perf/page_sets/data/rendering_desktop.json b/tools/perf/page_sets/data/rendering_desktop.json
index 51420dd0..6942eff 100644
--- a/tools/perf/page_sets/data/rendering_desktop.json
+++ b/tools/perf/page_sets/data/rendering_desktop.json
@@ -95,6 +95,18 @@
         },
         "ie_pirate_mark": {
             "DEFAULT": "tough_filters_cases_002.wprgo"
+	},
+        "guimark_vector_chart": {
+            "DEFAULT": "tough_path_rendering_cases_002.wprgo"
+        },
+        "motion_mark_canvas_fill_shapes": {
+            "DEFAULT": "tough_path_rendering_cases_002.wprgo"
+        },
+        "motion_mark_canvas_stroke_shapes": {
+            "DEFAULT": "tough_path_rendering_cases_002.wprgo"
+        },
+        "ie_chalkboard": {
+            "DEFAULT": "tough_path_rendering_cases_002.wprgo"
         }
     },
     "description": "Describes the Web Page Replay archives for a story set. Don't edit by hand! Use record_wpr for updating.",
diff --git a/tools/perf/page_sets/data/rendering_mobile.json b/tools/perf/page_sets/data/rendering_mobile.json
index 5e680ab2..d4f13592 100644
--- a/tools/perf/page_sets/data/rendering_mobile.json
+++ b/tools/perf/page_sets/data/rendering_mobile.json
@@ -443,6 +443,18 @@
         },
         "ie_pirate_mark": {
             "DEFAULT": "tough_filters_cases_002.wprgo"
+	},
+	"guimark_vector_chart": {
+            "DEFAULT": "tough_path_rendering_cases_002.wprgo"
+        },
+        "motion_mark_canvas_fill_shapes": {
+            "DEFAULT": "tough_path_rendering_cases_002.wprgo"
+        },
+        "motion_mark_canvas_stroke_shapes": {
+            "DEFAULT": "tough_path_rendering_cases_002.wprgo"
+        },
+        "ie_chalkboard": {
+            "DEFAULT": "tough_path_rendering_cases_002.wprgo"
         }
     },
     "description": "Describes the Web Page Replay archives for a story set. Don't edit by hand! Use record_wpr for updating.",
diff --git a/tools/perf/page_sets/data/tough_webgl_cases.json b/tools/perf/page_sets/data/tough_webgl_cases.json
index 449fe38..1786d0f 100644
--- a/tools/perf/page_sets/data/tough_webgl_cases.json
+++ b/tools/perf/page_sets/data/tough_webgl_cases.json
@@ -3,34 +3,34 @@
         "aquarium_20k": {
             "DEFAULT": "tough_webgl_cases_006.wprgo"
         },
-        "http://kenrussell.github.io/webgl-animometer/Animometer/tests/3d/webgl.html": {
+        "ken_russell": {
             "DEFAULT": "tough_webgl_cases_006.wprgo"
         },
-        "http://webglsamples.org/aquarium/aquarium.html": {
+        "aquarium": {
             "DEFAULT": "tough_webgl_cases_006.wprgo"
         },
-        "http://webglsamples.org/blob/blob.html": {
+        "blob": {
             "DEFAULT": "tough_webgl_cases_006.wprgo"
         },
-        "http://webglsamples.org/dynamic-cubemap/dynamic-cubemap.html": {
+        "dynamic_cube_map": {
             "DEFAULT": "tough_webgl_cases_006.wprgo"
         },
-        "http://www.khronos.org/registry/webgl/sdk/demos/google/nvidia-vertex-buffer-object/index.html": {
+        "nvidia_vertex_buffer_object": {
             "DEFAULT": "tough_webgl_cases_006.wprgo"
         },
-        "http://www.khronos.org/registry/webgl/sdk/demos/google/particles/index.html": {
+        "particles": {
             "DEFAULT": "tough_webgl_cases_006.wprgo"
         },
-        "http://www.khronos.org/registry/webgl/sdk/demos/google/san-angeles/index.html": {
+        "sans_angeles": {
             "DEFAULT": "tough_webgl_cases_006.wprgo"
         },
-        "http://www.khronos.org/registry/webgl/sdk/demos/webkit/Earth.html": {
+        "earth": {
             "DEFAULT": "tough_webgl_cases_006.wprgo"
         },
-        "http://www.khronos.org/registry/webgl/sdk/demos/webkit/ManyPlanetsDeep.html": {
+        "many_planets_deep": {
             "DEFAULT": "tough_webgl_cases_006.wprgo"
         }
     },
     "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/rendering/tough_path_rendering_cases.py b/tools/perf/page_sets/rendering/tough_path_rendering_cases.py
new file mode 100644
index 0000000..46d1442d
--- /dev/null
+++ b/tools/perf/page_sets/rendering/tough_path_rendering_cases.py
@@ -0,0 +1,70 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+from telemetry.page import shared_page_state
+from telemetry import story
+
+from page_sets.rendering import rendering_story
+
+
+class ToughPathRenderingPage(rendering_story.RenderingStory):
+  ABSTRACT_STORY = True
+
+  def RunPageInteractions(self, action_runner):
+    with action_runner.CreateInteraction('ClickStart'):
+      action_runner.Wait(10)
+
+
+class GUIMarkVectorChartPage(ToughPathRenderingPage):
+  BASE_NAME = 'guimark_vector_chart'
+  URL = 'http://www.craftymind.com/factory/guimark2/HTML5ChartingTest.html'
+
+
+class MotionMarkCanvasFillShapesPage(ToughPathRenderingPage):
+  BASE_NAME = 'motion_mark_canvas_fill_shapes'
+  # pylint: disable=line-too-long
+  URL = 'http://rawgit.com/WebKit/webkit/master/PerformanceTests/MotionMark/developer.html?test-name=Fillshapes&test-interval=20&display=minimal&tiles=big&controller=fixed&frame-rate=50&kalman-process-error=1&kalman-measurement-error=4&time-measurement=performance&suite-name=Canvassuite&complexity=1000'
+
+
+class MotionMarkCanvasStrokeShapesPage(ToughPathRenderingPage):
+  BASE_NAME = 'motion_mark_canvas_stroke_shapes'
+  # pylint: disable=line-too-long
+  URL = 'http://rawgit.com/WebKit/webkit/master/PerformanceTests/MotionMark/developer.html?test-name=Strokeshapes&test-interval=20&display=minimal&tiles=big&controller=fixed&frame-rate=50&kalman-process-error=1&kalman-measurement-error=4&time-measurement=performance&suite-name=Canvassuite&complexity=1000'
+
+
+class ChalkboardPage(rendering_story.RenderingStory):
+  BASE_NAME = 'ie_chalkboard'
+  URL = 'https://testdrive-archive.azurewebsites.net/performance/chalkboard/'
+
+  def RunPageInteractions(self, action_runner):
+    with action_runner.CreateInteraction('ClickStart'):
+      action_runner.EvaluateJavaScript(
+          'document.getElementById("StartButton").click()')
+      action_runner.Wait(20)
+
+
+# TODO(crbug.com/760553): remove this class after
+# smoothness.tough_path_rendering_cases benchmark is completely replaced
+# by rendering benchmarks
+class ToughPathRenderingCasesPageSet(story.StorySet):
+
+  """
+  Description: Self-driven path rendering examples
+  """
+
+  def __init__(self):
+    super(ToughPathRenderingCasesPageSet, self).__init__(
+      archive_data_file='../data/tough_path_rendering_cases.json',
+      cloud_storage_bucket=story.PARTNER_BUCKET)
+
+    page_classes = [
+      GUIMarkVectorChartPage,
+      MotionMarkCanvasFillShapesPage,
+      MotionMarkCanvasStrokeShapesPage,
+      ChalkboardPage
+    ]
+
+    for page_class in page_classes:
+      self.AddStory(page_class(
+          page_set=self,
+          shared_page_state_class=shared_page_state.SharedPageState))
diff --git a/tools/perf/page_sets/tough_path_rendering_cases.py b/tools/perf/page_sets/tough_path_rendering_cases.py
deleted file mode 100644
index 330ae16..0000000
--- a/tools/perf/page_sets/tough_path_rendering_cases.py
+++ /dev/null
@@ -1,51 +0,0 @@
-# Copyright 2015 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-from telemetry.page import page as page_module
-from telemetry import story
-
-
-class ToughPathRenderingCasesPage(page_module.Page):
-  def RunPageInteractions(self, action_runner):
-    with action_runner.CreateInteraction('ClickStart'):
-      action_runner.Wait(10)
-
-
-class ChalkboardPage(page_module.Page):
-
-  def RunPageInteractions(self, action_runner):
-    with action_runner.CreateInteraction('ClickStart'):
-      action_runner.EvaluateJavaScript(
-          'document.getElementById("StartButton").click()')
-      action_runner.Wait(20)
-
-class ToughPathRenderingCasesPageSet(story.StorySet):
-
-  """
-  Description: Self-driven path rendering examples
-  """
-
-  def __init__(self):
-    super(ToughPathRenderingCasesPageSet, self).__init__(
-      archive_data_file='data/tough_path_rendering_cases.json',
-      cloud_storage_bucket=story.PARTNER_BUCKET)
-
-    page_list = [
-      ('guimark_vector_chart',
-      'http://www.craftymind.com/factory/guimark2/HTML5ChartingTest.html'),
-      ('motion_mark_canvas_fill_shapes',
-      'http://rawgit.com/WebKit/webkit/master/PerformanceTests/MotionMark/developer.html?test-name=Fillshapes&test-interval=20&display=minimal&tiles=big&controller=fixed&frame-rate=50&kalman-process-error=1&kalman-measurement-error=4&time-measurement=performance&suite-name=Canvassuite&complexity=1000'), # pylint: disable=line-too-long
-      ('motion_mark_canvas_stroke_shapes',
-      'http://rawgit.com/WebKit/webkit/master/PerformanceTests/MotionMark/developer.html?test-name=Strokeshapes&test-interval=20&display=minimal&tiles=big&controller=fixed&frame-rate=50&kalman-process-error=1&kalman-measurement-error=4&time-measurement=performance&suite-name=Canvassuite&complexity=1000'), # pylint: disable=line-too-long
-    ]
-
-    for name, url in page_list:
-      self.AddStory(ToughPathRenderingCasesPage(name=name, url=url,
-          page_set=self))
-
-    # Chalkboard content linked from
-    # http://ie.microsoft.com/testdrive/Performance/Chalkboard/.
-    chalkboard_url = ('https://testdrive-archive.azurewebsites.net'
-                      '/performance/chalkboard/')
-    name = 'ie_chalkboard'
-    self.AddStory(ChalkboardPage(chalkboard_url, self, name=name))
diff --git a/tools/perf/page_sets/tough_webgl_cases.py b/tools/perf/page_sets/tough_webgl_cases.py
index abc7295..0a631e39 100644
--- a/tools/perf/page_sets/tough_webgl_cases.py
+++ b/tools/perf/page_sets/tough_webgl_cases.py
@@ -50,31 +50,29 @@
     urls_list = [
       # pylint: disable=line-too-long
       ('http://www.khronos.org/registry/webgl/sdk/demos/google/nvidia-vertex-buffer-object/index.html',
-       None),
+       'nvidia_vertex_buffer_object'),
       # pylint: disable=line-too-long
       ('http://www.khronos.org/registry/webgl/sdk/demos/google/san-angeles/index.html',
-       None),
+       'sans_angeles'),
       # pylint: disable=line-too-long
       ('http://www.khronos.org/registry/webgl/sdk/demos/google/particles/index.html',
-       None),
+       'particles'),
       ('http://www.khronos.org/registry/webgl/sdk/demos/webkit/Earth.html',
-       None),
+       'earth'),
       # pylint: disable=line-too-long
       ('http://www.khronos.org/registry/webgl/sdk/demos/webkit/ManyPlanetsDeep.html',
-       None),
+       'many_planets_deep'),
       ('http://webglsamples.org/aquarium/aquarium.html',
-       None),
+       'aquarium'),
       ('http://webglsamples.org/aquarium/aquarium.html?numFish=20000',
        'aquarium_20k'),
-      ('http://webglsamples.org/blob/blob.html', None),
+      ('http://webglsamples.org/blob/blob.html', 'blob'),
       # pylint: disable=line-too-long
       ('http://webglsamples.org/dynamic-cubemap/dynamic-cubemap.html',
-       None),
+       'dynamic_cube_map'),
       # pylint: disable=line-too-long
       ('http://kenrussell.github.io/webgl-animometer/Animometer/tests/3d/webgl.html',
-       None)
+       'ken_russell')
     ]
     for url, name in urls_list:
-      if name is None:
-        name = url
       self.AddStory(ToughWebglCasesPage(url, self, name))
diff --git a/tools/perf/process_perf_results.py b/tools/perf/process_perf_results.py
index 1c48344..74b33cd 100755
--- a/tools/perf/process_perf_results.py
+++ b/tools/perf/process_perf_results.py
@@ -179,6 +179,16 @@
   extra_links['Benchmarks logs'] = logdog_stream
 
 
+def _handle_benchmarks_shard_map(benchmarks_shard_map_file, extra_links):
+  with open(benchmarks_shard_map_file) as f:
+    benchmarks_shard_data = json.load(f)
+    logdog_file_name = _generate_unique_logdog_filename('Benchmarks_Shard_Map')
+    logdog_stream = logdog_helper.text(
+        logdog_file_name, json.dumps(benchmarks_shard_data, sort_keys=True,
+                                     indent=4, separators=(',', ': ')),
+        content_type=JSON_CONTENT_TYPE)
+    extra_links['Benchmarks shard map'] = logdog_stream
+
 
 def _get_benchmark_name(directory):
   return basename(directory).replace(" benchmark", "")
@@ -206,11 +216,14 @@
       if not isfile(join(task_output_dir, f))
   ]
   benchmark_directory_list = []
+  benchmarks_shard_map_file = None
   for directory in directory_list:
-    benchmark_directory_list += [
-      join(task_output_dir, directory, f)
-      for f in listdir(join(task_output_dir, directory))
-    ]
+    for f in listdir(join(task_output_dir, directory)):
+      path = join(task_output_dir, directory, f)
+      if path.endswith('benchmarks_shard_map.json'):
+        benchmarks_shard_map_file = path
+      else:
+        benchmark_directory_list.append(path)
 
   # Now create a map of benchmark name to the list of directories
   # the lists were written to.
@@ -231,7 +244,12 @@
 
   extra_links = {}
 
-  # First, upload all the benchmark logs to logdog and add a page entry for
+  # First, upload benchmarks shard map to logdog and add a page
+  # entry for it in extra_links.
+  if benchmarks_shard_map_file:
+    _handle_benchmarks_shard_map(benchmarks_shard_map_file, extra_links)
+
+  # Second, upload all the benchmark logs to logdog and add a page entry for
   # those links in extra_links.
   _handle_perf_logs(benchmark_directory_map, extra_links)
 
diff --git a/ui/accessibility/platform/ax_platform_node_win.cc b/ui/accessibility/platform/ax_platform_node_win.cc
index a88c0c89..c910b68 100644
--- a/ui/accessibility/platform/ax_platform_node_win.cc
+++ b/ui/accessibility/platform/ax_platform_node_win.cc
@@ -3244,6 +3244,12 @@
       // maps this to more general IA2_ROLE_LANDMARK.
       ia2_role = IA2_ROLE_LANDMARK;
       break;
+    case ax::mojom::Role::kContentDeletion:
+      ia2_role = IA2_ROLE_CONTENT_DELETION;
+      break;
+    case ax::mojom::Role::kContentInsertion:
+      ia2_role = IA2_ROLE_CONTENT_INSERTION;
+      break;
     case ax::mojom::Role::kContentInfo:
       ia2_role = IA2_ROLE_LANDMARK;
       break;
diff --git a/ui/gfx/shadow_value.cc b/ui/gfx/shadow_value.cc
index ba552ba..b248765 100644
--- a/ui/gfx/shadow_value.cc
+++ b/ui/gfx/shadow_value.cc
@@ -9,6 +9,7 @@
 #include <algorithm>
 
 #include "base/strings/stringprintf.h"
+#include "ui/gfx/color_palette.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/geometry/safe_integer_conversions.h"
 #include "ui/gfx/geometry/vector2d_conversions.h"
@@ -71,6 +72,27 @@
 }
 
 // static
+ShadowValues ShadowValue::MakeRefreshShadowValues(int elevation) {
+  constexpr SkColor shadow_base_color = gfx::kGoogleGrey800;
+  // Refresh uses hand-tweaked shadows corresponding to a small set of
+  // elevations. Use the Refresh spec and designer input to add missing shadow
+  // values.
+  switch (elevation) {
+    case 3: {
+      ShadowValue key = {gfx::Vector2d(0, 1), 12,
+                         SkColorSetA(shadow_base_color, 0x66)};
+      ShadowValue ambient = {gfx::Vector2d(0, 4), 64,
+                             SkColorSetA(shadow_base_color, 0x40)};
+      return {key, ambient};
+    }
+    default:
+      // This surface has not been updated for Refresh. Fall back to the
+      // deprecated style.
+      return MakeMdShadowValues(elevation);
+  }
+}
+
+// static
 ShadowValues ShadowValue::MakeMdShadowValues(int elevation) {
   ShadowValues shadow_values;
   // To match the CSS notion of blur (spread outside the bounding box) to the
diff --git a/ui/gfx/shadow_value.h b/ui/gfx/shadow_value.h
index d212678..e8e32b4 100644
--- a/ui/gfx/shadow_value.h
+++ b/ui/gfx/shadow_value.h
@@ -54,9 +54,9 @@
   // a uniform color.
   static Insets GetBlurRegion(const ShadowValues& shadows);
 
-  // Makes ShadowValues that should match MD style shadows for the given
-  // elevation.
+  // Makes ShadowValues matching MD or Refresh shadows for the given elevation.
   static ShadowValues MakeMdShadowValues(int elevation);
+  static ShadowValues MakeRefreshShadowValues(int elevation);
 
  private:
   gfx::Vector2d offset_;
diff --git a/ui/ozone/platform/drm/BUILD.gn b/ui/ozone/platform/drm/BUILD.gn
index 00c1368..2fa6e3c 100644
--- a/ui/ozone/platform/drm/BUILD.gn
+++ b/ui/ozone/platform/drm/BUILD.gn
@@ -153,8 +153,6 @@
     "common/drm_util_unittest.cc",
     "gpu/drm_overlay_validator_unittest.cc",
     "gpu/drm_window_unittest.cc",
-    "gpu/fake_plane_info.cc",
-    "gpu/fake_plane_info.h",
     "gpu/hardware_display_controller_unittest.cc",
     "gpu/hardware_display_plane_manager_unittest.cc",
     "gpu/mock_drm_device.cc",
diff --git a/ui/ozone/platform/drm/gpu/drm_overlay_validator_unittest.cc b/ui/ozone/platform/drm/gpu/drm_overlay_validator_unittest.cc
index 9c28cf54..288b9a0 100644
--- a/ui/ozone/platform/drm/gpu/drm_overlay_validator_unittest.cc
+++ b/ui/ozone/platform/drm/gpu/drm_overlay_validator_unittest.cc
@@ -19,7 +19,6 @@
 #include "ui/ozone/platform/drm/gpu/drm_device_generator.h"
 #include "ui/ozone/platform/drm/gpu/drm_device_manager.h"
 #include "ui/ozone/platform/drm/gpu/drm_window.h"
-#include "ui/ozone/platform/drm/gpu/fake_plane_info.h"
 #include "ui/ozone/platform/drm/gpu/hardware_display_controller.h"
 #include "ui/ozone/platform/drm/gpu/mock_drm_device.h"
 #include "ui/ozone/platform/drm/gpu/mock_scanout_buffer.h"
diff --git a/ui/ozone/platform/drm/gpu/fake_plane_info.cc b/ui/ozone/platform/drm/gpu/fake_plane_info.cc
deleted file mode 100644
index 2e9a2a7..0000000
--- a/ui/ozone/platform/drm/gpu/fake_plane_info.cc
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <drm_fourcc.h>
-
-#include "ui/ozone/platform/drm/gpu/fake_plane_info.h"
-
-namespace ui {
-
-FakePlaneInfo::FakePlaneInfo(uint32_t plane_id, uint32_t crtc_mask)
-    : id(plane_id), allowed_crtc_mask(crtc_mask) {
-  allowed_formats.push_back(DRM_FORMAT_XRGB8888);
-}
-
-FakePlaneInfo::FakePlaneInfo(uint32_t plane_id,
-                             uint32_t crtc_mask,
-                             const std::vector<uint32_t>& formats)
-    : id(plane_id), allowed_crtc_mask(crtc_mask), allowed_formats(formats) {}
-
-FakePlaneInfo::FakePlaneInfo(const FakePlaneInfo& other) = default;
-
-FakePlaneInfo::~FakePlaneInfo() {}
-
-}  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/fake_plane_info.h b/ui/ozone/platform/drm/gpu/fake_plane_info.h
deleted file mode 100644
index 79265ebe..0000000
--- a/ui/ozone/platform/drm/gpu/fake_plane_info.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_OZONE_PLATFORM_DRM_GPU_FAKE_PLANE_INFO_H_
-#define UI_OZONE_PLATFORM_DRM_GPU_FAKE_PLANE_INFO_H_
-
-#include <stdint.h>
-
-#include <vector>
-
-#include "base/macros.h"
-
-namespace ui {
-
-struct FakePlaneInfo {
-  FakePlaneInfo(uint32_t plane_id, uint32_t crtc_mask);
-  FakePlaneInfo(uint32_t plane_id,
-                uint32_t crtc_mask,
-                const std::vector<uint32_t>& formats);
-  FakePlaneInfo(const FakePlaneInfo& other);
-  ~FakePlaneInfo();
-
-  uint32_t id;
-  uint32_t allowed_crtc_mask;
-  std::vector<uint32_t> allowed_formats;
-};
-
-}  // namespace ui
-
-#endif  // UI_OZONE_PLATFORM_DRM_GPU_FAKE_PLANE_INFO_H_
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_unittest.cc b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_unittest.cc
index 3b62768..6eb77529 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_unittest.cc
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_unittest.cc
@@ -16,7 +16,6 @@
 #include "ui/gfx/gpu_fence.h"
 #include "ui/gfx/gpu_fence_handle.h"
 #include "ui/ozone/platform/drm/gpu/crtc_controller.h"
-#include "ui/ozone/platform/drm/gpu/fake_plane_info.h"
 #include "ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.h"
 #include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.h"
 #include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.h"
diff --git a/ui/views/bubble/bubble_border.cc b/ui/views/bubble/bubble_border.cc
index 6961a92..b746bb4 100644
--- a/ui/views/bubble/bubble_border.cc
+++ b/ui/views/bubble/bubble_border.cc
@@ -20,6 +20,7 @@
 #include "ui/gfx/shadow_value.h"
 #include "ui/gfx/skia_paint_util.h"
 #include "ui/resources/grit/ui_resources.h"
+#include "ui/views/layout/layout_provider.h"
 #include "ui/views/painter.h"
 #include "ui/views/resources/grit/views_resources.h"
 #include "ui/views/view.h"
@@ -423,8 +424,7 @@
   gfx::ShadowValues shadows;
   if (elevation.has_value()) {
     DCHECK(elevation.value() >= 0);
-    shadows = gfx::ShadowValues(
-        gfx::ShadowValue::MakeMdShadowValues(elevation.value()));
+    shadows = LayoutProvider::Get()->MakeShadowValues(elevation.value());
   } else {
     constexpr int kSmallShadowVerticalOffset = 2;
     constexpr int kSmallShadowBlur = 4;
diff --git a/ui/views/bubble/bubble_dialog_delegate.cc b/ui/views/bubble/bubble_dialog_delegate.cc
index af96e10f4..6d2f625b 100644
--- a/ui/views/bubble/bubble_dialog_delegate.cc
+++ b/ui/views/bubble/bubble_dialog_delegate.cc
@@ -136,15 +136,22 @@
     Widget* widget) {
   BubbleFrameView* frame = new BubbleDialogFrameView(title_margins_);
 
+  LayoutProvider* provider = LayoutProvider::Get();
   frame->set_footnote_margins(
-      LayoutProvider::Get()->GetInsetsMetric(INSETS_DIALOG_SUBSECTION));
+      provider->GetInsetsMetric(INSETS_DIALOG_SUBSECTION));
   frame->SetFootnoteView(CreateFootnoteView());
 
   BubbleBorder::Arrow adjusted_arrow = arrow();
   if (base::i18n::IsRTL() && mirror_arrow_in_rtl_)
     adjusted_arrow = BubbleBorder::horizontal_mirror(adjusted_arrow);
-  frame->SetBubbleBorder(std::unique_ptr<BubbleBorder>(
-      new BubbleBorder(adjusted_arrow, shadow(), color())));
+  std::unique_ptr<BubbleBorder> border =
+      std::make_unique<BubbleBorder>(adjusted_arrow, shadow(), color());
+  if (ui::MaterialDesignController::IsSecondaryUiMaterial()) {
+    border->SetCornerRadius(provider->GetCornerRadiusMetric(EMPHASIS_MEDIUM));
+    border->set_md_shadow_elevation(
+        provider->GetShadowElevationMetric(EMPHASIS_MEDIUM));
+  }
+  frame->SetBubbleBorder(std::move(border));
   return frame;
 }
 
@@ -323,13 +330,17 @@
   // change as well.
   if (!anchor_view || anchor_widget() != anchor_view->GetWidget()) {
     if (anchor_widget()) {
+      if (GetWidget() && GetWidget()->IsVisible())
+        UpdateAnchorWidgetRenderState(false);
       anchor_widget_->RemoveObserver(this);
       anchor_widget_ = NULL;
     }
     if (anchor_view) {
       anchor_widget_ = anchor_view->GetWidget();
-      if (anchor_widget_)
+      if (anchor_widget_) {
         anchor_widget_->AddObserver(this);
+        UpdateAnchorWidgetRenderState(GetWidget() && GetWidget()->IsVisible());
+      }
     }
   }
 
@@ -389,10 +400,8 @@
 
 void BubbleDialogDelegateView::HandleVisibilityChanged(Widget* widget,
                                                        bool visible) {
-  if (widget == GetWidget() && anchor_widget() &&
-      anchor_widget()->GetTopLevelWidget()) {
-    anchor_widget()->GetTopLevelWidget()->SetAlwaysRenderAsActive(visible);
-  }
+  if (widget == GetWidget())
+    UpdateAnchorWidgetRenderState(visible);
 
   // Fire ax::mojom::Event::kAlert for bubbles marked as
   // ax::mojom::Role::kAlertDialog; this instructs accessibility tools to read
@@ -412,4 +421,11 @@
     GetWidget()->Close();
 }
 
+void BubbleDialogDelegateView::UpdateAnchorWidgetRenderState(bool visible) {
+  if (!anchor_widget() || !anchor_widget()->GetTopLevelWidget())
+    return;
+
+  anchor_widget()->GetTopLevelWidget()->SetAlwaysRenderAsActive(visible);
+}
+
 }  // namespace views
diff --git a/ui/views/bubble/bubble_dialog_delegate.h b/ui/views/bubble/bubble_dialog_delegate.h
index c564cb8..216578e0 100644
--- a/ui/views/bubble/bubble_dialog_delegate.h
+++ b/ui/views/bubble/bubble_dialog_delegate.h
@@ -187,6 +187,9 @@
   // Called when a deactivation is detected.
   void OnDeactivate();
 
+  // When a bubble is visible, the anchor widget should always render as active.
+  void UpdateAnchorWidgetRenderState(bool visible);
+
   // A flag controlling bubble closure on deactivation.
   bool close_on_deactivate_;
 
diff --git a/ui/views/bubble/bubble_dialog_delegate_unittest.cc b/ui/views/bubble/bubble_dialog_delegate_unittest.cc
index 129caea..6b0e5d89 100644
--- a/ui/views/bubble/bubble_dialog_delegate_unittest.cc
+++ b/ui/views/bubble/bubble_dialog_delegate_unittest.cc
@@ -36,6 +36,8 @@
   }
   ~TestBubbleDialogDelegateView() override {}
 
+  using BubbleDialogDelegateView::SetAnchorView;
+
   // BubbleDialogDelegateView overrides:
   View* GetInitiallyFocusedView() override { return view_; }
   gfx::Size CalculatePreferredSize() const override {
@@ -476,4 +478,20 @@
             bubble_widget->GetWindowBoundsInScreen().height());
 }
 
+TEST_F(BubbleDialogDelegateTest, VisibleAnchorChanges) {
+  std::unique_ptr<Widget> anchor_widget(CreateTestWidget());
+  TestBubbleDialogDelegateView* bubble_delegate =
+      new TestBubbleDialogDelegateView(nullptr);
+  bubble_delegate->set_parent_window(anchor_widget->GetNativeView());
+
+  Widget* bubble_widget =
+      BubbleDialogDelegateView::CreateBubble(bubble_delegate);
+  bubble_widget->Show();
+  EXPECT_FALSE(anchor_widget->IsAlwaysRenderAsActive());
+  bubble_delegate->SetAnchorView(anchor_widget->GetContentsView());
+  EXPECT_TRUE(anchor_widget->IsAlwaysRenderAsActive());
+
+  bubble_widget->Hide();
+}
+
 }  // namespace views
diff --git a/ui/views/controls/focus_ring.cc b/ui/views/controls/focus_ring.cc
index 7a0acb5..c17bc86 100644
--- a/ui/views/controls/focus_ring.cc
+++ b/ui/views/controls/focus_ring.cc
@@ -32,6 +32,11 @@
   return ring;
 }
 
+// static
+bool FocusRing::IsPathUseable(const SkPath& path) {
+  return path.isRect(nullptr) || path.isOval(nullptr) || path.isRRect(nullptr);
+}
+
 void FocusRing::SetPath(const SkPath& path) {
   DCHECK(IsPathUseable(path));
   path_ = path;
@@ -115,10 +120,6 @@
     parent()->RemoveObserver(this);
 }
 
-bool FocusRing::IsPathUseable(const SkPath& path) {
-  return path.isRect(nullptr) || path.isOval(nullptr) || path.isRRect(nullptr);
-}
-
 SkRRect FocusRing::RingRectFromPathRect(const SkRect& rect) const {
   double thickness = PlatformStyle::kFocusHaloThickness / 2.f;
   double corner_radius = FocusableBorder::kCornerRadiusDp + thickness;
diff --git a/ui/views/controls/focus_ring.h b/ui/views/controls/focus_ring.h
index 300caeba..1b4d99ca2 100644
--- a/ui/views/controls/focus_ring.h
+++ b/ui/views/controls/focus_ring.h
@@ -48,6 +48,10 @@
   // |parent|.
   static std::unique_ptr<FocusRing> Install(View* parent);
 
+  // Returns whether this class can draw a focus ring from |path|. Not all paths
+  // are useable since not all paths can be easily outset.
+  static bool IsPathUseable(const SkPath& path);
+
   // Sets the path to draw this FocusRing around. This path is in the parent
   // view's coordinate system, *not* in the FocusRing's coordinate system.
   void SetPath(const SkPath& path);
@@ -75,10 +79,6 @@
  private:
   explicit FocusRing(View* parent);
 
-  // Returns whether this class can draw a focus ring from |path|. Not all paths
-  // are useable since not all paths can be easily outset.
-  bool IsPathUseable(const SkPath& path);
-
   // Translates the provided SkRect or SkRRect, which is in the parent's
   // coordinate system, into this view's coordinate system, then insets it
   // appropriately to produce the focus ring "halo" effect. If the supplied rect
diff --git a/ui/views/layout/layout_provider.cc b/ui/views/layout/layout_provider.cc
index 822088d..ed6bb221 100644
--- a/ui/views/layout/layout_provider.cc
+++ b/ui/views/layout/layout_provider.cc
@@ -160,8 +160,13 @@
 
 int LayoutProvider::GetShadowElevationMetric(
     EmphasisMetric emphasis_metric) const {
-  // Just return a value for now.
-  return 2;
+  // Return a value similar to the (deprecated) default shadow style for bubbles
+  // and dialogs.
+  return 3;
+}
+
+gfx::ShadowValues LayoutProvider::MakeShadowValues(int elevation) const {
+  return gfx::ShadowValue::MakeMdShadowValues(elevation);
 }
 
 }  // namespace views
diff --git a/ui/views/layout/layout_provider.h b/ui/views/layout/layout_provider.h
index c61276e..f139132 100644
--- a/ui/views/layout/layout_provider.h
+++ b/ui/views/layout/layout_provider.h
@@ -8,6 +8,7 @@
 #include "base/macros.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/geometry/size.h"
+#include "ui/gfx/shadow_value.h"
 #include "ui/views/style/typography_provider.h"
 #include "ui/views/views_export.h"
 
@@ -170,6 +171,10 @@
   // Returns the shadow elevation metric for the given emphasis.
   virtual int GetShadowElevationMetric(EmphasisMetric emphasis_metric) const;
 
+  // Creates shadows for the given elevation. Use GetShadowElevationMetric for
+  // the appropriate elevation.
+  virtual gfx::ShadowValues MakeShadowValues(int elevation) const;
+
  private:
   DefaultTypographyProvider typography_provider_;
 
diff --git a/ui/views/window/dialog_delegate.cc b/ui/views/window/dialog_delegate.cc
index 739e5e5..8ec2e6f 100644
--- a/ui/views/window/dialog_delegate.cc
+++ b/ui/views/window/dialog_delegate.cc
@@ -11,6 +11,7 @@
 #include "build/build_config.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/base/material_design/material_design_controller.h"
 #include "ui/gfx/color_palette.h"
 #include "ui/strings/grit/ui_strings.h"
 #include "ui/views/bubble/bubble_border.h"
@@ -205,13 +206,18 @@
 
 // static
 NonClientFrameView* DialogDelegate::CreateDialogFrameView(Widget* widget) {
+  LayoutProvider* provider = LayoutProvider::Get();
   BubbleFrameView* frame = new BubbleFrameView(
-      LayoutProvider::Get()->GetInsetsMetric(INSETS_DIALOG_TITLE),
-      gfx::Insets());
+      provider->GetInsetsMetric(INSETS_DIALOG_TITLE), gfx::Insets());
   const BubbleBorder::Shadow kShadow = BubbleBorder::DIALOG_SHADOW;
-  std::unique_ptr<BubbleBorder> border(
-      new BubbleBorder(BubbleBorder::FLOAT, kShadow, gfx::kPlaceholderColor));
+  std::unique_ptr<BubbleBorder> border = std::make_unique<BubbleBorder>(
+      BubbleBorder::FLOAT, kShadow, gfx::kPlaceholderColor);
   border->set_use_theme_background_color(true);
+  if (ui::MaterialDesignController::IsSecondaryUiMaterial()) {
+    border->SetCornerRadius(provider->GetCornerRadiusMetric(EMPHASIS_MEDIUM));
+    border->set_md_shadow_elevation(
+        provider->GetShadowElevationMetric(EMPHASIS_MEDIUM));
+  }
   frame->SetBubbleBorder(std::move(border));
   DialogDelegate* delegate = widget->widget_delegate()->AsDialogDelegate();
   if (delegate)
diff --git a/ui/webui/PLATFORM_OWNERS b/ui/webui/PLATFORM_OWNERS
index e685160d..29e5a4a 100644
--- a/ui/webui/PLATFORM_OWNERS
+++ b/ui/webui/PLATFORM_OWNERS
@@ -5,6 +5,7 @@
 dschuyler@chromium.org
 michaelpg@chromium.org
 pam@chromium.org
+scottchen@chromium.org
 stevenjb@chromium.org
 tommycli@chromium.org
 xiyuan@chromium.org